# 海龟交易系统EA - 完整MQL4源码(含ATR仓位管理)
本文提供一个完整的传奇海龟交易系统EA源码。该策略由Richard Dennis和William Eckhardt于1980年代开创,原始海龟交易员在培训期间创造了超过1亿美元的利润,至今仍是趋势跟踪策略的黄金标准。
策略概述
海龟交易系统是纯粹的趋势跟踪策略,包含两个子系统:系统1(20日突破)和系统2(55日突破)。EA使用唐奇安通道作为入场信号,ATR(平均真实波幅)计算仓位大小,并融入反马丁格尔原则——仅在盈利交易后增加仓位规模。
核心组件
1. 唐奇安通道突破:价格突破N周期最高点时入场
2. ATR仓位计算:风险 = 账户1% / (ATR × 每点价值)
3. 金字塔加仓:每方向最多4次递增入场
4. 反马丁格尔:基于盈利的仓位缩放
5. 波动率止损:初始止损2倍ATR,通道跟踪止损
完整MQL4代码
```mql4
//+------------------------------------------------------------------+
//| TurtleSystemEA.mq4 |
//| 自主编译 |
//| Based on Turtle Trading Rules |
//+------------------------------------------------------------------+
#property copyright "AI助手"
#property link ""
#property version "2.00"
#property strict
//--- 输入参数 - 系统选择
input int SystemChoice = 2; // 1=系统1(20日), 2=系统2(55日)
input int CustomBreakout = 0; // 自定义突破周期(0=使用系统默认)
input int CustomExit = 0; // 自定义离场周期(0=使用系统默认)
//--- 仓位管理
input double RiskPercent = 1.0; // 每笔风险占账户百分比
input double ATRMultiplier = 2.0; // 初始止损ATR倍数
input double MaxRiskPerUnit = 0.02; // 每单位最大风险(2%)
input double MaxDrawdownPercent = 30.0; // 最大回撤限制(紧急停止)
//--- 金字塔加仓设置
input int MaxAdds = 3; // 最大金字塔加仓次数(1-4)
input double AddDistance = 0.5; // 金字塔加仓间隔(ATR倍数)
//--- 反马丁格尔
input bool UseAntiMartingale = true; // 盈利后增加仓位
input int WinStreakRequired = 2; // 需要连续盈利次数
input double ScaleUpFactor = 0.25; // 仓位增加系数(25%)
//--- 追踪止损参数
input bool UseChandelierExit = true; // 使用通道离场(10日低点/高点)
input int ChandelierPeriod = 10; // 离场通道周期
//--- 风控保护
input int MaxSpread = 35; // 最大允许点差
input int Slippage = 10; // 最大滑点
input int MagicNumber = 198312; // EA魔术号(海龟年份)
input string TradeComment = "Turtle v2";
//--- 全局变量
int breakoutPeriod = 20;
int exitPeriod = 10;
double unitSize = 0;
double currentATR = 0;
int consecutiveWins = 0;
int entriesThisSignal = 0;
datetime lastSignalTime = 0;
double lastTradeProfit = 0;
int pointMultiplier = 10;
//--- 交易记录结构体
struct TradeRecord {
int ticket;
datetime openTime;
double openPrice;
double unitNumber;
double stopLoss;
};
TradeRecord activeTrades[];
//+------------------------------------------------------------------+
//| EA初始化函数 |
//+------------------------------------------------------------------+
int OnInit()
{
// 检测平台格式
if(Digits == 3 || Digits == 5)
pointMultiplier = 10;
else
pointMultiplier = 1;
// 根据选择设置系统参数
if(CustomBreakout > 0) {
breakoutPeriod = CustomBreakout;
} else if(SystemChoice == 1) {
breakoutPeriod = 20; // 系统1: 20日突破
exitPeriod = 10; // 系统1: 10日离场
} else {
breakoutPeriod = 55; // 系统2: 55日突破
exitPeriod = 20; // 系统2: 20日离场
}
if(CustomExit > 0) exitPeriod = CustomExit;
// 参数验证
if(breakoutPeriod < 10) {
Print("错误: 突破周期至少为10");
return(INIT_PARAMETERS_INCORRECT);
}
Print("海龟系统EA初始化成功");
Print("突破周期: ", breakoutPeriod, " | 离场周期: ", exitPeriod);
Print("每笔风险: ", RiskPercent, "% | 最大加仓: ", MaxAdds);
ArrayResize(activeTrades, 0);
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| EA反初始化函数 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
Print("海龟系统EA已移除");
}
//+------------------------------------------------------------------+
//| 检测新K线 |
//+------------------------------------------------------------------+
bool IsNewBar()
{
static datetime lastBarTime = 0;
datetime currentBarTime = iTime(Symbol(), 0, 0);
if(currentBarTime != lastBarTime) {
lastBarTime = currentBarTime;
return true;
}
return false;
}
//+------------------------------------------------------------------+
//| 计算唐奇安通道高点 |
//+------------------------------------------------------------------+
double GetDonchianHigh(int period, int shift)
{
double highest = 0;
for(int i = shift; i < shift + period; i++) {
double high = iHigh(Symbol(), 0, i);
if(high > highest || highest == 0)
highest = high;
}
return highest;
}
//+------------------------------------------------------------------+
//| 计算唐奇安通道低点 |
//+------------------------------------------------------------------+
double GetDonchianLow(int period, int shift)
{
double lowest = DBL_MAX;
for(int i = shift; i < shift + period; i++) {
double low = iLow(Symbol(), 0, i);
if(low < lowest)
lowest = low;
}
return lowest;
}
//+------------------------------------------------------------------+
//| 计算ATR(标准算法) |
//+------------------------------------------------------------------+
double CalculateATR(int period, int shift)
{
if(period < 1 || shift + period >= Bars) return 0;
double sumTR = 0;
for(int i = shift; i < shift + period; i++) {
double high = iHigh(Symbol(), 0, i);
double low = iLow(Symbol(), 0, i);
double prevClose = iClose(Symbol(), 0, i + 1);
double tr = MathMax(high, prevClose) - MathMin(low, prevClose);
sumTR += tr;
}
return sumTR / period;
}
//+------------------------------------------------------------------+
//| 使用海龟公式计算头寸单位 |
//| 单位 = 账户1% / (ATR × 每点美元价值) |
//+------------------------------------------------------------------+
double CalculateUnitSize()
{
currentATR = CalculateATR(14, 0);
if(currentATR <= 0) return 0.01;
double accountEquity = AccountBalance();
double riskAmount = accountEquity * RiskPercent / 100.0;
double tickValue = MarketInfo(Symbol(), MODE_TICKVALUE);
double tickSize = MarketInfo(Symbol(), MODE_TICKSIZE);
if(tickValue <= 0 || tickSize <= 0) return 0.01;
// 将ATR转换为货币价值
double atrPoints = currentATR / Point / pointMultiplier;
double dollarRiskPerUnit = atrPoints * tickValue;
if(dollarRiskPerUnit <= 0) return 0.01;
double calculatedUnit = riskAmount / dollarRiskPerUnit;
// 按步长取整
double lotStep = MarketInfo(Symbol(), MODE_LOTSTEP);
if(lotStep > 0) {
calculatedUnit = MathFloor(calculatedUnit / lotStep) * lotStep;
}
// 应用反马丁格尔缩放
if(UseAntiMartingale && consecutiveWins >= WinStreakRequired) {
double scaleBonus = 1.0 + (consecutiveWins - WinStreakRequired + 1) * ScaleUpFactor;
calculatedUnit = calculatedUnit * MathMin(scaleBonus, 2.0); // 上限2倍
}
// 最小/最大手数验证
double minLot = MarketInfo(Symbol(), MODE_MINLOT);
double maxLot = MarketInfo(Symbol(), MODE_MAXLOT);
if(calculatedUnit < minLot) calculatedUnit = minLot;
if(calculatedUnit > maxLot) calculatedUnit = maxLot;
return NormalizeDouble(calculatedUnit, 2);
}
//+------------------------------------------------------------------+
//| 检查突破买入信号 |
//+------------------------------------------------------------------+
bool IsBreakoutBuy()
{
double currentHigh = iHigh(Symbol(), 0, 1); // 前一根K线高点
double breakoutLevel = GetDonchianHigh(breakoutPeriod, 1); // N周期高点(不含当前)
if(breakoutLevel <= 0) return false;
bool breakout = (currentHigh > breakoutLevel);
// 防止同一K线重复信号
if(breakout && Time[0] == lastSignalTime) return false;
if(breakout) lastSignalTime = Time[0];
return breakout;
}
//+------------------------------------------------------------------+
//| 检查突破卖出信号 |
//+------------------------------------------------------------------+
bool IsBreakoutSell()
{
double currentLow = iLow(Symbol(), 0, 1); // 前一根K线低点
double breakoutLevel = GetDonchianLow(breakoutPeriod, 1); // N周期低点(不含当前)
if(breakoutLevel >= DBL_MAX - 1) return false;
bool breakout = (currentLow < breakoutLevel);
if(breakout && Time[0] == lastSignalTime) return false;
if(breakout) lastSignalTime = Time[0];
return breakout;
}
//+------------------------------------------------------------------+
//| 检查离场信号 - 突破N日低点/高点 |
//+------------------------------------------------------------------+
bool ShouldExitLong()
{
if(!UseChandelierExit) return false;
double currentLow = iLow(Symbol(), 0, 1);
double exitLevel = GetDonchianLow(exitPeriod, 1);
return (currentLow < exitLevel);
}
bool ShouldExitShort()
{
if(!UseChandelierExit) return false;
double currentHigh = iHigh(Symbol(), 0, 1);
double exitLevel = GetDonchianHigh(exitPeriod, 1);
return (currentHigh > exitLevel);
}
//+------------------------------------------------------------------+
//| 计算金字塔加仓价格 |
//| 每盈利0.5倍ATR加仓一次 |
//+------------------------------------------------------------------+
double GetPyramidBuyPrice(double entryPrice)
{
double addDistancePoints = AddDistance * currentATR / Point / pointMultiplier;
double nextLevel = entryPrice + addDistancePoints * Point * pointMultiplier;
return nextLevel;
}
double GetPyramidSellPrice(double entryPrice)
{
double addDistancePoints = AddDistance * currentATR / Point / pointMultiplier;
double nextLevel = entryPrice - addDistancePoints * Point * pointMultiplier;
return nextLevel;
}
//+------------------------------------------------------------------+
//| 计算初始止损(2倍ATR) |
//+------------------------------------------------------------------+
double GetInitialStopLoss(int cmd, double entryPrice)
{
double stopDistance = ATRMultiplier * currentATR;
if(cmd == OP_BUY)
return entryPrice - stopDistance;
else
return entryPrice + stopDistance;
}
//+------------------------------------------------------------------+
//| 开仓函数 |
//+------------------------------------------------------------------+
int OpenOrder(int cmd, double sl = 0, double tp = 0)
{
double price = (cmd == OP_BUY) ? Ask : Bid;
double lot = CalculateUnitSize();
if(lot <= 0) return -1;
// 自动计算止损
if(sl == 0 && currentATR > 0) {
sl = GetInitialStopLoss(cmd, price);
}
// 价格标准化
sl = NormalizeDouble(sl, Digits);
if(tp > 0) tp = NormalizeDouble(tp, Digits);
price = NormalizeDouble(price, Digits);
int ticket = OrderSend(Symbol(), cmd, lot, price, Slippage, sl, tp, TradeComment, MagicNumber, 0, clrNONE);
if(ticket > 0) {
Print("开仓成功: 订单号=", ticket, " | 方向=", (cmd==OP_BUY?"买入":"卖出"), " | 手数=", lot);
// 记录交易用于金字塔追踪
int newSize = ArraySize(activeTrades);
ArrayResize(activeTrades, newSize + 1);
activeTrades[newSize].ticket = ticket;
activeTrades[newSize].openTime = TimeCurrent();
activeTrades[newSize].openPrice = price;
activeTrades[newSize].unitNumber = entriesThisSignal + 1;
activeTrades[newSize].stopLoss = sl;
entriesThisSignal++;
} else {
Print("开仓失败. 错误码: ", GetLastError());
}
return ticket;
}
//+------------------------------------------------------------------+
//| 平仓指定订单 |
//+------------------------------------------------------------------+
bool CloseOrder(int ticket)
{
if(OrderSelect(ticket, SELECT_BY_TICKET)) {
double closePrice = (OrderType() == OP_BUY) ? Bid : Ask;
bool closed = OrderClose(ticket, OrderLots(), closePrice, Slippage, clrNONE);
if(closed) {
// 追踪盈利用于反马丁格尔
double profit = (closePrice - OrderOpenPrice()) * OrderLots() * MarketInfo(Symbol(), MODE_TICKVALUE);
if(profit > 0) consecutiveWins++;
else consecutiveWins = 0;
Print("平仓成功: 订单号=", ticket, " | 盈亏=", profit);
}
return closed;
}
return false;
}
//+------------------------------------------------------------------+
//| 平仓所有持仓 |
//+------------------------------------------------------------------+
void CloseAllPositions()
{
for(int i = OrdersTotal() - 1; i >= 0; i--) {
if(OrderSelect(i, SELECT_BY_POS)) {
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber) {
CloseOrder(OrderTicket());
}
}
}
entriesThisSignal = 0;
ArrayResize(activeTrades, 0);
}
//+------------------------------------------------------------------+
//| 平仓指定方向的所有持仓 |
//+------------------------------------------------------------------+
void ClosePositionsByType(int cmd)
{
for(int i = OrdersTotal() - 1; i >= 0; i--) {
if(OrderSelect(i, SELECT_BY_POS)) {
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber && OrderType() == cmd) {
CloseOrder(OrderTicket());
}
}
}
if((cmd == OP_BUY && CountPositions(OP_BUY) == 0) ||
(cmd == OP_SELL && CountPositions(OP_SELL) == 0)) {
entriesThisSignal = 0;
ArrayResize(activeTrades, 0);
}
}
//+------------------------------------------------------------------+
//| 统计持仓数量 |
//+------------------------------------------------------------------+
int CountPositions(int type = -1)
{
int count = 0;
for(int i = 0; i < OrdersTotal(); i++) {
if(OrderSelect(i, SELECT_BY_POS)) {
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber) {
if(type == -1 || OrderType() == type)
count++;
}
}
}
return count;
}
//+------------------------------------------------------------------+
//| 基于唐奇安通道更新追踪止损 |
//+------------------------------------------------------------------+
void UpdateTrailingStops()
{
if(!UseChandelierExit) return;
double exitLevelLong = GetDonchianLow(exitPeriod, 1);
double exitLevelShort = GetDonchianHigh(exitPeriod, 1);
for(int i = 0; i < OrdersTotal(); i++) {
if(OrderSelect(i, SELECT_BY_POS)) {
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber) {
double newSL = 0;
if(OrderType() == OP_BUY) {
if(exitLevelLong > OrderStopLoss() && exitLevelLong < Bid) {
newSL = exitLevelLong;
}
}
else if(OrderType() == OP_SELL) {
if(exitLevelShort < OrderStopLoss() && exitLevelShort > Ask) {
newSL = exitLevelShort;
}
}
if(newSL != 0 && newSL != OrderStopLoss()) {
OrderModify(OrderTicket(), OrderOpenPrice(), newSL, OrderTakeProfit(), 0, clrNONE);
Print("追踪止损已更新: 订单号=", OrderTicket(), " | 新止损=", newSL);
}
}
}
}
}
//+------------------------------------------------------------------+
//| 检查金字塔加仓机会 |
//+------------------------------------------------------------------+
void CheckPyramidEntries()
{
if(entriesThisSignal >= MaxAdds) return;
if(currentATR <= 0) return;
int currentDirection = 0;
double avgPrice = 0;
int positionCount = 0;
// 确定当前持仓方向和均价
for(int i = 0; i < OrdersTotal(); i++) {
if(OrderSelect(i, SELECT_BY_POS)) {
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber) {
if(OrderType() == OP_BUY) {
currentDirection = 1;
avgPrice += OrderOpenPrice() * OrderLots();
positionCount += OrderLots();
}
else if(OrderType() == OP_SELL) {
currentDirection = -1;
avgPrice += OrderOpenPrice() * OrderLots();
positionCount += OrderLots();
}
}
}
}
if(positionCount > 0) avgPrice = avgPrice / positionCount;
// 检查加仓条件
if(currentDirection == 1) {
double targetPrice = GetPyramidBuyPrice(avgPrice);
if(Ask >= targetPrice && CountPositions(OP_BUY) < MaxAdds) {
Print("金字塔买入加仓触发: 等级 ", entriesThisSignal + 1);
OpenOrder(OP_BUY);
}
}
else if(currentDirection == -1) {
double targetPrice = GetPyramidSellPrice(avgPrice);
if(Bid <= targetPrice && CountPositions(OP_SELL) < MaxAdds) {
Print("金字塔卖出加仓触发: 等级 ", entriesThisSignal + 1);
OpenOrder(OP_SELL);
}
}
}
//+------------------------------------------------------------------+
//| 检查最大回撤保护 |
//+------------------------------------------------------------------+
bool IsDrawdownExceeded()
{
double equity = AccountEquity();
double balance = AccountBalance();
if(balance <= 0) return false;
double drawdownPercent = (balance - equity) / balance * 100;
return (drawdownPercent >= MaxDrawdownPercent);
}
//+------------------------------------------------------------------+
//| 检查点差是否可接受 |
//+------------------------------------------------------------------+
bool IsSpreadOK()
{
if(MaxSpread <= 0) return true;
int currentSpread = (int)((Ask - Bid) / Point / pointMultiplier);
return (currentSpread <= MaxSpread);
}
//+------------------------------------------------------------------+
//| 更新ATR值 |
//+------------------------------------------------------------------+
void UpdateATR()
{
currentATR = CalculateATR(14, 0);
}
//+------------------------------------------------------------------+
//| 交易前检查 |
//+------------------------------------------------------------------+
bool CanTrade()
{
if(!IsSpreadOK()) return false;
if(IsDrawdownExceeded()) {
Print("最大回撤超限,交易暂停");
return false;
}
return true;
}
//+------------------------------------------------------------------+
//| EA报价处理函数 |
//+------------------------------------------------------------------+
void OnTick()
{
// 更新技术指标
UpdateATR();
// 安全检查
if(!CanTrade()) return;
// 更新现有持仓的追踪止损
if(UseChandelierExit) {
UpdateTrailingStops();
}
// 仅在新K线时检测新信号
if(!IsNewBar()) {
// 每tick检查金字塔加仓
if(MaxAdds > 0 && entriesThisSignal > 0 && entriesThisSignal < MaxAdds) {
CheckPyramidEntries();
}
return;
}
// 先检查离场信号
if(ShouldExitLong() && CountPositions(OP_BUY) > 0) {
Print("离场信号: 平仓所有多头");
ClosePositionsByType(OP_BUY);
entriesThisSignal = 0;
}
if(ShouldExitShort() && CountPositions(OP_SELL) > 0) {
Print("离场信号: 平仓所有空头");
ClosePositionsByType(OP_SELL);
entriesThisSignal = 0;
}
// 检查突破入场信号
if(IsBreakoutBuy() && CountPositions(OP_SELL) == 0) {
Print("检测到突破买入信号");
ClosePositionsByType(OP_SELL);
OpenOrder(OP_BUY);
}
else if(IsBreakoutSell() && CountPositions(OP_BUY) == 0) {
Print("检测到突破卖出信号");
ClosePositionsByType(OP_BUY);
OpenOrder(OP_SELL);
}
}
//+------------------------------------------------------------------+
```
参数详解
| 参数 | 说明 | 海龟原始值 |
|------|------|-----------|
| 系统选择 | 1=系统1(20日), 2=系统2(55日) | 1或2 |
| 自定义突破周期 | 覆盖突破周期(0=使用默认) | 0 |
| 风险百分比 | 每笔风险占账户百分比 | 1.0-2.0% |
| ATR倍数 | 初始止损的ATR倍数 | 2.0 |
| 最大加仓 | 每信号最多金字塔加仓次数 | 4 |
| 加仓间隔 | 金字塔加仓的ATR倍数间隔 | 0.5 |
| 使用反马丁格尔 | 盈利后增加仓位规模 | true |
| 连盈要求 | 增加仓位前需要的连续盈利次数 | 2 |
| 仓位增加系数 | 仓位规模增加比例 | 0.25 |
| 使用通道离场 | 通道突破时离场 | true |
| 离场通道周期 | 离场通道周期(系统1=10,系统2=20) | 10/20 |
| 最大回撤限制 | 紧急停止的最大回撤 | 30 |
安装步骤
1. 复制代码到MT4的MetaEditor(按F4)
2. 点击编译(F7)- 确保无错误
3. 将EA附加到图表(建议H1或H4周期)
4. 在输入参数选项卡中调整参数
5. 启用自动交易(Alt+T)
各品种推荐设置
| 品种 | 系统 | 风险% | 时间周期 | 说明 |
|------|------|-------|----------|------|
| EURUSD, GBPUSD | 系统2(55日) | 1.0 | H1/H4 | 低波动,趋势稳定 |
| 黄金(XAUUSD) | 系统1(20日) | 1.5 | H1 | 高波动,需要更快入场 |
| GBPJPY, EURJPY | 系统1(20日) | 1.0 | H1 | 强趋势,适合加仓 |
| USDJPY | 系统2(55日) | 1.0 | H4 | 慢趋势,假突破少 |
编译与修改技巧
理解海龟规则:
主要修改方向:
最佳市场环境:
海龟系统专为具有持续方向性走势的趋势市场设计。在盘整/震荡市场中表现较差。建议在较高时间周期(H1-H4)上使用以获得最佳效果。
参考来源
本文EA源码为自主编译,基于Richard Dennis和William Eckhardt记载的原始海龟交易系统规则。参考了MQL5及其他交易平台开源仓库中的相关实现。
*如需更专业的优化版EA策略(含机器学习过滤、20+品种完整回测组合、高级风控和专业支持),请查看我们的付费EA合集。订阅后可每周获取更新和独家交易工具。*