期货和现货补偿
原文:
www.backtrader.com/docu/order-creation-execution/futurespot/future-vs-spot/
发布1.9.32.116
添加了对在社区中提出的一个有趣用例的支持。
- 通过未来开始交易,其中包括实物交割
- 让指标告诉你些什么。
- 如果需要,通过对现货价格进行操作来关闭头寸,有效地取消物理交割,无论是为了收到货物还是为了交付货物(并有望获利)。
未来到期日与现货价格操作在同一天发生。
这意味着:
- 平台从两种不同的资产中获取数据点。
- 平台必须以某种方式理解资产之间的关系,并且操作现货价格将关闭在期货上开放的头寸。
实际上,未来并没有关闭,只有物理交割是补偿的。
使用补偿概念,backtrader
添加了一种让用户向平台传达在一个数据源上的事情将对另一个数据源产生补偿效果的方法。使用模式
import backtrader as bt cerebro = bt.Cerebro() data0 = bt.feeds.MyFavouriteDataFeed(dataname='futurename') cerebro.adddata(data0) data1 = bt.feeds.MyFavouriteDataFeed(dataname='spotname') data1.compensate(data0) # let the system know ops on data1 affect data0 cerebro.adddata(data1) ... cerebro.run()
将所有内容放在一起
例子总是值得一千篇文章,所以让我们把所有的东西放在一起。
- 使用
backtrader
源中的标准示例数据之一。这将是未来的。 - 通过重复使用相同的数据源并添加一个过滤器,该过滤器将随机将价格移动到上/下几个点,以创建一个价差,来模拟类似但不同的价格。如下所示:
# The filter which changes the close price def close_changer(data, *args, **kwargs): data.close[0] += 50.0 * random.randint(-1, 1) return False # length of stream is unchanged`
- 将绘图到相同的轴上会混合默认包含的
BuyObserver
标记,因此标准观察器将被禁用,并手动重新添加以使用不同的每个数据标记进行绘图。 - 位置将随机输入,并在 10 天后退出。
这与期货到期期限不符,但这只是为了实现功能,而不是检查交易日历。
!!! 注意
A simulation including execution on the spot price on the day of future expiration would require activating `cheat-on-close` to make sure the orders are executed when the future expires. This is not needed in this sample, because the expiration is being chosen at random.
- 注意策略
buy
操作在data0
上执行。sell
操作在data1
上执行。
class St(bt.Strategy): def __init__(self): bt.obs.BuySell(self.data0, barplot=True) # done here for BuySellArrows(self.data1, barplot=True) # different markers per data def next(self): if not self.position: if random.randint(0, 1): self.buy(data=self.data0) self.entered = len(self) else: # in the market if (len(self) - self.entered) >= 10: self.sell(data=self.data1)`
执行:
$ ./future-spot.py --no-comp • 1
使用此图形输出。
它是有效的:
buy
操作以指向上的绿色三角形的形式发出信号,图例告诉我们它们属于data0
,如预期的那样。sell
操作以向下箭头的形式发出信号,图例告诉我们它们属于data1
,如预期的那样。- 交易正在关闭,即使它们是以
data0
开仓并以data1
平仓,也能实现期望的效果(在现实生活中,这意味着避免通过期货获得的商品的实物交割)。
如果没有补偿,人们可能会想象会发生什么。让我们来做一下:
$ ./future-spot.py --no-comp
输出结果
很明显,这是失败的:
- 逻辑期望
data0
上的仓位在data1
上的操作关闭,并且只有在市场中没有仓位时才开放data0
上的仓位。 - 但补偿已被停用,并且对
data0
的初始操作(绿色三角形)从未关闭,因此不会启动任何其他操作,而data1
上的空头仓位开始累积。
示例用法
$ ./future-spot.py --help usage: future-spot.py [-h] [--no-comp] Compensation example optional arguments: -h, --help show this help message and exit --no-comp
示例代码
from __future__ import (absolute_import, division, print_function, unicode_literals) import argparse import random import backtrader as bt # The filter which changes the close price def close_changer(data, *args, **kwargs): data.close[0] += 50.0 * random.randint(-1, 1) return False # length of stream is unchanged # override the standard markers class BuySellArrows(bt.observers.BuySell): plotlines = dict(buy=dict(marker='$\u21E7$', markersize=12.0), sell=dict(marker='$\u21E9$', markersize=12.0)) class St(bt.Strategy): def __init__(self): bt.obs.BuySell(self.data0, barplot=True) # done here for BuySellArrows(self.data1, barplot=True) # different markers per data def next(self): if not self.position: if random.randint(0, 1): self.buy(data=self.data0) self.entered = len(self) else: # in the market if (len(self) - self.entered) >= 10: self.sell(data=self.data1) def runstrat(args=None): args = parse_args(args) cerebro = bt.Cerebro() dataname = '../../datas/2006-day-001.txt' # data feed data0 = bt.feeds.BacktraderCSVData(dataname=dataname, name='data0') cerebro.adddata(data0) data1 = bt.feeds.BacktraderCSVData(dataname=dataname, name='data1') data1.addfilter(close_changer) if not args.no_comp: data1.compensate(data0) data1.plotinfo.plotmaster = data0 cerebro.adddata(data1) cerebro.addstrategy(St) # sample strategy cerebro.addobserver(bt.obs.Broker) # removed below with stdstats=False cerebro.addobserver(bt.obs.Trades) # removed below with stdstats=False cerebro.broker.set_coc(True) cerebro.run(stdstats=False) # execute cerebro.plot(volume=False) # and plot def parse_args(pargs=None): parser = argparse.ArgumentParser( formatter_class=argparse.ArgumentDefaultsHelpFormatter, description=('Compensation example')) parser.add_argument('--no-comp', required=False, action='store_true') return parser.parse_args(pargs) if __name__ == '__main__': runstrat()
StopTrail(Limit)
原文:
www.backtrader.com/docu/order-creation-execution/trail/stoptrail/
版本1.9.35.116
将StopTrail
和StopTrailLimit
订单执行类型添加到回测武器库。
注意
这仅在回测中实现,尚未针对实时经纪人实现
注意
用版本1.9.36.116
更新。交互式经纪人支持StopTrail
、StopTrailLimit
和OCO
。
OCO
总是将第 1 个订单作为参数oco
指定为一组StopTrailLimit
:经纪人模拟和IB
经纪人具有相同的行为。指定:price
作为初始触发价格(也指定trailamount
),然后plimi
作为初始限价。两者之间的差异将确定limitoffset
(限价与停止触发价格之间的距离)
用法模式完全集成到策略实例的标准buy
、sell
和close
市场操作方法中。注意:
- 指示希望的执行类型,如
exectype=bt.Order.StopTrail
- 以及是否必须使用固定距离或百分比距离计算跟踪价格
- 固定距离:
trailamount=10
- 百分比距离:
trailpercent=0.02
(即:2%
)
如果通过发出buy
进入市场,这是sell
与StopTrail
和trailamount
一起的操作:
- 如果未指定
price
,则使用最新的close
价格 trailamount
从价格中减去以找到stop
(或触发)价格- 下一个迭代的经纪人检查触发价格是否已达到
- 如果是:订单以
Market
执行类型的方法执行 - 如果否,则使用最新的
close
价格减去trailamount
距离重新计算stop
价格 - 如果新价格上涨,则更新
- 如果新价格将下跌(或根本不变),则将其丢弃
也就是说:跟踪止损价格随价格上涨而上涨,但如果价格开始下跌,则保持不变,以潜在地获利。
如果通过sell
进入市场,然后通过StopTrail
发出buy
订单只是做相反的操作,即:价格向下跟随。
一些用法模式
# For a StopTrail going downwards # last price will be used as reference self.buy(size=1, exectype=bt.Order.StopTrail, trailamount=0.25) # or self.buy(size=1, exectype=bt.Order.StopTrail, price=10.50, trailamount=0.25) # For a StopTrail going upwards # last price will be used as reference self.sell(size=1, exectype=bt.Order.StopTrail, trailamount=0.25) # or self.sell(size=1, exectype=bt.Order.StopTrail, price=10.50, trailamount=0.25)
也可以指定trailpercent
而不是trailamount
,价格与价格的距离将被计算为价格的百分比
# For a StopTrail going downwards with 2% distance # last price will be used as reference self.buy(size=1, exectype=bt.Order.StopTrail, trailpercent=0.02) # or self.buy(size=1, exectype=bt.Order.StopTrail, price=10.50, trailpercent=0.0.02) # For a StopTrail going upwards with 2% difference # last price will be used as reference self.sell(size=1, exectype=bt.Order.StopTrail, trailpercent=0.02) # or self.sell(size=1, exectype=bt.Order.StopTrail, price=10.50, trailpercent=0.02)
对于StopTrailLimit
- 唯一的区别在于当触发跟踪止损价格时会发生什么。
- 在这种情况下,订单以
Limit
订单的形式执行(与StopLimit
订单的行为相同,但在这种情况下,触发价格是动态的)
注意:必须指定plimit=x.x
给buy
或sell
,这将是限价
注意:限价不像停止/触发价格那样动态更改
例如总是值得一看,因此通常的backtrader示例,其中
- 使用移动平均线上穿进入市场多头
- 使用跟踪止损退出市场
使用50
点固定价格距离的执行
$ ./trail.py --plot --strat trailamount=50.0
这产生了以下图表
和以下输出:
************************************************** 2005-02-14,3075.76,3025.76,3025.76 ---------- 2005-02-15,3086.95,3036.95,3036.95 2005-02-16,3068.55,3036.95,3018.55 2005-02-17,3067.34,3036.95,3017.34 2005-02-18,3072.04,3036.95,3022.04 2005-02-21,3063.64,3036.95,3013.64 ... ... ************************************************** 2005-05-19,3051.79,3001.79,3001.79 ---------- 2005-05-20,3050.45,3001.79,3000.45 2005-05-23,3070.98,3020.98,3020.98 2005-05-24,3066.55,3020.98,3016.55 2005-05-25,3059.84,3020.98,3009.84 2005-05-26,3086.08,3036.08,3036.08 2005-05-27,3084.0,3036.08,3034.0 2005-05-30,3096.54,3046.54,3046.54 2005-05-31,3076.75,3046.54,3026.75 2005-06-01,3125.88,3075.88,3075.88 2005-06-02,3131.03,3081.03,3081.03 2005-06-03,3114.27,3081.03,3064.27 2005-06-06,3099.2,3081.03,3049.2 2005-06-07,3134.82,3084.82,3084.82 2005-06-08,3125.59,3084.82,3075.59 2005-06-09,3122.93,3084.82,3072.93 2005-06-10,3143.85,3093.85,3093.85 2005-06-13,3159.83,3109.83,3109.83 2005-06-14,3162.86,3112.86,3112.86 2005-06-15,3147.55,3112.86,3097.55 2005-06-16,3160.09,3112.86,3110.09 2005-06-17,3178.48,3128.48,3128.48 2005-06-20,3162.14,3128.48,3112.14 2005-06-21,3179.62,3129.62,3129.62 2005-06-22,3182.08,3132.08,3132.08 2005-06-23,3190.8,3140.8,3140.8 2005-06-24,3161.0,3140.8,3111.0 ... ... ... ************************************************** 2006-12-19,4100.48,4050.48,4050.48 ---------- 2006-12-20,4118.54,4068.54,4068.54 2006-12-21,4112.1,4068.54,4062.1 2006-12-22,4073.5,4068.54,4023.5 2006-12-27,4134.86,4084.86,4084.86 2006-12-28,4130.66,4084.86,4080.66 2006-12-29,4119.94,4084.86,4069.94
而不是等待通常的下穿模式,系统使用跟踪止损退出市场。例如,让我们看看第一次操作。
- 进入多头时的收盘价:
3075.76
- 系统计算的跟踪止损价:
3025.76
(相距50
个单位) - 样本计算的跟踪止损价:
3025.76
(每行显示的最后价格)
在第一次计算之后:
- 收盘价上涨至
3086.95
,止损价调整为3036.95
- 以下收盘价不超过
3086.95
,触发价格不变
在其他两次操作中也可以看到相同的模式。
为了比较,仅使用30
点固定距离的执行(仅图表)
$ ./trail.py --plot --strat trailamount=30.0
和图表
最后一次执行后跟随trailpercent=0.02
$ ./trail.py --plot --strat trailpercent=0.02
相应的图表。
示例用法
$ ./trail.py --help usage: trail.py [-h] [--data0 DATA0] [--fromdate FROMDATE] [--todate TODATE] [--cerebro kwargs] [--broker kwargs] [--sizer kwargs] [--strat kwargs] [--plot [kwargs]] StopTrail Sample optional arguments: -h, --help show this help message and exit --data0 DATA0 Data to read in (default: ../../datas/2005-2006-day-001.txt) --fromdate FROMDATE Date[time] in YYYY-MM-DD[THH:MM:SS] format (default: ) --todate TODATE Date[time] in YYYY-MM-DD[THH:MM:SS] format (default: ) --cerebro kwargs kwargs in key=value format (default: ) --broker kwargs kwargs in key=value format (default: ) --sizer kwargs kwargs in key=value format (default: ) --strat kwargs kwargs in key=value format (default: ) --plot [kwargs] kwargs in key=value format (default: )
示例代码
from __future__ import (absolute_import, division, print_function, unicode_literals) import argparse import datetime import backtrader as bt class St(bt.Strategy): params = dict( ma=bt.ind.SMA, p1=10, p2=30, stoptype=bt.Order.StopTrail, trailamount=0.0, trailpercent=0.0, ) def __init__(self): ma1, ma2 = self.p.ma(period=self.p.p1), self.p.ma(period=self.p.p2) self.crup = bt.ind.CrossUp(ma1, ma2) self.order = None def next(self): if not self.position: if self.crup: o = self.buy() self.order = None print('*' * 50) elif self.order is None: self.order = self.sell(exectype=self.p.stoptype, trailamount=self.p.trailamount, trailpercent=self.p.trailpercent) if self.p.trailamount: tcheck = self.data.close - self.p.trailamount else: tcheck = self.data.close * (1.0 - self.p.trailpercent) print(','.join( map(str, [self.datetime.date(), self.data.close[0], self.order.created.price, tcheck]) ) ) print('-' * 10) else: if self.p.trailamount: tcheck = self.data.close - self.p.trailamount else: tcheck = self.data.close * (1.0 - self.p.trailpercent) print(','.join( map(str, [self.datetime.date(), self.data.close[0], self.order.created.price, tcheck]) ) ) def runstrat(args=None): args = parse_args(args) cerebro = bt.Cerebro() # Data feed kwargs kwargs = dict() # Parse from/to-date dtfmt, tmfmt = '%Y-%m-%d', 'T%H:%M:%S' for a, d in ((getattr(args, x), x) for x in ['fromdate', 'todate']): if a: strpfmt = dtfmt + tmfmt * ('T' in a) kwargs[d] = datetime.datetime.strptime(a, strpfmt) # Data feed data0 = bt.feeds.BacktraderCSVData(dataname=args.data0, **kwargs) cerebro.adddata(data0) # Broker cerebro.broker = bt.brokers.BackBroker(**eval('dict(' + args.broker + ')')) # Sizer cerebro.addsizer(bt.sizers.FixedSize, **eval('dict(' + args.sizer + ')')) # Strategy cerebro.addstrategy(St, **eval('dict(' + args.strat + ')')) # Execute cerebro.run(**eval('dict(' + args.cerebro + ')')) if args.plot: # Plot if requested to cerebro.plot(**eval('dict(' + args.plot + ')')) def parse_args(pargs=None): parser = argparse.ArgumentParser( formatter_class=argparse.ArgumentDefaultsHelpFormatter, description=( 'StopTrail Sample' ) ) parser.add_argument('--data0', default='../../datas/2005-2006-day-001.txt', required=False, help='Data to read in') # Defaults for dates parser.add_argument('--fromdate', required=False, default='', help='Date[time] in YYYY-MM-DD[THH:MM:SS] format') parser.add_argument('--todate', required=False, default='', help='Date[time] in YYYY-MM-DD[THH:MM:SS] format') parser.add_argument('--cerebro', required=False, default='', metavar='kwargs', help='kwargs in key=value format') parser.add_argument('--broker', required=False, default='', metavar='kwargs', help='kwargs in key=value format') parser.add_argument('--sizer', required=False, default='', metavar='kwargs', help='kwargs in key=value format') parser.add_argument('--strat', required=False, default='', metavar='kwargs', help='kwargs in key=value format') parser.add_argument('--plot', required=False, default='', nargs='?', const='{}', metavar='kwargs', help='kwargs in key=value format') return parser.parse_args(pargs) if __name__ == '__main__': runstrat()
BackTrader 中文文档(九)(2)https://developer.aliyun.com/article/1505289