版本 1.6.4.93 标志着 backtrader 的一个重要里程碑,即使版本号的更改很小。
职位大小调整是阅读Van K. Tharp的《Trade Your Way To Financial Freedom 》后,为这个项目奠定基础的事情之一。
这不是Van K. Tharp详细介绍他的职位调整方法的书,而是在书中提出和讨论的主题。关于这一点的范例之一具有此设置
-
如果不在市场上,扔硬币决定是否进入
-
如果已经在市场上,则使用止损控制仓位,止损为2 x ATR,如果价格有利地移动到所持仓位,则该止损会更新
关于这一点的重要部分:
-
进入市场是随机的
-
该方法使用不同的Size方案进行测试,这与动态停止一起使系统有利可图
遵循创建自己的“系统”(手动/自动/计算机化,技术/基础化等)的原则, backtrader 诞生是为了有朝一日测试这种情况。
这可以在任何现有的平台上进行测试,但在此过程中不会有任何乐趣,并且解决了许多挑战,在启动时甚至没有考虑这些挑战 backtrader
Sizers 从一开始就在平台上,但由于包括即时交易在内的许多其他事情开始成为障碍,因此隐藏了。但这现在已经结束了,Van K. Tharp的情景将受到考验。而不是迟早。
与此同时, Sizers 进行样品测试。
控制定位的Sizers
该示例显示了一个潜在的用例,在该用例中, Sizers 通过控制大小来更改策略的行为。请查看 backtrader.readthedocs.io 上的文档,了解大小调整接口。
2 sizers:
-
LongOnly
:如果当前仓位为0,将返回固定大小仓位,如果已经在市场上,将返回相同的固定大小以 close 它。class LongOnly(bt.Sizer): params = (('stake', 1),) def _getsizing(self, comminfo, cash, data, isbuy): if isbuy: return self.p.stake # Sell situation position = self.strategy.getposition(data) if not position.size: return 0 # do not sell if nothing is open return self.p.stake
-
FixedReverser
:如果不在市场上,将返回固定大小的股份,如果已经在市场上,将返回两倍的固定规模的股份,以允许逆转class FixedReverser(bt.Sizer): params = (('stake', 1),) def _getsizing(self, comminfo, cash, data, isbuy): position = self.broker.getposition(data) size = self.p.stake * (1 + (position.size != 0)) return size
这2 Sizers 将与一个非常简单的策略相结合。
class CloseSMA(bt.Strategy): params = (('period', 15),) def __init__(self): sma = bt.indicators.SMA(self.data, period=self.p.period) self.crossover = bt.indicators.CrossOver(self.data, sma) def next(self): if self.crossover > 0: self.buy() elif self.crossover < 0: self.sell()
请注意该策略如何使用 Close-SMA 交叉信号来发出买入和卖出命令,并考虑一件重要的事情:
- 策略中不运行定位检查
与以下运行中所示的策略相同,只需使用示例中的此代码更改 sizer (使用开关--longonly
控制),即可将行为从仅做多更改为多短
if args.longonly: cerebro.addsizer(LongOnly, stake=args.stake) else: cerebro.addsizer(FixedReverser, stake=args.stake)
仅长期运行
使用以下命令完成:
$ ./sizertest.py --longonly --plot
还有这个输出。
多空运行
使用以下命令完成:
$ ./sizertest.py --plot
还有这个输出。
哪个立即显示:
-
交易数量翻了一番
-
现金(除了开始时)永远不会等于价值,因为策略总是在市场上
示例用法
$ ./sizertest.py --help usage: sizertest.py [-h] [--data0 DATA0] [--fromdate FROMDATE] [--todate TODATE] [--cash CASH] [--longonly] [--stake STAKE] [--period PERIOD] [--plot [kwargs]] Sample for sizer optional arguments: -h, --help show this help message and exit --data0 DATA0 Data to be read in (default: ../../datas/yhoo-1996-2015.txt) --fromdate FROMDATE Starting date in YYYY-MM-DD format (default: 2005-01-01) --todate TODATE Ending date in YYYY-MM-DD format (default: 2006-12-31) --cash CASH Cash to start with (default: 50000) --longonly Use the LongOnly sizer (default: False) --stake STAKE Stake to pass to the sizers (default: 1) --period PERIOD Period for the Simple Moving Average (default: 15) --plot [kwargs], -p [kwargs] Plot the read data applying any kwargs passed For example: --plot style="candle" (to plot candles) (default: None)
完整代码
from __future__ import (absolute_import, division, print_function, unicode_literals) import argparse import datetime import random import backtrader as bt class CloseSMA(bt.Strategy): params = (('period', 15),) def __init__(self): sma = bt.indicators.SMA(self.data, period=self.p.period) self.crossover = bt.indicators.CrossOver(self.data, sma) def next(self): if self.crossover > 0: self.buy() elif self.crossover < 0: self.sell() class LongOnly(bt.Sizer): params = (('stake', 1),) def _getsizing(self, comminfo, cash, data, isbuy): if isbuy: return self.p.stake # Sell situation position = self.strategy.getposition(data) if not position.size: return 0 # do not sell if nothing is open return self.p.stake class FixedReverser(bt.Sizer): params = (('stake', 1),) def _getsizing(self, comminfo, cash, data, isbuy): position = self.broker.getposition(data) size = self.p.stake * (1 + (position.size != 0)) return size def runstrat(args=None): args = parse_args(args) cerebro = bt.Cerebro() cerebro.broker.set_cash(args.cash) dkwargs = dict() if args.fromdate: fromdate = datetime.datetime.strptime(args.fromdate, '%Y-%m-%d') dkwargs['fromdate'] = fromdate if args.todate: todate = datetime.datetime.strptime(args.todate, '%Y-%m-%d') dkwargs['todate'] = todate data0 = bt.feeds.YahooFinanceCSVData(dataname=args.data0, **dkwargs) cerebro.adddata(data0, name='Data0') cerebro.addstrategy(CloseSMA, period=args.period) if args.longonly: cerebro.addsizer(LongOnly, stake=args.stake) else: cerebro.addsizer(FixedReverser, stake=args.stake) cerebro.run() if args.plot: pkwargs = dict() if args.plot is not True: # evals to True but is not True pkwargs = eval('dict(' + args.plot + ')') # args were passed cerebro.plot(**pkwargs) def parse_args(pargs=None): parser = argparse.ArgumentParser( formatter_class=argparse.ArgumentDefaultsHelpFormatter, description='Sample for sizer') parser.add_argument('--data0', required=False, default='../../datas/yhoo-1996-2015.txt', help='Data to be read in') parser.add_argument('--fromdate', required=False, default='2005-01-01', help='Starting date in YYYY-MM-DD format') parser.add_argument('--todate', required=False, default='2006-12-31', help='Ending date in YYYY-MM-DD format') parser.add_argument('--cash', required=False, action='store', type=float, default=50000, help=('Cash to start with')) parser.add_argument('--longonly', required=False, action='store_true', help=('Use the LongOnly sizer')) parser.add_argument('--stake', required=False, action='store', type=int, default=1, help=('Stake to pass to the sizers')) parser.add_argument('--period', required=False, action='store', type=int, default=15, help=('Period for the Simple Moving Average')) # Plot options parser.add_argument('--plot', '-p', nargs='?', required=False, metavar='kwargs', const=True, help=('Plot the read data applying any kwargs passed\n' '\n' 'For example:\n' '\n' ' --plot style="candle" (to plot candles)\n')) if pargs is not None: return parser.parse_args(pargs) return parser.parse_args() if __name__ == '__main__': runstrat()