Backtrader 文档学习- 整体架构功能分析理解

Backtrader 文档学习- 架构功能分析理解

1. 概述

backtrader是一个用于开发和执行交易策略的Python框架。它提供了一套完整的工具和功能,使得用户可以方便地进行策略回测、实盘交易以及数据分析。

backtrader的入口为Cerebro类,该类将所有输入(Data Feeds)、策略 (Strategy)、观察者(Observers)、策略评估(Analyzers) 、经纪人(Broker)、订单(Order)、交易(Trade)和日志记录(Writers)整合起来,实现回测以及交易,并返回结果和图表。

backtrader优缺点,先说缺点:

(1)缺点:
  • 在于使用元类编程技术,使用了大量的类,调试的时候会比较难。
  • 对于python的要求高,需要掌握pandas,matplotlib ,有基础上手更容易。
  • 对环境存在兼容性, 使用pandas ,numpy ,matplotlib 的版本不能太高。
  • 对回测结果分析,需定制化做大量工作,比如需要将各类策略的测试结果,除了能打印日志外,需要保存到数据库中,便于后期对比分析,比如周期参数,5天、10天、20天同一个策略不同的周期,执行的绩效记录处理。
(2)优点:
  • 功能非常完善,支持多品种、多策略、多周期的回测和交易,编写策略简单,上手容易。
  • 交易的品种非常丰富(股票、期货、期权、外汇等)。
  • 详细的文档,提供的用例比较多,可以学习和根据需求改进 。
  • 在量化框架上,收费的居多,优矿,米筐,聚宽,不能本地化部署。免费开源的少。
  • 支持内部方法的重写,可以方便定制化策略。
  • 内置多个指标参数,很丰富,包括Talib 。
(3)使用目标:
  • 以研究策略为主,验证策略效果,选择标的股票。
  • 不需要太多的参数因子,基于通用指标,可以灵活组合。
  • 不做实盘高频交易。
  • 适合个人学习使用,探索基本量化方向,避免盲目交易,有数据支撑。

2.架构理解

backtrader的整体架构可以分为以下主要组件:
在这里插入图片描述

(1)数据源(Data Feeds):

backtrader支持多种数据源,包括CSV文件、Pandas DataFrame、实时数据源等。用户可以根据自己的需求选择适合的数据源,并通过数据加载器将数据加载到backtrader中进行处理。
backtrader回测的数据类型是由一系列的点组成的Lines,通常包括以下类别的数据:Open(开盘价)、High(最高价)、Low(最低价)、 Close(收盘价)、Volume(成交量)、OpenInterest(未平仓权益)。Data Feeds(数据加载)、Indicators(技术指标)和Strategies(策略)都会生成 Lines。价格数据中的所有的开盘价按时间组成一条 Line,因此一组含有以上6个类别的价格数据,共有6条 Lines。如果算上DateTime(时间,可以看作是一组数据的主键),一共有7条 Lines。Lines根据需要是可以扩展的。

核心概念:
Line的概念和Python中的list索引不同,Python List中索引-1,表示最后一个数据。
一条Line数据的下标为0表示访问当前时刻数据,-1表示访问下一个数据。在回测过程中,无需知道已经处理了多少条/分钟/天/月,”0”一直指向当前值,下标 -1 来访问下一个值,下标 -2 访问下下一个值。
在backtrader中,-1指的是当前处理数据(索引为0)的上一个数据。
Lines是随时变化的,run的时候,next不断改变Lines的长度,在数据载入,策略,指示器应用中,需要测量Lines的长度。
两个长度函数:len和buflen之间的区别:

  • len已处理了Lines
  • buflen为数据加载Lines的总数
(2)策略(Strategies):

backtrader提供了一个基类Strategy,用户可以继承该类并实现自己的交易策略。策略类中定义了一系列的回调函数,用户可以在回测函数中编写具体的交易逻辑。

