# SMI指标事件策略复现

By [不确定性守恒](https://paragraph.com/@uniswapv6) · 2022-06-09

---

[https://bbs.quantclass.cn/thread/12407](https://bbs.quantclass.cn/thread/12407)

一、指标说明
======

![](https://storage.googleapis.com/papyrus_images/516d598afa2fff7bebd0a56f0694edc57cf3d8b72ce2ccd7afc068360a046231.png)

二、策略代码
------

#### 2.1、指标计算

    # batch_parameters()
    
    def calculate(df, parameters, **kwargs):
        """
        计算单个币种的因子
        这边坚决不能进行去重或者删除空值等删除某行的操作
        :param df:  需要计算的币种数据
        :param parameters:  参数
        :param kwargs:    额外的参数
        :return:
        """
        N1, N2, N3 = parameters
        # print(N1)
        H = df['high'].rolling(N1).max()
        # print(H)
        L = df['low'].rolling(N1).min()
        # print(L)
        M = (H+L)/2
        # print(M)
        D = df['close']-M
        # print(D)
        # print(type(D))
        _DS = D.ewm(span=N2).mean()
        # print(_DS)
        DS = _DS.ewm(span=N2).mean()
        # print(DS)
        _DHL = (H-L).ewm(span=N2).mean()
        DHL = _DHL.ewm(span=N2).mean()
        SMI = 100*DS/DHL
        # print(SMI)
        SMIMA = SMI.rolling(window=N3).mean()
        # print(SMIMA)
        df['{}_{}'.format(factor_name,parameters)]=SMI
        df['{}MA_{}'.format(factor_name,parameters)] = SMIMA
    
        return df
    

#### 2.2、遍历代码

    def batch_parameters():
        """
        生成遍历的参数
        :return:
        """
    
        one_parameters_list = [2, 3, 5, 8, 13, 21, 34, 55, 89]
        _para = one_parameters_list[-1]
        while _para < 500:
            _para = int(_para/0.618)
            one_parameters_list.append(_para)
    
        n1n2_list = []
        for i in one_parameters_list:
            for j in one_parameters_list:
                n1n2_list.append([i, j])
    
        parameters_list=[]
        for i in n1n2_list:
            for j in one_parameters_list:
                parameters_list.append([i[0], i[1], j])
        print()
        return parameters_list[:300]
    

#### 2.3、事件定义

SMI指标上穿其均线时，买入。

（均线为当日收盘价与最近N天的最高价与最低价均值之间的距离值）

    def event_strategy(df, params, **kwargs):
        """
        计算事件
        :param df: 数据
        :param params:
        :return:
        """
        event_name = '{}_{}'.format(strategy_name, params)  # 策略名称
        #-----long-------------
        #条件1，SMI上穿其均线，做多
        con1 = df['SMI_{}'.format(params)] > df['SMIMA_{}'.format(params)]
    
        #条件2，上一周期，SMI在均线下
        #短线对应SMI
        df['SMI_shift_{}'.format(params)] = df.groupby('symbol')['SMI_{}'.format(params)].shift()
        #长线对应SMIMA
        df['SMIMA_shift_{}'.format(params)] = df.groupby('symbol')['SMIMA_{}'.format(params)].shift()
        con2=df['SMI_shift_{}'.format(params)] <df['SMIMA_shift_{}'.format(params)]
        df.loc[con1 & con2, event_name] = 1
    
    
        return df
    

三、回测结果（收益回撤比Top3）
=================

#### 3.1、TOP1资金曲线

![](https://storage.googleapis.com/papyrus_images/aa205df32c92f668c991814458f0bef6f8eaea08dc7701f9b5331b1a181bed59.png)

#### 3.2、事件频率

![](https://storage.googleapis.com/papyrus_images/a2170f18d23abe329000bd34df8c85eb34586620c51bc43f735719e50788886f.png)

#### 3.3、回测评价

![](https://storage.googleapis.com/papyrus_images/69201ed52542c1edd6a7acb346aa1f3592bb7b732ad26b3d1f6c17ecc493f946.png)

#### 3.4、最优结果每年表现

![](https://storage.googleapis.com/papyrus_images/b34eeb4a1d72d2dc3f6dcb237edf4df6e64128c9eec0092ec1aa9cc1d0ed6c1c.png)

#### 3.5、TOP1盈利最多三笔交易

![](https://storage.googleapis.com/papyrus_images/641d4e93ce28d30be3df6cf4db4cbb77b93f6779b2d14ecaf0ea6d3679cf5d35.png)

#### 3.6、TOP1亏损最多三笔交易

![](https://storage.googleapis.com/papyrus_images/bec5ed5b7a52257a9b4520dfe5f8704accd1f57983d490f1fef102553b4bd8f0.png)

四、遍历结果（收益回撤比前十）
---------------

![](https://storage.googleapis.com/papyrus_images/2620970d39f660be05150c7323ef2a1946e44a4d71d4b548c4750b02bed303eb.png)

五、附件内容
======

#### 1、指标计算代码

    import pandas as pd
    
    factor_name = 'SMI'
    
    
    def special_data(candle_df, symbol, agg_dict, **kwargs):
        """
        导入额外的数据
        :param candle_df:   基础数据
        :param symbol:  币种名
        :param agg_dict:   转换周期的字典
        :return:
        """
    
        return candle_df, agg_dict
    
    
    def batch_parameters():
        """
        生成遍历的参数
        :return:
        """
    
        one_parameters_list = [2, 3, 5, 8, 13, 21, 34, 55, 89]
        _para = one_parameters_list[-1]
        while _para < 500:
            _para = int(_para/0.618)
            one_parameters_list.append(_para)
    
        n1n2_list = []
        for i in one_parameters_list:
            for j in one_parameters_list:
                n1n2_list.append([i, j])
    
        parameters_list=[]
        for i in n1n2_list:
            for j in one_parameters_list:
                parameters_list.append([i[0], i[1], j])
        print()
        return parameters_list[:300]
    
    
    # batch_parameters()
    
    def calculate(df, parameters, **kwargs):
        """
        计算单个币种的因子
        这边坚决不能进行去重或者删除空值等删除某行的操作
        :param df:  需要计算的币种数据
        :param parameters:  参数
        :param kwargs:    额外的参数
        :return:
        """
        N1, N2, N3 = parameters
        # print(N1)
        H = df['high'].rolling(N1).max()
        # print(H)
        L = df['low'].rolling(N1).min()
        # print(L)
        M = (H+L)/2
        # print(M)
        D = df['close']-M
        # print(D)
        # print(type(D))
        _DS = D.ewm(span=N2).mean()
        # print(_DS)
        DS = _DS.ewm(span=N2).mean()
        # print(DS)
        _DHL = (H-L).ewm(span=N2).mean()
        DHL = _DHL.ewm(span=N2).mean()
        SMI = 100*DS/DHL
        # print(SMI)
        SMIMA = SMI.rolling(window=N3).mean()
        # print(SMIMA)
        df['{}_{}'.format(factor_name,parameters)]=SMI
        df['{}MA_{}'.format(factor_name,parameters)] = SMIMA
    
        return df
    
    
    def cross_section_calculate(df, parameters, **kwargs):
        """
        计算截面因子的函数
        :param df:  所有币种的数据
        :param parameters:  参数
        :param kwargs:    额外的参数
        :return:
        """
        df['{}_{}_排名' .format (factor_name,parameters)] = df.groupby('candle_begin_time')['{}_{}'.format(factor_name,parameters)].rank(ascending=False, method='first')
    
        return df
    

#### 2、事件生成代码

    # 事件需要的因子
    factors = ['SMI']
    # 事件的名称名
    strategy_name = 'SMI&event'  # 名称和程序文件名相同，不能含有_
    
    
    def batch_parameters():
        """
        生成遍历的参数
        :return:
        """
    
        one_parameters_list = [2, 3, 5, 8, 13, 21, 34, 55, 89]
        _para = one_parameters_list[-1]
        while _para < 500:
            _para = int(_para/0.618)
            one_parameters_list.append(_para)
    
        n1n2_list = []
        for i in one_parameters_list:
            for j in one_parameters_list:
                n1n2_list.append([i, j])
    
        parameters_list=[]
        for i in n1n2_list:
            for j in one_parameters_list:
                parameters_list.append([i[0], i[1], j])
    
        return parameters_list[:300]
    
    
    # print(batch_parameters())
    
    
    
    
    def event_strategy(df, params, **kwargs):
        """
        计算事件
        :param df: 数据
        :param params:
        :return:
        """
        event_name = '{}_{}'.format(strategy_name, params)  # 策略名称
        #-----long-------------
        #条件1，SMI上穿其均线，做多
        con1 = df['SMI_{}'.format(params)] > df['SMIMA_{}'.format(params)]
    
        #条件2，上一周期，SMI在均线下
        #短线对应SMI
        df['SMI_shift_{}'.format(params)] = df.groupby('symbol')['SMI_{}'.format(params)].shift()
        #长线对应SMIMA
        df['SMIMA_shift_{}'.format(params)] = df.groupby('symbol')['SMIMA_{}'.format(params)].shift()
        con2=df['SMI_shift_{}'.format(params)] <df['SMIMA_shift_{}'.format(params)]
        df.loc[con1 & con2, event_name] = 1
    
    
        return df
    

#### 3、遍历回测结果

[https://bbs.quantclass.cn/thread/12407](https://bbs.quantclass.cn/thread/12407)

---

*Originally published on [不确定性守恒](https://paragraph.com/@uniswapv6/smi)*
