掘金社区

趋势震荡恒温器交易策略Pinned highlighted

掘金小Q 【 论坛管理员 】 发表在策略分享 2021-08-05 09:01:00

策略分享
558
0
0

趋势震荡恒温器交易策略体现了市场状态转换的思想。利用潮汐指数(CMI)将市场划分为趋势市与震荡市,可以较好地区分牛市、熊市、震荡市,在不同的市场状态下采用不同的策略,进而可以得到更为优秀的复合策略。

1. 策略思路

1.潮汐指数(CMI)用于判断当前市场为震荡市/趋势市。CMI大于阈值时判断为趋势市,否则判断为震荡市。

2.震荡市采取开盘区间突破进场。若前一日收盘价≥average(前一日收盘价,前一日最高价,前一日最低价),则认为当前是宜买市,否则为宜卖市。当价格向上突破买入触发价时买入,当价格向下突破卖出触发价时卖出。

3.趋势市采取布林道突破进场。计算布林道上中下轨,当价格向上突破布林道上轨时买入,当价格向下突破布林道下轨时卖出。

2. 策略逻辑

  • 第一步:设置参数、订阅数据
  • 第二步:每次订阅数据执行on_bar任务,计算CMI、ATR,设置突破触发价、布林道上下轨

  • 第三步:每次判断是否为震荡市/趋势市。如果为震荡市,还要判断是否为宜买市/宜卖市,是否持仓,是否符合策略思路,符合的进行买卖交易。

  • 回测期:2005-01-17 13:00:00 到 2017-12-21 15:00:00

  • 回测初始资金:1000万

  • 手续费:0.0001

  • 滑点:0.0001

3. 策略代码

# coding=utf-8
from __future__ import print_function, absolute_import
from gm.api import *
import pandas as pd

"""
本策略以SHSE.000300为交易标的,判断股票行情为震荡市或是趋势市,建立趋势动荡恒温器模型。
1. 潮汐指数(CMI)用于判断当前市场为震荡市/趋势市。CMI大于阈值时判断为趋势市,否则判断为震荡市。
2. 震荡市采取开盘区间突破进场:
   若前一日收盘价 >= average(前一日收盘价,前 一日最高价,前一日最低价),则认为当前是宜买市,否则为宜卖市;
   当价格向上突破买入触发价时买入,当价格向下突破卖出触发价时卖出;
3. 趋势市采取布林道突破进场:
   计算布林道上中下轨,当价格向上突破布林道上轨时买入,当价格向下突破布林道下轨时卖出;
回测数据为:SHSE.000300的1d频度bar数据
回测时间为:2005-01-17 13:00:00到2017-12-21 15:00:00
"""
def CMI(close,high,low):
    '''
    计算潮汐指数
    潮汐指数(CMI)用于判断当前市场为震荡市/趋势市。
    潮汐指数(CMI) = abs(30 天的移动窗口中最后 1 天的收盘价 - 第 1 天的收盘价之差) ÷ 移动窗口的日内最高价的最高者与日内最低价的最低者之差 * 100。
    潮汐指数的取值范围在(0,100)之间。
    '''
    cmi=abs(close.iloc[-1]-close.iloc[0])/(max(close)-min(close))*100
    return cmi

def ATR(close,low,high,pre_close,N=14):
    '''
    求真实波幅的N日移动平均    参数:N 天数,默认取14
    TR:MAX(MAX((HIGH-LOW),ABS(REF(CLOSE,1)-HIGH)),ABS(REF(CLOSE,1)-LOW));
    ATR:MA(TR,N);'''
    maxx=high-low
    abs_high=(pre_close-high).abs()
    abs_low=(pre_close-low).abs()
    a=pd.DataFrame()
    a['maxx']=maxx.values 
    a['abs_high']=abs_high.values
    a['abs_low']=abs_low.values
    TR=a.max(axis=1)
    ATR=TR.rolling(N).mean()
    return ATR

# 策略中必须有init方法
def init(context):
    context.cmiPeriod = 30                                 # 计算cmi的参数
    context.switchSWTR:int = 20                            # 阈值
    context.maPeriod = 20                                  # 计算BOLL布林线中轨的参数
    context.stdPeriod = 20                                 # 计算BOLL 标准差的参数
    context.stdRange = 2                                   # 计算BOLL 上下轨和中轨距离的参数
    context.trendMW = 3                                    # 震荡市进出场均线移动窗口参数
    context.swingpct1 = 0.5                                # 宜买市与宜卖市的调节参数
    context.swingpct2 = context.swingpct1*1.5              # 宜买市与宜卖市的调节参数
    context.symbol = 'SHSE.000300'                         # 订阅交易标的
    context.period = context.cmiPeriod                     # 订阅数据滑窗长度
    subscribe(symbols=context.symbol,frequency='1d',count=context.period)           # 订阅行情
    
