目标订单
原文: https://www.backtrader.com/blog/posts/2016-09-02-target-orders/target-orders/
在 1.8.10.96 版本之前,通过反向交易者在策略方法上实现智能下注:买卖。这一切都是关于在等式中添加一个大小因子,该因子负责桩的大小。
规模商不能做的是决定该操作必须是买入还是卖出。这意味着需要一个新的概念,即增加一个小的智能层来做出这样的决策。
这里是策略中的order_target_xxx
方法家族发挥作用的地方。受zipline
中的启发,这些方法提供了简单指定最终目标的机会,即目标:
-
size
->特定资产组合中的股份、合同金额 -
value
->组合中资产的货币单位价值 -
percent
->当前投资组合中资产价值的百分比(来自当前投资组合)
笔记
这些方法的参考可在战略参考中找到。综上所述,这些方法使用的签名与buy
和sell
相同,但参数size
被参数target
替换
在本例中,所有操作都是指定最终的目标,该方法决定操作是买入还是卖出。同样的逻辑也适用于这三种方法。让我们从order_target_size
开始
-
如果目标大于发行了买入的头寸,差额为
target - position_size
示例:
-
Pos:
0
、目标:7
->买入(尺码=7-0)->买入(尺码=7) -
Pos:
3
、目标:7
->买入(尺码=7-3)->买入(尺码=4) -
Pos:
-3
、目标:7
->买入(尺码=7--3)->买入(尺码=10) -
Pos:
-3
、目标:-2
->买入(尺码=-2--3)->买入(尺码=1)
-
-
如果目标小于头寸 a卖出并发行差额
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_percent
的逻辑与order_target_value
的逻辑相同。该方法仅考虑投资组合的当前总价值,以确定资产的目标价值。
样本
backtrader尝试为每个新功能提供一个示例,这也不例外。没有任何提示,只是测试一下结果是否符合预期。这个在样本的order_target
目录下。
样本中的逻辑相当愚蠢,仅用于测试:
-
在奇数月(1 月、3 月……)内,以日为目标(如
order_target_value
日乘以1000
日)这模拟了一个不断增加的目标
-
在偶数月(2 月、4 月……)期间,使用
31 - day
作为目标这模拟了一个递减的目标
订单\目标\尺寸
让我们看看一月和二月发生了什么。
$ ./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
开始,在本年的第 1个交易日开始并增加。而位置大小最初从0
移动到3
,然后以1
为增量。
完成一月最后一个订单是针对31
的,并且在进入二月日st时报告位置大小,此时新的目标侧被请求为30
并且随着位置的变化而变化,递减为“1”。
订单\目标\价值
目标值预计会出现类似行为
$ ./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\
。带 from`30580.00
to30706.56
and then to28633.44
的数据值也是如此。等待:
-
30580
->30706.56
是积极的变化的确在这种情况下,为目标值计算出的大小符合开盘价,从而将价值猛增至
30706.56
如何避免这种影响:
-
该示例对订单使用
Market
类型执行,这种影响无法避免 -
order_target_xxx
方法允许指定执行类型和价格。可以指定
Limit
作为执行顺序,让价格成为收盘价格(如果没有提供其他信息,则由方法选择),甚至提供具体的定价
订单\目标\价值
在这种情况下,它只是当前投资组合价值的一个百分比。
$ ./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()