量化CTA策略简介

目录

1. 策略流程

2. 策略逻辑

2.1 回测参数

2.2 交易信号

2.3 交易设置

3. 代码实例

3.1 参数及源代码

  3.2 注意事项

4. 策略结果

4.1 结果展示

4.2 信号分析

4.3 交易明细

​编辑4.4 改进方向


免责声明:本文由作者参考相关资料,并结合自身实践和思考独立完成,对全文内容的准确性、完整性或可靠性不作任何保证。同时,文中提及的数据仅作为举例使用,不构成推荐;文中所有观点均不构成任何投资建议。请读者仔细阅读本声明,若读者阅读此文章,默认知晓此声明。本文使用掘金量化软件仅做为举例,不涉及软件宣传,推广及盈利。

本文主要简介一下量化CTA策略(改进版本BOLL策略),使用金量化3.0进行回测。

1. 策略流程

2. 策略逻辑

   为了简化思路,对许多步骤的处理设置为静态。

2.1 回测参数

回测参数
交易品种DCE.i2401, DCE.m2401, CZCE.MA401, SHFE.rb2401
交易价格收盘价
交易频率1分钟
起始时间2023-10-15 9:00
结束时间2023-10-20 15:00
交易费率0.01%
交易滑点0.01%

2.2 交易信号

   这里使用一个类似于BOLL策略的指标作为交易信号。考虑到1分钟K线的频率太高,可能出现较多的交易信号。因此,考虑使用5分钟的K线(使用1分钟的K线来进行合成)。

参数名称计算方式
均值过去X个5分钟K线对应收盘价序列的均值
标准差过去X个5分钟K线对应收盘价序列的标砖差
上轨低均值加上M个标准差
上轨高均值加上N个标准差
下轨低均值减去N个标准差
下轨高均值减去M个标准差

* 其中:X为整数,远大于M,N;M,N为整数,M<N 。

  设置最新的价格为1分钟K线的收盘价,记为p。

1. 当p介于上轨高和上轨低之间,同时标准差大于5时,触发买入信号。

1. 当p介于下轨高和下轨低之间,同时标准差大于5时,触发卖出信号。

   策略的主要逻辑还是基于动量效应,对于价格上涨和下跌分别设定相应的压力线和阻力线,当价格位于区间内时,触发相应的交易信号。需要注意,这里设置两根线的原因:以上涨为例,如果仅仅是突破上轨低线,此种情况下交易信号会非常多,会产生许多无效甚至是负向的信号;同时设定上轨高来作为限制,可以使买入的价格在可控范围内,不至于过高。

2.3 交易设置

交易品种交易数量止盈点位止损点位
DCE.i24011096
DCE.m240114028
CZCE.MA40112417
SHFE.rb240113827

    止损和止盈基于静态设置,基准为对应的开仓均价,实时价格为对应时点1分钟K线的收盘价。止盈空间大概是止损的70%。另外,为了降低隔夜持仓的风险,设置相应的定时任务,在每天的15:00,对所有的持仓进行平仓。

     为了进一步防止连续出现交易信号的情况,在交易上,对于单个交易品种,设定如下规则:

1. 在当前无持仓的情况下,出现交易信号,进行相应的开仓交易。

2. 在当前有持仓的情况下,即使出现交易信号,也不再进行开仓交易。

3. 代码实例

3.1 参数及源代码

  设置5分钟频率线的数量X为40,M,N的值分别为1.5和3。源代码如下:

from gm.api import *
import numpy as np

"""
多品种改进boll策略
"""


def get_five_min(m):
    # 获取5分钟对应的序列
    symbol_list = ['00', '05']
    for x in range(2, 13):
        symbol_list.append(str(x * 5))
    return 1 if m in symbol_list else 0


def init(context):
    # 设置历史数据的参数,订阅相关品种数据
    context.x = 40  # 数据量
    context.M, context.N = 1.5, 3  # 标准差的下限和上限
    context.count = (context.x + 1) * 5 + 1  # 分钟线的数量,多订阅便于合成5分钟线
    context.frequency = '1m'
    context.goods = ['DCE.i2401', 'DCE.m2401', 'CZCE.MA401', 'SHFE.rb2401']
    schedule(schedule_func=all_close, date_rule='1d', time_rule='15:00:00')  # 定时任务

    for code in context.goods:
        subscribe(symbols=code, frequency=context.frequency, count=context.count, wait_group=True)


def all_close(context):
    position = context.account().positions()
    if position:
        order_close_all()
        print('尾盘已清仓!')


def per(code):
    # 设置止损止盈的标准
    per_dict = {'DCE.i2401': 9, 'DCE.m2401': 40, 'CZCE.MA401': 24, 'SHFE.rb2401': 38}
    win = per_dict.get(code)
    lose = round(win * 0.7, 0)
    return win, lose


def set_vol(code):
    # 设置交易数量
    vol_dict = {'DCE.i2401': 10, 'DCE.m2401': 1, 'CZCE.MA401': 1, 'SHFE.rb2401': 1}
    return vol_dict.get(code)


