版本 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()