v11是第一個策略回測程序的最後一個版本,將對策略參數進行優化。
很多財經書籍裡都指出,不同股票、不同書籍都有個各自的特點和節奏,因此使用同一策略參數應用於所有的交易是不明智的。
在之前的例子中,我們嘗試了使用了簡單移動均值指標,週期參數選定為15,v11將對這個參數進行優化,通過優化找出簡單移動均值的最佳週期數。程序的主要修改部分為在向cerebro添加策略時使用如下代碼:
<code>strats
=cerebro.optstrategy(
TestStrategy,
maperiod
=range(10, 31))
/<code>
這樣backtrader就會將將簡單移動均值所選取的週期數從10到31進行逐個嘗試,通過計算最終收益,便可以選擇出最優的參數。
v11輸出為:
<code>2020
-02
-04
,
(MA
Period
10
)
Ending
Value
99852.22
2020
-02
-04
,
(MA
Period
11
)
Ending
Value
99914.55
2020
-02
-04
,
(MA
Period
12
)
Ending
Value
99946.91
2020
-02
-04
,
(MA
Period
13
)
Ending
Value
99948.27
2020
-02
-04
,
(MA
Period
14
)
Ending
Value
99957.74
2020
-02
-04
,
(MA
Period
15
)
Ending
Value
99990.11
2020
-02
-04
,
(MA
Period
16
)
Ending
Value
99990.11
2020
-02
-04
,
(MA
Period
17
)
Ending
Value
100031.43
2020
-02
-04
,
(MA
Period
18
)
Ending
Value
100031.43
2020
-02
-04
,
(MA
Period
19
)
Ending
Value
99985.39
2020
-02
-04
,
(MA
Period
20
)
Ending
Value
100003.41
2020
-02
-04
,
(MA
Period
21
)
Ending
Value
99977.38
2020
-02
-04
,
(MA
Period
22
)
Ending
Value
99990.39
2020
-02
-04
,
(MA
Period
23
)
Ending
Value
99985.39
2020
-02
-04
,
(MA
Period
24
)
Ending
Value
100036.74
2020
-02
-04
,
(MA
Period
25
)
Ending
Value
100036.74
2020
-02
-04
,
(MA
Period
26
)
Ending
Value
100022.75
2020
-02
-04
,
(MA
Period
27
)
Ending
Value
100022.75
2020
-02
-04
,
(MA
Period
28
)
Ending
Value
100022.75
2020
-02
-04
,
(MA
Period
29
)
Ending
Value
100022.75
2020
-02
-04
,
(MA
Period
30
)
Ending
Value
99977.52
/<code>
通過上面的輸出信息我們就可以清晰地看到,選擇不同週期參數時最後的資產情況。本例中,當週期參數選擇為24和25時,可以獲得最大收益。
程序v10-優化:
<code>from __future__
import (absolute_import, division, print_function, unicode_literals) import datetime import os.path import sys import backtrader as btclass
TestStrategy
(bt
.Strategy
): params = ( ('maperiod'
,15
), ('printlog'
, False), )def
log
(
self
, txt, dt=None, doprint = False):''
' 策略的日誌函數'
''
if
self
.params.printlogor
doprint:
dt = dtor
self
.datas[0
].datetime.date(0
) print('%s, %s'
% (dt.isoformat(), txt))def
__init__
(
self
):self
.dataclose =self
.datas[0
].closeself
.order = Noneself
.buyprice = Noneself
.buycomm = Noneself
.sma = bt.indicators.SimpleMovingAverage(self
.datas[0
], period =self
.params.maperiod)def
notify_order
(
self
, order):if
order.statusin
[order.Submitted, order.Accepted]:return
if
order.statusin
[order.Completed]:if
order.isbuy():self
.log('BUY EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f'
% (order.executed.price, order.executed.value, order.executed.comm))self
.buyprice = order.executed.priceself
.buycomm = order.executed.commelse:
self
.log('SELL EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f'
% (order.executed.price, order.executed.value, order.executed.comm))self
.bar_executed = len(self
) elif order.statusin
[order.Canceled, order.Margin, order.Rejected]:self
.log('Order Canceled/Margin/Rejected'
)self
.order = Nonedef
notify_trade
(
self
, trade):if
not
trade.isclosed:
return
self
.log('OPERATION PROFIT, GROSS %.2f, NET %.2f'
% (trade.pnl, trade.pnlcomm))def
next
(
self
):self
.log('Close, %.2f'
%self
.dataclose[0
])if
self
.order:
return
if
not
self
.position:
if
self
.dataclose[0
] >self
.sma[0
]:self
.log('BUY CREATE, %.2f'
%self
.dataclose[0
])self
.order =self
.buy()else:
if
self
.dataclose[0
]self
.sma[0
]:self
.log('SELL CREATE, %.2f'
%self
.dataclose[0
])self
.order =self
.sell()def
stop
(
self
):self
.log('(MA Period %2d) Ending Value %.2f'
% (self
.params.maperiod,self
.broker.getvalue()), doprint = True) cerebro = bt.Cerebro() strats = cerebro.optstrategy( TestStrategy, maperiod = range(10
,31
)) modpath = os.path.dirname(os.path.abspath(sys.argv[0
])) datapath = os.path.join(modpath,'../../TQDat/day/stk/000001.csv'
) data = bt.feeds.GenericCSVData( dataname = datapath, fromdate = datetime.datetime(2019
,10
,1
), todate = datetime.datetime(2020
,2
,29
), nullvalue =0
.0
, dtformat = ('%Y/%m/%d'
), datetime =0
, open =1
, high =2
, low =3
, close =4
, volume =5
, openinterest = -1
) cerebro.adddata(data) cerebro.broker.setcash(100000.0
) cerebro.addsizer(bt.sizers.FixedSize, stake =100
) cerebro.broker.setcommission(commission=0
.001
) cerebro.run(maxcpus =1
)/<code>