掘金社区

干货 | RSJ指标与ROC指标的“遇见”策略Pinned highlighted

掘金小Q 【 论坛管理员 】 发表在策略分享 2021-08-19 10:02:16

策略分享
447
4
0

高频波动率不对称性指标(RSJ)可以很好地代表市场对上涨下跌的情绪变化。变动率技术指标(ROC)测量股价动量,可以用来监视常态性和极端性两种行情,对买卖股票提供强有力的参考。

当将RSJROC相结合构建综合指标时,RSJ和ROC指标同时发出看多信号,则在当日收盘的时候发出看多信号;若两个指标同时发出看空信号,则在当日收盘的时候发出看空信号;否则,不发出任何信号。

本策略运行所基于的环境:python3.8 掘金终端IDE


1. 策略思路

  • 在每日的14:55分计算前1小时内的RSJ指标,计算ROC指标,ROC指标参数为12;
  • 若在14:55分前1小时内的RSJ小于0,且昨日的ROC大于前一日的ROC,则今日在收盘的时候看多,若持有空仓先平空仓,再开多仓;
  • 若在14:55分前1小时内的RSJ大于0,且昨日的ROC小于前一日的ROC,则今日在收盘的时候看空,若持有多仓先平多仓,再开空仓;
  • 不满足开仓条件时,不发出任何信号。


2. 策略逻辑

  • 第一步:设置参数、设置定时任务。
  • 第二步:每天执行algo任务,每次计算收盘前1小时的RSJ指标、昨日与前一日的ROC指标 。
  • 第三步:RSJ与ROC指标相结合,判断是否符合交易条件,符合的发出相应的交易信号。
  • 回测期:2018-01-01 08:00:00 到 2020-12-31 16:00:00
  • 回测初始资金:1000万
  • 手续费:0.0001
  • 滑点:0.0001


3. 策略代码

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

'''
本策略采用定时任务结构(每天14:55:00定时执行algo),建立RSJ与ROC的“遇见”模型
1、当 RSJ 小于 0 时, 说明上涨的情绪相对于下跌的情绪稳定一些,发出看多信号;
   当 RSJ 大于 0 时, 说明上涨的情绪相对于下跌的情绪更不稳定,发出看空信号;
2、若昨日的 ROC 大于前一日的 ROC,则今日在收盘的时候看多;反之,发出看空信号。
3、当 RSJ 和 ROC 指标同时发出看多信号,则在当日收盘的时候发出看多信号;
   若两个指标同时发出看空信号,则在当日收盘的时候发出看空信号;
   否则,不发出任何信号。
回测数据:交易标的CFFEX.IF 300s与1d的bar数据
回测时间:2018-01-01 08:00:00 到 2020-12-31 16:00:00
'''

# 策略中必须有init方法
def init(context):
    # 设置rsj指标参数
    context.rsj_n = 12
    # 设置roc指标参数
    context.roc_n = 12
    # 设置开多单记号
    context.long = False
    # 设置开空单记号
    context.short = False
    # 订阅交易标的
    context.symbol = 'CFFEX.IF'
    # 每天14:55:00定时执行algo
    schedule(schedule_func=algo,date_rule='1d',time_rule='14:55:00')

def RSJ(data):
    data['r'] = data.close/data.close.shift(1) -1
    rv = sum(i**2 for i in data['r'][1:])
    rv_positive = sum(ri**2 for ri in data['r'] if ri > 0)
    rv_negative = sum(ri**2 for ri in data['r'] if ri < 0)
    rsj_result = (rv_positive - rv_negative)/rv
    return rsj_result

def ROC(data,N):
    data['roc'] = data.close/data.close.shift(N-1) -1
    return data['roc'].values

