🚀 AI 一键生成 joinquant 策略代码

JoinQuant 量化实战:估波曲线(Coppock Curve)长线投资策略

joinquant | 发布时间: 2025/12/23
以下内容由 EasyQuant 生成。

问题描述

怎么写一个基于估波曲线 Coppock Curve 的长线策略?

解决方案

这是一个基于 估波曲线 (Coppock Curve) 的长线交易策略实现。

策略原理

估波曲线(Coppock Curve)是一种主要用于识别股票市场长期底部动量指标。它的计算逻辑如下:

  1. 计算变化率 (ROC):分别计算 14 个月和 11 个月的价格变化率。
  2. 求和:将两个 ROC 值相加。
  3. 加权移动平均 (WMA):对上述和值进行 10 个月的加权移动平均。

交易信号

  • 买入:当估波曲线在零轴下方(或任意位置,视激进程度而定)拐头向上时。
  • 卖出:当估波曲线拐头向下时(原版主要用于抄底,卖出信号通常结合其他指标,但本策略演示采用拐头向下即卖出)。

策略代码

本策略以 沪深300 ETF (510300.XSHG) 为标的,每月第一个交易日运行一次,捕捉长期趋势。

# -*- coding: utf-8 -*-
import numpy as np
import pandas as pd

def initialize(context):
    """
    初始化函数
    """
    # 设定标的:这里使用沪深300ETF作为长线投资标的
    g.security = '510300.XSHG'
    
    # 设定基准
    set_benchmark('000300.XSHG')
    
    # 开启动态复权模式(真实价格)
    set_option('use_real_price', True)
    
    # 过滤掉order系列API产生的比error级别低的log
    log.set_level('order', 'error')
    
    # 设定ETF交易手续费(模拟真实ETF费率)
    set_order_cost(OrderCost(close_tax=0, open_commission=0.0003, close_commission=0.0003, min_commission=5), type='fund')
    
    # 设定按月运行:每月第一个交易日开盘运行
    run_monthly(trade_logic, 1, time='open')

def calculate_wma(series, weights):
    """
    计算加权移动平均 (WMA)
    """
    if len(series) != len(weights):
        return None
    return np.average(series, weights=weights)

def get_coppock_curve(security, context):
    """
    计算估波曲线指标
    """
    # 获取过去 30 个月的收盘价数据 (14个月ROC + 10个月WMA + 缓冲)
    # unit='1M' 获取的是过去每个月的月线数据
    h = attribute_history(security, 30, '1M', ['close'], df=True)
    
    if len(h) < 25:
        return None, None
    
    close = h['close']
    
    # 1. 计算 14个月 和 11个月 的 ROC (Rate of Change)
    # pct_change(N) 计算的是 (现价 - N天前价格) / N天前价格 * 100
    roc_14 = close.pct_change(14) * 100
    roc_11 = close.pct_change(11) * 100
    
    # 2. 将两个 ROC 相加
    roc_sum = roc_14 + roc_11
    
    # 去除前面的 NaN 值
    roc_sum = roc_sum.dropna()
    
    # 3. 计算 10 个月的 WMA (加权移动平均)
    # 权重为 1, 2, 3, ..., 10 (越近的数据权重越大)
    weights = np.arange(1, 11)
    
    if len(roc_sum) < 11:
        return None, None
        
    # 计算当前的 Coppock 值 (使用最近10个数据)
    current_series = roc_sum.iloc[-10:]
    coppock_curr = calculate_wma(current_series, weights)
    
    # 计算上个月的 Coppock 值 (使用倒数第11到倒数第2个数据)
    prev_series = roc_sum.iloc[-11:-1]
    coppock_prev = calculate_wma(prev_series, weights)
    
    return coppock_curr, coppock_prev

def trade_logic(context):
    """
    交易逻辑函数
    """
    security = g.security
    
    # 获取当前和上期的估波值
    cc_curr, cc_prev = get_coppock_curve(security, context)
    
    if cc_curr is None or cc_prev is None:
        log.info("数据不足,跳过计算")
        return
    
    # 记录指标值以便在回测图中查看
    record(Coppock=cc_curr)
    
    # 获取当前持仓
    position = context.portfolio.positions[security].total_amount
    cash = context.portfolio.available_cash
    
    # --- 交易信号判断 ---
    
    # 信号1:买入
    # 逻辑:曲线拐头向上 (当前值 > 上期值) 且 上期值 < 0 (经典用法是在零轴下方金叉,也可去掉 <0 限制做趋势跟随)
    # 这里采用经典用法:底部反转
    if cc_curr > cc_prev and cc_prev < 0:
        if position == 0:
            log.info("估波曲线底部拐头向上 (Curr: %.2f, Prev: %.2f),全仓买入" % (cc_curr, cc_prev))
            order_value(security, cash)
            
    # 信号2:卖出
    # 逻辑:曲线拐头向下 (当前值 < 上期值)
    elif cc_curr < cc_prev:
        if position > 0:
            log.info("估波曲线拐头向下 (Curr: %.2f, Prev: %.2f),清仓卖出" % (cc_curr, cc_prev))
            order_target(security, 0)

代码要点解析

  1. 数据频率 (1M)

    • 估波曲线是一个长线指标,传统定义是基于月线数据的。
    • 使用 attribute_history(security, 30, '1M', ...) 获取过去30个月的收盘价。
    • 使用 run_monthly 确保策略只在每月的第一个交易日运行,避免日内波动干扰。
  2. WMA (加权移动平均) 实现

    • Pandas 默认没有直接的 WMA 函数(只有 EMA 和 SMA)。
    • 代码中定义了 calculate_wma 函数,利用 numpy.average 配合 weights 参数(权重序列为 1 到 10)来实现线性加权。
  3. 买卖逻辑

    • 买入cc_curr > cc_prev and cc_prev < 0。这表示曲线在负值区域(通常意味着市场处于低估或熊市底部)开始向上转折。这是 Coppock Curve 最核心的用法。
    • 卖出cc_curr < cc_prev。当动量减弱,曲线开始掉头向下时离场。
  4. 标的选择

    • 代码中使用了 510300.XSHG (沪深300 ETF)。估波曲线最适合用于大盘指数或宽基 ETF,用于判断宏观市场的牛熊转换,而不适合波动剧烈的小盘股。

如何在聚宽中使用

  1. 点击聚宽导航栏的“策略研究” -> “我的策略” -> “新建策略”。
  2. 将上述代码完整复制到代码编辑区。
  3. 设置回测时间,建议设置较长的时间跨度(例如 2010年至今),因为这是一个长线策略,信号频率很低。
  4. 资金建议设置 10万 以上。
  5. 点击“编译运行”或“运行回测”。

Q&A

Q: 为什么买入条件要加上 cc_prev < 0?
A: 估波曲线的设计初衷是寻找熊市底部。在零轴下方的拐头通常代表市场极度悲观后的复苏。如果在零轴上方拐头向上,可能只是上涨中继,虽然也可以买入,但风险收益比通常不如底部反转高。

Q: 这个策略的缺点是什么?
A: 这是一个极其滞后的指标。因为它使用了14个月和11个月的ROC,再加上10个月的平滑,它对市场反应很慢。它能让你吃到牛市中间最肥美的一段,但绝对买不到最低点,也卖不到最高点。在震荡市中可能会失效。

Q: 可以应用在个股上吗?
A: 可以,但建议选择大盘蓝筹股。对于妖股或小盘股,由于波动率过大,ROC指标会产生剧烈抖动,导致WMA平滑后依然产生虚假信号。