该模块是回测系统最核心的部分,需要设计交易决策,得出买入/卖出信号。策略类代码包含重要的参数和用于执行策略的功能,要定义的参数或函数名如下:
(1)params:全局参数,可选,用于更改交易策略中变量/参数的值,可用于参数调优。
(2)log:日志,可选,用于记录策略的执行日志,可以打印出该函数提供的日期时间和txt变量。
(3)init:用于初始化交易策略,类中使用的全局变量定义,在其中声明的任何指标都会在next()方法调用之前进行计算。部分python操作符不支持,需要使用bt内置函数来处理,例如bt.And, bt.Or, bt.All, bt.Any等。
(4)notify_order,可选,用于跟踪交易订单(order)的状态。order具有提交,接受,买入/卖出执行和价格,已取消/拒绝等状态。
(5)notify_trade,可选,用于跟踪交易的状态,任何已平仓的交易都将报告毛利和净利润。
(6)next,必选,用于制定交易策略的函数,策略模块最核心的部分。
当满足所有data/indicators的最小周期后,执行将对所有剩余数据点调用。
如period是10 ,最小周期达到后,开始执行next方法。
(7)nextstart(),可选,方法执行一次,在最小周期的data/indicators满足时,默认执行next方法。
如period是10 ,在到达9执行最小周期达到后,执行一次nextstart,之后执行next方法。
(8)prenext(),可选,方法将在所有数据/指标的最小周期满足策略开始执行之前调用执行。
如period是10 ,在1到9执行最小周期之间,执行prenext。
(9)start(),可选,在回测即将开始之前调用,执行一次。
(10)stop(),可选,在回测即将结束之前调用,执行一次。
(11)notify_cashvalue(),可选,接收当前使用的资金余额
(12)notify_fund(),可选,策略的资金和价值发生变化时被调用。用于跟踪和记录策略的现金、价值、基金价值和持仓股票的数量。

(3)指标(Indicators):

backtrader内置了许多常用的技术指标,如移动平均线、MACD、RSI等。用户可以通过指标类来计算这些指标,并在策略中使用。
backtrader在指标中也集成了TA-Lib库

(4)订单(Order):

backtrader提供了一套完善的订单管理系统,用户可以通过创建订单对象来执行买入和卖出操作。订单管理系统还支持订单状态的跟踪和管理。
将策略中逻辑做出的决策转换为适合经纪人执行操作的消息,通常在交易策略中调用。
Order将strategy的逻辑做出的决策转换为适合broker执行操作的消息。通过以下方式完成:
创建 ,通过strategy 的方法:buy sell 和close 都可以返回Order的实例
取消,通过strategy的方法:cancel 也可以产生Order实例
通知,notify_order 的方法,也返回Order 实例
订单还作为反馈给用户的通信方法,通知代理中的运行情况

(5)经纪人(Broker):

通过设置回测的初始自己、佣金费率、税收费率、滑点率等交易条件,模拟不同的订单类型,限价订单,控制订单的有效期,并根据现金检查订单,计算每次交易的现金和权益,保存交易数据。
Trade :backtrader会自动记录每笔交易的详细信息,包括买入价格、卖出价格、手续费等。用户可以通过这些交易记录进行后续的分析和统计。
Position :仓位管理 。

(6)策略评估(Analyzers):

用于分析交易策略的利润和风险,分析交易系统的绩效。
内置各类评价指标,可以加入到评测对象中,对结果进行绩效分析,分析指标:年度回报率,卡尔玛比率,最大回撤,资金杠杆,仓位资金,组合投资值等,是对策略的效果评估参数。

(7)观察者(Observers):

用于记录交易过程,包括现金、权益、费用以及交易动作、买卖订单等数据。

(8)回测引擎(Backtesting Engine):

cerebro.run()
backtrader提供了一个强大的回测引擎,用户可以通过指定回测时间段和初始资金等参数来进行策略回测。回测引擎会模拟真实的交易环境,并根据用户定义的策略进行交易。

