版本1.9.35.116將StopTrail和StopTrailLimit訂單執行類型添加到回測庫中。
筆記
這僅在回測中實現,還沒有針對實時經紀人的實現
筆記
更新為1.9.36.116版本。盈透證券支持StopTrail 、 StopTrailLimit和OCO 。
OCO始終將組中的oco一個訂單指定為參數ocoStopTrailLimit:代理模擬和IB代理具有相同的行為。指定:price作為初始止損觸發價格(同時指定trailamount),然後plimi作為初始限價。兩者的差值將決定limitoffset(限價與止損觸發價之間的距離)
使用模式完全集成到策略實例的標準buy 、 sell和close操作方法中。通知:
指示希望使用哪種執行類型,如
exectype=bt.Order.StopTrail以及是否必須使用固定距離或基於百分比的距離計算追踪價格
固定距離:
trailamount=10基於百分比的距離:
trailpercent=0.02(即:2%)
如果一個人通過發出buy進入市場多頭,這就是使用StopTrail和trailamount的sell的作用:
如果未指定
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訂單的行為相同,但在這種情況下具有動態觸發價格)注意:
buy或sell必須指定plimit=xx,這將是限價注意:限價不會像止損/觸發價格那樣動態變化
一個例子總是值一千字,因此通常的反向交易樣本,
使用移動平均線向上交叉進入市場做多
使用追踪止損退出市場
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且觸發價不變
在其他 2 個操作中可以看到相同的模式。
為了比較,只有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()