操作平台#
行迭代器 *** ** ** **
为了进行操作,平台使用了“行迭代器”的概念。它们与Python的迭代器有一定的相似之处,但实际上与其无关。
策略和指标是“行迭代器”。
“行迭代器”概念试图描述以下内容:
行迭代器 通知从属于它的 行迭代器 开始迭代。
然后, 行迭代器 将迭代其自己声明的具有名称的行来设置数值。
与常规的Python迭代器一样,迭代的关键在于:
next 方法
它将在每次迭代时调用。 行迭代器 具有的将作为逻辑/计算基础的 datas 数组已经被平台移动到了下一个索引位置(除非进行了“数据回放”)。
当满足 行迭代器 的 最小周期 时,将调用该方法。关于这一点稍后会详细解释。但是因为它们不是常规的迭代器,所以还有两个额外的方法:
prenext
在`行迭代器``的 最小周期 满足之前调用。
nextstart
当`行迭代器``的 最小周期 满足时,仅调用 一次 。
默认行为是将调用转发给
next
,但如果需要的话,当然可以覆盖它。
针对 指示器 的额外方法#
为了加快操作速度, 指示器 支持了一个批处理操作模式,被称为 runonce 。这并不是严格需要的(只需要一个 next
方法就足够了),但它大大缩短了时间。
runonce 方法规则无效化了使用索引为0的 get/set 位置,并依赖于对保存数据的底层数组进行直接访问,并传递给每个状态的正确索引。
下面定义的方法遵循了 next
家族的命名方式: - once(self, start, end)
当满足最小周期时调用。在内部数组的从开始到结束的位置之间必须进行处理,这些位置是从内部数组的起始位置开始计算的。
preonce(self, start, end)
在满足最小周期之前调用。
oncestart(self, start, end)
当满足最小周期时,仅调用一次。
默认行为是将调用转发给
once
,但如果需要,可以覆盖。
最小周期#
图片胜过千言万语,在这种情况下可能还有一个例子。SimpleMovingAverage能够解释它:
- class SimpleMovingAverage(指标):
lines = (‘sma’,) params = dict(period=20) def __init__(self):
… # 不相关的解释
- def prenext(self):
print(‘prenext:: 当前周期:’, len(self))
- def nextstart(self):
print(‘nextstart:: 当前周期:’, len(self)) # 模拟默认操作…调用next self.next()
- def next(self):
print(‘next:: 当前周期:’, len(self))
实例化可以如下所示:
sma = btind.SimpleMovingAverage(self.data, period=25)
简要解释:
假设传递给移动平均值的数据是标准数据源,其默认周期为
1
,即数据源产生一根没有初始延迟的K线。然后, “period=25” 实例化的移动平均值将按以下方式调用其方法:
prenext
调用 24 次
nextstart
调用 1 次(随后调用next
)
next
调用 n 次,直到 数据源 耗尽为止让我们来使用杀手指标:在另一个简单移动平均线(SimpleMovingAverage)上计算一个简单移动平均线(SimpleMovingAverage)。实例化的代码如下:
```python sma1 = btind.SimpleMovingAverage(self.data, period=25)
sma2 = btind.SimpleMovingAverage(sma1, period=20) ```
现在发生的情况是:
对于 sma1 ,与上述相同
sma2 接收了一个 数据源 ,其 最小周期 为25,即我们的 sma1
执行了以下 SMA2 方法:
prenext 方法首先执行了25 + 18次,总共43次 - 前25次用于产生 sma1 的第一个可靠值 - 后18次用于累计额外的 sma1 值- 以19个值的总数进行调用(在第25次调用之后还有18次)。
nextstart
会被调用1次(依次调用next
)next
会被调用n次,直到 数据流 用尽
当系统已经处理了44个条形图时,平台会调用 next
。
最小周期 已经自动调整为传入的`数据`。
策略和指标都遵循这种行为:
只有当自动计算的最小周期达到之后,才会调用
next
(除了首次调用nextstart
)
备注
对于 runonce 批处理操作模式,对于 preonce
, oncestart
和 once
也适用相同的规则。
备注
最小周期 的行为可以被修改,尽管不建议这样做。如果希望修改,可以在策略或指标中使用 setminperiod(minperiod)
方法。
开始运行#
- 启动和运行至少需要 3 个 Lines 对象:- 一个数据源
一个策略(实际上是从策略派生的类)
一个Cerebro(西班牙语中的“大脑”)
数据源#
这些对象提供将通过应用计算(直接和/或通过指标)进行回测的数据
该平台提供了几种数据源:
几个 CSV 格式和通用的 CSV 读取器
雅虎在线获取器
支持接收 Pandas DataFrames 和 blaze 对象
使用 Interacive Brokers、Visual Chart 和 Oanda 的实时数据源
该平台对数据源的内容(如时间范围和压缩)不做任何假设。这些值和名称可以提供用于信息目的和高级操作,比如数据源重新采样(将例如一个5分钟的数据源转换为每日的数据源)
设置雅虎财经数据源的示例:
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))``
只要在数据源中出现, fromdate 和*todate*都将被包括在内。
如上所述,可以添加时间范围、压缩和名称:
- ``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’
)``
如果绘制数据,将使用这些值。
策略(派生)类#
备注
在继续之前,并为了更简化的方法,请检查文档中的*信号*部分,如果不希望子类化策略。使用本平台的目标是对数据进行回测,这是在策略(派生类)中完成的。
至少需要自定义的有两个方法:
__init__
next
在初始化过程中,对数据进行指标计算和其他计算的准备,以便后续应用逻辑。
接下来的方法会被调用来对每个数据条应用逻辑。
注意
如果传递了不同时间框架的数据源(因此有不同的数据条数),则 next
方法将被调用用于主数据源(在cerebro中传递的第一个数据源),它必须是时间框架较小的数据。
注意
如果使用了数据回放功能, next
方法将多次被调用,用于对同一数据条进行回放。
一个基本的策略派生类:
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()
策略有其他可重写的方法(或挂钩点):
```python 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(‘即将开始回测’)
- def stop(self):
print(‘回测已完成’)
- def notify_order(self, order):
print(‘已接收到一个新的/更改的/执行的/撤销的订单’)
“start”和“stop”方法应该是不言自明的。按照打印函数中的文本,预计“notify_order”方法会在策略需要通知时被调用。使用案例:
请求买入或卖出(如下所示)
买入/卖出将返回一个“order”,该订单将被提交给经纪人。将提交的订单保留下来由调用者决定。
例如,可用于确保在订单未决时不提交新订单。
如果订单被接受/执行/取消/更改,经纪人将通过通知方法将状态更改(例如执行数量)发送回策略
在“notify_order”方法中,《快速入门指南》有一个完整且功能完备的订单管理示例。更多的功能可以通过其他的策略类来实现:
buy
/sell
/close
使用底层的 经纪人 和*大小配置器*向经纪人发送买入/卖出订单。
也可以手动创建一个订单并将其传递给经纪人来完成相同的操作。但是,这个平台旨在让使用者能够轻松使用。
close
会获取当前的市场仓位并立即平仓。
getposition
(或属性 “position”)返回当前的市场仓位。
setsizer
/getsizer
(或属性 “sizer”)这些允许设置/获取底层的仓位大小配置器。可以通过不同的仓位大小配置器来对同一情况进行不同的配置(固定大小、与资本成比例、指数增长)。
有很多相关的文献,但Van K. Tharp在这个主题上有很多优秀的著作。策略是 Lines 对象,它们支持参数,这些参数使用标准的Python kwargs参数来收集。
```python class MyStrategy(bt.Strategy):
params = ((‘period’, 20),)
def __init__(self):
self.sma = btind.SimpleMovingAverage(self.data, period=self.params.period)
…#
注意如何不再用固定值20实例化 SimpleMovingAverage
,而是用已为策略定义的参数”period”来实例化。
Cerebro *** ** **
一旦数据源可用并且策略已定义,Cerebro实例是将所有内容汇总并执行操作的关键。实例化一个很简单:
```python cerebro = bt.Cerebro() ```如果没有特殊要求,缺省值会生效。
创建了一个默认的经纪人
操作没有佣金
数据源将被预加载
默认的执行模式是batch(批量操作),速度更快
所有指标都必须支持
runonce
模式以获得最快的速度。平台中包含的指标都支持。自定义指标不需要实现runonce功能。
Cerebro
将模拟它,这意味着那些不兼容runonce的指标将运行得较慢。但系统的大部分操作仍将在批处理模式下运行。
由于已经有了数据源和策略(之前创建的),将它们组合并使其运行的标准方式是:
cerebro.adddata(data) cerebro.addstrategy(MyStrategy, period=25) cerebro.run()
请注意以下事项:
数据源“实例”被添加进来
MyStrategy “类”与参数(kwargs)一起被添加进来,这些参数将被传递给它。
MyStrategy的实例化将由cerebro在后台完成,并且
addstrategy
中的任何kwargs都将被传递给它。用户可以根据需要添加任意多的策略和数据源。策略之间如何协调通信(如果需要的话)并没有受到平台的限制。
当然,Cerebro提供了额外的可能性:
决定预加载和操作模式:
cerebro = bt.Cerebro(runonce=True, preload=True)这里有一个限制:
runonce
需要预加载(否则无法运行批量操作)。当然,预加载数据源不强制要求使用runonce
。
setbroker
/getbroker
(和 broker 属性)如果需要,可以设置自定义的经纪人。也可以访问实际的经纪人实例。
绘图。在普通情况下非常简单:
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
具有相同的签名,但会执行额外的工作以确保优化如预期运行。
一个策略可以期望作为策略的一个普通参数的是一个 range ,而 addstrategy
不会对传递的参数进行任何假设。
另一方面, optstrategy
会理解可迭代对象是一组值,必须按照顺序传递给每个 Strategy 类的实例化。
需要注意的是,传递的是一组值而不是单个值。在这个简单的例子中,将尝试使用这个策略的 10 个值 10 -> 19(20 是上限)。
如果开发了一个具有额外参数的更复杂的策略,可以将它们全部传递给 optstrategy 。不需要进行优化的参数可以直接传递,而无需用户创建一个只包含一个值的假可迭代对象。例如:
`
cerebro.optstrategy(MyStrategy, period=xrange(10, 20), factor=3.5)
`
optstrategy
方法会创建 dummy iterable 来处理 factor(这是一个必需的),dummy iterable 只有一个元素(在这个例子中为 3.5)。
备注
交互式 Python Shell 和某些类型的冻结可执行文件在 Windows 下对 Python 的 multiprocessing
模块存在问题。
请阅读有关 multiprocessing
的 Python 文档。