一、开发环境构建
-
MiniConda下载:
wget -c https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh
-
赋予可执行权限:
chmod 755 Miniconda3-latest-Linux-x86_64.sh
-
MiniConda安装:
sh Miniconda3-latest-Linux-x86_64.sh
-
.bashrc文件导出Conda路径
export PATH=$PATH:/usr/local/anaconda/bin
-
增加镜像:
conda config --add channels bioconda conda config --add channels conda-forge conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/ conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/ conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/ conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/bioconda/
-
查看已添加Channels
conda config --get channels
-
查看Conda环境
conda env list
-
创建Conda环境
conda create -n python3 python=3
-
切换至Conda环境
conda activate python3
-
退出Conda环境
conda deactivate
-
删除Conda环境
conda remove -n python3 --all
-
查看当前Conda环境已经安装的包
conda list
-
查看指定Conda环境的已经安装包
conda list -n python3
-
安装包到指定Conda环境
conda install -n python3 numpy
-
安装BackTrader
pip install BackTrader plotting
-
安装tushare
pip install tushare
-
在当前Conda环境安装numpy
conda install numpy
二、BackTrader简介
1、BackTrader简介
-
BackTrader是2015年开源的Python量化回测框架(支持实盘交易),功能丰富,操作方便灵活,特性如下:
- 品种多:股票、期货、期权、外汇、数字货币;
- 周期全:Ticks级、秒级、分钟级、日度、周度、月度、年度;
- 速度快:pandas矢量运算、多策略并行运算;
- 组件多:内置Ta-lib技术指标库、PyFlio分析模块、plot绘图模块、参数自动优化等;
- 扩展灵活:可以随意搭配组件扩展开发,支持pyflio、empyrica分析模块库、alphalens多因子分析模块库等;可以集成TensorFlow、PyTorch和Keras等机器学习、神经网络分析模块。
- 社区活跃、帮助文档齐全。
-
官网:https://www.BackTrader.com/
-
BackTrader安装:
pip install BackTrader [plotting] pip index versions BackTrader # 版本查看
-
安装并导入BackTrader后,通过
__path__
属性可以拿到源码的路径。import BackTrader as bt print(bt.__path__)
2、BackTrader模块
-
Cerebro:Cerebro是BackTrader的核心模块,处理收集所有数据、运行策略,执行回溯测试,执行交易,策略绩效分析和可视化等。
-
DataFeeds:加载不同行情数据表格集合到回测框架。
-
Strategy:根据行情数据实现交易决策,决定具体买卖等动作。
-
Indicators:指标,如股票相对强弱指标(RSI)、随机指标(KD)、趋向指标(DMI)、平滑异同平均线(MACD)、能量潮(OBV)等。
-
Orders:订单管理模块。交易订单执行单元触发了相应策略,发出订单信号,报单操作交给Orders处理。BackTrader提供了一套完善的订单管理系统,用户可以通过创建订单对象来执行买入和卖出操作。订单管理系统还支持订单状态的跟踪和管理。Orders将Strategy的触发的交易信号转换为适合Broker执行操作的消息。
-
Broker:经纪人,交易所角色,收取手续费,管理账户资金。通过设置回测的初始资金、佣金费率、税收费率、滑点率等交易条件,模拟不同的订单类型,限价订单,控制订单的有效期,并根据现金检查订单,计算每次交易的现金和权益,保存交易数据。
- Trade :成交记录,BackTrader会自动记录每笔交易的详细信息,包括买入价格、卖出价格、手续费等。用户可以通过交易记录进行后续的分析和统计。
- Position :持仓记录
-
Analyzers:策略评价指标分析模块,用于分析交易策略的利润和风险,分析交易系统的绩效。BackTrader内置各类评价指标,可以加入到评测对象中,对回测结果进行绩效分析,如年度回报率、卡尔玛比率、夏普率、最大回撤、资金杠杆、仓位资金、组合投资值等。
-
Observers:观测器,用于记录交易过程,包括现金、权益、费用以及交易动作、买卖订单等数据。
-
Sizers:仓位管理
-
Writers:日志记录
-
回测引擎:BackTrader提供了一个强大的回测引擎,用户可以通过指定回测时间段和初始资金等参数来进行策略回测。执行回测:
Cerebro.run(),
,回测引擎会模拟真实的交易环境,并根据用户定义的策略进行交易。 -
可视化分析模块:BackTrader提供了多种分析工具和可视化功能,用户可以对回测结果进行详细的分析和可视化展示,可以增加图示内容,包括收益曲线、风险指标、交易位置等。通过图形的方式显示交易测量回测的结果,绘图显示的结果包括三部分类型:现金及权益、交易损益、买卖动作。绘图设置通过plotinfo来设置,其参数主要有:plot(是否绘图,默认为True),subplot(是否单独窗口绘图,默认为True,MA类指标该参数为False),plotname(指标图名,默认为指标类名),plotabove(绘图位置在数据上方,默认为False),plotlinelabels, plotymargin, plotyticks,plothlines, plotyhlines, plotforce。
三、Data Feeds
1、行情数据
- 使用tushare获取历史行情数据,返回pandas的DataFrame格式。
import tushare as ts
import pandas as pd
if __name__ == '__main__':
# 设置Token
ts.set_token('b31e0ac207a5a45e0f7503aff25bf6bd929b88fe1d017a034ee0d530')
# 初始化接口
ts_api = ts.pro_api()
data = ts_api.daily(ts_code='300750.SZ',
start_date='20180611', end_date='20210713')
data.to_csv("300750.csv", header=None, mode='a')
2、Data Feeds
-
Backtrader中Feeds模块提供了灵活的数据加载和处理功能,支持多种数据源和格式,可以添加一个或者多个股票数据。
-
BackTrader的feeds.DataBase数据集需要初始化7列数据,分别为:datetime、open、high、low、close、volume、openinterest,其中datetime是索引列。载入自定义行情数据时需要设定每列的含义,比如开盘价在第4列,则open=3。
-
Feed是一个数据源对象,负责向策略提供时间序列数据,如股票的开盘价、收盘价、成交量等。每个Feed对象代表一个数据源,可以是本地的CSV文件,也可以是实时的股票数据。BackTrader支持多种数据源,包括CSV文件、Pandas DataFrame、实时数据源等。用户可以根据自己的需求选择适合的数据源,并通过数据加载器将数据加载到BackTrader中进行处理。Backtrader内置了多种常用的Feed类:
- GenericCSVData:用于加载通用的CSV格式数据。
- YahooFinanceData:用于从Yahoo Finance下载数据。
- PandasData:用于从Pandas DataFrame加载数据。
- IBData:用于从Interactive Brokers API获取实时数据。
-
PandasData数据加载方式如下:
stock_hfq_df = pd.read_csv("../data/000300.SH.csv",index_col='date',parse_dates=True) start_date = datetime(2021, 9, 1) # 回测开始时间 end_date = datetime(2021, 9, 30) # 回测结束时间 data = bt.feeds.PandasData(dataname=stock_hfq_df, fromdate=start_date, todate=end_date) # 加载数据
-
Pandas方便进行数据的预处理,是量化数据常用格式数据,为了后续直接获取数据方便处理,统一使用PandasData进行说明。
四、BackTrader回测流程
-
BackTrader以Cerebro为统一调度中心,数据、策略、回测条件等信息都会导入Cerebro中,并由Cerebro启动和完成回测,最后返回回测结果。
-
BackTrader各模块各司其职,对模块进行灵活的配置可满足绝大部分的回测需求。回测流程如下:
-
构建策略
- 确定策略潜在的可调参数;
- 计算策略中用于生成交易信号的指标;
- 按需打印交易信息;
- 编写买入、卖出的交易逻辑。
-
实例化策略引擎Cerebro,由Cerebro来驱动回测
- 由DataFeeds加载数据,再将加载的数据添加给Cerebro;
- 将策略添加给Cerebro;
- 按需添加策略分析指标或观测器;
- 运行Cerebro.run()执行回测;
- 运行Cerebro.plot()对回测结果可视化展示
-
-
BackTrader回测流程如下:
import BackTrader as bt # 导入 BackTrader import BackTrader.indicators as btind # 导入策略分析模块 import BackTrader.feeds as btfeeds # 导入数据模块 # 创建策略 class TestStrategy(bt.Strategy): # 可选,设置回测的可变参数:如移动均线的周期 params = ( (...,...), # 最后一个“,”最好别删! ) def log(self, txt, dt=None): '''可选,构建策略打印日志的函数:可用于打印订单记录或交易记录等''' dt = dt or self.datas[0].datetime.date(0) print('%s, %s' % (dt.isoformat(), txt)) def __init__(self): '''必选,初始化属性、计算指标等''' pass def notify_order(self, order): '''可选,打印订单信息''' pass def notify_trade(self, trade): '''可选,打印交易信息''' pass def next(self): '''必选,编写交易策略逻辑''' sma = btind.SimpleMovingAverage(...) # 计算均线 pass # 实例化 Cerebro Cerebro = bt.Cerebro() # 通过 feeds 读取数据 data = btfeeds.BackTraderCSVData(...) # 将数据传递给 “大脑” Cerebro.adddata(data) # 通过经纪商设置初始资金 Cerebro.broker.setcash(...) # 设置单笔交易的数量 Cerebro.addsizer(...) # 初始资金 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) # 添加策略 Cerebro.addstrategy(TestStrategy) # 添加策略分析指标 Cerebro.addanalyzer(...) # 添加观测器 Cerebro.addobserver(...) # 启动回测 Cerebro.run() # 可视化回测结果 Cerebro.plot()
1、Cerebro实例
Cerebro = bt.Cerebro(**kwargs)
Cerebro = bt.Cerebro(runonce=True, preload=True)
- 决定预装载和操作模式。runonce模式下,必须使用preload,如果没有启用preload,批量处理就不会进行。启用preload时,却不必使用runonce。
2、添加数据
data = bt.BackTraderCSVData(dataname=**'path.days'**, timeframe=bt.TimeFrame.Days)
Cerebro.adddata(data)
- BackTrader的数据集要求是datetime ,必须做好转换才能载入数据。
3、重采样数据
data = bt.BackTraderCSVData(dataname=**'mypath.min'**, timeframe=bt.TimeFrame.Minutes)
Cerebro.resampledata(data, timeframe=bt.TimeFrame.Days)
- 重采样数据是将粒度小的数据重新抽样为粒度大的数据,例如日线转为周线。
4、数据回放
data = bt.BackTraderCSVData(dataname=**'mypath.min'**, timeframe=bt.TimeFrame.Minutes)
Cerebro.replaydata(data, timeframe=bt.TimeFrame.Days)
- 数据回放是将粒度小的数据替代大粒度数据,比如用分钟线重演替代日线。
5、添加交易策略
-
BackTrader提供了一个基类Strategy,用户可以继承Strategy类并实现自己的交易策略。策略类中定义了一系列的回调函数,用户可以在回测函数中编写具体的交易逻辑。策略类代码包含重要的参数和用于执行策略的功能,要定义的参数或函数名如下:
- params:全局参数,可选,用于更改交易策略中变量/参数的值,可用于参数调优。
- init:用于初始化交易策略,类中使用的全局变量定义,在其中声明的任何指标都会在next()方法调用之前进行计算。部分python操作符不支持,需要使用bt内置函数来处理,例如bt.And, bt.Or, bt.All, bt.Any等。
- notify_order:可选,用于跟踪交易订单的状态。order具有提交,接受,买入/卖出执行和价格,已取消/拒绝等状态。
- notify_trade:可选,用于跟踪交易的状态,任何已平仓的交易都将报告毛利和净利润。
- next:必选,用于制定交易策略的函数,策略模块最核心的部分。当满足所有data/indicators的最小周期后,执行将对所有剩余数据点调用。如period是10 ,最小周期达到后,开始执行next方法。
- nextstart():可选,方法执行一次,在最小周期的data/indicators满足时,默认执行next方法。如period是10 ,在到达9执行最小周期达到后,执行一次nextstart,之后执行next方法。
- prenext():可选,方法将在所有数据/指标的最小周期满足策略开始执行之前调用执行。如period是10 ,在1到9执行最小周期之间,执行prenext。
- start():可选,在回测即将开始之前调用,执行一次。
- stop():可选,在回测即将结束之前调用,执行一次。
- notify_cashvalue():可选,接收当前使用的资金余额
- notify_fund():可选,策略的资金和价值发生变化时被调用。用于跟踪和记录策略的现金、价值、基金价值和持仓股票的数量。
-
增加交易策略
Cerebro.addstrategy(TestStrategy, myparam1=value1, myparam2=value2)
6、Indicators指标
- BackTrader内置了许多常用的技术指标并集成了TA-Lib库,如移动平均线SMA、MACD、RSI等。用户可以通过指标类来计算指标,并在策略中使用。
- 使用SMA指标(n日的收盘价平均线)和收盘价来确定股票买卖的触发条件。通过判定收盘价和n日期内的均线,是否有交叉来判定买卖,比如收盘价上穿过均线,就购买,当收盘价下穿均线就是卖出。
class TestStrategy(bt.Strategy):
# 自定义均线的实践间隔,默认是5天
params = (
('maperiod', 5),
)
def log(self, txt, dt=None):
''' Logging function for this strategy'''
dt = dt or self.datas[0].datetime.date(0)
print('%s, %s' % (dt.isoformat(), txt))
def __init__(self):
# Keep a reference to the "close" line in the data[0] dataseries
self.dataclose = self.datas[0].close
# To keep track of pending orders
self.order = None
# buy price
self.buyprice = None
# buy commission
self.buycomm = None
# 增加均线,简单移动平均线(SMA)又称“算术移动平均线”,是指对特定期间的收盘价进行简单平均化
self.sma = bt.indicators.SimpleMovingAverage(
self.datas[0], period=self.params.maperiod)
#订单状态改变回调方法 be notified through notify_order(order) of any status change in an order
def notify_order(self, order):
if order.status in [order.Submitted, order.Accepted]:
# Buy/Sell order submitted/accepted to/by broker - Nothing to do
return
# Check if an order has been completed
# Attention: broker could reject order if not enough cash
if 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))
self.buyprice = order.executed.price
self.buycomm = order.executed.comm
elif order.issell():
self.log('SELL EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %
(order.executed.price,
order.executed.value,
order.executed.comm))
self.bar_executed = len(self)
elif order.status in [order.Canceled, order.Margin, order.Rejected]:
self.log('Order Canceled/Margin/Rejected')
# Write down: no pending order
self.order = None
#交易状态改变回调方法 be notified through notify_trade(trade) of any opening/updating/closing trade
def notify_trade(self, trade):
if not trade.isclosed:
return
# 每笔交易收益 毛利和净利
self.log('OPERATION PROFIT, GROSS %.2f, NET %.2f' %
(trade.pnl, trade.pnlcomm))
def next(self):
# Simply log the closing price of the series from the reference
self.log('Close, %.2f' % self.dataclose[0])
# Check if an order is pending ... if yes, we cannot send a 2nd one
if self.order:
return
# Check if we are in the market(当前账户持股情况,size,price等等)
if not self.position:
# Not yet ... we MIGHT BUY if ...
if self.dataclose[0] >= self.sma[0]:
#当收盘价,大于等于均线的价格
# BUY, BUY, BUY!!! (with all possible default parameters)
self.log('BUY CREATE, %.2f' % self.dataclose[0])
# Keep track of the created order to avoid a 2nd order
self.order = self.buy()
else:
# Already in the market ... we might sell
if self.dataclose[0] < self.sma[0]:
#当收盘价,小于均线价格
# SELL, SELL, SELL!!! (with all possible default parameters)
self.log('SELL CREATE, %.2f' % self.dataclose[0])
# Keep track of the created order to avoid a 2nd order
self.order = self.sell()
- 修改自定义策略中的next方法,判断5日均线和每日收盘价的大小,来判断是否买卖。目前默认参数设置为3,为三日。通过传入参数5,实现5日。
7、参数优化
Cerebro.optstrategy(MyStrategy, myparam1=range(10, 20))
- 设置参数优化方案
8、交易管理
# 设置投资金额1000000.0
Cerebro.broker.setcash(1000000.0)
# 每笔交易使用固定交易量
Cerebro.addsizer(bt.sizers.FixedSize, stake=100)
# 设置佣金为0.001,除以100去掉%号
Cerebro.broker.setcommission(commission=0.001)
- 设置交易条件:现金、头寸管理、佣金、滑点等
9、观测器
-
BackTrader提供三个标准观测器:
- bt.observers.Broker:现金和价值
- bt.observers.Trades:交易
- bt.observers.BuySell:买卖下单
-
观测器存在于BackTrader.observers子模块中。 Cerebro有参数支持自动添加(或不添加)观测器到策略中:
Cerebro = bt.Cerebro() #默认参数: stdstats=True Cerebro.addobserver(bt.observers.Broker) Cerebro.addobserver(bt.observers.Trades) Cerebro.addobserver(bt.observers.BuySell)
- stdstats默认值True
- 重复增加时会显示多次。
-
BackTrader.observers.BuySell:记录了回测过程中的买入和卖出信号;可视化时,会在价格曲线上标注买卖点;
-
BackTrader.observers.Trades:记录了回测过程中每次交易的盈亏(从买入建仓到卖出清仓算一次交易);可视化时,会绘制盈亏点;
-
BackTrader.observers.TimeReturn:记录了回测过程中的收益序列;可视化时,会绘制TimeReturn收益曲线;
-
BackTrader.observers.DrawDown:记录了回测过程的回撤序列;可视化时,绘制回撤曲线;
-
BackTrader.observers.Benchmark:记录了业绩基准的收益序列,业绩基准的数据必须事先通过 adddata、resampledata、replaydata 等数据添加函数添加进大脑中 Cerebro;可视化时,会同时绘制策略本身的收益序列(即:BackTrader.observers.TimeReturn 绘制的收益曲线)和业绩基准的收益曲线。
10、评价指标
-
BackTrader通过Analyzer对投资策略的性能提供评价,用于分析交易策略的利润和风险,分析交易系统的绩效,提供的策略评价指标如下:
- AnnualReturn:年化收益率
- Calmar:Calmar比率,年化收益率与最大回撤的比值
- DrawDown:计算回撤
- TimeDrawDown:期间回撤
- GrossLeverage:杠杆率
- PositionsValue:头寸价值
- PyFolio:与pyfolio库交互
- LogReturnsRolling:滚动对数收益率
- PeriodStats:期间统计指标
- Returns:收益率
- SharpeRatio:夏普比率
- SharpeRatio_A:年化夏普比率
- SQN:策略绩效衡量指标
- TimeReturn:期间收益
- TradeAnalyzer:交易分析指标
- Transactions:交易成本
- VWR:波动性加权收益率
-
添加分析评价指标代码如下:
# 添加分析评价指标 Cerebro.addanalyzer(ancls, *args, **kwargs) tframes = dict( days=bt.TimeFrame.Days, weeks=bt.TimeFrame.Weeks, months=bt.TimeFrame.Months, years=bt.TimeFrame.Years) Cerebro.addanalyzer(bt.analyzers.SharpeRatio, _name='SharpeRatio') Cerebro.addanalyzer(bt.analyzers.DrawDown,_name = 'DrawDown') Cerebro.addanalyzer(bt.analyzers.AnnualReturn,_name = 'AnnualReturn') Cerebro.addanalyzer(bt.analyzers.SQN,_name = 'SQN') Cerebro.addanalyzer(bt.analyzers.TradeAnalyzer,_name = 'TradeAnalyzer') Cerebro.addanalyzer(bt.analyzers.PositionsValue,_name = 'PositionsValue') Cerebro.addanalyzer(bt.analyzers.Returns,_name = 'Returns') Cerebro.addanalyzer(bt.analyzers.LogReturnsRolling,timeframe=tframes['years'],_name = 'LogReturnsRolling') Cerebro.addanalyzer(bt.analyzers.Transactions, _name='Transactions')
-
评价指标使用示例如下:
# -*- coding: utf-8 -*- from __future__ import (absolute_import, division, print_function, unicode_literals) import datetime # For datetime objects import pandas as pd import BackTrader as bt import numpy as np # SMA Stratey class SMAStrategy(bt.Strategy): params = ( ('ssa_window', 15), ('maperiod', 15), ) def log(self, txt, dt=None): ''' Logging function fot this strategy''' dt = dt or self.datas[0].datetime.date(0) print('%s, %s' % (dt.isoformat(), txt)) def __init__(self): # Keep a reference to the "close" line in the data[0] dataseries self.dataclose = self.datas[0].close # To keep track of pending orders and buy price/commission self.order = None self.buyprice = None self.buycomm = None self.sma = bt.indicators.SimpleMovingAverage( self.datas[0], period=self.params.maperiod) def notify_order(self, order): if order.status in [order.Submitted, order.Accepted]: return if 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)) self.buyprice = order.executed.price self.buycomm = order.executed.comm else: # Sell self.log('SELL EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' % (order.executed.price, order.executed.value, order.executed.comm)) self.bar_executed = len(self) elif order.status in [order.Canceled, order.Margin, order.Rejected]: self.log('Order Canceled/Margin/Rejected') self.order = None def notify_trade(self, trade): if not trade.isclosed: return self.log('OPERATION PROFIT, GROSS %.2f, NET %.2f' % (trade.pnl, trade.pnlcomm)) def next(self): self.log('Close, %.2f' % self.dataclose[0]) if self.order: return if not self.position: if self.dataclose[0] > self.sma[0]: self.log('BUY CREATE, %.2f' % self.dataclose[0]) self.order = self.buy() else: if self.dataclose[0] < self.sma[0]: self.log('SELL CREATE, %.2f' % self.dataclose[0]) self.order = self.sell() if __name__ == '__main__': Cerebro = bt.Cerebro() Cerebro.addstrategy(SMAStrategy) dataframe = pd.read_csv('dfqc.csv', index_col=0, parse_dates=True) dataframe['openinterest'] = 0 data = bt.feeds.PandasData(dataname=dataframe, fromdate = datetime.datetime(2015, 1, 1), todate = datetime.datetime(2016, 12, 31) ) Cerebro.adddata(data) Cerebro.broker.setcash(100.0) Cerebro.addsizer(bt.sizers.FixedSize, stake=10) Cerebro.broker.setcommission(commission=0.0) print('Starting Portfolio Value: %.2f' % Cerebro.broker.getvalue()) # 增加SharpeRatio,名称为SharpeRatio Cerebro.addanalyzer(bt.analyzers.SharpeRatio, _name = 'SharpeRatio') # 增加DrawDown,名称为DW Cerebro.addanalyzer(bt.analyzers.DrawDown, _name='DW') results = Cerebro.run() strat = results[0] print('Final Portfolio Value: %.2f' % Cerebro.broker.getvalue()) # 查看SharpeRatio print('SR:', strat.analyzers.SharpeRatio.get_analysis()) # 查看DW print('DW:', strat.analyzers.DW.get_analysis()) Cerebro.plot()
11、策略回测
result = Cerebro.run(**kwargs)
- 执行回测
12、结果可视化
Cerebro.plot()
-
绘制策略执行结果。
-
如果在jupyter notebook中直接绘图,报错
Javascript Error: IPython is not defined
,需要在代码中增加%matplotlib inline
,然后调用绘图`Cerebro.plot(iplot=False) -
-推荐参考学习资料:
量化IT技术专栏
QuantFabric开源量化交易系统
量化IT工程师实战