以下代码对比亚迪2020年1月1日至2022年10月5日进行均线策略回测策略逻辑是:如果5日均线大于10日均线,那么就持有一手仓位,否则不持有仓位,就这么简单明了,然后换算成净值,看净值情况,查看回测的效果。根据净值,最高净值可达到最初净值的20倍。ps:现实情况肯定没这么简单,比如核心要解决的问题就是选股先选到比亚迪,其次是坚定不宜的持有这么久,才能得到这么高的收益。
import tushare as ts
ts.set_token("你的token")
# 以比亚迪前复权日线数据为例进行回测
df = ts.pro_bar(ts_code="002594.SZ", adj="qfq", start_date="20200101", end_date="20221004")
df.sort_values(by="trade_date",inplace=True)
df
667 | 002594.SZ | 20200102 | 47.6292 | 48.4082 | 47.4495 | 48.1086 | 47.6093 | 0.4993 | 1.0487 | 159345.70 | 765516.493 |
---|
666 | 002594.SZ | 20200103 | 48.1386 | 48.9276 | 47.6192 | 47.9788 | 48.1086 | -0.1298 | -0.2698 | 129936.07 | 628361.961 |
---|
665 | 002594.SZ | 20200106 | 47.3296 | 49.1273 | 47.1299 | 48.2185 | 47.9788 | 0.2397 | 0.4996 | 169871.38 | 822172.472 |
---|
664 | 002594.SZ | 20200107 | 48.2484 | 48.4382 | 47.7091 | 47.9888 | 48.2185 | -0.2297 | -0.4764 | 93400.58 | 449013.435 |
---|
663 | 002594.SZ | 20200108 | 47.4894 | 48.2884 | 47.0899 | 47.2198 | 47.9888 | -0.7690 | -1.6025 | 110974.45 | 529249.404 |
---|
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
---|
4 | 002594.SZ | 20220926 | 266.4000 | 276.0000 | 263.5800 | 272.9500 | 266.4000 | 6.5500 | 2.4587 | 143606.40 | 3911414.127 |
---|
3 | 002594.SZ | 20220927 | 272.9600 | 274.8300 | 270.0600 | 273.9600 | 272.9500 | 1.0100 | 0.3700 | 83095.93 | 2262822.665 |
---|
2 | 002594.SZ | 20220928 | 273.0000 | 273.0000 | 265.5000 | 265.6200 | 273.9600 | -8.3400 | -3.0442 | 85620.18 | 2293531.516 |
---|
1 | 002594.SZ | 20220929 | 270.0000 | 271.8000 | 264.0000 | 264.5700 | 265.6200 | -1.0500 | -0.3953 | 88356.68 | 2355802.796 |
---|
0 | 002594.SZ | 20220930 | 263.0000 | 263.5500 | 249.0000 | 252.0100 | 264.5700 | -12.5600 | -4.7473 | 214035.88 | 5421385.039 |
---|
import talib as ta
# 两条均线的参数
L1=5
L2=10
# 使用talib计算移动均线
df["ma1"]=ta.SMA(df.close.values,timeperiod=L1)
df["ma2"]=ta.SMA(df.close.values,timeperiod=L2)
df
667 | 002594.SZ | 20200102 | 47.6292 | 48.4082 | 47.4495 | 48.1086 | 47.6093 | 0.4993 | 1.0487 | 159345.70 | 765516.493 | NaN | NaN |
---|
666 | 002594.SZ | 20200103 | 48.1386 | 48.9276 | 47.6192 | 47.9788 | 48.1086 | -0.1298 | -0.2698 | 129936.07 | 628361.961 | NaN | NaN |
---|
665 | 002594.SZ | 20200106 | 47.3296 | 49.1273 | 47.1299 | 48.2185 | 47.9788 | 0.2397 | 0.4996 | 169871.38 | 822172.472 | NaN | NaN |
---|
664 | 002594.SZ | 20200107 | 48.2484 | 48.4382 | 47.7091 | 47.9888 | 48.2185 | -0.2297 | -0.4764 | 93400.58 | 449013.435 | NaN | NaN |
---|
663 | 002594.SZ | 20200108 | 47.4894 | 48.2884 | 47.0899 | 47.2198 | 47.9888 | -0.7690 | -1.6025 | 110974.45 | 529249.404 | 47.9029 | NaN |
---|
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
---|
4 | 002594.SZ | 20220926 | 266.4000 | 276.0000 | 263.5800 | 272.9500 | 266.4000 | 6.5500 | 2.4587 | 143606.40 | 3911414.127 | 270.4220 | 272.107 |
---|
3 | 002594.SZ | 20220927 | 272.9600 | 274.8300 | 270.0600 | 273.9600 | 272.9500 | 1.0100 | 0.3700 | 83095.93 | 2262822.665 | 270.3720 | 271.023 |
---|
2 | 002594.SZ | 20220928 | 273.0000 | 273.0000 | 265.5000 | 265.6200 | 273.9600 | -8.3400 | -3.0442 | 85620.18 | 2293531.516 | 269.4960 | 269.752 |
---|
1 | 002594.SZ | 20220929 | 270.0000 | 271.8000 | 264.0000 | 264.5700 | 265.6200 | -1.0500 | -0.3953 | 88356.68 | 2355802.796 | 268.7000 | 269.259 |
---|
0 | 002594.SZ | 20220930 | 263.0000 | 263.5500 | 249.0000 | 252.0100 | 264.5700 | -12.5600 | -4.7473 | 214035.88 | 5421385.039 | 265.8220 | 267.862 |
---|
# 计算趋势,0代表没有趋势不做操作,1代表多头趋势,第二日买入,-1代表空头趋势,第二日卖出平仓,或者不做操作
df["trend"]=0
con_long=df["ma1"]>df["ma2"]
con_short=df["ma1"]<=df["ma2"]
df.loc[con_long,"trend"]=1
df.loc[con_short,"trend"]=0
df
667 | 002594.SZ | 20200102 | 47.6292 | 48.4082 | 47.4495 | 48.1086 | 47.6093 | 0.4993 | 1.0487 | 159345.70 | 765516.493 | NaN | NaN | 0 |
---|
666 | 002594.SZ | 20200103 | 48.1386 | 48.9276 | 47.6192 | 47.9788 | 48.1086 | -0.1298 | -0.2698 | 129936.07 | 628361.961 | NaN | NaN | 0 |
---|
665 | 002594.SZ | 20200106 | 47.3296 | 49.1273 | 47.1299 | 48.2185 | 47.9788 | 0.2397 | 0.4996 | 169871.38 | 822172.472 | NaN | NaN | 0 |
---|
664 | 002594.SZ | 20200107 | 48.2484 | 48.4382 | 47.7091 | 47.9888 | 48.2185 | -0.2297 | -0.4764 | 93400.58 | 449013.435 | NaN | NaN | 0 |
---|
663 | 002594.SZ | 20200108 | 47.4894 | 48.2884 | 47.0899 | 47.2198 | 47.9888 | -0.7690 | -1.6025 | 110974.45 | 529249.404 | 47.9029 | NaN | 0 |
---|
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
---|
4 | 002594.SZ | 20220926 | 266.4000 | 276.0000 | 263.5800 | 272.9500 | 266.4000 | 6.5500 | 2.4587 | 143606.40 | 3911414.127 | 270.4220 | 272.107 | 0 |
---|
3 | 002594.SZ | 20220927 | 272.9600 | 274.8300 | 270.0600 | 273.9600 | 272.9500 | 1.0100 | 0.3700 | 83095.93 | 2262822.665 | 270.3720 | 271.023 | 0 |
---|
2 | 002594.SZ | 20220928 | 273.0000 | 273.0000 | 265.5000 | 265.6200 | 273.9600 | -8.3400 | -3.0442 | 85620.18 | 2293531.516 | 269.4960 | 269.752 | 0 |
---|
1 | 002594.SZ | 20220929 | 270.0000 | 271.8000 | 264.0000 | 264.5700 | 265.6200 | -1.0500 | -0.3953 | 88356.68 | 2355802.796 | 268.7000 | 269.259 | 0 |
---|
0 | 002594.SZ | 20220930 | 263.0000 | 263.5500 | 249.0000 | 252.0100 | 264.5700 | -12.5600 | -4.7473 | 214035.88 | 5421385.039 | 265.8220 | 267.862 | 0 |
---|
# 假设出现信号后,第二天开盘进行交易,每次开仓1手(即100股)
# 注意pos代表目标仓位,如果trend>1,也就是当5日均线大于10日均线,就持有一手仓位,否则补充持有仓位
# new_pos为当日新增仓位,old_pos为老仓位
# 新旧仓位这里不好理解,可按照表格自己计算梳理下
df["pos"]=100*df["trend"].shift(1)
df["pos_shift"]=df["pos"].shift(1)
df["new_pos"]=df["pos"]-df["pos"].shift(1)
df["old_pos"]=df["pos"]-df["new_pos"]
df
667 | 002594.SZ | 20200102 | 47.6292 | 48.4082 | 47.4495 | 48.1086 | 47.6093 | 0.4993 | 1.0487 | 159345.70 | 765516.493 | NaN | NaN | 0 | NaN | NaN | NaN | NaN |
---|
666 | 002594.SZ | 20200103 | 48.1386 | 48.9276 | 47.6192 | 47.9788 | 48.1086 | -0.1298 | -0.2698 | 129936.07 | 628361.961 | NaN | NaN | 0 | 0.0 | NaN | NaN | NaN |
---|
665 | 002594.SZ | 20200106 | 47.3296 | 49.1273 | 47.1299 | 48.2185 | 47.9788 | 0.2397 | 0.4996 | 169871.38 | 822172.472 | NaN | NaN | 0 | 0.0 | 0.0 | 0.0 | 0.0 |
---|
664 | 002594.SZ | 20200107 | 48.2484 | 48.4382 | 47.7091 | 47.9888 | 48.2185 | -0.2297 | -0.4764 | 93400.58 | 449013.435 | NaN | NaN | 0 | 0.0 | 0.0 | 0.0 | 0.0 |
---|
663 | 002594.SZ | 20200108 | 47.4894 | 48.2884 | 47.0899 | 47.2198 | 47.9888 | -0.7690 | -1.6025 | 110974.45 | 529249.404 | 47.9029 | NaN | 0 | 0.0 | 0.0 | 0.0 | 0.0 |
---|
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
---|
4 | 002594.SZ | 20220926 | 266.4000 | 276.0000 | 263.5800 | 272.9500 | 266.4000 | 6.5500 | 2.4587 | 143606.40 | 3911414.127 | 270.4220 | 272.107 | 0 | 0.0 | 0.0 | 0.0 | 0.0 |
---|
3 | 002594.SZ | 20220927 | 272.9600 | 274.8300 | 270.0600 | 273.9600 | 272.9500 | 1.0100 | 0.3700 | 83095.93 | 2262822.665 | 270.3720 | 271.023 | 0 | 0.0 | 0.0 | 0.0 | 0.0 |
---|
2 | 002594.SZ | 20220928 | 273.0000 | 273.0000 | 265.5000 | 265.6200 | 273.9600 | -8.3400 | -3.0442 | 85620.18 | 2293531.516 | 269.4960 | 269.752 | 0 | 0.0 | 0.0 | 0.0 | 0.0 |
---|
1 | 002594.SZ | 20220929 | 270.0000 | 271.8000 | 264.0000 | 264.5700 | 265.6200 | -1.0500 | -0.3953 | 88356.68 | 2355802.796 | 268.7000 | 269.259 | 0 | 0.0 | 0.0 | 0.0 | 0.0 |
---|
0 | 002594.SZ | 20220930 | 263.0000 | 263.5500 | 249.0000 | 252.0100 | 264.5700 | -12.5600 | -4.7473 | 214035.88 | 5421385.039 | 265.8220 | 267.862 | 0 | 0.0 | 0.0 | 0.0 | 0.0 |
---|
# 把开盘价作为交易价格
df["entry_p"]=df["open"]
df
667 | 002594.SZ | 20200102 | 47.6292 | 48.4082 | 47.4495 | 48.1086 | 47.6093 | 0.4993 | 1.0487 | 159345.70 | 765516.493 | NaN | NaN | 0 | NaN | NaN | NaN | NaN | 47.6292 |
---|
666 | 002594.SZ | 20200103 | 48.1386 | 48.9276 | 47.6192 | 47.9788 | 48.1086 | -0.1298 | -0.2698 | 129936.07 | 628361.961 | NaN | NaN | 0 | 0.0 | NaN | NaN | NaN | 48.1386 |
---|
665 | 002594.SZ | 20200106 | 47.3296 | 49.1273 | 47.1299 | 48.2185 | 47.9788 | 0.2397 | 0.4996 | 169871.38 | 822172.472 | NaN | NaN | 0 | 0.0 | 0.0 | 0.0 | 0.0 | 47.3296 |
---|
664 | 002594.SZ | 20200107 | 48.2484 | 48.4382 | 47.7091 | 47.9888 | 48.2185 | -0.2297 | -0.4764 | 93400.58 | 449013.435 | NaN | NaN | 0 | 0.0 | 0.0 | 0.0 | 0.0 | 48.2484 |
---|
663 | 002594.SZ | 20200108 | 47.4894 | 48.2884 | 47.0899 | 47.2198 | 47.9888 | -0.7690 | -1.6025 | 110974.45 | 529249.404 | 47.9029 | NaN | 0 | 0.0 | 0.0 | 0.0 | 0.0 | 47.4894 |
---|
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
---|
4 | 002594.SZ | 20220926 | 266.4000 | 276.0000 | 263.5800 | 272.9500 | 266.4000 | 6.5500 | 2.4587 | 143606.40 | 3911414.127 | 270.4220 | 272.107 | 0 | 0.0 | 0.0 | 0.0 | 0.0 | 266.4000 |
---|
3 | 002594.SZ | 20220927 | 272.9600 | 274.8300 | 270.0600 | 273.9600 | 272.9500 | 1.0100 | 0.3700 | 83095.93 | 2262822.665 | 270.3720 | 271.023 | 0 | 0.0 | 0.0 | 0.0 | 0.0 | 272.9600 |
---|
2 | 002594.SZ | 20220928 | 273.0000 | 273.0000 | 265.5000 | 265.6200 | 273.9600 | -8.3400 | -3.0442 | 85620.18 | 2293531.516 | 269.4960 | 269.752 | 0 | 0.0 | 0.0 | 0.0 | 0.0 | 273.0000 |
---|
1 | 002594.SZ | 20220929 | 270.0000 | 271.8000 | 264.0000 | 264.5700 | 265.6200 | -1.0500 | -0.3953 | 88356.68 | 2355802.796 | 268.7000 | 269.259 | 0 | 0.0 | 0.0 | 0.0 | 0.0 | 270.0000 |
---|
0 | 002594.SZ | 20220930 | 263.0000 | 263.5500 | 249.0000 | 252.0100 | 264.5700 | -12.5600 | -4.7473 | 214035.88 | 5421385.039 | 265.8220 | 267.862 | 0 | 0.0 | 0.0 | 0.0 | 0.0 | 263.0000 |
---|
# 计算旧仓位的盈利和新仓位的盈利
# 新仓位当日盈亏为当日收盘价与开盘价之差乘以新仓位
# 旧仓位当日盈亏为当日收盘价与昨日收盘价之差乘以旧仓位
df["p&l_new"]=(df["close"]-df["entry_p"])*df["new_pos"]
df["p&l_old"]=(df["close"]-df["close"].shift(1))*df["old_pos"]
# 交易费用,new_pos为100或者-100表示有交易,交易费用统一为5元
df["fee"]=0
df.loc[(df["new_pos"]==100) | (df["new_pos"]==-100),"fee"]=5
# 每日盈亏由三部分组成
df["p&l"]=df["p&l_new"]+df["p&l_old"]+df["fee"]
df
667 | 002594.SZ | 20200102 | 47.6292 | 48.4082 | 47.4495 | 48.1086 | 47.6093 | 0.4993 | 1.0487 | 159345.70 | ... | 0 | NaN | NaN | NaN | NaN | 47.6292 | NaN | NaN | 0 | NaN |
---|
666 | 002594.SZ | 20200103 | 48.1386 | 48.9276 | 47.6192 | 47.9788 | 48.1086 | -0.1298 | -0.2698 | 129936.07 | ... | 0 | 0.0 | NaN | NaN | NaN | 48.1386 | NaN | NaN | 0 | NaN |
---|
665 | 002594.SZ | 20200106 | 47.3296 | 49.1273 | 47.1299 | 48.2185 | 47.9788 | 0.2397 | 0.4996 | 169871.38 | ... | 0 | 0.0 | 0.0 | 0.0 | 0.0 | 47.3296 | 0.0 | 0.0 | 0 | 0.0 |
---|
664 | 002594.SZ | 20200107 | 48.2484 | 48.4382 | 47.7091 | 47.9888 | 48.2185 | -0.2297 | -0.4764 | 93400.58 | ... | 0 | 0.0 | 0.0 | 0.0 | 0.0 | 48.2484 | -0.0 | -0.0 | 0 | 0.0 |
---|
663 | 002594.SZ | 20200108 | 47.4894 | 48.2884 | 47.0899 | 47.2198 | 47.9888 | -0.7690 | -1.6025 | 110974.45 | ... | 0 | 0.0 | 0.0 | 0.0 | 0.0 | 47.4894 | -0.0 | -0.0 | 0 | 0.0 |
---|
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
---|
4 | 002594.SZ | 20220926 | 266.4000 | 276.0000 | 263.5800 | 272.9500 | 266.4000 | 6.5500 | 2.4587 | 143606.40 | ... | 0 | 0.0 | 0.0 | 0.0 | 0.0 | 266.4000 | 0.0 | 0.0 | 0 | 0.0 |
---|
3 | 002594.SZ | 20220927 | 272.9600 | 274.8300 | 270.0600 | 273.9600 | 272.9500 | 1.0100 | 0.3700 | 83095.93 | ... | 0 | 0.0 | 0.0 | 0.0 | 0.0 | 272.9600 | 0.0 | 0.0 | 0 | 0.0 |
---|
2 | 002594.SZ | 20220928 | 273.0000 | 273.0000 | 265.5000 | 265.6200 | 273.9600 | -8.3400 | -3.0442 | 85620.18 | ... | 0 | 0.0 | 0.0 | 0.0 | 0.0 | 273.0000 | -0.0 | -0.0 | 0 | 0.0 |
---|
1 | 002594.SZ | 20220929 | 270.0000 | 271.8000 | 264.0000 | 264.5700 | 265.6200 | -1.0500 | -0.3953 | 88356.68 | ... | 0 | 0.0 | 0.0 | 0.0 | 0.0 | 270.0000 | -0.0 | -0.0 | 0 | 0.0 |
---|
0 | 002594.SZ | 20220930 | 263.0000 | 263.5500 | 249.0000 | 252.0100 | 264.5700 | -12.5600 | -4.7473 | 214035.88 | ... | 0 | 0.0 | 0.0 | 0.0 | 0.0 | 263.0000 | -0.0 | -0.0 | 0 | 0.0 |
---|
# 计算累计盈亏
df["p&l_cum"]=df["p&l"].cumsum()
# 计算净值曲线,假设初始资金是1000
ini_cap=1000
df["capital"]=df["p&l_cum"]+ini_cap
df["net_value"]=df["capital"]/ini_cap
df["net_value"]=df.net_value.apply(lambda x:x if x>=0 else 0)
df
667 | 002594.SZ | 20200102 | 47.6292 | 48.4082 | 47.4495 | 48.1086 | 47.6093 | 0.4993 | 1.0487 | 159345.70 | ... | NaN | NaN | 47.6292 | NaN | NaN | 0 | NaN | NaN | NaN | 0.00000 |
---|
666 | 002594.SZ | 20200103 | 48.1386 | 48.9276 | 47.6192 | 47.9788 | 48.1086 | -0.1298 | -0.2698 | 129936.07 | ... | NaN | NaN | 48.1386 | NaN | NaN | 0 | NaN | NaN | NaN | 0.00000 |
---|
665 | 002594.SZ | 20200106 | 47.3296 | 49.1273 | 47.1299 | 48.2185 | 47.9788 | 0.2397 | 0.4996 | 169871.38 | ... | 0.0 | 0.0 | 47.3296 | 0.0 | 0.0 | 0 | 0.0 | 0.00 | 1000.00 | 1.00000 |
---|
664 | 002594.SZ | 20200107 | 48.2484 | 48.4382 | 47.7091 | 47.9888 | 48.2185 | -0.2297 | -0.4764 | 93400.58 | ... | 0.0 | 0.0 | 48.2484 | -0.0 | -0.0 | 0 | 0.0 | 0.00 | 1000.00 | 1.00000 |
---|
663 | 002594.SZ | 20200108 | 47.4894 | 48.2884 | 47.0899 | 47.2198 | 47.9888 | -0.7690 | -1.6025 | 110974.45 | ... | 0.0 | 0.0 | 47.4894 | -0.0 | -0.0 | 0 | 0.0 | 0.00 | 1000.00 | 1.00000 |
---|
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
---|
4 | 002594.SZ | 20220926 | 266.4000 | 276.0000 | 263.5800 | 272.9500 | 266.4000 | 6.5500 | 2.4587 | 143606.40 | ... | 0.0 | 0.0 | 266.4000 | 0.0 | 0.0 | 0 | 0.0 | 14668.52 | 15668.52 | 15.66852 |
---|
3 | 002594.SZ | 20220927 | 272.9600 | 274.8300 | 270.0600 | 273.9600 | 272.9500 | 1.0100 | 0.3700 | 83095.93 | ... | 0.0 | 0.0 | 272.9600 | 0.0 | 0.0 | 0 | 0.0 | 14668.52 | 15668.52 | 15.66852 |
---|
2 | 002594.SZ | 20220928 | 273.0000 | 273.0000 | 265.5000 | 265.6200 | 273.9600 | -8.3400 | -3.0442 | 85620.18 | ... | 0.0 | 0.0 | 273.0000 | -0.0 | -0.0 | 0 | 0.0 | 14668.52 | 15668.52 | 15.66852 |
---|
1 | 002594.SZ | 20220929 | 270.0000 | 271.8000 | 264.0000 | 264.5700 | 265.6200 | -1.0500 | -0.3953 | 88356.68 | ... | 0.0 | 0.0 | 270.0000 | -0.0 | -0.0 | 0 | 0.0 | 14668.52 | 15668.52 | 15.66852 |
---|
0 | 002594.SZ | 20220930 | 263.0000 | 263.5500 | 249.0000 | 252.0100 | 264.5700 | -12.5600 | -4.7473 | 214035.88 | ... | 0.0 | 0.0 | 263.0000 | -0.0 | -0.0 | 0 | 0.0 | 14668.52 | 15668.52 | 15.66852 |
---|
# 绘制净值曲线
df=df.set_index("trade_date")
df.plot(figsize=(12,6),y=["net_value"])
<AxesSubplot:xlabel="trade_date">
文章为作者独立观点,不代表股票交易接口观点