(9)绘图(Plot):

分析和可视化(Analysis and Visualization):
backtrader提供了多种分析工具和可视化功能,用户可以对回测结果进行详细的分析和可视化展示,可以增加图示内容,包括收益曲线、风险指标、交易位置等。
通过图形的方式显示交易测量回测的结果,绘图显示的结果包括三部分类型:现金及权益、交易损益、买卖动作。

绘图设置通过plotinfo来设置,其参数主要有:plot(是否绘图,默认为True),subplot(是否单独窗口绘图,默认为True,MA类指标该参数为False),plotname(指标图名,默认为指标类名),plotabove(绘图位置在数据上方,默认为False),plotlinelabels, plotymargin, plotyticks,plothlines, plotyhlines, plotforce。

3.代码示例

做一个最简单的SMA测试示例。
取100个股票,进行测试,测试的时间范围从2016年到2020年,一共5年的数据。
在策略中的init()初始化定义了Indicator ,在next()中买卖操作。
增加了Observer和Analyzer ,把大多数的监控绩效的指标都加入了。

#!/usr/bin/env python
import datetime
import pandas as pd
import numpy as np
import pymysql

import backtrader as bt
import backtrader.feeds as btfeeds
import backtrader.indicators as btind
from sqlalchemy import create_engine

engine_ts = create_engine(
    'mysql+pymysql://user:pwd@ip:3306/dbname?charset=utf8&use_unicode=1')

def get_stock_code():
    sql = "select t.ts_code from ts_stock_basic t where t.list_status='L' limit 100;"

    stock_data = pd.read_sql(sql, con=engine_ts)
    return stock_data

def get_code(stock_code):
    sql = "select t.trade_date as date,t.`open`,t.high,t.low,t.`close`,t.vol,t.amount from ts_stock_daily t where  \
          t.ts_code='" + stock_code + "' and t.trade_date > '2016-01-01' and t.trade_date < '2020-12-31' order by date ;"

    # print(sql)
    # stock_data = pd.read_sql(sql, con=engine_ts,index_col="date")
    # 因为BackTrader日期类型必须是datetime ,从数据库中读取的日期类型是date 。
    # 读数据,先不设置索引
    stock_data = pd.read_sql(sql, con=engine_ts)  # ,index_col="date"

    # 增加一列,select 字段名是date,赋值到trade_date,同时转datetime类型
    stock_data['trade_date'] = pd.to_datetime(stock_data['date'], format='%Y%m%d %H:%M:%S')

    # 删除原来的date列
    stock_data.drop(columns=['date'])

    # 新datetime列作为索引列
    stock_data.set_index(['trade_date'], inplace=True)

    # 索引列改名
    stock_data.index.name = 'date'

    # 按backtrader 格式要求,第7列openinterest ,也可以不用
    # stock_data['openinterest'] = 0

    data = stock_data.sort_index(ascending=True)

    #engine_ts.dispose()
    return data

class OrderObserver(bt.observer.Observer):
    lines = ('created', 'expired',)

    plotinfo = dict(plot=True, subplot=True, plotlinelabels=True)

    plotlines = dict(
        created=dict(marker='*', markersize=8.0, color='lime', fillstyle='full'),
        expired=dict(marker='s', markersize=8.0, color='red', fillstyle='full')
    )

    def next(self):
        for order in self._owner._orderspending:
            if order.data is not self.data:
                continue

            if not order.isbuy():
                continue

            # Only interested in "buy" orders, because the sell orders
            # in the strategy are Market orders and will be immediately
            # executed

            if order.status in [bt.Order.Accepted, bt.Order.Submitted]:
                self.lines.created[0] = order.created.price

            elif order.status in [bt.Order.Expired]:
                self.lines.expired[0] = order.created.price

