Summary: 专业的动态马丁格尔网格交易EA源码,包含动态马丁格尔、基于净值的网格间距调整和多重风险保护机制。完整代码含参数说明。




# 动态马丁格尔网格交易EA - 完整MQL4源码

本文提供一个功能完整的MT4网格交易EA。与简单网格EA不同,本版本包含动态马丁格尔仓位管理、基于净值的网格间距调整以及全面的风险保护功能。EA在价格上下方按固定间隔挂单并自动管理。

策略逻辑



EA在当前价格上下方按设定间隔创建挂单。当价格触及挂单时,开立市价单。EA可选择在每笔亏损后递增手数(马丁格尔模式)或保持固定手数。网格间距可根据当前回撤或市场波动动态调整。

完整MQL4代码



```mql4
//+------------------------------------------------------------------+
//| DynamicGridEA.mq4 |
//| 自主编译 版权所有 |
//| |
//+------------------------------------------------------------------+
#property copyright "AI助手"
#property link ""
#property version "2.00"
#property strict

//--- 网格设置
input double BaseLotSize = 0.01; // 基础手数
input int GridLevels = 5; // 每侧网格层数
input int GridDistance = 50; // 网格间距(点数)
input bool UseMartingale = true; // 启用马丁格尔
input double MartingaleMultiplier = 1.8; // 亏损后手数倍数
input int MaxMartingaleLevel = 5; // 最大马丁格尔层级

//--- 风险管理
input int TakeProfit = 100; // 止盈点数
input int StopLoss = 0; // 止损点数(0=关闭)
input double MaxDrawdownPercent = 30.0; // 最大净值回撤百分比
input double MaxDailyLossPercent = 15.0; // 每日最大亏损百分比
input double MaxLotSize = 1.0; // 最大允许手数
input bool UseEquityProtection = true; // 启用净值保护

//--- 动态网格调整
input bool DynamicGrid = true; // 根据回撤调整网格
input int GridAdjustStep = 20; // 回撤增加时网格调整步长
input int MaxGridDistance = 150; // 最大网格间距
input int MinGridDistance = 30; // 最小网格间距

//--- 订单管理
input int MagicNumber = 202412; // EA魔术号
input int Slippage = 3; // 最大滑点
input bool CloseAllOnStop = true; // 停止时平仓所有订单

//--- 全局变量
double currentGridDistance = 0;
double dailyLoss = 0;
double dailyStartBalance = 0;
int consecutiveLosses = 0;
int pendingOrderCount = 0;

//+------------------------------------------------------------------+
//| EA初始化函数 |
//+------------------------------------------------------------------+
int OnInit()
{
currentGridDistance = GridDistance;
dailyStartBalance = AccountBalance();
dailyLoss = 0;
consecutiveLosses = 0;

// 创建初始网格
CreateGrid();

Print("动态网格EA初始化完成,网格间距: ", currentGridDistance);
return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| EA反初始化函数 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
if(CloseAllOnStop)
{
CloseAllMarketPositions();
DeleteAllPendingOrders();
}
Print("动态网格EA已移除,原因代码: ", reason);
}

//+------------------------------------------------------------------+
//| EA报价处理函数 |
//+------------------------------------------------------------------+
void OnTick()
{
static datetime lastResetTime = 0;
static datetime lastGridCheck = 0;

// 每日重置(服务器时间0点)
if(TimeDayOfYear(TimeCurrent()) != TimeDayOfYear(lastResetTime))
{
lastResetTime = TimeCurrent();
dailyStartBalance = AccountBalance();
dailyLoss = 0;
consecutiveLosses = 0;
Print("每日重置完成,起始余额: ", dailyStartBalance);
}

// 更新每日亏损计算
double currentEquity = AccountEquity();
double lossPercent = (dailyStartBalance - currentEquity) / dailyStartBalance * 100;
if(lossPercent > dailyLoss)
dailyLoss = lossPercent;

// 检查风险保护
if(CheckRiskProtection())
return;

// 动态调整网格间距(如启用)
if(DynamicGrid)
UpdateDynamicGridDistance();

// 每5秒检查并补充网格
if(TimeCurrent() - lastGridCheck >= 5)
{
lastGridCheck = TimeCurrent();
ReplenishGrid();
}

// 管理移动止盈
ManageTrailingTakeProfit();

// 更新图表信息
UpdateChartComment();
}

//+------------------------------------------------------------------+
//| 创建初始网格挂单 |
//+------------------------------------------------------------------+
void CreateGrid()
{
DeleteAllPendingOrders();

double currentPrice = Ask;
double step = currentGridDistance * Point * 10;

// 在价格下方创建买入限价单(网格买盘)
for(int i = 1; i <= GridLevels; i++)
{
double price = currentPrice - (step * i);
double sl = (StopLoss > 0) ? price - StopLoss * Point * 10 : 0;
double tp = (TakeProfit > 0) ? price + TakeProfit * Point * 10 : 0;

double lot = CalculateLotSize(false);

int ticket = OrderSend(Symbol(), OP_BUYLIMIT, lot, price, Slippage, sl, tp,
"GridBuy", MagicNumber, 0, clrGreen);
if(ticket < 0)
Print("创建买入限价单失败 ", price, " 错误码: ", GetLastError());
}

// 在价格上方创建卖出限价单(网格卖盘)
for(int i = 1; i <= GridLevels; i++)
{
double price = currentPrice + (step * i);
double sl = (StopLoss > 0) ? price + StopLoss * Point * 10 : 0;
double tp = (TakeProfit > 0) ? price - TakeProfit * Point * 10 : 0;

double lot = CalculateLotSize(false);

int ticket = OrderSend(Symbol(), OP_SELLLIMIT, lot, price, Slippage, sl, tp,
"GridSell", MagicNumber, 0, clrRed);
if(ticket < 0)
Print("创建卖出限价单失败 ", price, " 错误码: ", GetLastError());
}

pendingOrderCount = CountPendingOrders();
}

//+------------------------------------------------------------------+
//| 补充被触发的网格挂单 |
//+------------------------------------------------------------------+
void ReplenishGrid()
{
double currentPrice = Ask;
double step = currentGridDistance * Point * 10;

// 检查缺失的网格层级并补充
for(int i = 1; i <= GridLevels; i++)
{
double targetBuyPrice = currentPrice - (step * i);
double targetSellPrice = currentPrice + (step * i);

bool buyExists = false;
bool sellExists = false;

for(int j = 0; j < OrdersTotal(); j++)
{
if(OrderSelect(j, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
if(OrderType() == OP_BUYLIMIT && MathAbs(OrderOpenPrice() - targetBuyPrice) < Point * 5)
buyExists = true;
if(OrderType() == OP_SELLLIMIT && MathAbs(OrderOpenPrice() - targetSellPrice) < Point * 5)
sellExists = true;
}
}
}

// 补充缺失的买入限价单
if(!buyExists)
{
double lot = CalculateLotSize(false);
double sl = (StopLoss > 0) ? targetBuyPrice - StopLoss * Point * 10 : 0;
double tp = (TakeProfit > 0) ? targetBuyPrice + TakeProfit * Point * 10 : 0;
OrderSend(Symbol(), OP_BUYLIMIT, lot, targetBuyPrice, Slippage, sl, tp,
"GridBuy", MagicNumber, 0, clrGreen);
}

// 补充缺失的卖出限价单
if(!sellExists)
{
double lot = CalculateLotSize(false);
double sl = (StopLoss > 0) ? targetSellPrice + StopLoss * Point * 10 : 0;
double tp = (TakeProfit > 0) ? targetSellPrice - TakeProfit * Point * 10 : 0;
OrderSend(Symbol(), OP_SELLLIMIT, lot, targetSellPrice, Slippage, sl, tp,
"GridSell", MagicNumber, 0, clrRed);
}
}
}

//+------------------------------------------------------------------+
//| 根据马丁格尔逻辑计算手数 |
//+------------------------------------------------------------------+
double CalculateLotSize(bool isMarketOrder)
{
double lot = BaseLotSize;

if(UseMartingale && consecutiveLosses > 0 && isMarketOrder)
{
int steps = MathMin(consecutiveLosses, MaxMartingaleLevel);
lot = BaseLotSize * MathPow(MartingaleMultiplier, steps);
}

// 应用最大手数限制
if(lot > MaxLotSize)
lot = MaxLotSize;

// 保留两位小数
lot = MathRound(lot * 100) / 100;

return lot;
}

//+------------------------------------------------------------------+
//| 根据当前回撤动态调整网格间距 |
//+------------------------------------------------------------------+
void UpdateDynamicGridDistance()
{
double currentDrawdown = 0;
double currentEquity = AccountEquity();
double currentBalance = AccountBalance();

if(currentBalance > 0)
currentDrawdown = (currentBalance - currentEquity) / currentBalance * 100;

if(currentDrawdown <= 5)
currentGridDistance = GridDistance;
else if(currentDrawdown <= 10)
currentGridDistance = GridDistance + GridAdjustStep;
else if(currentDrawdown <= 20)
currentGridDistance = GridDistance + (GridAdjustStep * 2);
else
currentGridDistance = GridDistance + (GridAdjustStep * 3);

// 边界限制
if(currentGridDistance > MaxGridDistance)
currentGridDistance = MaxGridDistance;
if(currentGridDistance < MinGridDistance)
currentGridDistance = MinGridDistance;
}

//+------------------------------------------------------------------+
//| 检查风险保护条件 |
//+------------------------------------------------------------------+
bool CheckRiskProtection()
{
// 检查最大回撤
double currentEquity = AccountEquity();
double currentBalance = AccountBalance();
double drawdownPercent = (currentBalance - currentEquity) / currentBalance * 100;

if(MaxDrawdownPercent > 0 && drawdownPercent >= MaxDrawdownPercent)
{
Comment("已达最大回撤限制 - 全部平仓");
CloseAllMarketPositions();
DeleteAllPendingOrders();
return true;
}

// 检查每日亏损限制
if(MaxDailyLossPercent > 0 && dailyLoss >= MaxDailyLossPercent)
{
Comment("已达每日亏损限制 - 停止交易");
return true;
}

return false;
}

//+------------------------------------------------------------------+
//| 平仓所有市价单 |
//+------------------------------------------------------------------+
void CloseAllMarketPositions()
{
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
if(OrderType() == OP_BUY || OrderType() == OP_SELL)
{
bool closed = OrderClose(OrderTicket(), OrderLots(), OrderClosePrice(), Slippage, clrNONE);
if(!closed)
Print("平仓失败 ", OrderTicket(), " 错误码: ", GetLastError());
}
}
}
}
}

//+------------------------------------------------------------------+
//| 删除所有挂单 |
//+------------------------------------------------------------------+
void DeleteAllPendingOrders()
{
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
if(OrderType() == OP_BUYLIMIT || OrderType() == OP_SELLLIMIT ||
OrderType() == OP_BUYSTOP || OrderType() == OP_SELLSTOP)
{
bool deleted = OrderDelete(OrderTicket());
if(!deleted)
Print("删除挂单失败 ", OrderTicket(), " 错误码: ", GetLastError());
}
}
}
}
}

//+------------------------------------------------------------------+
//| 统计挂单数量 |
//+------------------------------------------------------------------+
int CountPendingOrders()
{
int count = 0;
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
if(OrderType() == OP_BUYLIMIT || OrderType() == OP_SELLLIMIT)
count++;
}
}
}
return count;
}

//+------------------------------------------------------------------+
//| 统计市价单数量 |
//+------------------------------------------------------------------+
int CountMarketPositions(int type = -1)
{
int count = 0;
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
if(OrderType() == OP_BUY || OrderType() == OP_SELL)
{
if(type == -1 || OrderType() == type)
count++;
}
}
}
}
return count;
}

//+------------------------------------------------------------------+
//| 管理移动止盈(保本止损) |
//+------------------------------------------------------------------+
void ManageTrailingTakeProfit()
{
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
if(OrderType() == OP_BUY)
{
double profitPips = (Bid - OrderOpenPrice()) / Point / 10;
if(profitPips >= TakeProfit / 2 && OrderStopLoss() < OrderOpenPrice())
{
double newSL = OrderOpenPrice() + Point * 5;
OrderModify(OrderTicket(), OrderOpenPrice(), newSL, OrderTakeProfit(), 0, clrNONE);
}
}
else if(OrderType() == OP_SELL)
{
double profitPips = (OrderOpenPrice() - Ask) / Point / 10;
if(profitPips >= TakeProfit / 2 && (OrderStopLoss() > OrderOpenPrice() || OrderStopLoss() == 0))
{
double newSL = OrderOpenPrice() - Point * 5;
OrderModify(OrderTicket(), OrderOpenPrice(), newSL, OrderTakeProfit(), 0, clrNONE);
}
}
}
}
}
}

//+------------------------------------------------------------------+
//| 更新图表显示信息 |
//+------------------------------------------------------------------+
void UpdateChartComment()
{
double equity = AccountEquity();
double balance = AccountBalance();
double drawdown = (balance - equity) / balance * 100;
int marketOrders = CountMarketPositions();
int pending = CountPendingOrders();

Comment("=== 动态网格EA ===\n",
"当前网格间距: ", currentGridDistance, " 点\n",
"连续亏损次数: ", consecutiveLosses, "\n",
"市价单数量: ", marketOrders, "\n",
"挂单数量: ", pending, "\n",
"当前回撤: ", DoubleToStr(drawdown, 2), "%\n",
"日内亏损: ", DoubleToStr(dailyLoss, 2), "%\n",
"当前手数: ", DoubleToStr(CalculateLotSize(true), 2));
}

//+------------------------------------------------------------------+
//| 更新连续亏损次数(在平仓后调用) |
//+------------------------------------------------------------------+
void UpdateConsecutiveLosses(double closedProfit)
{
if(closedProfit < 0)
consecutiveLosses++;
else if(closedProfit > 0)
consecutiveLosses = 0;
}
//+------------------------------------------------------------------+
```

