用于量化交易的机器人,可以对于数据进行有效回测和指定策略等。
平台工作的基础将通过策略完成。这些将通过数据馈送,数据馈送以数组的形式自动提供给策略的成员变量,并提供数组位置的快捷方式。
class MyStrategy(bt.Strategy):
params = dict(period=20)
def __init__(self):
sma = btind.SimpleMovingAverage(self.datas[0], period=self.params.period)
...
cerebro = bt.Cerebro()
...
data = btfeeds.MyFeed(...)
cerebro.adddata(data)
...
cerebro.addstrategy(MyStrategy, period=30)
...
Tip:
*args
和**kwargs
该策略的__init__
方法可以接收使用self.datas
为一个数组/列表/可迭代对象,需要至少有一项数据self.data
表示self.datas[0]self.dataX
表示self.datas[X]class MyStrategy(bt.Strategy):
params = dict(period=20)
def __init__(self):
sma = btind.SimpleMovingAverage(self.data, period=self.params.period)
class MyStrategy(bt.Strategy):
params = dict(period=20)
def __init__(self):
sma = btind.SimpleMovingAverage(period=self.params.period)
大部分对象都包含一个Line
对象,用户角度上来看
比较经典的线是由股票收盘价形成的线,称为收盘时线
class MyStrategy(bt.Strategy):
params = dict(period=20)
def __init__(self):
self.movav = btind.SimpleMovingAverage(self.data, period=self.params.period)
def next(self):
if self.movav.lines.sma[0] < self.data.lines.close[0]:
print('平均线高于收盘价格')
常见的缩写形式
定义对象中的线对象,可以采用line
的类参数。
class SimpleMovingAverage(Indicator):
lines = ('sma', )
访问:
lines
in Data Feed通过省略形式访问lines数据
data = btfeeds.BacktraderCSVData(dataname="mydata.csv")
...
class MyStrategy(bt.Strategy):
...
def next(self):
if self.data.close[0] > 30.0:
...
线对象是一组点,并在执行过程中,动态增长,可以通过调用len
随时测量长度。
这些适用于:
buflen
可以查看预加载的数量。len
返回已处理的条数,buflen
报告已经加载的组数。
backtrader
不支持对于线对象的切片方法。切片本身的意义不大。
backtrader
支持通过get
方法对于对象进行切片。
myslice = self.my_sma.get(ago=0, size=1)
class MyStrategy(bt.Strategy):
params = dict(period=20)
def __init__(self):
self.movav = btind.SimpleMovingAverage(self.data, period=self.p.period)
sefl.cmpval = self.data.close(-1) > self.sam
def next(self):
if self.cmpval[0]:
print("收盘价高于平均线")
class MyStrategy(bt.Strategy):
params = dict(period=20)
def __init__(self):
sma0 = btind.SMA(self.data0, period=15)
sma1 = btind.SMA(self.data1, period=5)
self.butsig = sma0 > sma1
def next(self):
if self.buysig[0]:
print('日均移线大雨周均平均线')
支持操作符:
支持逻辑控制:
支持函数:
class SimpleMovingAverage(Indicator):
lines = ('sma', )
params = dict(period=20)
def __init__(self):
...
def prenext(self):
print(len(self))
def nextstart(self):
print(len(self))
self.next()
def next(self):
print(len(self))
平台提供了集合数据源
雅虎财经数据
import backtrader as bt
import backtrader.feeds as btfeeds
...
datapath = "path/to/you/yahoo/data.csv"
data = btfeeds.YahooFinanceCSVData(
dataname=datapath,
reversed=True
)
如果平台使用者是为了对数据进行回测,这些是在Strategy
的派生类中实现的,至少需要定制两种方法:
基础使用
class MyStrategy(bt.Strategy):
def __init__(self):
self.sma = btind.SMA(self.data, period=20)
def next(self):
if self.sma > self.data.close:
self.buy()
if self.sma < self.data.close:
self.sell()
更多方法定制
class MyStrategy(bt.Strategy):
def __init__(self):
self.sma = btind.SimpleMovingAverage(self.data, period=20)
def next(self):
if self.sma > self.data.close:
submitted_order = self.buy()
elif self.sma < self.data.close:
submitted_order = self.sell()
def start(self):
print('Backtesting is about to start')
def stop(self):
print('Backtesting is finished')
def notify_order(self, order):
print('An order new/changed/executed/canceled has been received')
更多的方法
一旦数据馈送可用并定义了策略,Cerebro就会将所有的内容整合到一起并执行操作。
cerebro = bt.Cerebro()
一些默认值
添加数据和策略
cerebro.adddata(data)
cerebro.addstrategy(MyStrategy, period=25)
cerebro.run()
以上操作产生的效果如下:
更多的操作可见如下:
cerebro = bt.Cerebro(runonce=True, preload=True)
getbroker
/setbroker
cerebro.run()
cerebro.plot()
plot支持的几个参数
- numfigs=1 设置分图数量
- plotter=None 传递给绘图仪表实例
- **kwargs 常用的关键词参数
cerebro.optstrategy(MyStrategy, period=xrange(10, 20))
此类为backtrader的基石,主要从事下列事务:
cerebro
cerebro = bt.Cerebro(**kwargs)
data = bt.BacktraderCSVData(dataname="mypath.days", timeframe=bt.TimeFrame.Days)
cerebro.adddata(data)
可以对于数据进行重新采样
data = bt.BacktraderCSVData(dataname="mypath.min", timeframe=bt.TimeFrame.Minutes)
cerebro.resampledata(data, timeframe=bt.TimeFrame.Days)
或者
data = bt.BacktraderCSVData(dataname="mypath.min", timeframe=bt.TimeFrame.Minutes)
cerebro.replaydatadata(data, timeframe=bt.TimeFrame.Days)
cerebro.addstrategy(MyStrategy, myparam1=value1, myparam2=value2)
优化参数可以采用迭代器
cerebro.optstrategy(MyStrategy, myparam1=range(10, 20)
还可以通过一些其它元素来增强回测体验,相关方法为:
broker = MyBroker()
cerebro.broker = broker
当代理商或者数据馈送发送通知是,他们将通过Cerebro.notify_store
执行。有三种方法可以执行这些通知:
cerebro.addnotifycallback(callback)
callback(msg, *args, **kwargs)
notify_store
方法notify_store
方法result = cerebro.run(**kwargs)
标准观察员
cash
和value
result = cerebro.run(**kwargs)
result
的格式取决于run
是否使用了优化
addstrategy
,返回一个结果optstrategy
,但会一组结果cerebro.plot()
next
方法,允许策略评估新数据writer
将数据写入新目标backtrader
认为通过绘图获得的视觉反馈是一个很好且必须的事情。因此设计上将所有内容放在了内存中,这样设计有一些缺陷:
array.array
用于数据存储的数据必须在超出某些边界时分配和移动数据backtrader
运行在嵌入式设备中因此需要backtrader
支持动态内存方案:
exactbars(default: False)
使用默认False
值则内容存储在内存中
可能的值
preload
和runonce
plotting
和preloading
runonce
将会停用backtrader
支持改善Data Feed和结果返回等。
支持的参数:
编写异常可以尽早可能的退出,让用户清晰了解错误的发生。
所有异常都是基于BacktraderError
实现。
import backtrader as bt
...
class MyStrategy(bt.Strategy):
def __init__(self):
if something_goes_wrong():
raise bt.errors.StrategySkipError # or raise bt.StrategySkipError
以下内容将写入流
writer
参数addwriter
方法cerebro.addwriter(bt.WriterFile, csv=True)
backtrader
提供了一组数据馈送器
可以通过继承的方法,扩展数据馈送,增加部分数据。
from backtrader.feeds import GenericCSVData
class GenericCSV_PE(GenericCSVData):
lines = ('pe', )
params = (('pe', 8), )
有时投资决策是在不同的时间范围内做出的,比如每周的趋势指标和每日的执行指标。甚至5分钟和60分钟。
这意味着需要组合多个时间范围的数据,backtrader
支持这种组合。
cerebro已经支持了该种策略,用户应该遵循以下规则:
函数cerebro.resample
用于创建更大的时间范围。
# Load the Data
datapath = args.dataname or '../../datas/2006-day-001.txt'
data = btfeeds.BacktraderCSVData(dataname=datapath)
cerebro.adddata(data) # First add the original data - smaller timeframe
tframes = dict(daily=bt.TimeFrame.Days, weekly=bt.TimeFrame.Weeks,
monthly=bt.TimeFrame.Months)
# Handy dictionary for the argument timeframe conversion
# Resample the data
if args.noresample:
datapath = args.dataname2 or '../../datas/2006-week-001.txt'
data2 = btfeeds.BacktraderCSVData(dataname=datapath)
# And then the large timeframe
cerebro.adddata(data2)
else:
cerebro.resampledata(data, timeframe=tframes[args.timeframe],
compression=args.compression)
# Run over everything
cerebro.run()
函数cerebro.resampledata
支持的参数
import datetime
import backtrader as bt
cerebro = bt.Cerebro()
data = bt.feeds.YahooFinanceCSVData(
dataname="./data/orcl-1995-2014.txt",
fromdate=datetime.datetime(2000, 1, 1),
todate=datetime.datetime(2000, 12, 31),
reverse=0
)
cerebro.adddata(data)
cerebro.resampledata(data, timeframe=bt.TimeFrame.Weeks)
cerebro.run()
cerebro.plot(style='bar')
函数cerebro.replaydata
支持的参数
定义datafilter需要满足两种形式:
简单示例
class SessionFilter(object):
def __init__(self, data):
pass
def __call__(self, data):
if data.p.sessionstart <= data.datetime.time() <= data.p.sessionend:
# bar is in the session
return False # tell outer data loop the bar can be processed
# bar outside of the regular session times
data.backwards() # remove bar from data stack
return True # tell outer data loop to fetch a new bar