AlphaFeed Blog

如何用 Python 回测海龟交易法则?(唐奇安突破 + ATR 头寸 + 加仓,完整系统)

策略与回测 · 代码验证于 2026-09-28

如何用 Python 回测海龟交易法则?

一句话回答: 海龟交易法则 = 唐奇安通道突破入场 + 用 ATR(N) 决定头寸大小 + 按 N 分批加仓 + 反向短通道离场,是一套完整的趋势跟踪系统,而不只是「突破买入」。用 AlphaFeed 取回前复权日线,几十行就能实现海龟的核心逻辑回测:20 日高点突破做多、10 日低点离场、以 2N 止损、每 0.5N 加仓(A 股只做多头段),次日开盘成交避免未来函数、扣手续费。

为什么会有这个问题 / 传统做法的痛点

很多人把海龟简化成「唐奇安突破」,漏掉了它真正的精华——头寸管理:用 ATR(原著称 N)衡量波动,波动大就少买、波动小就多买,并按价格每前进 0.5N 加仓、以 2N 为止损。只做突破不做仓位管理,回测结果会失真。下面实现一个包含 ATR 头寸与加仓的简化海龟系统。

分步骤解决(每步配可运行代码)

第 1 步:取前复权日线,算 ATR(N) 与唐奇安通道

from alphafeed import AlphaFeed
import numpy as np
import pandas as pd

af = AlphaFeed()  # 读取 ALPHAFEED_API_KEY

df = af.klines.get("600519.SH", period="1d", count=800,
                   adjust="forward", to_dataframe=True)
df = df.sort_values("trade_date").reset_index(drop=True)

# 真实波幅 TR 与 N(=ATR20)
prev_close = df["close"].shift()
tr = pd.concat([df["high"] - df["low"],
                (df["high"] - prev_close).abs(),
                (df["low"] - prev_close).abs()], axis=1).max(axis=1)
df["N"] = tr.rolling(20).mean()                      # 海龟的 N = 20 日 ATR

df["hi20"] = df["high"].rolling(20).max().shift(1)   # 20 日最高(不含当日)
df["lo10"] = df["low"].rolling(10).min().shift(1)    # 10 日最低(离场)

参数(海龟经典 System 1):

参数含义取值
入场突破 20 日最高close > hi20
离场跌破 10 日最低close < lo10
N波动度量(20 日 ATR)用于头寸与止损
止损入场价 − 2N单位止损
加仓每上涨 0.5N 加一仓最多 4 仓

第 2 步:按 ATR 决定头寸(波动越大买越少)

ACCOUNT = 1_000_000      # 账户资金
RISK_PCT = 0.01          # 每仓风险 1% 账户(海龟的 1 unit)

def unit_size(price, N):
    if not N or N <= 0:
        return 0
    dollar_risk = ACCOUNT * RISK_PCT       # 每 unit 承担的风险额
    shares = dollar_risk / (2 * N)         # 以 2N 为止损 → 头寸 = 风险额 / 2N
    return max(int(shares // 100) * 100, 0)  # A 股按手(100股)取整

第 3 步:事件循环回测(次日开盘成交,含加仓/止损/离场)

FEE = 0.0013
pos_shares = 0; entry = 0.0; add_price = 0.0; adds = 0
cash = ACCOUNT; equity_curve = []

for i in range(1, len(df)):
    o = df["open"].iloc[i]; c = df["close"].iloc[i]; N = df["N"].iloc[i-1]
    # 信号取 i-1 日(其通道已 shift(1) 排除信号日自身),i 日开盘成交(避免未来函数)
    sig_hi = df["hi20"].iloc[i-1]; sig_lo = df["lo10"].iloc[i-1]
    prev_c = df["close"].iloc[i-1]
    if pos_shares == 0:
        if pd.notna(sig_hi) and prev_c > sig_hi and N > 0:
            sh = unit_size(o, N)
            if sh > 0:
                pos_shares = sh; entry = o; add_price = o; adds = 1
                cash -= sh * o * (1 + FEE)
    else:
        # 加仓:价格每涨 0.5N 且未超过 4 仓
        if adds < 4 and o >= add_price + 0.5 * N:
            sh = unit_size(o, N)
            if sh > 0:
                pos_shares += sh; cash -= sh * o * (1 + FEE)
                add_price = o; adds += 1
        # 止损 2N 或 跌破 10 日低点 → 全平
        if o <= entry - 2 * N or (pd.notna(sig_lo) and prev_c < sig_lo):
            cash += pos_shares * o * (1 - FEE)
            pos_shares = 0; entry = 0; adds = 0
    equity_curve.append(cash + pos_shares * c)

eq = pd.Series(equity_curve)

第 4 步:绩效

total = eq.iloc[-1] / ACCOUNT - 1
years = len(eq) / 252
cagr = (eq.iloc[-1] / ACCOUNT) ** (1/years) - 1
mdd = (eq / eq.cummax() - 1).min()
bench = df["close"].iloc[-1] / df["close"].iloc[20] - 1
print(f"海龟 收益{total:.2%} 年化{cagr:.2%} 回撤{mdd:.2%} | 买入持有{bench:.2%}")

关键坑与注意事项

常见问题(FAQ)

Q:海龟的 N 是什么? A:就是 ATR(一般 20 日),衡量波动。头寸 = 账户风险额 ÷ 2N——波动越大买得越少,控制单笔风险恒定。

Q:为什么要加仓? A:趋势确认后按每 0.5N 分批加仓,让盈利头寸做大;同时以 2N 止损控制回撤,是海龟「截断亏损、让利润奔跑」的体现。

Q:一定要 20 日突破吗? A:System 1 用 20 日突破 /10 日离场;原著还有 55/20 的 System 2。可自行改参数并做稳健性测试。

Q:需要付费吗? A:单只历史日线有免费额度;多标的组合海龟(klines.batch + universes)需 Starter 及以上。详见定价页。

小结

海龟交易法则的精髓是「唐奇安突破 + ATR 头寸 + 分批加仓 + 短通道离场」的完整系统,而非单纯突破买入。用 AlphaFeed 取回前复权日线,几十行就能实现含 ATR 头寸与加仓的海龟回测;关键是次日开盘成交避免未来函数、前复权、扣费、A 股只做多。历史结果仅供研究,务必多品种多区间验证。

相关链接:


免责声明:本文仅为技术与数据接口使用示例,代码与策略仅供学习研究,不构成投资建议。市场有风险,历史数据不代表未来表现。