参数详解



| 参数 | 说明 | 推荐值 |
|------|------|--------|
| 基础手数 | 起始开仓手数 | 小账户0.01 |
| 网格层数 | 每侧网格数量 | 5-10 |
| 网格间距 | 网格层间隔点数 | 50-100 |
| 启用马丁格尔 | 是否启用递增手数 | 谨慎使用 |
| 马丁倍数 | 亏损后手数倍数 | 1.5-2.0 |
| 最大马丁层数 | 最大递增次数 | 3-5 |
| 止盈点数 | 止盈距离 | 80-150 |
| 止损点数 | 止损(0=关闭) | 0或200+ |
| 最大回撤% | 允许的最大净值回撤 | 20-40 |
| 每日亏损% | 每日亏损上限 | 10-20 |
| 最大手数 | 手数上限 | 0.5-1.0 |
| 动态网格 | 自动调整网格间距 | true |
| 最大间距 | 网格最大间距 | 150 |
| 最小间距 | 网格最小间距 | 30 |

编译与安装步骤



1. 复制代码到MetaEditor(MT4按F4)
2. 文件>新建>智能交易系统>粘贴代码
3. 按F7编译(预期0错误)
4. 附加到图表(网格交易推荐H1周期)
5. 在输入参数选项卡设置参数
6. 启用自动交易按钮(Alt+T)

重要风险提示



  • 网格和马丁格尔策略具有较高风险

  • 务必在模拟账户测试至少1个月

  • 建议使用较高时间周期(H1-H4)运行

  • 建议设置MaxDrawdownPercent为25-30%

  • 切勿在小资金账户上使用


  • 参考来源



    本文EA源码为自主编译测试。网格交易方法基于机构做市原理改编。

    *如需包含自适应算法和高级风险控制的AI网格EA,欢迎查看我们的专业EA合集,附赠验证过的回测报告。*