分步交易一天
原文: https://www.backtrader.com/blog/posts/2016-07-13-day-in-steps/day-in-steps/
似乎在世界的某个地方有一种兴趣可以概括如下:
- 引入使用每日酒吧但使用开盘价的订单
这来自票据【105 订单执行逻辑与当日数据和【101 动态桩计算的对话
反向交易者在处理每日酒吧时,尽量保持现实,以下前提适用:
- 在评估每日酒吧时,酒吧已经结束
这是有意义的,因为所有价格(开/高/低/关组件都是已知的。在close
价格已知的情况下,允许对open
价格采取行动实际上似乎不合逻辑。
显而易见的方法是使用日内数据,并在已知开盘价时输入。但日内数据似乎并不普遍。
这就是在数据馈送中添加过滤器的地方。一种过滤器,用于:
- 将日数据转换为日内数据
起泡的藤壶!!!好奇的读者会立即指出向上采样例如Minutes
到Days
是合乎逻辑且可行的,但向下采样Days
到Minutes
是不可能的。
这是百分之百正确的。下面介绍的过滤器不会尝试这一点,而是一个非常简单和简单的目标:
-
将每日酒吧分成两部分
-
只有开价而没有数量的酒吧
-
一个 2和酒吧,它是日常酒吧的复制品
-
这仍然可以作为一种逻辑方法:
-
当看到开盘价格时,交易者可以采取行动
-
订单在当天剩余时间内匹配(实际上可能匹配,也可能不匹配,具体取决于执行类型和价格限制)
完整代码如下所示。我们来看一个样本运行,已知数据为255
日条:
$ ./daysteps.py --data ../../datas/2006-day-001.txt
输出:
Calls,Len Strat,Len Data,Datetime,Open,High,Low,Close,Volume,OpenInterest
0001,0001,0001,2006-01-02T23:59:59,3578.73,3578.73,3578.73,3578.73,0.00,0.00
- I could issue a buy order during the Opening
0002,0001,0001,2006-01-02T23:59:59,3578.73,3605.95,3578.73,3604.33,0.00,0.00
0003,0002,0002,2006-01-03T23:59:59,3604.08,3604.08,3604.08,3604.08,0.00,0.00
- I could issue a buy order during the Opening
0004,0002,0002,2006-01-03T23:59:59,3604.08,3638.42,3601.84,3614.34,0.00,0.00
0005,0003,0003,2006-01-04T23:59:59,3615.23,3615.23,3615.23,3615.23,0.00,0.00
- I could issue a buy order during the Opening
0006,0003,0003,2006-01-04T23:59:59,3615.23,3652.46,3615.23,3652.46,0.00,0.00
...
...
0505,0253,0253,2006-12-27T23:59:59,4079.70,4079.70,4079.70,4079.70,0.00,0.00
- I could issue a buy order during the Opening
0506,0253,0253,2006-12-27T23:59:59,4079.70,4134.86,4079.70,4134.86,0.00,0.00
0507,0254,0254,2006-12-28T23:59:59,4137.44,4137.44,4137.44,4137.44,0.00,0.00
- I could issue a buy order during the Opening
0508,0254,0254,2006-12-28T23:59:59,4137.44,4142.06,4125.14,4130.66,0.00,0.00
0509,0255,0255,2006-12-29T23:59:59,4130.12,4130.12,4130.12,4130.12,0.00,0.00
- I could issue a buy order during the Opening
0510,0255,0255,2006-12-29T23:59:59,4130.12,4142.01,4119.94,4119.94,0.00,0.00
发生以下情况:
-
next
被称为:510 times
即255 x 2
-
策略的
len
和数据的len
总计达到255
,这是预期的:数据只有那么多条 -
每次数据的
len
增加时,4 个价格分量的值都相同,即open
价格这里打印了一条备注,表明在期初阶段可以采取行动,例如购买。
实际上:
- 每日数据馈送被重播,每天有 2 个步骤,可以选择在
open
和其他价格组成部分之间进行操作
过滤器将在下一版本中添加到backtrader的默认分发中。
包含过滤器的示例代码。
from __future__ import (absolute_import, division, print_function,
unicode_literals)
import argparse
from datetime import datetime, time
import backtrader as bt
class DayStepsFilter(object):
def __init__(self, data):
self.pendingbar = None
def __call__(self, data):
# Make a copy of the new bar and remove it from stream
newbar = [data.lines[i][0] for i in range(data.size())]
data.backwards() # remove the copied bar from stream
openbar = newbar[:] # Make an open only bar
o = newbar[data.Open]
for field_idx in [data.High, data.Low, data.Close]:
openbar[field_idx] = o
# Nullify Volume/OpenInteres at the open
openbar[data.Volume] = 0.0
openbar[data.OpenInterest] = 0.0
# Overwrite the new data bar with our pending data - except start point
if self.pendingbar is not None:
data._updatebar(self.pendingbar)
self.pendingbar = newbar # update the pending bar to the new bar
data._add2stack(openbar) # Add the openbar to the stack for processing
return False # the length of the stream was not changed
def last(self, data):
'''Called when the data is no longer producing bars
Can be called multiple times. It has the chance to (for example)
produce extra bars'''
if self.pendingbar is not None:
data.backwards() # remove delivered open bar
data._add2stack(self.pendingbar) # add remaining
self.pendingbar = None # No further action
return True # something delivered
return False # nothing delivered here
class St(bt.Strategy):
params = ()
def __init__(self):
pass
def start(self):
self.callcounter = 0
txtfields = list()
txtfields.append('Calls')
txtfields.append('Len Strat')
txtfields.append('Len Data')
txtfields.append('Datetime')
txtfields.append('Open')
txtfields.append('High')
txtfields.append('Low')
txtfields.append('Close')
txtfields.append('Volume')
txtfields.append('OpenInterest')
print(','.join(txtfields))
self.lcontrol = 0
def next(self):
self.callcounter += 1
txtfields = list()
txtfields.append('%04d' % self.callcounter)
txtfields.append('%04d' % len(self))
txtfields.append('%04d' % len(self.data0))
txtfields.append(self.data.datetime.datetime(0).isoformat())
txtfields.append('%.2f' % self.data0.open[0])
txtfields.append('%.2f' % self.data0.high[0])
txtfields.append('%.2f' % self.data0.low[0])
txtfields.append('%.2f' % self.data0.close[0])
txtfields.append('%.2f' % self.data0.volume[0])
txtfields.append('%.2f' % self.data0.openinterest[0])
print(','.join(txtfields))
if len(self.data) > self.lcontrol:
print('- I could issue a buy order during the Opening')
self.lcontrol = len(self.data)
def runstrat():
args = parse_args()
cerebro = bt.Cerebro()
data = bt.feeds.BacktraderCSVData(dataname=args.data)
data.addfilter(DayStepsFilter)
cerebro.adddata(data)
cerebro.addstrategy(St)
cerebro.run(stdstats=False, runonce=False, preload=False)
if args.plot:
cerebro.plot(style='bar')
def parse_args():
parser = argparse.ArgumentParser(
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
description='Sample for pivot point and cross plotting')
parser.add_argument('--data', required=False,
default='../../datas/2005-2006-day-001.txt',
help='Data to be read in')
parser.add_argument('--plot', required=False, action='store_true',
help=('Plot the result'))
return parser.parse_args()
if __name__ == '__main__':
runstrat()