在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()