做套:惊呆了!探究如果机器用MACD法对股票进行做套的胜率,竟高达%80。
前言:
今天在整理自研量化系统的代码屎山时,无意间想记录一下如果仅仅严格按照MACD法对买入的股票进行做套,胜率将会是多少呢?
接下来就来尝试一下,首先得简单了解一下什么是MACD,这个我也不多解释了,“dddd”,所以简单百度百科一下:MACD称为异同移动平均线,是从双指数移动平均线发展而来的,由快的指数移动平均线(EMA12)减去慢的指数移动平均线(EMA26)得到快线DIF,再用2×(快线DIF-DIF的9日加权移动均线DEA)得到MACD柱。MACD的意义和双移动平均线基本相同,即由快、慢均线的离散、聚合表征当前的多空状态和股价可能的发展变化趋势,但阅读起来更方便。MACD的变化代表着市场趋势的变化,不同K线级别的MACD代表当前级别周期中的买卖趋势。
简单了解完之后就可以开始实现了:
-
第一步导入数据:
df = pd.read_csv("2021-11-19.csv")这里用一下2021年11月19号的上证A股所有股票的日分钟数据
忽略’code’列股票代码显示不全的问题,从数据库备份转csv时没注意code特征列数值的属性,但对这次实验不造成影响,后期把“0”补回去就好了,就是csv保存时把前面的“0”都去掉了,数据量有54w条,足够大了。
-
构造计算MACD的函数
这里就需要用到macd的公式了
-
12日EMA的算式为
EMA(12)=前一日EMA(12)×11/13+今日收盘价×2/13
26日EMA的算式为
EMA(26)=前一日EMA(26)×25/27+今日收盘价×2/27
-
DIF=今日EMA(12)-今日EMA(26)
-
今日DEA(MACD)=前一日DEA×8/10+今日DIF×2/10
主要是计算EMA和DIF,EMA我使用pandas的ewm函数来计算
def EMA(arr): """ :param arr:价格列表 :return: """ df = pd.DataFrame(arr) return df.ewm(span=len(arr), min_periods=len(arr)).mean().values[-1]完整的计算MACD函数的代码为:
def CalculateMACD(price_list, _dea): """ :param price_list: 包含26个数据的列表 :param _dea: 值 例如:11 :return: """ def EMA(arr): """ :param arr: :return: """ df = pd.DataFrame(arr) return df.ewm(span=len(arr), min_periods=len(arr)).mean().values[-1] EMA_12 = int(EMA(price_list[:12]).flat[-1]) EMA_26 = int(EMA(price_list[12:26]).flat[-1]) DIF = EMA_12 - EMA_26 DEA = _dea * 8 / 10 + DIF * 2 / 10 BAR = 2 * (DIF - DEA) MACD = [round(DIF, 2), round(DEA, 2), round(BAR, 2)] return MACD -
-
此外,还利用sklearn的线性模型库linear_model构造一个可以计算一段时间内价格变动趋势的函数,其实就是分析线性方程组的斜率,从而得知在这段时间,价格变动的趋势
def CalculateTrend(price_list): """ :param price_list: 包含26个数据的列表 :return: """ regr = linear_model.LinearRegression() X = list() for x in range(1, len(price_list) + 1): X.append([x]) regr.fit(X, price_list) return regr.coef_.tolist()[0] -
重头戏来了,函数都备好了,万事俱备只欠东风,得构造一个逻辑函数或称为步骤函数去利用数据然后作出决策,然后再判断决策是否正确
-
做套的两件大事(不要做反套):
- 卖的时候比原本持仓价格要高
- 买的时候比原本持仓价格要低
因此在函数中设置一个阈值"threshold"来从当这个原本持仓价格
-
初始值设立:
- 如上文中计算MACD公式所提,由于计算MACD是得有前一次MACD值才能开始计算,一般都会将这家公式上市头天设为0然后开始计算。而在实验中我将先默认前MACD为0,然后把每天的前26个数据(数据是由上午9:30开始获取直到下午14:57)用于计算初始MACD(即从当前一次MACD值),这里的做法可能稍有不佳,欠妥,应该把盘前数据也容纳进来,这样就能用盘前数据获取初始MACD了,往后再做讨论。
- 原本持仓价格我们不可能每一个股票都输入一个持仓价格,因此我将持仓价格设置为当天开盘的数据,其实原本持仓价格只是从当一个阈值效果,帮助我们不要做反套,所以这样设立也确实没多大问题。
- 用排除掉前26个价格(用于计算初始MACD的)的数据就能开始测试了
- 不设立买入和卖出限制的股数,因为要尽可能的多测试样例,但这在现时中是不合理的,因为我们买入卖出的股份不可能是无限多。所以在想要使用的时候,可以添加一个限制操作股数的值,当买或卖达到了这个值,就不能也不用再操作这只股票了
MACD就是遵循两种操作方式:
-
# 当上次数据DIF小于DEA,即DIF线在DEA线下方 # 当DIF大于等于DEA,即DIF从下方向上与DEA产生交点,俗称金叉时,可以着手买入 -
# 当上次数据DIF大于DEA,即DIF线在DEA线上方 # 当DIF小于等于DEA,即DIF从上方向下与DEA产生交点,俗称死叉时,可以着手卖出然后我叠加了一个用于判断趋势的函数:
- 当判断到买时趋势是向下的(<0),则放弃购买
- 当判断到卖时趋势是向上的(>0),则放弃售出
判断胜负的方式用,买入(卖出)时的价格低于(高于)接下来10分钟的平均价,则判为胜。
# 由于数据是全部A股在每一分钟的交易数据,因此要单独抽出每个数据进行计算 for code in tqdm(set(df['code'].to_list())): price_list = df[df['code'] == code]['最新价'].to_list() r = 0 t = 0 threshold = price_list[0] _MACD = CalculateMACD(price_list[: 26], _dea=0) price_list = price_list[26:] for i in range(26, len(price_list), 26): MACD = CalculateMACD(price_list[i - 26:i], _dea=_MACD[1]) if price_list[i] < threshold: if _MACD[0] < _MACD[1]: if MACD[0] >= MACD[1]: if CalculateTrend(price_list[i - 26:i]) > 0: buy['code'].append(code) buy['买入价'].append(price_list[i]) buy['买入后十分钟平均价:'].append(np.mean(price_list[i + 1:i + 11])) r += 1 if price_list[i] <= np.mean(price_list[i + 1:i + 11]) else -1 t += 1 if price_list[i] > threshold: if _MACD[0] > _MACD[1]: if MACD[0] <= MACD[1]: if CalculateTrend(price_list[i - 26:i]) < 0: sell['code'].append(code) sell['卖出价'].append(price_list[i]) sell['卖出后十分钟平均价:'].append(np.mean(price_list[i + 1:i + 11])) r += 1 if price_list[i] >= np.mean(price_list[i + 1:i + 11]) else -1 t += 1 _MACD = MACD # print(CalculateTrend(price_list[i - 26:i])) if t != 0: rate['code'].append(code) rate['胜率'].append(r / t)
一通捣鼓下来,观看实验效果:
还是那个问题,code前面的缺少的0不见了,我也懒的补了,志在查看结果,而不是表现的形式,心中补0就好了。
可以看出无论买卖,根据MACD法操作,好像似乎真能找到一段时间内的高低点,从而实现高抛低吸。
查看胜率:
-
似乎有点意思这个胜率,计算可能会有错,但无论如何机器是死的,莫得感情。
一个精明的策略往往可以带来意想不到的收获。
完整代码:
import pandas as pd
from sklearn import linear_model
from tqdm import tqdm
import numpy as np
# 计算MACD
def CalculateMACD(price_list, _dea):
"""
:param price_list: 包含26个数据的列表
:param _dea: 值 例如:11
:return:
"""
def EMA(arr):
"""
:param arr:
:return:
"""
df = pd.DataFrame(arr)
return df.ewm(span=len(arr), min_periods=len(arr)).mean().values[-1]
EMA_12 = int(EMA(price_list[:12]).flat[-1])
EMA_26 = int(EMA(price_list[12:26]).flat[-1])
DIF = EMA_12 - EMA_26
DEA = _dea * 8 / 10 + DIF * 2 / 10
BAR = 2 * (DIF - DEA)
MACD = [round(DIF, 2), round(DEA, 2), round(BAR, 2)]
return MACD
def CalculateTrend(price_list):
"""
:param price_list: 包含25个数据的列表
:return:
"""
regr = linear_model.LinearRegression()
X = list()
for x in range(1, len(price_list) + 1):
X.append([x])
regr.fit(X, price_list)
return regr.coef_.tolist()[0]
if __name__ == '__main__':
# 当上次数据DIF小于DEA,即DIF线在DEA线下方
# 当DIF大于等于DEA,即DIF从下方向上与DEA产生交点,俗称金叉时,可以着手买入
# 当上次数据DIF大于DEA,即DIF线在DEA线上方
# 当DIF小于等于DEA,即DIF从上方向下与DEA产生交点,俗称死叉时,可以着手卖出
df = pd.read_csv("min_test_data.csv")
buy = {'code': list(), '买入价': list(), '买入后十分钟平均价:': list()}
sell = {'code': list(), '卖出价': list(), '卖出后十分钟平均价:': list()}
rate = {'code': list(), '胜率': list()}
for code in tqdm(set(df['code'].to_list())):
price_list = df[df['code'] == code]['最新价'].to_list()
r = 0
t = 0
threshold = price_list[0]
_MACD = CalculateMACD(price_list[: 26], _dea=0)
price_list = price_list[26:]
for i in range(26, len(price_list), 26):
MACD = CalculateMACD(price_list[i - 26:i], _dea=_MACD[1])
if price_list[i] < threshold:
if _MACD[0] < _MACD[1]:
if MACD[0] >= MACD[1]:
if CalculateTrend(price_list[i - 26:i]) > 0:
buy['code'].append(code)
buy['买入价'].append(price_list[i])
buy['买入后十分钟平均价:'].append(np.mean(price_list[i + 1:i + 11]))
r += 1 if price_list[i] <= np.mean(price_list[i + 1:i + 11]) else -1
t += 1
if price_list[i] > threshold:
if _MACD[0] > _MACD[1]:
if MACD[0] <= MACD[1]:
if CalculateTrend(price_list[i - 26:i]) < 0:
sell['code'].append(code)
sell['卖出价'].append(price_list[i])
sell['卖出后十分钟平均价:'].append(np.mean(price_list[i + 1:i + 11]))
r += 1 if price_list[i] >= np.mean(price_list[i + 1:i + 11]) else -1
t += 1
_MACD = MACD
# print(CalculateTrend(price_list[i - 26:i]))
if t != 0:
rate['code'].append(code)
rate['胜率'].append(r / t)
print(pd.DataFrame(buy))
print(pd.DataFrame(sell))
print(pd.DataFrame(rate))