1. 海龟交易策略一个被公开的“圣杯”传说在量化交易的世界里很少有策略能像“海龟交易策略”这样既充满传奇色彩又如此彻底地暴露在公众视野之下。它源于上世纪80年代一场著名的“海龟实验”由商品交易大师理查德·丹尼斯和他的合伙人威廉·埃克哈特发起。他们通过一场公开的赌约证明了交易才能并非天生而是可以通过一套严格的规则体系来培养。他们招募并培训了一批普通人这些学员后来被称为“海龟”而他们所遵循的那套完整的趋势跟踪交易系统就是“海龟交易策略”。这套策略的核心不是预测市场而是通过一套机械化的规则捕捉并跟随市场的主要趋势在趋势中获取利润并通过严格的资金管理来控制风险。对于每一个想要踏入系统化交易大门的人来说海龟策略都是一本绝佳的“入门教科书”它剥离了交易中的情绪干扰将复杂的市场行为简化为可执行、可回测、可复制的规则。今天我们就来深入拆解这个经典策略从核心理念到参数计算再到用Python一步步实现它并分享在实际应用中那些容易被忽略的“坑”和优化思路。2. 海龟策略的四大核心支柱理解其运作逻辑海龟策略之所以经典且有效在于它构建了一个完整的交易系统闭环这个系统建立在四个相互支撑的支柱之上市场选择、入市信号、头寸规模资金管理和离市信号。理解这四者之间的关系比记住具体参数更重要。2.1 市场选择与时间框架趋势的土壤海龟策略是一个典型的中长期趋势跟踪策略。这意味着它并非为短线震荡市或日内交易设计。它的目标是捕捉持续数周甚至数月的大级别趋势。因此策略对交易品种和市场有明确要求高流动性品种需要有足够的成交量和市场深度以确保大资金进出时滑点成本可控。海龟们最初交易的是期货市场如原油、外汇、债券期货这些市场流动性好且自带杠杆。低相关性为了分散风险海龟们会在多个不相关的市场同时寻找机会。一个市场的亏损可能被另一个市场的盈利所抵消。在A股市场我们可以选择不同行业板块的ETF或流动性极佳的大盘股。时间框架原版海龟策略使用日线数据进行计算和决策。这是策略的“心跳”频率。所有关键指标如突破、ATR都基于日线收盘价计算。试图在分钟线上套用海龟规则往往会因为市场噪音过大而失效。注意很多初学者第一个坑就是选错了市场或周期。试图在比特币的1小时线或一只成交稀薄的小盘股上运行海龟策略结果通常是频繁的假突破和持续的磨损。策略的土壤选错了再好的种子也无法生长。2.2 入市信号双重突破系统海龟策略的入场并非主观判断而是等待价格突破特定的边界。它采用了一套双重入场系统兼顾了趋势的初次确认和回调后的再次介入。系统一以20日突破为基础的初始入场这是主要的入场信号。当今日收盘价突破过去20个交易日约一个月的最高价时产生买入信号当收盘价跌破过去20个交易日的最低价时产生卖出信号。这里的“突破”定义为收盘价确认而非盘中价格这过滤了许多盘中假突破的噪音。这个20日的窗口是捕捉中级趋势启动的一个经验参数。系统二以55日突破为基础的备用入场这是更长期的趋势过滤器。如果错过了系统一的入场点价格继续运行那么当价格突破过去55个交易日约一个季度的最高/最低点时会给出第二次入场机会。系统二的信号更强意味着趋势可能更持久但入场位置往往更差离止损更远。原版规则中海龟们被要求同时执行两套系统。2.3 头寸规模与资金管理生存的基石这是海龟策略的灵魂也是其与普通“突破就买”策略的本质区别。海龟不是简单地“买1手”而是根据市场波动性和账户风险动态计算买入数量。其核心公式是头寸规模单位 (账户总风险) / (每单位资产的风险)账户总风险通常设定为账户总资金的1%。例如100万账户单次交易可承受风险为1万元。每单位资产的风险这里引入了海龟策略最著名的指标——平均真实波幅。ATR衡量了资产在过去一段时间通常为20日内的平均波动幅度。每单位资产的风险通常设置为1个ATR。意思是如果股价波动了1个ATR的幅度我们就认为它发生了“正常”的波动。因此具体计算步骤为计算当前资产的20日ATR值例如ATR 2.0元。确定每手或每单位合约所代表的资产数量例如A股是100股/手期货有合约乘数。计算每手股票的风险金额 ATR * 每手股数 2.0元 * 100 200元。计算可购买手数 账户总风险 / 每手风险金额 10,000元 / 200元 50手。最终买入数量 可购买手数 * 每手股数 50 * 100 5000股。这套方法的美妙之处在于“波动性标准化”。在波动大的市场ATR大自动减少头寸控制单笔亏损在波动小的市场ATR小适当增加头寸不浪费机会。这保证了在不同波动特性的品种上单笔交易的理论风险是基本一致的。2.4 离市信号让利润奔跑但适时收网“截断亏损让利润奔跑”是趋势跟踪的格言。海龟的离市止损和止盈规则完美体现了这一点。止损非常严格。入场后会在相反方向距离入场价2个ATR的位置设置初始止损单。这是一个硬性退出规则绝不扛单。例如在10元买入ATR为0.5元则止损价设为 10 - 2*0.5 9元。这笔交易的最大亏损被锁定在约2%的资产价格波动对应账户1%的风险。止盈趋势跟踪者没有固定的盈利目标。海龟的离场信号是反向突破。对于多头头寸当价格跌破过去10日最低价时离场对于空头头寸当价格升破过去20日最高价时离场。注意这里的离场周期10日/20日与入场周期20日/55日不同是一种不对称的优化旨在更快地保护上涨利润同时给下跌趋势更多确认时间。3. 用Python构建海龟交易策略从理论到代码理解了原理我们开始动手实现。我们将使用pandas和backtrader一个功能强大的Python量化回测框架来构建策略。这里会详细解释每一步的代码意图。3.1 数据准备与ATR计算首先我们需要获取历史日线数据并计算核心指标ATR。import pandas as pd import numpy as np import backtrader as bt # 假设我们已经有了一个包含OHLCV的DataFrame df索引为日期 # 计算真实波幅(TR) df[high_low] df[high] - df[low] df[high_close_prev] abs(df[high] - df[close].shift(1)) df[low_close_prev] abs(df[low] - df[close].shift(1)) df[TR] df[[high_low, high_close_prev, low_close_prev]].max(axis1) # 计算20日简单移动平均的ATR df[ATR] df[TR].rolling(window20).mean() # 计算突破通道20日最高价和最低价 df[20_day_high] df[high].rolling(window20).max() df[20_day_low] df[low].rolling(window20).min() # 计算55日突破通道系统二备用 df[55_day_high] df[high].rolling(window55).max() df[55_day_low] df[low].rolling(window55).min()注意TR的计算考虑了缺口情况昨日收盘与今日最高/最低的差距这比单纯用最高-最低更能反映真实波动。rolling().mean()用于计算移动平均确保我们用的是历史数据避免未来函数。3.2 在Backtrader中实现策略逻辑我们将创建一个TurtleStrategy类继承自bt.Strategy。class TurtleStrategy(bt.Strategy): params ( (system1_period, 20), # 系统一突破周期 (system2_period, 55), # 系统二突破周期 (atr_period, 20), # ATR计算周期 (risk_per_trade, 0.01), # 单笔交易风险比例1% (stop_loss_atr, 2.0), # 止损ATR倍数 (exit_long_period, 10), # 多头离场周期 (exit_short_period, 20),# 空头离场周期 ) def __init__(self): # 保存数据引用 self.dataclose self.datas[0].close self.datahigh self.datas[0].high self.datalow self.datas[0].low # 计算指标 # 1. 突破通道 self.high_20 bt.indicators.Highest(self.datahigh, periodself.p.system1_period) self.low_20 bt.indicators.Lowest(self.datalow, periodself.p.system1_period) self.high_55 bt.indicators.Highest(self.datahigh, periodself.p.system2_period) self.low_55 bt.indicators.Lowest(self.datalow, periodself.p.system2_period) # 2. ATR self.atr bt.indicators.ATR(self.datas[0], periodself.p.atr_period) # 3. 离场通道 self.exit_high bt.indicators.Highest(self.datahigh, periodself.p.exit_short_period) self.exit_low bt.indicators.Lowest(self.datalow, periodself.p.exit_long_period) # 状态变量 self.entry_price 0 # 入场价格 self.stop_price 0 # 当前止损价 self.position_size 0 # 理论头寸规模用于计算 self.system1_entry False # 系统一是否已触发 self.system2_entry False # 系统二是否已触发__init__方法初始化了所有需要的指标。bt.indicators模块提供了高效的计算。我们定义了状态变量来跟踪交易。3.3 核心交易逻辑next函数next函数在每个K线周期这里是每天都会被调用是策略决策的核心。def next(self): # 如果还没有仓位 if not self.position: self.system1_entry False self.system2_entry False # 系统一入场条件收盘价突破20日高点 if self.dataclose[0] self.high_20[-1]: # 比较当前收盘价和前一日最高价通道 self.entry_price self.dataclose[0] self.system1_entry True self._enter_long() # 系统一入场条件收盘价跌破20日低点做空A股融券或期货适用 elif self.dataclose[0] self.low_20[-1]: self.entry_price self.dataclose[0] self.system1_entry True self._enter_short() # 如果系统一未触发检查系统二55日突破 elif self.dataclose[0] self.high_55[-1]: self.entry_price self.dataclose[0] self.system2_entry True self._enter_long() elif self.dataclose[0] self.low_55[-1]: self.entry_price self.dataclose[0] self.system2_entry True self._enter_short() # 如果持有多头仓位 elif self.position.size 0: # 更新跟踪止损初始止损为入场价 - 2*ATR之后随着价格上涨止损价上移至 收盘价 - 2*ATR但不低于初始止损 new_stop self.dataclose[0] - self.p.stop_loss_atr * self.atr[0] self.stop_price max(self.stop_price, new_stop) # 止损只上移不下移 # 离场条件1价格跌破跟踪止损价 if self.dataclose[0] self.stop_price: self.close() # 离场条件2价格跌破10日最低价原版离场规则 elif self.dataclose[0] self.exit_low[-1]: self.close() # 如果持有空头仓位逻辑对称 elif self.position.size 0: new_stop self.dataclose[0] self.p.stop_loss_atr * self.atr[0] self.stop_price min(self.stop_price, new_stop) # 止损只下移不上移 if self.dataclose[0] self.stop_price: self.close() elif self.dataclose[0] self.exit_high[-1]: self.close()这段代码清晰地体现了海龟规则的机械化。入场等待突破信号持仓期间不断计算并上移或下移止损离场则依据止损价或反向突破信号。3.4 动态头寸计算与下单这是资金管理的关键在_enter_long和_enter_short方法中实现。def _enter_long(self): # 计算头寸规模 risk_capital self.broker.getvalue() * self.p.risk_per_trade # 风险资金 总资产 * 1% risk_per_share self.p.stop_loss_atr * self.atr[0] # 每股风险 2 * ATR if risk_per_share 0: return # 避免除零错误 # 计算应买入的股数 size risk_capital / risk_per_share size int(size) // 100 * 100 # A股按手取整1手100股 if size 0: self.buy(sizesize) # 记录初始止损价 self.stop_price self.entry_price - risk_per_share def _enter_short(self): # 做空逻辑类似方向相反 risk_capital self.broker.getvalue() * self.p.risk_per_trade risk_per_share self.p.stop_loss_atr * self.atr[0] if risk_per_share 0: return size risk_capital / risk_per_share size int(size) // 100 * 100 if size 0: self.sell(sizesize) self.stop_price self.entry_price risk_per_share这里self.broker.getvalue()获取当前账户总资产实现了动态调整。// 100 * 100是A股以手为单位的取整方式。4. 策略回测、绩效分析与参数敏感度写完策略后我们需要在历史数据上检验其表现。使用backtrader可以方便地进行回测和分析。4.1 执行回测与基础绩效指标# 创建引擎 cerebro bt.Cerebro() # 加载数据假设df已准备好 data bt.feeds.PandasData(datanamedf) cerebro.adddata(data) # 添加策略 cerebro.addstrategy(TurtleStrategy) # 设置初始资金 cerebro.broker.setcash(100000.0) # 设置交易手续费以A股万2.5为例 cerebro.broker.setcommission(commission0.00025) # 运行回测 print(‘起始资金: %.2f’ % cerebro.broker.getvalue()) cerebro.run() print(‘最终资金: %.2f’ % cerebro.broker.getvalue()) # 绘制图表 cerebro.plot()运行后我们可以得到最终净值。但更重要的是分析绩效报告。backtrader可以通过analyzers添加分析器。# 在cerebro.run()前添加分析器 cerebro.addanalyzer(bt.analyzers.SharpeRatio, _namesharpe, riskfreerate0.03, annualizeTrue) cerebro.addanalyzer(bt.analyzers.DrawDown, _namedrawdown) cerebro.addanalyzer(bt.analyzers.TradeAnalyzer, _nametrades) cerebro.addanalyzer(bt.analyzers.Returns, _namereturns) # 运行 results cerebro.run() strat results[0] # 打印关键指标 print(‘夏普比率:’ strat.analyzers.sharpe.get_analysis()[‘sharperatio’]) print(‘最大回撤:’ strat.analyzers.drawdown.get_analysis()[‘max’][‘drawdown’] ‘%’) print(‘总收益率:’ strat.analyzers.returns.get_analysis()[‘rtot’] * 100 ‘%’) # 交易分析 ta strat.analyzers.trades.get_analysis() if ‘total’ in ta: print(‘总交易次数:’ ta.total.total) print(‘胜率:’ ta.won.total / ta.total.total * 100 if ta.total.total 0 else 0 ‘%’) print(‘平均盈利:’ ta.won.pnl.average if ta.won.total 0 else 0) print(‘平均亏损:’ ta.lost.pnl.average if ta.lost.total 0 else 0)一个典型的海龟策略回测结果可能呈现以下特征胜率较低通常在30%-40%左右因为趋势行情只占市场时间的少部分大部分突破是假信号。盈亏比较高平均盈利远大于平均亏损这正是“截断亏损让利润奔跑”的体现。最大回撤可能较大在漫长的震荡市中策略会连续产生小额亏损导致资金曲线出现显著回撤20%-30%甚至更高。长期年化收益为正在存在趋势的市场中长期来看能获得超越基准的回报。4.2 参数敏感度测试与过拟合陷阱海龟策略的参数20日、55日、2ATR止损等是丹尼斯时代基于商品期货数据总结的。在不同的市场如A股、不同的时期这些参数未必最优。我们需要进行参数优化但要警惕过拟合。# 使用Backtrader的优化功能 cerebro.optstrategy( TurtleStrategy, system1_periodrange(15, 26, 5), # 测试15, 20, 25 system2_periodrange(50, 61, 5), # 测试50, 55, 60 stop_loss_atr[1.5, 2.0, 2.5], ) # 运行优化会比较慢因为要跑多组参数 opt_results cerebro.run()优化后可以对比不同参数组合下的夏普比率、最大回撤、总收益等指标。关键经验是寻找在较长时间段内最好包含牛市、熊市、震荡市表现稳健的参数区域而不是追求单一历史数据上收益最高的“尖峰”参数。参数在某个区间如突破周期18-22日表现稳定比一个孤立的“20日”更有说服力。5. 实盘中的挑战与进阶优化思路将回测漂亮的海龟策略投入实盘你会立刻遇到一系列回测中不曾显现的挑战。这部分是真正的“干货”。5.1 流动性冲击与滑点成本回测假设我们能在收盘价瞬间成交但实盘中尤其是按收盘信号操作时会遇到“抢跑”和流动性不足的问题。问题当收盘价刚好满足突破条件时很多程序化交易者会同时发出订单集中在收盘前几分钟或开盘后几分钟导致实际成交价远差于预期价格。应对成交价模拟在回测中主动加入滑点成本例如固定滑点0.1%或比例滑点成交金额的万分之五。backtrader中可以通过cerebro.broker.set_slippage_perc(0.0005)来设置。订单执行算法不要使用市价单改用限价单并尝试在突破确认后的下一个交易日开盘一段时间内分批成交。放宽突破条件例如要求收盘价突破20日高点至少0.5%或1个ATR以过滤掉那些强度不够的微弱突破这能显著减少无效交易但也会错过一些早期信号。5.2 震荡市的磨损与心理考验这是趋势跟踪策略的天敌。在漫长的无趋势震荡行情中策略会反复“突破买入-止损卖出-反向突破卖出-止损买入”产生一连串的小额亏损资金曲线持续阴跌。这个过程可能持续数月对执行者的心理是巨大考验。应对多市场、多品种分散这是原版海龟的核心。当A品种震荡时B品种可能正走趋势。整体资金曲线会被平滑。引入市场状态过滤器可以尝试用ADX平均趋向指数等指标来量化市场趋势强度。只有当ADX高于某个阈值如25时才启用海龟突破信号。这能有效减少震荡市的交易次数但缺点是也会过滤掉趋势启动初期的部分利润。接受它认识到“磨损”是策略的必要成本就像生意的租金。只要长期期望收益为正并且回撤在风险承受范围内就必须坚持系统。执行力是量化交易最难的部分。5.3 头寸规模计算的现实调整原版公式在实盘中可能需要微调。问题计算出的头寸可能过大超过品种的日成交量一定比例导致无法顺利建仓或平仓。应对在计算出的头寸规模上增加一个流动性限制。例如规定单次下单量不超过该品种过去20日平均成交量的1%。这需要你在头寸计算函数中加入额外的判断逻辑。5.4 策略的变体与融合纯粹的原版海龟在当今高度有效的市场中其阿尔法超额收益可能已被稀释。可以考虑将其与其他逻辑融合。与基本面筛选结合只在财务健康、处于景气行业的股票池中运行海龟策略避免踩雷和跟随无逻辑的下跌趋势。多时间框架确认在日线发出突破信号时检查周线图是否也处于同向的趋势中增加信号的可靠性。动态风险暴露根据整体市场的波动率如VIX指数或策略自身的回撤幅度动态调整risk_per_trade。在波动极大或连续亏损后主动降低风险比例保护本金。海龟交易策略远非一个“印钞机”代码。它是一个完整的交易哲学和风险管理体系的代码化体现。它的最大价值在于教育交易者如何系统地思考市场、管理风险和约束自身。通过亲手实现它、回测它、思考它的缺陷并尝试改进你所学到的关于系统化、纪律性和概率思维的东西远比策略本身的盈亏曲线更有价值。在实盘中我最大的体会是策略本身可能只贡献了30%的成功因素而资金管理海龟的核心和坚定不移的执行力占了剩下的70%。当你因为连续五次止损而怀疑系统却依然能按下“确认下单”按钮时你才真正开始理解什么是交易。