量化交易学习(七)backtrader策略参数调优

今天这篇来讲一下backtrader的策略参数调优,这块有点像机器学习,一遍一遍地迭代,找出最佳的参数值。
还是以之前的单均线策略为例子,讲一下怎么找出收益最高时的均线的周期参数。
参数调优的原理就是在回测时,让程序把不同参数值一个个代入回测,然后根据每个参数所得到的最终收益来确定哪个参数是最优参数。
执行回测时,把addstrategy改为optstrategy,再加上参数的调优范围即可。如果是对指定值进行调优,可以用元组指定要回测的参数值,如果想对连续的一系列连续的参数值进行回测,可以用range来指定范围。
指定要回测的参数值:

1
2
3
cerebro.optstrategy(
SingleMAStrategy,
period=(10,20,30,60,90))

指定要回测的参数范围:

1
2
3
cerebro.optstrategy(
SingleMAStrategy,
period=range(10, 30))

主程序如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
if __name__ == '__main__':

cerebro = bt.Cerebro()
#cerebro.addstrategy(SingleMAStrategy)
cerebro.optstrategy(
SingleMAStrategy,
period=(10,20,30,60,90))
cerebro.adddata(data)

cerebro.broker.setcash(100000)

cerebro.broker.setcommission(commission=0.00015)
# 如果在jupyter中跑,务必要设置为单进程模式,不然会没有输出
cerebro.run(maxcpus=1)

另外还要修改一下策略中的打印输出部分,屏蔽中间买卖过程信息,输出停止回测后的结果信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
class SingleMAStrategy(bt.Strategy):
# 设置均线的周期
params=(
('period',15),
)

# 打印日志的函数
def log(self,txt,dt=None):
dt=dt or self.datas[0].datetime.date(0)
print('%s, %s' % (dt.isoformat(),txt))

# 策略初始化,设置一些参数
def __init__(self):
# 标第的收盘价
self.dataclose = self.data.close

self.order = None
self.buyprice = None
self.buycomm = None

self.sma = bt.indicators.SimpleMovingAverage(
self.data,period=self.params.period
)

self.cross=bt.indicators.CrossOver(self.dataclose,self.sma)
def notify_order(self,order):
if order.status in [order.Submitted,order.Accepted]:
return

if order.status in [order.Completed]:
if order.isbuy():
self.buyprice = order.executed.price
self.buycomm = order.executed.comm
else:
pass

self.bar_executed=len(self)
elif order.status in [order.Canceled,order.Margin,order.Rejected]:
pass

self.order = None

def notify_trade(self,trade):
if not trade.isclosed:
return

# 每条k线都会执行这个函数
def next(self):
if self.order:
return
if not self.position:
if self.cross==1:
vol=self.broker.getvalue()/self.dataclose[0]//100*100
self.order =self.buy(size=vol)
else:
if self.cross==-1:
self.order=self.sell(size=self.position.size)
# 策略结束后打印结果信息
def stop(self):
self.log('(MA Period %2d) Ending Value %.2f' %
(self.params.period, self.broker.getvalue()))

回测数据不能用实时数据源,要用已经准备好的数据,不然跑完第一次回测后会卡住。

1
2
3
4
5
6
7
gmdata = history(symbol='SHSE.510300', frequency='1d', start_time='2020-01-01 09:00:00', end_time='2023-08-09 16:00:00',
adjust=ADJUST_PREV, adjust_end_time='2023-08-09', df=True)

data=bt.feeds.PandasData(
dataname=gmdata,
datetime='eob'
)

程序跑完后的结果如下:
图片
可以看到,当均线的周期设置为10、20、30时都亏钱了,设置为60、90时赚钱了。在这几个参数中,period设置为90时赚的钱最多,最优参数为90。
这一篇就到这里啦。欢迎大家点赞、转发、私信。

江达小记