def on_bar(context, bars):
    # 获取数据滑窗,只要在init里面有订阅,在这里就可以取的到,返回值是pandas.DataFrame
    data = context.data(symbol=context.symbol, frequency='1d', count=context.period, fields='close,low,high,pre_close,open')

    # 计算CMI:用于判断趋势市/震荡市
    cmi = CMI(close=data['close'],
              high=data['high'],
             low=data['low'])
    
    # 获取现有持仓
    pos = context.account().position(symbol=context.symbol, side=PositionSide_Long)

    # 趋势市,采取布林道突破进场:
    if cmi > context.switchSWTR:
        # 趋势市,计算boll的上下界
        mean_close=data['close'].rolling(context.maPeriod).mean()
        std_close=context.stdRange * data['close'].rolling(context.stdPeriod).std()
        bollUpper =  mean_close + std_close
        bollBottom = mean_close - std_close

        # cmi大于当前阈值,认为当前为趋势市
        # 当没有持仓,且股价穿过BOLL上界的时候买入股票。
        if not pos:  
            if data.close.values[-1] > bollUpper.values[-1] and data.close.values[-2] < bollUpper.values[-2]:
                order_percent(symbol= context.symbol , percent=0.8, side=OrderSide_Buy, 
                                order_type=OrderType_Market, position_effect=PositionEffect_Open)
                print('以市价单买入')
        elif pos:
            # 当有持仓,且股价穿过BOLL下界的时候卖出股票。
            if data.close.values[-1] < bollBottom.values[-1] and data.close.values[-2] > bollBottom.values[-2]:
                order_close_all()
                print('以市价单全部卖出')
   

    # cmi小于当前阈值,认为当前为震荡市,震荡市采取开盘区间突破进场:
    else:
        # 计算ATR:用于计算震荡市的突破触发价
        atr = ATR(close=data['close'].iloc[:-1],
                low=data['low'].iloc[:-1],
                high=data['high'].iloc[:-1],
                pre_close=data['pre_close'].iloc[:-1],
                N=3)

        # 计算震荡市中宜买市的触发价上下界
        atr_Upper = max(data.high.mean(),data.open.values[-1] + context.swingpct1*atr.values[-1])
        atr_Lower = min(data.low.mean(),data.open.values[-1] - context.swingpct2*atr.values[-1])

        # 认为当前是宜买市
        if data.close.iloc[-2] > (data.close.iloc[-2] + data.high.iloc[-2] + data.low.iloc[-2])/3:
            # 当没有持仓
            if not pos: 
                # 且价格向上突破买入触发价时买入
                if data.close.values[-1] > atr_Upper and data.close.values[-2] < atr_Upper :
                    order_percent(symbol= context.symbol , percent=0.8, side=OrderSide_Buy, 
                                    order_type=OrderType_Market, position_effect=PositionEffect_Open)
                    print('以市价单买入')
            # 当有持仓,
            elif pos:
                # 当价格向下突破卖出触发价时卖出
                if data.close.values[-1] < atr_Lower  and data.close.values[-2] > atr_Lower :
                    order_close_all()
                    print('以市价单全部卖出')

        # 认为当前是宜卖市
        elif data.close.iloc[-2] < (data.close.iloc[-2] + data.high.iloc[-2] + data.low.iloc[-2])/3:
            # 计算宜卖市的促发价上下界           
            context.swingpct1 , context.swingpct2 = context.swingpct2 , context.swingpct1
            atr_Upper = max(data.high.rolling(context.trendMW).mean().max(),data.open.values[-1] + context.swingpct1 * atr.values[-1])
            atr_Lower = min(data.low.rolling(context.trendMW).mean().max(), data.open.values[-1] - context.swingpct2 * atr.values[-1])

            # 当没有持仓
            if not pos: 
                # 且价格向上突破买入触发价时买入
                if data.close.values[-1] > atr_Upper and data.close.values[-2] < atr_Upper:
                    order_percent(symbol= context.symbol , percent=0.8, side=OrderSide_Buy, 
                                    order_type=OrderType_Market, position_effect=PositionEffect_Open)
                    print('以市价单买入')

            # 有持仓
            elif pos:  
                # 且价格向下突破卖出触发价时卖出
                if data.close.values[-1] < atr_Lower and data.close.values[-2] > atr_Lower:
                    order_close_all()
                    print('以市价单全部卖出')



if __name__ == '__main__':
    '''
        strategy_id策略ID,由系统生成
        filename文件名,请与本文件名保持一致
        mode实时模式:MODE_LIVE回测模式:MODE_BACKTEST
        token绑定计算机的ID,可在系统设置-密钥管理中生成
        backtest_start_time回测开始时间
        backtest_end_time回测结束时间
        backtest_adjust股票复权方式不复权:ADJUST_NONE前复权:ADJUST_PREV后复权:ADJUST_POST
        backtest_initial_cash回测初始资金
        backtest_commission_ratio回测佣金比例
        backtest_slippage_ratio回测滑点比例
        '''

    run(strategy_id='xxxxxxxxxxxxxxxx',
        filename='main.py',
        mode=MODE_BACKTEST,
        token='xxxxxxxxxxxxxxxxxxx',
        backtest_start_time='2005-01-17 13:00:00',
        backtest_end_time='2017-12-21 15:00:00',
        backtest_adjust=ADJUST_PREV,
        backtest_initial_cash=10000000,
        backtest_commission_ratio=0.0001,
        backtest_slippage_ratio=0.0001
        )
    

4. 回测结果与稳健性分析

0_1628126333304_2c48853e-dafc-4d21-8587-c5f845bf0ac3-image.png
回测期策略累计收益率为585.96%,年化收益率为45.29%。同期沪深300指数收益率为320.47%,跑赢当期沪深300。最大回撤为31.47%,夏普比率为0.65。

注:此策略只用于学习、交流、演示,不构成任何投资建议。

暂无评论

Looks like your connection to 掘金量化社区 - 量化交易者的交流社区 was lost, please wait while we try to reconnect.