量化投资学习笔记84——实现量化交易经典策略:网格交易法

这次来实现网格交易法,主要参考了:
https://zhuanlan.zhihu.com/p/63739282https://blog.csdn.net/u010805023/article/details/103109129
网格交易主要解决何时买卖,买卖多少的问题。首先确定要交易品种的波动范围,然后将波动范围均分为N个网格,价格每上涨一个网格,卖出1/N,每下跌一个网格,买入1/N。
策略的主要问题是波动范围估测准不准。如果上限估高了,可能卖早了。如果下限估高了,可能还在跌却没资金了。还有交易成本的问题。
开始撸代码吧。

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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# coding:utf-8
# 网格交易策略实现


import backtrader as bt
import backtrader.indicators as bi
import backtest
import pandas as pd
import numpy as np
import math
import matplotlib.pyplot as plt


class GridStrategy(bt.Strategy):
params = (
("printlog", True),
("top", 4.2),
("buttom", 3.5),
)
def __init__(self):
self.mid = (self.p.top + self.p.buttom)/2.0
# 百分比区间计算
#这里多1/2,是因为arange函数是左闭右开区间。
perc_level = [x for x in np.arange(1 + 0.02 * 5, 1 - 0.02 * 5 - 0.02/2, -0.02)]
# 价格区间
# print(self.mid)
self.price_levels = [self.mid * x for x in perc_level]
# 记录上一次穿越的网格
self.last_price_index = None
# 总手续费
self.comm = 0.0

def next(self):
# print(self.last_price_index)
# 开仓
if self.last_price_index == None:
# print("b", len(self.price_levels))
for i in range(len(self.price_levels)):
price = self.data.close[0]
# print("c", i, price, self.price_levels[i][0])
if self.data.close[0] > self.price_levels[i]:
self.last_price_index = i
self.order_target_percent(target=i/(len(self.price_levels) - 1))
print("a")
return
# 调仓
else:
signal = False
while True:
upper = None
lower = None
if self.last_price_index > 0:
upper = self.price_levels[self.last_price_index - 1]
if self.last_price_index < len(self.price_levels) - 1:
lower = self.price_levels[self.last_price_index + 1]
# 还不是最轻仓,继续涨,再卖一档
if upper != None and self.data.close > upper:
self.last_price_index = self.last_price_index - 1
signal = True
continue
# 还不是最重仓,继续跌,再买一档
if lower != None and self.data.close < lower:
self.last_price_index = self.last_price_index + 1
signal = True
continue
break
if signal:
self.long_short = None
self.order_target_percent(target=self.last_price_index/(len(self.price_levels) - 1))

# 输出交易记录
def log(self, txt, dt = None, doprint = False):
if self.params.printlog or doprint:
dt = dt or self.datas[0].datetime.date(0)
print('%s, %s' % (dt.isoformat(), txt))

def notify_order(self, order):
# 有交易提交/被接受,啥也不做
if order.status in [order.Submitted, order.Accepted]:
return
# 交易完成,报告结果
if order.status in [order.Completed]:
if order.isbuy():
self.log(
'执行买入, 价格: %.2f, 成本: %.2f, 手续费 %.2f' %
(order.executed.price,
order.executed.value,
order.executed.comm))
self.buyprice = order.executed.price
self.comm += order.executed.comm
else:
self.log(
'执行卖出, 价格: %.2f, 成本: %.2f, 手续费 %.2f' %
(order.executed.price,
order.executed.value,
order.executed.comm))
self.comm += order.executed.comm
elif order.status in [order.Canceled, order.Margin, order.Rejected]:
self.log("交易失败")
self.order = None

# 输出手续费
def stop(self):
self.log("手续费:%.2f 成本比例:%.5f" % (self.comm, self.comm/self.broker.getvalue()))


if __name__ == "__main__":
start = "2018-01-01"
end = "2020-07-05"
name = ["300etf"]
code = ["510300"]
backtest = backtest.BackTest(GridStrategy, start, end, code, name, 100000)
result = backtest.run()
# backtest.output()
print(result)

鼓捣了半天,困在选择上下震幅那里。按博文里的做法是用前十个交易日的最高点最低点来算,我怎么调试也不对。最后我用了一个”作弊”的方法,直接人肉看k线图然后指定最高最低点,成了。先跑起来再说。


还不错,年化收益率9%。但这是“上帝视角”,也就是使用了未来数据。如果我能提前知道最低最高点在哪,直接在最低点梭哈然后等到最高点清仓不就完啦?
下面再看看怎么改进。先用参数优化功能看一下设置不同间隔的效果吧。

1
2
3
4
5
6
7
8
9
# 看选择不同网格宽度的效果
result = backtest.optRun(gap = np.arange(0.005, 0.055, 0.005))
plt.figure()
plt.plot(result.参数值, result.年化收益率)
plt.savefig("网格策略宽度优化.png")
ret = result.loc[:, "年化收益率"]
maxindex = ret[ret == ret.max()].index
bestResult = result.loc[maxindex,:]
print(bestResult.loc[:, ["夏普比率", "参数名", "参数值", "年化收益率"]])



网格宽度为1%时收益最高,年化12%。

代码地址还是: https://github.com/zwdnet/MyQuant/tree/master/47
策略文件为Grid.py。

我发文章的三个地方,欢迎大家在朋友圈等地方分享,欢迎点“在看”。
我的个人博客地址:https://zwdnet.github.io
我的知乎文章地址: https://www.zhihu.com/people/zhao-you-min/posts
我的微信个人订阅号:赵瑜敏的口腔医学学习园地

欢迎打赏!感谢支持!