2经典策略之移动平均策略
在1节中,我们使用了一个简单的回测,验证了一下小瓦的“低买高卖”策略。总体来看,这个策略虽然没有让小瓦损失太多资产,但是也没有带来期待的回报。小瓦迫不及待地想知道,有没有一个更好的策略能提高交易的回报。
1单一移动平均指标
移动平均策略的核心思想非常简单,且十分容易理解。当股价上升且向上穿过N日的均线时,说明股价在向上突破,此时下单买入;当股价下降且向下穿过N日的均线时,说明股价整体出现下跌的趋势,此时下单卖出。或者当M日均价上升穿过N日的均线时,说明股票的交易数据接口,股票处于上升的趋势,应下单买入;反之,当M日均价下降且穿过N日均线时,说明股票的交易数据接口,股票处于下降的趋势,应下单卖出。
在这个策略中,需要用到的指标便是均线。下面我们使用代码来演示股价均线的绘制,还是使用1节中下载的股票数据,选取20个交易日的股票均价作为均线,输入代码如下:
#导入必要的库
import tushare as ts
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
pd.set_option("expand_frame_repr", False) # True就是可以换行显示。设置成False的时候不允许换行
pd.set_option("display.max_columns", None) # 显示所有列
pd.set_option("display.max_rows", None) # 显示所有行
pd.set_option("colheader_justify", "centre") # 显示居中
ts.set_token("你的token")
pro = ts.pro_api()
# 股票代码
ts_code="002624.SZ"
start = "20210101"
end = "20230310"
# # 日线行情
zgpa = pro.daily(ts_code=ts_code, start_date=start, end_date=end)
zgpa.sort_values("trade_date", inplace=True)
zgpa.set_index("trade_date", inplace=True)
#这里使用20日均线
period = 20
#设置一个空列表,用来存储每20天的股价
avg_20 = []
#再设置一个空列表,用来存储每20天股价的均值
avg_value = []
for price in zgpa["close"]:
#把每天的股价传入avg_20列表
avg_20.append(float(price))
#当列表中存储的数值多于20个时
if len(avg_20) > period:
#就把前面数据删除,确保列表中始终存储20天数据
del avg_20[0]
#把20天股价的均值传入avg_value列表
avg_value.append(np.mean(avg_20))
#把计算好的20天均价写到数据表中
zgpa = zgpa.assign(avg_20 = pd.Series(avg_value, index = zgpa.index))
zgpa.head(10)
#设置token
s.set_token("你的token")
需要去tushare官网注册账户,就有了你的token,登录后个人主页中找
输出结果:
为了直观地展示股价与均价的关系,可以使用下面的代码对数据进行可视化:
# 设置字体 显示汉字
plt.rcParams["font.sans-serif"] = "SimHei"
# 设置画布的尺寸为18*8
plt.figure(figsize=(18, 8))
# 使用折线图绘制出每天的收盘价
plt.plot(zgpa["close"], lw=2, c="k",label="收盘价")
plt.plot(zgpa["avg_20"], "--", lw=2, c="b", label="20均线")
plt.legend()
# plt.grid()
# 将图像进行展示
plt.show()
输出:
结果分析】从输出中可以看到,实线是从2021年1月1日至2023年3月10日的股票调整后价格;虚线是该股票的20日均价。
最下面黑乎乎的是日期,太多了所以成了一团
整体来看,在此期间,该股的整体趋势处于下行,不过不要紧,我们就基于这种“逆境”来尝试创建交易策略。
2双移动平均策略的实现
顾名思义,双移动平均策略就是使用两条均线来判断股价未来的走势。在两条均线中,一条是长期均线,另一条是短期均线。这种策略基于这样一种假设:股票价格的动量会朝着短期均线的方向移动。当短期均线穿过过长期均线,超过长期移动平均线时,动量将向上,此时股价可能会上涨。然而,如果短期均线的移动方向相反,则股价可能下跌。
根据这个原理,我们来创建一个双移动平均交易策略,输入代码如下:
# 新建一个数据表,命名为strategy(策略)
# 序列号保持和原始数据一致
strategy = pd.DataFrame(index = zgpa.index)
# 添加一个signal字段,用来存储交易信号
strategy["signal"] = 0
# 5日均价
strategy["avg_5"] = zgpa["close"].rolling(5).mean()
# 20日均价
strategy["avg_20"] = zgpa["close"].rolling(20).mean()
# 当5日均价大于20日均价,标记为1; 否则标记为0
strategy["signal"] = np.where(strategy["avg_5"]>strategy["avg_20"], 1, 0)
# 根据交易信号的变化下单,当交易信号从0变成1时买入
# 当交易信号从1变成0时卖出
strategy["order"] = strategy["signal"].diff()
# 查看倒数52~86行数据
strategy[-86:-52]
输出:
【结果分析】
从输出数据中可以看到,在2022年11月8日这一天,该股票的5日均价约为1048元,而20日均价约为10910元,5日均价小于10日均价,故此程序给出的交易信号是0;同样,在11月9日这一天,5日均价约为1058元,而20日均价约为10600元,交易信号不变,仍然是0,所以这一天不进行任何交易;但到了11月10日,5日均价上涨至约1148元,大于10日均价,与前一天相比,交易信号的变化为所以下单买入一手股票。
在2022年12月14日这一天,相反。
可以使用可视化的方法来直观感受这个过程,输入代码如下:
# 设置画布的尺寸为18*8
plt.figure(figsize=(18, 8))
# 使用实线绘制每天的收盘价
plt.plot(zgpa["close"], lw=1.3, label="price")
# 使用虚线绘制5日均价
plt.plot(strategy["avg_5"], lw=0.9, ls="--", label="avg5")
# 使用-.线绘制20日均价
plt.plot(strategy["avg_20"], lw=0.9, ls="-.", label="avg20")
#将买入信号用正三角标识
plt.scatter(strategy.loc[strategy.order == 1].index,
zgpa["close"][strategy.order == 1],
marker="^", s=80, c="r", label="buy")
#将卖出信号用倒三角标识
plt.scatter(strategy.loc[strategy.order == -1].index,
zgpa["close"][strategy.order == -1],
marker="v", s=80, c="g", label="shell")
#添加图注
plt.legend()
# plt.grid()
#显示
plt.show()
3对双移动平均策略进行回测
虽然我们用肉眼也可以看出在股价整体下跌的过程中,双移动平均策略的业绩表现并不好,不过我们还是可以写一点简单的代码来进行回测。输入代码如下:
# 启动资金
initial_cash = 100000
# 新建一个数据表positions,序列号保持和strategy数据表一致
# 用0代替空值
positions = pd.DataFrame(index = strategy.index).fillna(0)
# 设置stock字段为signal的3000倍,即买卖3000股
positions["stock"] = strategy["signal"] * 3000
# 新建一个数据表portfolio,序列号保持和strategy数据表一致
# 用0代替空值
portfolio = pd.DataFrame(index = strategy.index).fillna(0)
# 用持仓股票数量乘以股价得出持仓股票市值
portfolio["stock_value"] = positions.multiply(zgpa["close"], axis=0)
# 同样仓位的变化就是下单的数量
order = positions.diff()
# 剩余资金 = 初始资金 - 下单金额
portfolio["cash"] = initial_cash - order.multiply(zgpa["close"], axis=0).cumsum()
# 总资金 = 剩余资金 + 持仓市值
portfolio["total"] = portfolio["stock_value"] + portfolio["cash"]
print(portfolio.head(10))
portfolio.tail(10)
也用可视化的方法来展示一下双移动平均策略的回测结果。输入代码如下:
plt.figure(figsize=(18, 8))
plt.plot(portfolio["total"], lw=2, label="total")
plt.plot(portfolio["stock_value"], lw=2, ls="--", label="stock_value")
plt.legend()
# plt.grid()
plt.show()
经过测试,双移动平均策略作为经典交易策略之有一定的可取之处;但是在股价下行的趋势中,也没有实现“逆势赚钱”。看来我们还需要和小瓦一起,再了解一下其他的交易策略。
文章为作者独立观点,不代表 股票程序化软件自动交易接口观点