# CrossOver Strategy
class St_CrossOver(bt.Strategy):
    params = (
        ('smaperiod', 15),
        ('limitperc', 1.0),
        ('valid', 7),
        ('print', False),
    )

    def log(self, txt, dt=None):
        ''' Logging function fot this strategy'''
        dt = dt or self.data.datetime[0]
        if isinstance(dt, float):
            dt = bt.num2date(dt).date() # no Hour mintue second
        if self.params.print :
            print('%s, %s' % (dt.isoformat(), txt))

    def notify_order(self, order):
        if order.status in [order.Submitted, order.Accepted]:
            # Buy/Sell order submitted/accepted to/by broker - Nothing to do
            self.log('ORDER ACCEPTED/SUBMITTED', dt=order.created.dt)
            self.order = order
            return

        if order.status in [order.Expired]:
            self.log('BUY EXPIRED')

        elif order.status in [order.Completed]:
            if order.isbuy():
                self.log(
                    'BUY EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %
                    (order.executed.price,
                     order.executed.value,
                     order.executed.comm))

            else:  # Sell
                self.log('SELL EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %
                         (order.executed.price,
                          order.executed.value,
                          order.executed.comm))

        # Sentinel to None: new orders allowed
        self.order = None

    def __init__(self):
        # SimpleMovingAverage on main data
        # Equivalent to -> sma = btind.SMA(self.data, period=self.p.smaperiod)
        sma = btind.SMA(period=self.p.smaperiod)

        # CrossOver (1: up, -1: down) close / sma
        self.buysell = btind.CrossOver(self.data.close, sma, plot=True)

        # Sentinel to None: new ordersa allowed
        self.order = None

    def next(self):
        if self.order:
            # pending order ... do nothing
            return

        # Check if we are in the market
        if self.position:
            if self.buysell < 0:
                self.log('SELL CREATE, %.2f' % self.data.close[0])
                self.sell()

        elif self.buysell > 0:
            plimit = self.data.close[0] * (1.0 - self.p.limitperc / 100.0)
            valid = self.data.datetime.date(0) + \
                datetime.timedelta(days=self.p.valid)
            self.log('BUY CREATE, %.2f' % plimit)
            self.buy(exectype=bt.Order.Limit, price=plimit, valid=valid)


def runstrat(stock_code):
    cerebro = bt.Cerebro()
    # 初始资金 100,000,000
    cerebro.broker.setcash(100000.0)
    # 佣金,双边各 0.0003
    cerebro.broker.setcommission(commission=0.0003)
    # 滑点:双边各 0.0001
    cerebro.broker.set_slippage_perc(perc=0.0001)

    # 获取数据
    stock_df = get_code(stock_code)

    start_date = datetime.datetime(2016, 1, 1)  # 回测开始时间
    end_date = datetime.datetime(2020, 12, 31)  # 回测结束时间
    data = bt.feeds.PandasData(dataname=stock_df, fromdate=start_date, todate=end_date)  # 加载数据

    cerebro.adddata(data)

    cerebro.addobserver(OrderObserver)
    cerebro.addstrategy(St_CrossOver)

    tframes = dict(
        days=bt.TimeFrame.Days,
        weeks=bt.TimeFrame.Weeks,
        months=bt.TimeFrame.Months,
        years=bt.TimeFrame.Years)

    cerebro.addanalyzer(bt.analyzers.SharpeRatio, _name='mysharpe')
    cerebro.addanalyzer(bt.analyzers.DrawDown,_name = 'mydrown')
    cerebro.addanalyzer(bt.analyzers.AnnualReturn,_name = 'myannualreturn')
    cerebro.addanalyzer(bt.analyzers.SQN,_name = 'mysqn')
    cerebro.addanalyzer(bt.analyzers.TradeAnalyzer,_name = 'mytradeanalyzer')
    cerebro.addanalyzer(bt.analyzers.PositionsValue,_name = 'mypositionvalue')
    cerebro.addanalyzer(bt.analyzers.Returns,_name = 'myreturns')
    cerebro.addanalyzer(bt.analyzers.LogReturnsRolling,timeframe=tframes['years'],_name = 'mylogreturnsrolling')
    cerebro.addanalyzer(bt.analyzers.Transactions, _name='mytransactions')

    thestrats = cerebro.run()
    thestrat = thestrats[0]

    print('Sharpe Ratio:', thestrat.analyzers.mysharpe.get_analysis())
    print('DrawDown:', thestrat.analyzers.mydrown.get_analysis())
    print('AnnualReturn:', thestrat.analyzers.myannualreturn.get_analysis())
    print('SQN:', thestrat.analyzers.mysqn.get_analysis())
    print('TradeAnalyzer:', thestrat.analyzers.mytradeanalyzer.get_analysis())
    print('PositionsValue:', thestrat.analyzers.mypositionvalue.get_analysis())
    print('Returns:', thestrat.analyzers.myreturns.get_analysis())
    print('LogReturnsRolling:', thestrat.analyzers.mylogreturnsrolling.get_analysis())
    print('Transactions:', thestrat.analyzers.mytransactions.get_analysis())

    #cerebro.plot()


