BackTrader 中文文档(二十八)(1)https://developer.aliyun.com/article/1505486
执行类型:止损限价
设置了信号价格上涨 1%的止损价。但限价设置在信号(收盘)价格上涨 0.5%,这可以解释为:等待力量展现,但不要买入高峰。等待下跌。
有效期限制为 20(日历)天
elif self.p.exectype == 'StopLimit': price = self.data.close * (1.0 + self.p.perc1 / 100.0) plimit = self.data.close * (1.0 + self.p.perc2 / 100.0) self.buy(exectype=bt.Order.StopLimit, price=price, valid=valid, plimit=plimit) if self.p.valid: txt = ('BUY CREATE, exectype StopLimit, price %.2f,' ' valid: %s, pricelimit: %.2f') self.log(txt % (price, valid.strftime('%Y-%m-%d'), plimit)) else: txt = ('BUY CREATE, exectype StopLimit, price %.2f,' ' pricelimit: %.2f') self.log(txt % (price, plimit))
输出图表。
命令行和输出:
$ ./order-execution-samples.py --exectype StopLimit --perc1 1 --perc2 0.5 --valid 20 2006-01-26T23:59:59+00:00, BUY CREATE, exectype StopLimit, price 3677.83, valid: 2006-02-15, pricelimit: 3659.63 2006-01-26T23:59:59+00:00, ORDER ACCEPTED/SUBMITTED 2006-02-03T23:59:59+00:00, BUY EXECUTED, Price: 3659.63, Cost: 3659.63, Comm 0.00 2006-03-02T23:59:59+00:00, SELL CREATE, 3763.73 2006-03-02T23:59:59+00:00, ORDER ACCEPTED/SUBMITTED 2006-03-03T23:59:59+00:00, SELL EXECUTED, Price: 3763.95, Cost: 3763.95, Comm 0.00 2006-03-10T23:59:59+00:00, BUY CREATE, exectype StopLimit, price 3836.44, valid: 2006-03-30, pricelimit: 3817.45 2006-03-10T23:59:59+00:00, ORDER ACCEPTED/SUBMITTED 2006-03-21T23:59:59+00:00, BUY EXECUTED, Price: 3817.45, Cost: 3817.45, Comm 0.00 2006-03-28T23:59:59+00:00, SELL CREATE, 3811.45 2006-03-28T23:59:59+00:00, ORDER ACCEPTED/SUBMITTED 2006-03-29T23:59:59+00:00, SELL EXECUTED, Price: 3811.85, Cost: 3811.85, Comm 0.00 2006-03-30T23:59:59+00:00, BUY CREATE, exectype StopLimit, price 3913.36, valid: 2006-04-19, pricelimit: 3893.98 2006-03-30T23:59:59+00:00, ORDER ACCEPTED/SUBMITTED 2006-04-19T23:59:59+00:00, BUY EXPIRED ... ... 2006-12-11T23:59:59+00:00, BUY CREATE, exectype StopLimit, price 4093.42, valid: 2006-12-31, pricelimit: 4073.15 2006-12-11T23:59:59+00:00, ORDER ACCEPTED/SUBMITTED 2006-12-22T23:59:59+00:00, BUY EXECUTED, Price: 4073.15, Cost: 4073.15, Comm 0.00
测试脚本执行
在命令行的help中详细说明:
$ ./order-execution-samples.py --help usage: order-execution-samples.py [-h] [--infile INFILE] [--csvformat {bt,visualchart,sierrachart,yahoo,yahoo_unreversed}] [--fromdate FROMDATE] [--todate TODATE] [--plot] [--plotstyle {bar,line,candle}] [--numfigs NUMFIGS] [--smaperiod SMAPERIOD] [--exectype EXECTYPE] [--valid VALID] [--perc1 PERC1] [--perc2 PERC2] Showcase for Order Execution Types optional arguments: -h, --help show this help message and exit --infile INFILE, -i INFILE File to be read in --csvformat {bt,visualchart,sierrachart,yahoo,yahoo_unreversed}, -c {bt,visualchart,sierrachart,yahoo,yahoo_unreversed} CSV Format --fromdate FROMDATE, -f FROMDATE Starting date in YYYY-MM-DD format --todate TODATE, -t TODATE Ending date in YYYY-MM-DD format --plot, -p Plot the read data --plotstyle {bar,line,candle}, -ps {bar,line,candle} Plot the read data --numfigs NUMFIGS, -n NUMFIGS Plot using n figures --smaperiod SMAPERIOD, -s SMAPERIOD Simple Moving Average Period --exectype EXECTYPE, -e EXECTYPE Execution Type: Market (default), Close, Limit, Stop, StopLimit --valid VALID, -v VALID Validity for Limit sample: default 0 days --perc1 PERC1, -p1 PERC1 % distance from close price at order creation time for the limit/trigger price in Limit/Stop orders --perc2 PERC2, -p2 PERC2 % distance from close price at order creation time for the limit price in StopLimit orders
完整代码
from __future__ import (absolute_import, division, print_function, unicode_literals) import argparse import datetime import os.path import time import sys import backtrader as bt import backtrader.feeds as btfeeds import backtrader.indicators as btind class OrderExecutionStrategy(bt.Strategy): params = ( ('smaperiod', 15), ('exectype', 'Market'), ('perc1', 3), ('perc2', 1), ('valid', 4), ) def log(self, txt, dt=None): ''' Logging function fot this strategy''' dt = dt or self.data.datetime[0] if isinstance(dt, float): dt = bt.num2date(dt) print('%s, %s' % (dt.isoformat(), txt)) def notify_order(self, order): if order.status in [order.Submitted, order.Accepted]: # Buy/Sell order submitted/accepted to/by broker - Nothing to do self.log('ORDER ACCEPTED/SUBMITTED', dt=order.created.dt) self.order = order return if order.status in [order.Expired]: self.log('BUY EXPIRED') elif order.status in [order.Completed]: if order.isbuy(): self.log( 'BUY EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' % (order.executed.price, order.executed.value, order.executed.comm)) else: # Sell self.log('SELL EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' % (order.executed.price, order.executed.value, order.executed.comm)) # Sentinel to None: new orders allowed self.order = None def __init__(self): # SimpleMovingAverage on main data # Equivalent to -> sma = btind.SMA(self.data, period=self.p.smaperiod) sma = btind.SMA(period=self.p.smaperiod) # CrossOver (1: up, -1: down) close / sma self.buysell = btind.CrossOver(self.data.close, sma, plot=True) # Sentinel to None: new ordersa allowed self.order = None def next(self): if self.order: # An order is pending ... nothing can be done return # Check if we are in the market if self.position: # In the maerket - check if it's the time to sell if self.buysell < 0: self.log('SELL CREATE, %.2f' % self.data.close[0]) self.sell() elif self.buysell > 0: if self.p.valid: valid = self.data.datetime.date(0) + \ datetime.timedelta(days=self.p.valid) else: valid = None # Not in the market and signal to buy if self.p.exectype == 'Market': self.buy(exectype=bt.Order.Market) # default if not given self.log('BUY CREATE, exectype Market, price %.2f' % self.data.close[0]) elif self.p.exectype == 'Close': self.buy(exectype=bt.Order.Close) self.log('BUY CREATE, exectype Close, price %.2f' % self.data.close[0]) elif self.p.exectype == 'Limit': price = self.data.close * (1.0 - self.p.perc1 / 100.0) self.buy(exectype=bt.Order.Limit, price=price, valid=valid) if self.p.valid: txt = 'BUY CREATE, exectype Limit, price %.2f, valid: %s' self.log(txt % (price, valid.strftime('%Y-%m-%d'))) else: txt = 'BUY CREATE, exectype Limit, price %.2f' self.log(txt % price) elif self.p.exectype == 'Stop': price = self.data.close * (1.0 + self.p.perc1 / 100.0) self.buy(exectype=bt.Order.Stop, price=price, valid=valid) if self.p.valid: txt = 'BUY CREATE, exectype Stop, price %.2f, valid: %s' self.log(txt % (price, valid.strftime('%Y-%m-%d'))) else: txt = 'BUY CREATE, exectype Stop, price %.2f' self.log(txt % price) elif self.p.exectype == 'StopLimit': price = self.data.close * (1.0 + self.p.perc1 / 100.0) plimit = self.data.close * (1.0 + self.p.perc2 / 100.0) self.buy(exectype=bt.Order.StopLimit, price=price, valid=valid, plimit=plimit) if self.p.valid: txt = ('BUY CREATE, exectype StopLimit, price %.2f,' ' valid: %s, pricelimit: %.2f') self.log(txt % (price, valid.strftime('%Y-%m-%d'), plimit)) else: txt = ('BUY CREATE, exectype StopLimit, price %.2f,' ' pricelimit: %.2f') self.log(txt % (price, plimit)) def runstrat(): args = parse_args() cerebro = bt.Cerebro() data = getdata(args) cerebro.adddata(data) cerebro.addstrategy( OrderExecutionStrategy, exectype=args.exectype, perc1=args.perc1, perc2=args.perc2, valid=args.valid, smaperiod=args.smaperiod ) cerebro.run() if args.plot: cerebro.plot(numfigs=args.numfigs, style=args.plotstyle) def getdata(args): dataformat = dict( bt=btfeeds.BacktraderCSVData, visualchart=btfeeds.VChartCSVData, sierrachart=btfeeds.SierraChartCSVData, yahoo=btfeeds.YahooFinanceCSVData, yahoo_unreversed=btfeeds.YahooFinanceCSVData ) dfkwargs = dict() if args.csvformat == 'yahoo_unreversed': dfkwargs['reverse'] = True if args.fromdate: fromdate = datetime.datetime.strptime(args.fromdate, '%Y-%m-%d') dfkwargs['fromdate'] = fromdate if args.todate: fromdate = datetime.datetime.strptime(args.todate, '%Y-%m-%d') dfkwargs['todate'] = todate dfkwargs['dataname'] = args.infile dfcls = dataformat[args.csvformat] return dfcls(**dfkwargs) def parse_args(): parser = argparse.ArgumentParser( description='Showcase for Order Execution Types') parser.add_argument('--infile', '-i', required=False, default='../datas/2006-day-001.txt', help='File to be read in') parser.add_argument('--csvformat', '-c', required=False, default='bt', choices=['bt', 'visualchart', 'sierrachart', 'yahoo', 'yahoo_unreversed'], help='CSV Format') parser.add_argument('--fromdate', '-f', required=False, default=None, help='Starting date in YYYY-MM-DD format') parser.add_argument('--todate', '-t', required=False, default=None, help='Ending date in YYYY-MM-DD format') parser.add_argument('--plot', '-p', action='store_false', required=False, help='Plot the read data') parser.add_argument('--plotstyle', '-ps', required=False, default='bar', choices=['bar', 'line', 'candle'], help='Plot the read data') parser.add_argument('--numfigs', '-n', required=False, default=1, help='Plot using n figures') parser.add_argument('--smaperiod', '-s', required=False, default=15, help='Simple Moving Average Period') parser.add_argument('--exectype', '-e', required=False, default='Market', help=('Execution Type: Market (default), Close, Limit,' ' Stop, StopLimit')) parser.add_argument('--valid', '-v', required=False, default=0, type=int, help='Validity for Limit sample: default 0 days') parser.add_argument('--perc1', '-p1', required=False, default=0.0, type=float, help=('%% distance from close price at order creation' ' time for the limit/trigger price in Limit/Stop' ' orders')) parser.add_argument('--perc2', '-p2', required=False, default=0.0, type=float, help=('%% distance from close price at order creation' ' time for the limit price in StopLimit orders')) return parser.parse_args() if __name__ == '__main__': runstrat()
扩展数据源
原文:
www.backtrader.com/blog/posts/2015-08-07-extending-a-datafeed/extending-a-datafeed/
GitHub 上的问题实际上正在推动完成文档部分或帮助我了解 backtrader 是否具有我最初设想的易用性和灵活性,并沿途做出的决定。
在这种情况下是问题#9。
最终问题似乎归结为:
- 最终用户是否可以轻松扩展现有机制,以添加额外信息,以行的形式传递到其他现有价格信息点,如
open,high等?
就我理解的问题,答案是:是的
发帖人似乎有这些要求(来自问题#6):
- 一个被解析为 CSV 格式的数据源
- 使用
GenericCSVData来加载信息
这种通用的 CSV 支持是为了响应这个问题#6而开发的 - 一个额外字段,显然包含需要传递到解析的 CSV 数据中的 P/E 信息
让我们在 CSV 数据源开发和通用 CSV 数据源示例帖子的基础上构建。
步骤:
- 假设 P/E 信息被设置在被解析的 CSV 数据中
- 使用
GenericCSVData作为基类 - 将现有线(open/high/low/close/volumen/openinterest)扩展为
pe - 添加一个参数,让调用者确定 P/E 信息的列位置
结果:
from backtrader.feeds import GenericCSVData class GenericCSV_PE(GenericCSVData): # Add a 'pe' line to the inherited ones from the base class lines = ('pe',) # openinterest in GenericCSVData has index 7 ... add 1 # add the parameter to the parameters inherited from the base class params = (('pe', 8),)
工作完成了…
稍后,在策略中使用这个数据源时:
import backtrader as bt .... class MyStrategy(bt.Strategy): ... def next(self): if self.data.close > 2000 and self.data.pe < 12: # TORA TORA TORA --- Get off this market self.sell(stake=1000000, price=0.01, exectype=Order.Limit) ...
绘制额外的 P/E 线
显然,在数据源中没有对该额外线的自动绘图支持。
最好的选择是对该线进行简单移动平均,并在单独的轴上绘制它:
import backtrader as bt import backtrader.indicators as btind .... class MyStrategy(bt.Strategy): def __init__(self): # The indicator autoregisters and will plot even if no obvious # reference is kept to it in the class btind.SMA(self.data.pe, period=1, subplot=False) ... def next(self): if self.data.close > 2000 and self.data.pe < 12: # TORA TORA TORA --- Get off this market self.sell(stake=1000000, price=0.01, exectype=Order.Limit) ...
BackTrader 中文文档(二十八)(3)https://developer.aliyun.com/article/1505488