在1.8.10.96
版本之前,可以使用反向交易者通过策略方法进行智能质押: buy
和sell
。这一切都是为了在负责赌注大小的等式中添加一个Sizer
。
Sizer不能做的是决定操作是买入还是卖出。这意味着需要一个新概念,在其中添加一个小的智能层来做出这样的决定。
这是 Strategy 中的order_target_xxx
方法家族发挥作用的地方。受zipline
中的启发,这些方法提供了简单地指定最终目标的机会,成为目标:
size
-> 特定资产投资组合中的股份数量、合约数量value
-> 投资组合中资产的货币单位价值percent
-> 当前投资组合中资产的百分比(来自当前投资组合)
笔记
这些方法的参考可以在策略中找到。总结是这些方法使用与buy
和sell
相同的签名,除了参数size
被参数target
替换
在这种情况下,一切都与指定最终目标有关,并且该方法决定操作是买入还是卖出。相同的逻辑适用于 3 种方法。让我们从order_target_size
如果目标大于发出买入的头寸,则差异
target - position_size
例子:
位置:
0
,目标:7
-> 购买(尺寸=7 - 0)-> 购买(尺寸=7)位置:
3
,目标:7
-> 购买(尺寸=7 - 3)-> 购买(尺寸=4)位置:
-3
,目标:7
-> 购买(尺寸=7 - -3)-> 购买(尺寸=10)位置:
-3
,目标:-2
-> 购买(尺寸=-2 - -3)-> 购买(尺寸=1)
如果目标小于头寸,则发出卖出,差异为
position_size - target
例子:
位置:
0
,目标:-7
-> 销售(大小=0 - -7) -> 销售(大小=7)职位:
3
,目标:-7
-> 出售(大小=3 - -7)-> 出售(大小=10)位置:
-3
,目标:-7
-> 出售(尺寸=-3 - -7)-> 出售(尺寸=4)职位:
3
,目标:2
-> 销售(大小=3 - 2) -> 销售(大小=1)
当使用order_target_value
一个值时,投资组合中资产的当前价值和头寸规模都被考虑在内,以决定最终的基础操作是什么。推理:
- 如果头寸规模为负(空头)并且目标值必须大于当前值,这意味着:卖出更多
因此,逻辑工作如下:
如果
target > value
和size >=0
-> 购买如果
target > value
和size < 0
-> 卖出如果
target < value
和size >= 0
-> 卖出如果
target < value
和size < 0
-> 购买
order_target_value
的逻辑与order_target_percent
的逻辑相同。该方法仅考虑投资组合的当前总价值来确定资产的目标价值。
样本
backtrader尝试为每个新功能提供一个示例,这也不例外。没有花里胡哨的东西,只是测试一下结果是否符合预期。这个在示例中的order_target
目录下。
示例中的逻辑相当愚蠢,仅用于测试:
在奇数月(一月、三月、...),使用天作为目标(在
order_target_value
将天乘以1000
的情况下)这模仿了一个不断增加的目标
在偶数月(2 月、4 月……)使用
31 - day
作为目标这仿真了一个递减的目标
order_target_size
让我们看看 1 月和 2 月会发生什么。
$ ./order_target.py --target-size -- plot 0001 - 2005-01-03 - Position Size: 00 - Value 1000000.00 0001 - 2005-01-03 - Order Target Size: 03 0002 - 2005-01-04 - Position Size: 03 - Value 999994.39 0002 - 2005-01-04 - Order Target Size: 04 0003 - 2005-01-05 - Position Size: 04 - Value 999992.48 0003 - 2005-01-05 - Order Target Size: 05 0004 - 2005-01-06 - Position Size: 05 - Value 999988.79 ... 0020 - 2005-01-31 - Position Size: 28 - Value 999968.70 0020 - 2005-01-31 - Order Target Size: 31 0021 - 2005-02-01 - Position Size: 31 - Value 999954.68 0021 - 2005-02-01 - Order Target Size: 30 0022 - 2005-02-02 - Position Size: 30 - Value 999979.65 0022 - 2005-02-02 - Order Target Size: 29 0023 - 2005-02-03 - Position Size: 29 - Value 999966.33 0023 - 2005-02-03 - Order Target Size: 28 ...
1 月,目标从3
点开始,从一年中的第一个交易日开始,然后增加。头寸大小最初从0
移动到3
,然后以1
为增量。
1 月结束时,最后一个order_target 为31
,并且在进入 2 月 1日时报告仓位大小,此时新的目标方被要求为30
,并且随着仓位的递减“1”而变化。
order_target_value
预计目标值会出现类似的行为
$ ./order_target.py --target-value --plot 0001 - 2005-01-03 - Position Size: 00 - Value 1000000.00 0001 - 2005-01-03 - data value 0.00 0001 - 2005-01-03 - Order Target Value: 3000.00 0002 - 2005-01-04 - Position Size: 78 - Value 999854.14 0002 - 2005-01-04 - data value 2853.24 0002 - 2005-01-04 - Order Target Value: 4000.00 0003 - 2005-01-05 - Position Size: 109 - Value 999801.68 0003 - 2005-01-05 - data value 3938.17 0003 - 2005-01-05 - Order Target Value: 5000.00 0004 - 2005-01-06 - Position Size: 138 - Value 999699.57 ... 0020 - 2005-01-31 - Position Size: 808 - Value 999206.37 0020 - 2005-01-31 - data value 28449.68 0020 - 2005-01-31 - Order Target Value: 31000.00 0021 - 2005-02-01 - Position Size: 880 - Value 998807.33 0021 - 2005-02-01 - data value 30580.00 0021 - 2005-02-01 - Order Target Value: 30000.00 0022 - 2005-02-02 - Position Size: 864 - Value 999510.21 0022 - 2005-02-02 - data value 30706.56 0022 - 2005-02-02 - Order Target Value: 29000.00 0023 - 2005-02-03 - Position Size: 816 - Value 999130.05 0023 - 2005-02-03 - data value 28633.44 0023 - 2005-02-03 - Order Target Value: 28000.00 ...
有一条额外的信息线说明实际数据值(在投资组合中)是什么。这有助于找出是否已达到目标值。
初始目标是3000.0
,报告的初始值为2853.24
。这里的问题是这是否足够接近。答案是肯定的
该示例使用每日柱结束时的
Market
单和最后可用价格来计算满足目标值的目标大小然后运行使用第二天的
open
,这不太可能是前一个close
价
以任何其他方式这样做都意味着一个人在欺骗他/她自己。
下一个目标值和最终值更接近: 4000
和3938.17
。
当更改为二月时,目标值开始从31000
减少到30000
和29000
。从30580.00
到30706.56
再到28633.44
的数据值也是如此。等待:
30580
->30706.56
是一个积极的变化的确。在这种情况下,目标值的计算大小满足开盘价,将值推高至
30706.56
如何避免这种影响:
示例对订单使用
Market
类型运行,这种影响无法避免order_target_xxx
方法允许指定运行类型和价格。可以将
Limit
指定为运行订单,并将价格设为收盘价(如果没有提供其他信息,则由方法选择)甚至提供具体定价
order_target_percent
在这种情况下,它只是当前投资组合价值的百分比。
$ ./order_target.py --target-percent --plot 0001 - 2005-01-03 - Position Size: 00 - Value 1000000.00 0001 - 2005-01-03 - data percent 0.00 0001 - 2005-01-03 - Order Target Percent: 0.03 0002 - 2005-01-04 - Position Size: 785 - Value 998532.05 0002 - 2005-01-04 - data percent 0.03 0002 - 2005-01-04 - Order Target Percent: 0.04 0003 - 2005-01-05 - Position Size: 1091 - Value 998007.44 0003 - 2005-01-05 - data percent 0.04 0003 - 2005-01-05 - Order Target Percent: 0.05 0004 - 2005-01-06 - Position Size: 1381 - Value 996985.64 ... 0020 - 2005-01-31 - Position Size: 7985 - Value 991966.28 0020 - 2005-01-31 - data percent 0.28 0020 - 2005-01-31 - Order Target Percent: 0.31 0021 - 2005-02-01 - Position Size: 8733 - Value 988008.94 0021 - 2005-02-01 - data percent 0.31 0021 - 2005-02-01 - Order Target Percent: 0.30 0022 - 2005-02-02 - Position Size: 8530 - Value 995005.45 0022 - 2005-02-02 - data percent 0.30 0022 - 2005-02-02 - Order Target Percent: 0.29 0023 - 2005-02-03 - Position Size: 8120 - Value 991240.75 0023 - 2005-02-03 - data percent 0.29 0023 - 2005-02-03 - Order Target Percent: 0.28 ...
并且信息已更改以查看数据在投资组合中代表的%
。
示例使用
$ ./order_target.py --help usage: order_target.py [-h] [--data DATA] [--fromdate FROMDATE] [--todate TODATE] [--cash CASH] (--target-size | --target-value | --target-percent) [--plot [kwargs]] Sample for Order Target optional arguments: -h, --help show this help message and exit --data DATA Specific 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 Ending date in YYYY-MM-DD format (default: 1000000) --target-size Use order_target_size (default: False) --target-value Use order_target_value (default: False) --target-percent Use order_target_percent (default: False) --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 from datetime import datetime import backtrader as bt class TheStrategy(bt.Strategy): ''' This strategy is loosely based on some of the examples from the Van K. Tharp book: *Trade Your Way To Financial Freedom*. The logic: - Enter the market if: - The MACD.macd line crosses the MACD.signal line to the upside - The Simple Moving Average has a negative direction in the last x periods (actual value below value x periods ago) - Set a stop price x times the ATR value away from the close - If in the market: - Check if the current close has gone below the stop price. If yes, exit. - If not, update the stop price if the new stop price would be higher than the current ''' params = ( ('use_target_size', False), ('use_target_value', False), ('use_target_percent', False), ) def notify_order(self, order): if order.status == order.Completed: pass if not order.alive(): self.order = None # indicate no order is pending def start(self): self.order = None # sentinel to avoid operrations on pending order def next(self): dt = self.data.datetime.date() portfolio_value = self.broker.get_value() print('%04d - %s - Position Size: %02d - Value %.2f' % (len(self), dt.isoformat(), self.position.size, portfolio_value)) data_value = self.broker.get_value([self.data]) if self.p.use_target_value: print('%04d - %s - data value %.2f' % (len(self), dt.isoformat(), data_value)) elif self.p.use_target_percent: port_perc = data_value / portfolio_value print('%04d - %s - data percent %.2f' % (len(self), dt.isoformat(), port_perc)) if self.order: return # pending order execution size = dt.day if (dt.month % 2) == 0: size = 31 - size if self.p.use_target_size: target = size print('%04d - %s - Order Target Size: %02d' % (len(self), dt.isoformat(), size)) self.order = self.order_target_size(target=size) elif self.p.use_target_value: value = size * 1000 print('%04d - %s - Order Target Value: %.2f' % (len(self), dt.isoformat(), value)) self.order = self.order_target_value(target=value) elif self.p.use_target_percent: percent = size / 100.0 print('%04d - %s - Order Target Percent: %.2f' % (len(self), dt.isoformat(), percent)) self.order = self.order_target_percent(target=percent) def runstrat(args=None): args = parse_args(args) cerebro = bt.Cerebro() cerebro.broker.setcash(args.cash) dkwargs = dict() if args.fromdate is not None: dkwargs['fromdate'] = datetime.strptime(args.fromdate, '%Y-%m-%d') if args.todate is not None: dkwargs['todate'] = datetime.strptime(args.todate, '%Y-%m-%d') # data data = bt.feeds.YahooFinanceCSVData(dataname=args.data, **dkwargs) cerebro.adddata(data) # strategy cerebro.addstrategy(TheStrategy, use_target_size=args.target_size, use_target_value=args.target_value, use_target_percent=args.target_percent) cerebro.run() if args.plot: pkwargs = dict(style='bar') if args.plot is not True: # evals to True but is not True npkwargs = eval('dict(' + args.plot + ')') # args were passed pkwargs.update(npkwargs) cerebro.plot(**pkwargs) def parse_args(pargs=None): parser = argparse.ArgumentParser( formatter_class=argparse.ArgumentDefaultsHelpFormatter, description='Sample for Order Target') parser.add_argument('--data', required=False, default='../../datas/yhoo-1996-2015.txt', help='Specific 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=1000000, help='Ending date in YYYY-MM-DD format') pgroup = parser.add_mutually_exclusive_group(required=True) pgroup.add_argument('--target-size', required=False, action='store_true', help=('Use order_target_size')) pgroup.add_argument('--target-value', required=False, action='store_true', help=('Use order_target_value')) pgroup.add_argument('--target-percent', required=False, action='store_true', help=('Use order_target_percent')) # 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()