if __name__ == '__main__':
    
    t1 = datetime.datetime.now()
    stock_data = get_stock_code()
    
    for i in range(len(stock_data)):
        print(stock_data.loc[i, 'ts_code'])
        runstrat(stock_data.loc[i, 'ts_code'])
    
    t2 = datetime.datetime.now()
    print('Spend time:', t2 - t1)
    engine_ts.dispose()
    print('ok!')

详细结果内容多,就不展示了。
结果是用了32.59秒完成计算,100个股票,5年的数据,速度还可以。

Spend time: 0:00:32.591037
ok!

4.小结

backtrader的整体架构是基于事件驱动的,用户通过编写策略类来定义交易逻辑,并通过回测引擎进行策略回测和分析。
通过上面的比较全面的功能测试,基本上覆盖前期学习的backtrader主要功能,完成了一个自定义的策略回测验证。

在不考虑数据采集的情况下,假设数据全部在数据库中,需要后期完成的工作很多:

  • 组合策略的定义,肯定不能是简单的SMA,可以结合MACD /RSI/KDJ/ADX 等等指标。
  • 时间周期的定义,触发不同指标的效果。
  • 策略评估结果保存入库后,进行横向比较。
  • 参数调优,最简单的如前篇文章,测试胜率和盈亏比组合,组合参数的递增或递减,绩效结果对比。
  • 黑天鹅事件,比如千股跌停的情况,异常极端情况的风险控制,2008年6月次贷危机世界经济危机,2015年6月到8月去杠杆,2016年1月熔断机制上线,还有今年2月5日 。
  • 加仓机制实现
  • 止损机制实现
  • 止盈机制实现
  • 仓位管理实现
  • 对于不同走势定义不同的策略,如短期打板策略,需要看成交量、换手率,严格的止盈止损。
  • 策略模块化,可以是内置类
  • 日志,绩效结果模块化,到数据库
  • 绩效参考基准,比如沪深500

对于自定义的回测功能或控制内容,欢迎大家可以留言探讨。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/401525.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于Jenkins实现的CI/CD方案

基于Jenkins实现的CI/CD方案 前言 最近基于Jenkins的基座&#xff0c;搭建了一套适用于我们项目小组的持续集成环境。现在把流程整理分享出来&#xff0c;希望可以给大家提供一些帮助和思路。 使用到的组件和版本 组件名称组件版本作用Harbor2.7.3镜像仓库Jenkins2.319.2持…

C++ Primer 笔记(总结,摘要,概括)——第5章 语句

目录 5.1 简单语句 5.2 语句作用域 5.3 条件语句 5.3.1 if语句 5.3.2 switch语句 5.4 迭代语句 5.4.1 while语句 5.4.2 传统的for语句 5.4.3 范围for语句 5.4.4 do while语句 5.5 跳转语句 5.5.1 break语句 5.5.2 continue语句 5.5.3 goto语句 5.6 try语句块和异常处理 5…

