🚀 AI 一键生成 joinquant 策略代码
立即体验

如何在聚宽中实现经典海龟突破策略?(附 300ETF 多品种组合与周期优化 Python 源码)

joinquant | 发布时间: 2026/8/9
以下内容由 EasyQuant 生成。

问题描述

聚宽海龟交易策略:300ETF 如何扩展为多品种组合并优化周期?
如何在聚宽中实现经典海龟突破策略,并比较 300ETF 单品种、多品种组合及不同 K 线周期对回测结果的影响?

解决方案

经典海龟交易策略核心原理

海龟交易策略(Turtle Trading System)是量化交易领域的经典之作,其核心思想基于**唐奇安通道(Donchian Channel)的突破系统,并结合真实波幅均值(ATR)**进行动态仓位管理和加仓减仓。其主要规则如下:

  1. 系统一突破(短期):以 20 日突破(新高)买入,10 日突破(新低)卖出。
  2. 仓位管理(N 值):使用 20 日 ATR(即 N 值)来衡量波动率。单次建仓单位(Unit) = 账户总资产的 1% / (N * 合约乘数 * 价格)。
  3. 动态加仓:价格每上涨 0.5N,加仓一个 Unit,最多加仓至 4 个 Unit。
  4. 止损与止盈:价格下跌 2N 强制止损;跌破 10 日唐奇安通道下轨止盈。

聚宽(JoinQuant)多品种海龟策略源码实现

以下代码展示了如何在聚宽平台实现一个支持**多品种组合(如 300ETF、500ETF、创业板ETF)并支持动态复权(真实价格)**的海龟突破策略:

import numpy as np
import pandas as pd
from jqdata import *

def initialize(context):
    # 1. 策略基本设置
    set_benchmark('000300.XSHG')
    set_option('use_real_price', True) # 开启真实价格模式,避免未来函数
    
    # 2. 设定交易费率
    set_order_cost(OrderCost(close_tax=0.001, open_commission=0.0003, close_commission=0.0003, min_commission=5), type='stock')
    
    # 3. 策略参数定义
    g.donchian_high_window = 20  # 唐奇安通道上轨周期
    g.donchian_low_window = 10   # 唐奇安通道下轨周期
    g.atr_window = 20            # ATR周期
    g.max_units = 4              # 最大加仓次数
    
    # 4. 扩展为多品种组合(例如:沪深300ETF、中证500ETF、创业板ETF)
    g.security_list = ['510300.XSHG', '510500.XSHG', '159915.XSHE']
    
    # 5. 记录每个品种的交易状态
    g.turtle_status = {}
    for security in g.security_list:
        g.turtle_status[security] = {
            'units': 0,          # 当前持仓Unit数
            'last_buy_price': 0  # 上一次买入价格
        }
    
    # 每日开盘运行
    run_daily(market_open, time='every_bar')

def get_atr_and_donchian(security, context):
    # 获取历史数据(包含当前Bar的前N个Bar)
    hist = attribute_history(security, g.donchian_high_window + 1, '1d', ['high', 'low', 'close'])
    if len(hist) < g.donchian_high_window:
        return None, None, None
    
    # 计算唐奇安通道上下轨(不含当前Bar)
    high_limit = hist['high'][:-1].max()
    low_limit = hist['low'][:-1].max() # 止盈参考下轨
    if g.donchian_low_window < g.donchian_high_window:
        low_limit = hist['low'][-g.donchian_low_window-1:-1].min()
        
    # 计算ATR (N值)
    close_prev = hist['close'][:-1].values
    high = hist['high'][-g.atr_window-1:-1].values
    low = hist['low'][-g.atr_window-1:-1].values
    
    tr_list = []
    for i in range(len(high)):
        if i == 0:
            tr = high[i] - low[i]
        else:
            tr = max(high[i] - low[i], abs(high[i] - close_prev[i-1]), abs(low[i] - close_prev[i-1]))
        tr_list.append(tr)
    atr = np.mean(tr_list)
    
    return atr, high_limit, low_limit

def market_open(context):
    total_value = context.portfolio.total_value
    
    for security in g.security_list:
        status = g.turtle_status[security]
        current_data = get_current_data()
        
        if current_data[security].paused:
            continue
            
        current_price = current_data[security].last_price
        atr, high_limit, low_limit = get_atr_and_donchian(security, context)
        
        if atr is None or atr == 0:
            continue
            
        # 计算单个Unit的股数(假设资金分配到每个品种的权重相等)
        target_value_per_unit = (total_value / len(g.security_list)) * 0.01
        unit_size = int(target_value_per_unit / atr)
        unit_size = (unit_size // 100) * 100 # A股100股整数倍限制
        
        if unit_size == 0:
            continue
            
        # 1. 止损与止盈判断
        if status['units'] > 0:
            # 止损:价格低于上一次买入价 - 2 * ATR
            # 止盈:价格跌破唐奇安通道下轨
            if current_price < (status['last_buy_price'] - 2 * atr) or current_price < low_limit:
                order_target(security, 0)
                log.info(f"[止损/止盈] 卖出 {security},清空仓位")
                status['units'] = 0
                status['last_buy_price'] = 0
                continue
        
        # 2. 突破买入/加仓逻辑
        if status['units'] == 0:
            # 突破上轨首笔建仓
            if current_price > high_limit:
                order(security, unit_size)
                log.info(f"[建仓] 突破上轨,买入 {security} {unit_size}股")
                status['units'] = 1
                status['last_buy_price'] = current_price
        elif status['units'] < g.max_units:
            # 价格每上涨 0.5 * ATR,加仓一个 Unit
            if current_price >= (status['last_buy_price'] + 0.5 * atr):
                order(security, unit_size)
                log.info(f"[加仓] 价格上涨0.5N,买入 {security} {unit_size}股")
                status['units'] += 1
                status['last_buy_price'] = current_price

策略对比与优化分析

在聚宽平台进行回测时,通过调整品种配置K线周期,可以观察到显著的业绩差异:

1. 单品种(300ETF) vs 多品种组合

  • 单品种(300ETF):海龟策略在单边牛市中表现极佳,但在震荡市中容易频繁假突破导致磨损。300ETF 在 2016-2018 年震荡期间回撤较大。
  • 多品种组合(300ETF + 500ETF + 创业板ETF):通过引入多品种,策略的夏普比率(Sharpe Ratio)显著提升。由于不同宽基指数的牛熊转换节奏不同,多品种组合能有效分散单一品种假突破的风险,平滑资金曲线,降低最大回撤(Max Drawdown)

2. K线周期优化(日线 vs 分钟线)

  • 日线周期(Daily):信号稳定,交易频次低,受滑点和佣金的影响较小。适合资金量较大、追求稳健的投资者。缺点是突破信号可能存在滞后,无法在日内极端行情中及时止损。
  • 分钟周期(如 30m, 60m):利用聚宽的 get_barsrun_daily(..., time='every_bar') 在分钟级别运行。分钟线能更灵敏地捕捉日内突破,止损更及时。然而,**交易摩擦成本(佣金+印花税+滑点)**会大幅上升。在实际回测中,若未设置合理的滑点(如未调用 set_slippage),分钟线可能表现虚高;一旦加入真实滑点,日内假突破的磨损可能导致净值跑输日线周期。

优化建议

  1. 开启真实价格:在 initialize 中务必使用 set_option('use_real_price', True),确保回测与模拟盘价格一致,避免前复权带来的未来函数。
  2. 滑点设置:使用 set_slippage(PriceRelatedSlippage(0.00246)) 模拟真实交易摩擦,尤其在分钟级回测中,这能过滤掉无法实盘的虚假高收益。