def Trade_signal(context, close, new_close):
    # 交易信号处理
    '''
    L和H分别表示均值减去标准差对应的倍数
    '''
    his_close = close[:-1]
    M, N = context.M, context.N
    ma, std = np.mean(his_close), np.std(his_close)
    down_L = ma - N * std
    down_H = ma - M * std
    up_L = ma + M * std
    up_H = ma + N * std
    if down_L <= new_close <= down_H and std >= 5:
        trade_signal = 'sell'
    elif up_L <= new_close <= up_H and std >= 5:
        trade_signal = 'buy'
    else:
        trade_signal = 'none'
    return trade_signal


def open_trade(trade_signal, code):
    # 开仓交易
    vol = set_vol(code)
    if trade_signal == 'buy':
        order_volume(symbol=code, volume=vol, order_type=OrderType_Market,
                     side=OrderSide_Buy, position_effect=PositionEffect_Open)
        print(code, '多单信号')
    elif trade_signal == 'sell':
        order_volume(symbol=code, volume=vol, order_type=OrderType_Market,
                     side=OrderSide_Sell, position_effect=PositionEffect_Open)
        print(code, '空单信号')

    else:
        pass


def close_trade(code, cost, side, new_close, lose, win):
    # 平仓交易
    buy_lose, buy_win = cost - lose, cost + win
    sell_lose, sell_win = cost + lose, cost - win
    vol = set_vol(code)
    if side == 1:
        # 多单止损
        if new_close <= buy_lose:
            order_volume(symbol=code, volume=vol, order_type=OrderType_Market,
                         side=OrderSide_Sell, position_effect=PositionEffect_Close)
        # 多单止盈
        if new_close >= buy_win:
            order_volume(symbol=code, volume=vol, order_type=OrderType_Market,
                         side=OrderSide_Sell, position_effect=PositionEffect_Close)
    if side == 2:
        # 空单止损
        if new_close >= sell_lose:
            order_volume(symbol=code, volume=vol, order_type=OrderType_Market,
                         side=OrderSide_Buy, position_effect=PositionEffect_Close)
        # 空单止盈
        if new_close <= sell_win:
            order_volume(symbol=code, volume=vol, order_type=OrderType_Market,
                         side=OrderSide_Buy, position_effect=PositionEffect_Close)
    else:
        pass


def one_code_trade(context, code):
    # 单个合约的交易
    data = context.data(symbol=code, frequency=context.frequency, count=context.count)
    # 合成对应的5min_K线
    data['date'] = data['eob'].apply(lambda x: get_five_min(str(x).split(':')[1].split(' ')[0]))
    data_5min = data.loc[data['date'] == 1]
    close = data_5min['close'][len(data_5min) - 40:].tolist()
    new_close = data['close'].values[-1]

    # 设置交易信号所需参数,获取交易信号
    trade_signal = Trade_signal(context, close, new_close)
    # 获取单个产品对应的持仓
    long_position = context.account().position(symbol=code, side=PositionSide_Long)
    short_position = context.account().position(symbol=code, side=PositionSide_Short)

    # 如果没有持仓,判断交易信号,满足条件下进行开仓交易
    if not long_position and not short_position:
        open_trade(trade_signal, code)
    # 如果有持仓,判断止损和止盈,满足条件下进行平仓交易
    else:
        try:
            cost = long_position.get('vwap')  # 持仓均价
            side = long_position.get('side')  # 持仓方向
        except:
            cost = short_position.get('vwap')  # 持仓均价
            side = short_position.get('side')  # 持仓方向

        win, lose = per(code)
        close_trade(code, cost, side, new_close, lose, win)


def on_bar(context, bar):
    # 计算单个指标的
    for code in context.goods:
        one_code_trade(context, code)


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='',
        filename='main.py',
        mode=MODE_BACKTEST,
        token='',
        backtest_start_time='2023-10-15 09:00:00',
        backtest_end_time='2023-10-20 15:00:00',
        backtest_adjust=ADJUST_PREV,
        backtest_initial_cash=500000,
        backtest_commission_ratio=0.0001,
        backtest_slippage_ratio=0.0001)

  3.2 注意事项

1. 代码中的strategy_id由掘金量化软件自动生成,token由掘金设定。

2. 代码需要在掘金量化平台上运行,才能在软件上获得可视化的结果。

4. 策略结果

4.1 结果展示

  从上图可以看出,策略整体取得正收益,但是基于回测角度且时间较短,稳定性未进一步检验。同时各类绩效指标的计算感觉还是不叫迷,同时收益的计算 是按照股票的规则,并未按照期货(即没有考虑杠杆)。

4.2 信号分析

4.3 交易明细

4.4 改进方向

   可以改进的方向还有很多,以下主要例举几点:

1. 过往5分钟线的数量

2. 将5分钟线切换为其他频率

3. 调整M,N的值

4. 拉长回测时间

5. 增加交易品种

   改进的效果主要还是基于收益和回撤来做判断,稳定性基于时间区间作为判断。

  本期分享结束,有何问题欢迎交流!

免责声明:本文由作者参考相关资料,并结合自身实践和思考独立完成,对全文内容的准确性、完整性或可靠性不作任何保证。同时,文中提及的数据仅作为举例使用,不构成推荐;文中所有观点均不构成任何投资建议。请读者仔细阅读本声明,若读者阅读此文章,默认知晓此声明。本文使用掘金量化软件仅做为举例,不涉及软件宣传,推广及盈利。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值