http和https的区别(简述)

HTTP&#xff08;HyperText Transfer Protocol&#xff09;和HTTPS&#xff08;HTTP Secure&#xff09;都是用于在客户端和服务器之间传输数据的协议&#xff0c;但它们在安全性方面有重要的区别。 1.HTTP: 概述&#xff1a; HTTP是一种用于传输超文本的协议&#xff08;超文…

前端基础自学整理|DOM树

DOM&#xff0c;文档对象模型&#xff08;Document Object Model&#xff09;&#xff0c;简单的说&#xff0c;DOM是一种理念&#xff0c;一种思想&#xff0c;一个与系统平台和编程语言无关的接口&#xff0c;一种方法, 使 Web开发人员可以访问HTML元素&#xff01;不是具体方…

D6208——双向马达驱动电路芯片,噪声低 内设马达驱动功率晶体管,工作电源电压范围宽用 TTL 逻辑信号直接控制

D6208 是一块单片双向马达驱动电路&#xff0c;它使用TTL电平的逻辑信号就能控制卡式录音机和其它电子设备中的双向马达。该电路由一个逻辑部分和一个功率输出部分组成。逻辑部分控制马达正、反转向及制动&#xff0c;功率输出部分根据逻辑控制能提供100mA&#xff08;典型值&a…

【python】 脚本检查文本里是否包含特殊字符

【python】 脚本检查文本里是否包含特殊字符 完整代码&#xff1a; # 代码片段功能: 检查文本里是否包含特殊字符 # 将utf-8格式文本&#xff0c;先转为16进制格式 # 检查完成&#xff0c;再将16进制格式转为utf-8格式。 import re# 包含特殊字符的字符串 sample "I arg…

当别人在用AI一分钟画几十张图,你还在埋头苦苦设计?AI时代一定要学会和AI共存!

AI绘画即指人工智能绘画&#xff0c;是一种计算机生成绘画的方式。是AIGC应用领域内的一大分支。 AI绘画主要分为两个部分&#xff0c;一个是对图像的分析与判断&#xff0c;即“学习”&#xff0c;一个是对图像的处理和还原&#xff0c;即“输出”。 人工智能通过对数以万计…

数据分析(二)自动生成分析报告

1. 报告生成思路概述 怎么快速一份简单的数据分析报告&#xff0c;注意这个报告的特点&#xff1a; --网页版&#xff0c;可以支持在线观看或者分享HTML文件 --标题&#xff0c;动图&#xff0c;原始数据应有尽有 --支持交互&#xff0c;比如plotly交互画面&#xff0c;数据…

Windows安装PHP及在VScode中配置插件,使用PHP输出HelloWorld

安装PHP PHP官网下载地址(8.3版本)&#xff1a;PHP For Windows&#xff1a;二进制文件和源代码发布 点击下载.zip格式压缩包&#xff1a; 历史版本在Old archives中下载。推荐在Documentation download中下载官方文档&#xff0c;方便学习。 下载完成后在一个顺眼的地方解压压…

net反射

1.1 查找dll文件 Load需要把dll放到程序当前路径加载&#xff0c;也可以读取字符串形式。LoadFrom需要写全路径&#xff0c;如果test1.dll引用了test2.dll&#xff0c;同时也会加载test2.dll进来。LoadFile不会加载test2.dll。 Assembly assembly1 Assembly.Load("DllTe…

互联网加竞赛 大数据疫情分析及可视化系统

文章目录 0 前言2 开发简介3 数据集4 实现技术4.1 系统架构4.2 开发环境4.3 疫情地图4.3.1 填充图(Choropleth maps)4.3.2 气泡图 4.4 全国疫情实时追踪4.6 其他页面 5 关键代码最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 大数据疫…

