Line 反复运算器
为了参与操作,plaftorm使用 line 反复运算器的概念。它们已经松散地模仿了Python的反复运算器,但实际上与它们无关。
策略和指针是 line 反复运算器。
line反复运算器概念试图描述以下内容:
-
Line反复运算器踢从line反复运算器,告诉他们反复运算
-
然后, Line 反复运算器循环访问其自己声明的命名 lines 设置值
反复运算的关键,就像常规的Python迭代器一样,是:
-
方法
next
它将在每次反复运算时调用。
datas
line反复运算器具有并用作逻辑/计算基础的数组已经被平台移动到下一个索引(数据重放除外)在满足line反复运算器的最小周期时调用。下面对此进行更多介绍。
但是由于它们不是常规反复运算器,因此存在两种其他方法:
-
prenext
在满足line反复运算器“的最小期限之前调用。
-
nextstart
在满足line反复运算器“的最小周期时正好调用一次。
默认行为是将调用
next
转接到,但如果需要,当然可以覆盖。
指针的额外方法
为了加快操作速度,指针支持批处理操作模式,该模式被称为 runonce。它不是严格需要的(一next
种方法就足够了),但它大大减少了时间。
runonce 方法规则使索引为 0 的 get/set 点失效,并依赖于对保存数据的基础数组的直接访问,并为每个状态传递正确的索引。
定义的方法遵循下一个族的命名:
-
once(self, start, end)
在达到最小期限时调用。内部数字必须在开始和结束之间进行处理,从内部数字的开始开始为零
-
preonce(self, start, end)
在达到最小期限之前调用。
-
oncestart(self, start, end)
在满足最小期限时正好调用一次 。
默认行为是将调用
once
转接到,但如果需要,当然可以覆盖。
最短期限
一张图片胜过千言万语,在这种情况下,也可能是一个例子。一个简单的移动Average能够解释它:
class SimpleMovingAverage(Indicator): lines = ('sma',) params = dict(period=20) def __init__(self): ... # Not relevant for the explanation def prenext(self): print('prenext:: current period:', len(self)) def nextstart(self): print('nextstart:: current period:', len(self)) # emulate default behavior ... call next self.next() def next(self): print('next:: current period:', len(self))
实例化可能如下所示:
sma = btind.SimpleMovingAverage(self.data, period=25)
简要说明:
-
假设传递到移动平均线的数据是标准 data feed 其缺省周期是
1
: data feed 产生一个没有初始延迟的柱。 -
然后,“period=25” 实例化移动平均线将调用其方法,如下所示:
-
prenext
24 次 -
nextstart
1次(依次通话next
) -
next
再增加 n 次,直到 data feed 用尽
-
让我们来看看杀手级指针:一个 SimpleMovingAverage over 另一个 SimpleMovingAverage。实例化可能如下所示:
sma1 = btind.SimpleMovingAverage(self.data, period=25) sma2 = btind.SimpleMovingAverage(sma1, period=20)
现在发生了什么:
-
同上
sma1
-
sma2
正在接收一个 data feed ,其最短期限为25,这是我们sma1
,因此 -
这些
sma2
方法的调用方式如下:-
prenext
前 25 + 18 次,共 43 次 -
25次让
sma1
其产生1st 合理值 -
18次累积额外
sma1
价值 -
总共 19 个值(25 次调用后为 1,之后为 18 次)
-
nextstart
然后1次(依次调用next
) -
next
再增加 n 次,直到 data feed 用尽
-
当系统已经处理了 44 根柱线时,平台正在调用next
。
最小周期已根据传入数据自动调整。
策略和指针遵循以下行为:
- 仅当达到自动计算的最小周期时,才会
next
调用(除了对nextstart
) 的初始钩子调用
注意
相同的规则适用于preonce
oncestart
运行批处理操作模式,once
注意
可以操纵最小周期 行为,但不建议这样做。是否希望在策略或指针中使用该 setminperiod(minperiod)
方法
启动并运行
启动与运行至少涉及3个 Lines 对象:
-
Data feed
-
策略(实际上是从策略派生的类)
-
一 Cerebro (西班牙文中的大脑)
Data Feeds
显然,这些对象提供的数据将通过应用计算(直接和/或指针)进行回溯测试
该平台提供了几个 data feeds:
-
几种 CSV 格式和一个通用 CSV 阅读器
-
雅虎在线获取器
-
支持接收熊猫数据帧和火焰对象
-
与Interacive Brokers,Visual Chart和Oanda进行即时Data Feeds
该平台对 data feed 的内容(例如时间帧和压缩)不做任何假设。这些值与名称一起可以提供,用于提供信息和高级操作,例如 Data Feed 重新采样(例如,将5分钟的 Data Feed 转换为每日 Data Feed)
设置雅虎财经 Data Feed的示例:
import backtrader as bt import backtrader.feeds as btfeeds ... datapath = 'path/to/your/yahoo/data.csv' data = btfeeds.YahooFinanceCSVData( dataname=datapath, reversed=True)
显示 Yahoo 的可选reversed
参数,因为直接从 Yahoo 下载的 CSV 档以最晚的日期开头,而不是以最旧的日期开头。
如果您的数据跨越了较大的时间范围,则可以按如下方式限制实际加载的数据:
data = btfeeds.YahooFinanceCSVData( dataname=datapath, reversed=True fromdate=datetime.datetime(2014, 1, 1), todate=datetime.datetime(2014, 12, 31))
如果 data feed中存在,则起始日期和截止日期都将包括在内。
如前所述,可以添加压缩和名称:
data = btfeeds.YahooFinanceCSVData( dataname=datapath, reversed=True fromdate=datetime.datetime(2014, 1, 1), todate=datetime.datetime(2014, 12, 31) timeframe=bt.TimeFrame.Days, compression=1, name='Yahoo' )
如果绘制了数据,则将使用这些值。
策略(派生)类
注意
在继续之前,对于更简化的方法,如果不希望对策略进行子类化,请查看文档的“信号”部分。
使用该平台的任何人都的目标是回溯测试数据,这是在策略(派生类)内完成的。
有2种方法至少需要自定义:
-
__init__
-
next
在初始化期间,将创建数据和其他计算的指示器,以便为以后应用逻辑做好准备。
稍后调用下一个方法来为数据的每个条形应用逻辑。
注意
如果传递了不同时间帧的data feeds(因此传递了next
不同的柱计数),则将为主数据调用方法(传递给cerebro的第一个,见下文),该方法必须是具有较小时间帧的数据
注意
如果使用数据重播功能,则在next
重放柱的开发时,将为同一柱线多次调用该方法。
基本策略派生类:
class MyStrategy(bt.Strategy): def __init__(self): self.sma = btind.SimpleMovingAverage(self.data, period=20) def next(self): if self.sma > self.data.close: self.buy() elif 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')
start
和 stop
方法应该是不言自明的。正如预期的那样,在打印函数中的文本之后,notify_order
当策略需要通知时,将调用该方法。用例:
-
请求买入或卖出(如下图所示)
买入/卖出将返回提交给经纪商的订单。保留对此提交订单的引用由调用方决定。
例如,它可用于确保在订单仍处于待处理状态时不会提交新订单。
-
如果订单被接受/运行/取消/更改,经纪人将通过 notify 方法将状态更改(例如运行大小)通知回策略
快速入门指南在该方法中notify_order
提供了订单管理的完整功能示例。
其他策略类可以做更多任务作:
-
buy
/sell
/close
使用基础经纪商和 sizer 向经纪商发送买入/卖出订单
同样可以通过手动创建订单并将其传递给经纪人来完成。但该平台旨在使使用它的人变得容易。
close
将获得当前的市场地位并立即 close 。 -
getposition
(或属性“位置”)返回当前市场位置
-
setsizer
/getsizer
(或财产“sizer”)这些允许设置/获取标的权益 Sizer。可以对照 Sizers 检查相同的逻辑,这些为相同情况提供不同的赌注(固定大小,与资本成比例,指数)
有很多文学作品,但Van K. Tharp有关于这个主题的优秀书籍。
策略是一个 Lines 对象和这些支持参数,这些参数是使用标准的 Python kwargs 参数收集的:
class MyStrategy(bt.Strategy): params = (('period', 20),) def __init__(self): self.sma = btind.SimpleMovingAverage(self.data, period=self.params.period) ... ...
请注意,不再SimpleMovingAverage
使用固定值 20 实例化 ,而是使用为策略定义的参数“period” 实例化。
Cerebro
一旦 Data Feeds 可用并且定义了战略, Cerebro 实例就是将所有内容集成在一起并运行操作。实例化一个很容易:
cerebro = bt.Cerebro()
如果不希望有任何特殊情况,则缺省会处理。
-
创建缺省代理
-
无佣金
-
Data Feeds 将被预加载
-
默认运行模式为runonce(批处理操作),这是更快的
所有指针必须支持
runonce
全速模式。平台中包含的那些确实如此。自定义指针不需要实现 runonce 功能。
Cerebro
将模拟它,这意味着那些与runonce不兼容的指针将运行得更慢。但是,大多数系统仍将以批处理模式运行。
由于 Data feed 已经可用,并且策略也(更早创建)将其放在一起并使其启动并运行的标准方法是:
cerebro.adddata(data) cerebro.addstrategy(MyStrategy, period=25) cerebro.run()
请注意以下几点:
-
添加了 Data Feed “实例”
-
MyStrategy“类”与将传递给它的参数(kwargs)一起添加。
MyStrategy的实例化将由后台 cerebro 完成,并且“addstrategy”中的任何kwargs都将传递给它。
用户可以根据需要添加任意数量的策略和 Data Feeds 。策略如何相互沟通以实现协调(如果愿意的话)不受平台的运行/限制。
当然, Cerebro 还提供了更多的可能性:
-
确定预加载与操作模式:
cerebro = bt.Cerebro(runonce=True, preload=True)
这里有一个约束:
runonce
需要预加载(如果没有,则无法运行批处理操作)当然,预加载 Data Feeds 不会强制runonce
-
setbroker
/getbroker
(和经纪人财产)如果需要,可以设置自定义代理。也可以访问实际的代理实例
-
标图。在正常情况下,就像这样简单:
cerebro.run() cerebro.plot()
plot 为自定义取一些参数
-
numfigs=1
如果情节太密集,可能会分解为几个情节
-
plotter=None
可以传递客户绘图仪实例,并且 cerebro 不会实例化默认绘图仪实例
-
**kwargs
- 标准关键字参数这将传递给绘图仪。
有关详细信息,请参阅绘图部分。
-
-
策略优化。
如上所述, Cerebro 获取一个 Strategy 派生类(不是实例)和关键字参数,这些参数将在实例化时传递给它,这将在调用“run”时发生。
这是为了实现优化。同一个 Strategy 类将根据需要使用新参数实例化多次。如果实体已传递给 cerebro ...这是不可能的。
优化请求如下:
cerebro.optstrategy(MyStrategy, period=xrange(10, 20))
该方法
optstrategy
具有与相同的签名,addstrategy
但会运行额外的内务处理,以确保优化按预期运行。策略可能期望一个范围作为策略的正常参数,并且addstrategy
不会对传递的参数做出任何假设。另一方面,
optstrategy
将理解可反复运算是一组值,必须按顺序传递给 Strategy 类的每个实例化。请注意,传递的值范围不是单个值。在这个简单的情况下,将为此策略尝试 10 个值 10 -> 19(20 是上限)。
如果使用额外的参数开发更复杂的策略,则可以将它们全部传递给选择策略。不得进行优化的参数可以直接传递,而无需最终用户创建一个仅可反复运算一个值的虚拟参数。例:
cerebro.optstrategy(MyStrategy, period=xrange(10, 20), factor=3.5)
该
optstrategy
方法看到因数,并在后台为具有单个元素的因数创建(所需的)可反复运算的虚拟(在示例 3.5 中)注意
交互式Python shell和Windows下某些类型的冻结可运行档在Python
multiprocessing
模块中存在问题请阅读 Python 文档
multiprocessing
。