惊呆了,探究如果机器用MACD法对股票进行做套的胜率,竟高达%80

1,008 阅读7分钟

做套:惊呆了!探究如果机器用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))