def algo(context):
    # 获取计算RSJ指标的数据
    rsj_data = history_n(symbol=context.symbol,frequency='300s',
                         end_time=context.now,count=context.rsj_n,fields='close,bob,eob',df=True)
    # 获取计算ROC指标的数据
    roc_data = history_n(symbol=context.symbol,frequency='1d',
                         end_time=context.now,count=13,fields='close,bob,eob',df=True)
    # 计算rsj与roc
    rsj = RSJ(rsj_data)
    roc = ROC(roc_data,context.roc_n)
    # 交易逻辑
    # 查询持仓
    position_long = context.account().position(symbol=context.symbol, side=PositionSide_Long)
    position_short = context.account().position(symbol=context.symbol, side=PositionSide_Short)

    # 按14:55:00的收盘价下限价单
    price = rsj_data['close'].values[-1]

    # 当 RSJ 和 ROC 指标同时发出看多信号,则在当日收盘的时候发出看多信号
    if rsj < 0 and roc[-1] > roc[-2]:
        # 有持仓:
        # 若持有空仓先平空仓,再开多(在on_order_status里开多)
        if position_short:
            context.long = True
            print('现持有空仓:', position_short['volume'])
            order_volume(symbol=context.symbol,volume=position_short['volume'],side=OrderSide_Buy,
                         order_type=OrderType_Limit,position_effect=PositionEffect_Close,price=price)
            print('以市价单平空仓')

        # 若持有多仓,则不再开多仓
        elif position_long:
            print('无交易,现持有多仓:',position_long['volume'])
            context.long = False
            context.short = False

        # 无持仓:
        # 若无持有空仓和多仓则直接开多仓
        else:
            order_volume(symbol=context.symbol,volume=10,side=OrderSide_Buy,order_type=OrderType_Limit,
                         position_effect=PositionEffect_Open,price=price)
            position_long = context.account().position(symbol=context.symbol, side=PositionSide_Long)
            print('以市价单开多仓,现持有多仓:',position_long['volume'])


    # 当 RSJ 和 ROC 指标同时发出看空信号,则在当日收盘的时候发出看空信号
    elif rsj > 0 and roc[-1] < roc[-2]:
        # 有持仓:
        # 若持有多仓先平多仓,再开空(在on_order_status里开空)
        if position_long:
            context.short = True
            print('现持有多仓:', position_long['volume'])
            order_volume(symbol=context.symbol,volume=position_long['volume'],side=OrderSide_Sell,
                         order_type=OrderType_Limit,position_effect=PositionEffect_Close,price=price)
            print('以市价单平多仓')

        # 若持有空仓,则不再开空仓
        elif position_short:
            print('无交易,现持有空仓:', position_short['volume'])
            context.long = False
            context.short = False

        # 无持仓:
        # 若无持有多仓和空仓则直接开空仓
        else:
            order_volume(symbol=context.symbol,volume=10,side=OrderSide_Sell,order_type=OrderType_Limit,
                         position_effect=PositionEffect_Open,price=price)
            position_short = context.account().position(symbol=context.symbol, side=PositionSide_Short)
            print('以市价单开空仓,现持有空仓:',position_short['volume'])


    # 否则,不发出任何信号
    else:
        context.long = False
        context.short = False
        print('无交易信号')


def on_order_status(context,order):
    # 当平仓委托全成后,才开仓
    if order['status'] == 3:
        # 当side为买入,position_effect为平仓时,说明平空仓全成,开多仓
        if order['side']==1 and order['position_effect']==2 and context.long:
            context.long = False
            order_volume(symbol=context.symbol,volume=10,side=OrderSide_Buy,order_type=OrderType_Limit,
                         position_effect=PositionEffect_Open,price=order['price'])
            position_long = context.account().position(symbol=context.symbol, side=PositionSide_Long)
            print('平空仓完成后,以市价单开多仓,现持有多仓:',position_long['volume'])

        # 当side为卖出,position_effect为平仓时,说明平多仓全成,开空仓
        if order['side']==2 and order['position_effect']==2 and context.short:
            context.short = False
            order_volume(symbol=context.symbol, volume=10, side=OrderSide_Sell, order_type=OrderType_Limit,
                         position_effect=PositionEffect_Open,price=order['price'] )
            position_short = context.account().position(symbol=context.symbol, side=PositionSide_Short)
            print('平多仓完成后,以市价单开空仓,现持有空仓:',position_short['volume'])


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='xxxxxxxxxxxxxxx',
        filename='main.py',
        mode=MODE_BACKTEST,
        token='xxxxxxxxxxxxxxxxxxxxxxx',
        backtest_start_time='2018-01-01 08:00:00',
        backtest_end_time='2020-12-31 16:00:00',
        backtest_adjust=ADJUST_PREV,
        backtest_initial_cash=10000000,
        backtest_commission_ratio=0.0001,
        backtest_slippage_ratio=0.0001)


    


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

0_1629338007315_e1b6f2f0-9d51-4ae7-bc1f-be65b3f31ff8.png


回测期策略累计收益率为100.32%,年化收益率为33.41%,最大回撤为13.45%,夏普比率为1.22,胜率为54.73%。

声明:本内容仅供学习、交流、演示之用,不构成任何投资建议!
评论: 4

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