如何用 Python 回测海龟交易法则?(唐奇安突破 + ATR 头寸 + 加仓,完整系统)
如何用 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%}")
关键坑与注意事项
- 海龟 ≠ 只做突破:核心是 ATR 头寸管理与加仓,缺了它回测不真实。
- 避免未来函数:用 i−1 日收盘判信号、i 日开盘成交;通道用
.shift(1)排除当日。 - 前复权价:跨除权日必须前复权,否则 ATR、通道、收益全失真。
- A 股只做多头段:原版海龟可做空,A 股个股难裸卖空,本文只做多、空仓代替做空。
- 手续费与滑点:加仓频繁,务必扣费;本文未计滑点与冲击成本,实盘更保守。
- T+1 与涨跌停:突破日若涨停可能买不进,本文为简化模型未完全模拟。
- 参数与过拟合:20/10、2N、0.5N、4 仓是经典值,换股换期需稳健性检验。
常见问题(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 股只做多。历史结果仅供研究,务必多品种多区间验证。
相关链接:
- AlphaFeed 官网:https://alphafeed.org/
- Python SDK 快速开始:https://docs.alphafeed.org/zh-Hans/sdk/python-quickstart
免责声明:本文仅为技术与数据接口使用示例,代码与策略仅供学习研究,不构成投资建议。市场有风险,历史数据不代表未来表现。