Open AI — Sora 如何发挥其魔力 — 近距离观察该技术

OpenAI 的大模型 Sora 可以制作一整分钟的高质量视频。他们的工作成果表明,使视频生成模型更大是为现实世界创建多功能模拟器的好方法。Sora 是一种灵活的可视化数据模型。它可以创建不同长度、形状和大小的视频和图片,甚至可以创建长达一分钟的高清视频。我阅读了 OpenAI 的…

[C#]winform使用引导APSF和梯度自适应卷积增强夜间雾图像的可见性算法实现夜间雾霾图像的可见度增强

【算法介绍】 提升夜间雾霾图像可见度的技术研究&#xff1a;引导APSF与梯度自适应卷积的应用 随着城市化的快速发展&#xff0c;雾霾现象日益严重&#xff0c;尤其是在夜间&#xff0c;雾霾对图像的可见度造成了极大的影响。因此&#xff0c;提升夜间雾霾图像的可见度成为了…

ElasticSearch聚合操作

目录 ElasticSearch聚合操作 基本语法 聚合的分类 后续示例数据 Metric Aggregation Bucket Aggregation ES聚合分析不精准原因分析 提高聚合精确度 ElasticSearch聚合操作 Elasticsearch除搜索以外&#xff0c;提供了针对ES 数据进行统计分析的功能。聚合(aggregation…

一文彻底搞懂JVM垃圾回收算法

文章目录 1. 标记-清除算法&#xff08;Mark and Sweep&#xff09;2. 复制算法&#xff08;Copying&#xff09;3. 标记-整理算法&#xff08;Mark and Compact&#xff09;4. 分代算法&#xff08;Generational&#xff09;4.1 执行流程 1. 标记-清除算法&#xff08;Mark an…

《雾锁王国》超简单0成本自建个人专属16人联机服务器教程

阿里云雾锁王国服务器搭建教程是基于计算巢服务&#xff0c;3分钟即可成功创建Enshrouded游戏服务器&#xff0c;阿里云8核32G雾锁王国专用游戏服务器90元1个月、271元3个月&#xff0c;阿里云服务器网aliyunfuwuqi.com亲自整理雾锁王国服务器详细搭建教程&#xff1a; 一、前…

「实战应用」如何使用图表控件LightningChart创建数据采集系统?(一)

LightningChart.NET完全由GPU加速&#xff0c;并且性能经过优化&#xff0c;可用于实时显示海量数据-超过10亿个数据点。 LightningChart包括广泛的2D&#xff0c;高级3D&#xff0c;Polar&#xff0c;Smith&#xff0c;3D饼/甜甜圈&#xff0c;地理地图和GIS图表以及适用于科学…

了解电力测试中负载箱的重要性?

电力测试是电力系统运行和维护的重要环节&#xff0c;其中负载箱作为一种重要的测试设备&#xff0c;其重要性不言而喻。负载箱主要用于模拟实际的电力负载&#xff0c;对电力设备进行性能测试和故障诊断&#xff0c;以确保电力系统的稳定运行。 负载箱可以模拟实际的电力负载。…

电商网站的大规模网页抓取 (终极指南)

电商网站的大规模网页抓取|电商数据采集API接口 与小型项目相比&#xff0c;大规模的网页抓取带来了一系列截然不同的挑战&#xff0c;例如基础结构搭建、管理资源成本、绕过爬虫检测措施等。 本文将指导您完成大规模数据收集&#xff0c;并以电商领域为重点。 Oxylabs 网页…

安卓系统和iOS系统的手机备忘录同步数据方法

在这个智能手机时代&#xff0c;安卓与iOS系统犹如两位王者&#xff0c;各自拥有庞大的用户群体。有人钟情于安卓的开放与多样&#xff0c;有人偏爱iOS的流畅与稳定。甚至&#xff0c;有些人为了满足不同需求&#xff0c;同时使用着两个系统的手机。我就是其中的一员。 工作中…