操作平台
行迭代器
为了参与操作,plaftorm 使用行迭代器的概念。它们松散地模仿 Python 的迭代器,但实际上与它们无关。
策略和指标是行迭代器。
行迭代器概念试图描述以下内容:
-
行迭代器踢从行迭代器,告诉它们进行迭代
-
然后,行迭代器迭代其自己声明的命名行设置值
与常规 Python 迭代器一样,迭代的关键是:
-
next
方法每次迭代都会调用它。行迭代器拥有并作为逻辑/计算基础的
datas
数组已经被平台移动到下一个索引(禁止数据重放)当满足行迭代器的最短周期时调用。下面再详细介绍一下。
但由于它们不是常规迭代器,因此还存在两种附加方法:
-
prenext
在满足行迭代器`的最短周期之前调用。
-
nextstart
当满足行迭代器`的最短周期时,精确调用一次。
默认行为是将调用转发到
next
,但如果需要,当然可以重写。
指标的附加方法
为了加快运行速度,指示器支持称为 runonce 的批量运行模式。它不是严格需要的(一个next
方法就足够了),但它大大减少了时间。
runonce 方法规则使索引为 0 的 get/set 点无效,并依赖于对包含数据的底层数组的直接访问,并为每个状态传递正确的索引。
定义的方法遵循下一个族的命名:
-
once(self, start, end)
在满足最短周期时调用。必须在起始和结束之间处理内部数组,从内部数组的起始处算起,起始和结束为零
-
preonce(self, start, end)
在满足最短周期之前调用。
-
oncestart(self, start, end)
当满足最短周期时,精确调用一次。
默认行为是将调用转发到
once
,但如果需要,当然可以重写。
最短周期
一幅画抵得上千言万语,在这种情况下,也可能是一个例子。SimpleMovingAverage 能够解释这一点:
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)
简要说明:
-
假设传递给移动平均线的数据是标准数据馈送,其默认周期为
1
,即:数据馈送产生一个没有初始延迟的条。 -
那么“period=25”实例化移动平均线的方法调用如下:
-
prenext
24 次 -
nextstart
1 次(依次呼叫next
-
next
n 额外次数,直到数据馈送耗尽
-
让我们来看杀手指标:一个简单移动平均值超过另一个简单移动平均值。实例化可能如下所示:
sma1 = btind.SimpleMovingAverage(self.data, period=25)
sma2 = btind.SimpleMovingAverage(sma1, period=20)
现在发生了什么:
-
sma1
同上 -
sma2
正在接收数据馈送,其最短周期为 25,这是我们的sma1
,因此 -
sma2
方法的调用如图所示:-
prenext
前 25+18 次共 43 次 -
25 次,让
sma1
产生其 1st敏感值 -
18 次累积额外的
sma1
值 -
总共 19 个值(25 次调用后为 1 次,然后再调用 18 次)
-
nextstart
然后 1 次(依次呼叫next
-
next
在数据馈送耗尽之前,再重复 n 次
-
当系统已经处理 44 条时,平台正在调用next
。
最短周期已根据传入数据自动调整。
战略和指标遵循以下行为:
- 只有在达到自动计算的最短周期时才会调用
next
(除非对nextstart
进行初始钩子调用)
笔记
对于跳动批量操作模式,同样的规则适用于preonce
、oncestart
和once
笔记
最短周期行为可以被操纵,尽管不建议这样做。是否希望在战略或指标中使用setminperiod(minperiod)
方法
正常运转
起跑至少涉及 3 条线对象:
-
数据源
-
策略(实际上是从策略派生的类)
-
大脑(西班牙语中的大脑)
数据源
显然,这些对象提供的数据将通过应用计算(直接和/或使用指标)进行回溯测试
该平台提供了几个数据源:
-
几种 CSV 格式和一个通用的 CSV 阅读器
-
雅虎在线抓取器
-
支持接收熊猫数据帧和火焰对象
-
通过互动经纪人、视觉图表和Oanda提供实时数据源
该平台对数据提要的内容(如时间框架和压缩)不作任何假设。这些值以及名称可用于提供信息和数据馈送重采样等高级操作(将例如 5 分钟的数据馈送转换为每日数据馈送)
设置 Yahoo Finance 数据源的示例:
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'
)
如果绘制数据,将使用这些值。
策略(派生)类
笔记
在继续之前,如果不希望对策略进行子类化,请检查文档中的信号部分,以获得更简化的方法。
任何使用该平台的人的目标都是对数据进行回溯测试,这是在一个策略(派生类)中完成的。
至少有 2 种方法需要定制:
-
__init__
-
next
在初始化期间,将创建数据和其他计算的指标,以备以后应用逻辑。
接下来调用下一个方法,为每个数据条应用逻辑。
笔记
如果传递了不同时间段的数据馈送(因此也传递了不同的条数),则将调用next
方法来获取主数据(传递给 Cerbero 的 1st方法,见下文),该主数据必须是具有较小时间段的数据
笔记
如果使用数据重放功能,则在重放条形图的开发过程中,同一条形图将多次调用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
将获取当前市场头寸并立即关闭。 -
getposition
(或物业“位置”)返回当前市场位置
-
setsizer
/getsizer
(或物业“sizer”)这些允许设置/获取基本的桩尺寸。同样的逻辑可以对照为相同情况(固定规模、与资本成比例、指数型)提供不同股权的规模确定器进行检查
有大量的文学作品,但范·K·萨普在这方面有很多优秀的书籍。
策略是一个行对象和这些支持参数,它们是使用标准 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 = bt.Cerebro()
如果不希望有任何特殊情况,默认值将得到处理。
-
将创建一个默认代理
-
业务没有佣金
-
数据源将被预加载
-
默认执行模式为 runonce(批处理操作),速度更快
所有指示器必须支持全速的
runonce
模式。平台中包含的那些都可以。自定义指示器不需要实现 runonce 功能。
Cerebro
将模拟它,这意味着那些与跳动不兼容的指示器将运行较慢。但大多数系统仍将以批处理模式运行。
由于数据馈送已经可用,并且也有一个策略(在前面创建),因此将所有数据馈送放在一起并使其运行的标准方法是:
cerebro.adddata(data)
cerebro.addstrategy(MyStrategy, period=25)
cerebro.run()
请注意以下事项:
-
添加了数据提要“实例”
-
MyStrategy“class”与将传递给它的参数(kwargs)一起添加。
MyStrategy 的实例化将由 Cerbero 在后台完成,“addstrategy”中的任何 Kwarg 都将传递给它
用户可以根据需要添加任意多的策略和数据源。平台不强制/限制战略如何相互沟通以实现协调(如果愿意)。
当然,大脑提供了额外的可能性:
-
决定预加载和运行模式:
py cerebro = bt.Cerebro(runonce=True, preload=True)
这里有一个约束:
runonce
需要预加载(如果没有,则无法运行批处理操作),当然预加载数据源不会强制runonce
-
setbroker
/getbroker
(及经纪人物业)如果需要,可以设置自定义代理。也可以访问实际的代理实例
-
策划。在常规情况下,尽可能简单:
py cerebro.run() cerebro.plot()
plot 接受一些自定义参数
-
numfigs=1
如果绘图过于密集,可能会将其分解为多个绘图
-
plotter=None
可以传递客户绘图仪实例,而 Cerbero 不会实例化默认绘图仪实例
-
**kwargs
-标准关键字参数这将传递给绘图仪。
有关更多信息,请参见绘图部分。
-
-
优化战略。
如上所述,Cerbero 获得一个策略派生类(不是实例)和关键字参数,这些参数将在实例化时传递给它,这将在调用“run”时发生。
这是为了实现优化。同一个策略类将根据需要使用新参数多次实例化。如果一个实例被传递给了大脑…这是不可能的。
优化要求如下:
py cerebro.optstrategy(MyStrategy, period=xrange(10, 20))
方法
optstrategy
与addstrategy
具有相同的特征码,但进行额外的内务处理以确保优化按预期运行。策略可能期望范围作为策略的正常参数,addstrategy
不会对传递的参数进行假设。另一方面,
optstrategy
将理解 iterable 是一组必须按顺序传递给 Strategy 类的每个实例化的值。请注意,传递的不是单个值,而是一个值的范围。在这种简单的情况下,将为该策略尝试 10 个值 10->19(20 是上限)。
如果使用额外参数开发更复杂的策略,则它们都可以传递给optstrategy。不必进行优化的参数可以直接传递,而最终用户不必创建一个只有一个值的伪 iterable。例子:
py cerebro.optstrategy(MyStrategy, period=xrange(10, 20), factor=3.5)
optstrategy
方法查看 factor,并在背景中为具有单个元素的 factor 创建(所需的)伪 iterable(在示例 3.5 中)笔记
交互式 Python shell 和Windows下的某些类型的冻结可执行文件与 Python
multiprocessing
模块存在问题请阅读关于
multiprocessing
的 Python 文档。