Summary: 专业的随机指标与马丁格尔资金管理结合的MT4 EA。包含最大订单限制、移动止损、回撤保护和可调节倍数。完整源码可直接编译运行。




# 随机指标马丁格尔EA - 完整MQL4源码

本文提供一个结合随机指标(Stochastic Oscillator)信号与受控马丁格尔仓位管理的完整自动交易EA。与纯马丁格尔系统不同,本EA仅在随机指标确认超买/超卖条件时才启动马丁格尔序列,通过关键过滤降低风险。

策略逻辑



EA使用随机指标识别潜在反转点。当随机指标跌破超卖线(默认20)后回升至上时产生买入信号;当随机指标突破超买线(默认80)后回落至下时产生卖出信号。亏损后EA按可配置倍数增加手数,但严格执行最大订单限制以防止几何级数增长。

完整MQL4代码



```mql4
//+------------------------------------------------------------------+
//| StochasticMartingaleEA.mq4 |
//| 自主编译 |
//+------------------------------------------------------------------+
#property copyright "AI助手"
#property link ""
#property version "1.00"
#property strict

//--- 输入参数
input double InitialLot = 0.01; // 初始手数
input double LotMultiplier = 2.0; // 马丁格尔倍数
input int MaxOrders = 5; // 最大连续马丁格尔订单数
input int StochasticK = 5; // 随机指标%K周期
input int StochasticD = 3; // 随机指标%D周期
input int StochasticSlowing = 3; // 随机指标减速
input int Overbought = 80; // 超买线
input int Oversold = 20; // 超卖线
input int StopLoss = 50; // 止损点数
input int TakeProfit = 80; // 止盈点数
input int TrailingStop = 25; // 移动止损点数(0=关闭)
input double ProfitTarget = 50.0; // 每日止盈目标金额(美元)
input double MaxDrawdown = 30.0; // 最大回撤百分比(触发暂停)
input int MaxSpread = 35; // 最大允许点差
input int MagicNumber = 202413; // EA魔术号
input bool CloseOnReverse = true; // 反向信号时平仓反向单

//--- 全局变量
double stochMain_curr = 0, stochSignal_curr = 0;
double stochMain_prev = 0, stochSignal_prev = 0;
double dailyProfit = 0;
datetime lastResetTime = 0;
int consecutiveLosses = 0;
bool halted = false;
int pointMultiplier = 10;

//+------------------------------------------------------------------+
//| EA初始化函数 |
//+------------------------------------------------------------------+
int OnInit()
{
// 检测经纪商报价格式
if(Digits == 3 || Digits == 5)
pointMultiplier = 10;
else if(Digits == 2 || Digits == 4)
pointMultiplier = 1;

if(InitialLot <= 0 || LotMultiplier <= 1)
{
Print("错误: 无效的手数参数");
return(INIT_PARAMETERS_INCORRECT);
}

lastResetTime = TimeCurrent();
return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| EA反初始化函数 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
}

//+------------------------------------------------------------------+
//| EA报价处理函数 |
//+------------------------------------------------------------------+
void OnTick()
{
// 检查是否因回撤而暂停
if(halted)
{
if(CheckDrawdown())
halted = false;
else
return;
}

// 重置每日盈利记录
ResetDailyProfit();

// 检查止盈目标
if(CheckProfitTarget())
return;

// 点差检查
if(!IsSpreadOK())
return;

// 计算随机指标值
CalculateStochastic();

// 管理现有持仓
ManageTrailingStop();
UpdateConsecutiveLosses();

// 检查是否已达最大马丁格尔订单数
if(CountCurrentOrders() >= MaxOrders)
return;

// 信号检测
if(IsBuySignal())
{
if(CloseOnReverse) CloseSellPositions();
if(CountBuyPositions() == 0)
OpenOrder(OP_BUY);
}
else if(IsSellSignal())
{
if(CloseOnReverse) CloseBuyPositions();
if(CountSellPositions() == 0)
OpenOrder(OP_SELL);
}
}

//+------------------------------------------------------------------+
//| 计算随机指标值 |
//+------------------------------------------------------------------+
void CalculateStochastic()
{
stochMain_curr = iStochastic(Symbol(), 0, StochasticK, StochasticD, StochasticSlowing, MODE_SMA, 0, MODE_MAIN, 0);
stochSignal_curr = iStochastic(Symbol(), 0, StochasticK, StochasticD, StochasticSlowing, MODE_SMA, 0, MODE_SIGNAL, 0);
stochMain_prev = iStochastic(Symbol(), 0, StochasticK, StochasticD, StochasticSlowing, MODE_SMA, 0, MODE_MAIN, 1);
stochSignal_prev = iStochastic(Symbol(), 0, StochasticK, StochasticD, StochasticSlowing, MODE_SMA, 0, MODE_SIGNAL, 1);
}

//+------------------------------------------------------------------+
//| 检查买入信号 - 随机指标从下方上穿超卖线 |
//+------------------------------------------------------------------+
bool IsBuySignal()
{
if(stochMain_curr == EMPTY_VALUE || stochMain_prev == EMPTY_VALUE)
return false;

// 主线上穿超卖线
return (stochMain_prev < Oversold && stochMain_curr > Oversold);
}

//+------------------------------------------------------------------+
//| 检查卖出信号 - 随机指标从上方下穿超买线 |
//+------------------------------------------------------------------+
bool IsSellSignal()
{
if(stochMain_curr == EMPTY_VALUE || stochMain_prev == EMPTY_VALUE)
return false;

// 主线下穿超买线
return (stochMain_prev > Overbought && stochMain_curr < Overbought);
}

//+------------------------------------------------------------------+
//| 基于马丁格尔进度计算手数 |
//+------------------------------------------------------------------+
double CalculateMartingaleLot()
{
int currentOrders = CountCurrentOrders();

if(currentOrders == 0)
return InitialLot;

// 根据连续亏损次数计算马丁格尔手数
double lot = InitialLot * MathPow(LotMultiplier, consecutiveLosses);

// 限制最大手数
double maxLot = MarketInfo(Symbol(), MODE_MAXLOT);
if(lot > maxLot)
lot = maxLot;

return NormalizeDouble(lot, 2);
}

//+------------------------------------------------------------------+
//| 统计当前EA的未平仓订单数 |
//+------------------------------------------------------------------+
int CountCurrentOrders()
{
int count = 0;
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
count++;
}
}
return count;
}

//+------------------------------------------------------------------+
//| 更新连续亏损计数 |
//+------------------------------------------------------------------+
void UpdateConsecutiveLosses()
{
int lossCount = 0;
for(int i = OrdersHistoryTotal() - 1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_HISTORY))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
if(OrderProfit() < 0)
lossCount++;
else if(OrderProfit() > 0)
break;
}
}
}
consecutiveLosses = lossCount;
if(consecutiveLosses > MaxOrders)
consecutiveLosses = MaxOrders;
}

//+------------------------------------------------------------------+
//| 开仓函数 |
//+------------------------------------------------------------------+
void OpenOrder(int cmd)
{
double price = (cmd == OP_BUY) ? Ask : Bid;
double sl = 0, tp = 0;
double lot = CalculateMartingaleLot();

if(StopLoss > 0)
{
if(cmd == OP_BUY)
sl = price - StopLoss * Point * pointMultiplier;
else
sl = price + StopLoss * Point * pointMultiplier;
}

if(TakeProfit > 0)
{
if(cmd == OP_BUY)
tp = price + TakeProfit * Point * pointMultiplier;
else
tp = price - TakeProfit * Point * pointMultiplier;
}

int ticket = OrderSend(Symbol(), cmd, lot, price, 3, sl, tp, "StochMarti", MagicNumber, 0, clrNONE);

if(ticket < 0)
Print("开仓失败. 错误码: ", GetLastError());
else
Print("开仓成功. 订单号: ", ticket, " 手数: ", lot, " 连续亏损次数: ", consecutiveLosses);
}

//+------------------------------------------------------------------+
//| 管理现有持仓的移动止损 |
//+------------------------------------------------------------------+
void ManageTrailingStop()
{
if(TrailingStop <= 0) return;

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 newSL = Bid - TrailingStop * Point * pointMultiplier;
if(newSL > OrderStopLoss())
OrderModify(OrderTicket(), OrderOpenPrice(), newSL, OrderTakeProfit(), 0, clrNONE);
}
else if(OrderType() == OP_SELL)
{
double newSL = Ask + TrailingStop * Point * pointMultiplier;
if(newSL < OrderStopLoss() || OrderStopLoss() == 0)
OrderModify(OrderTicket(), OrderOpenPrice(), newSL, OrderTakeProfit(), 0, clrNONE);
}
}
}
}
}

//+------------------------------------------------------------------+
//| 检查点差条件 |
//+------------------------------------------------------------------+
bool IsSpreadOK()
{
if(MaxSpread <= 0) return true;

int currentSpread = (int)((Ask - Bid) / Point / pointMultiplier);
return (currentSpread <= MaxSpread);
}

//+------------------------------------------------------------------+
//| 每日午夜重置盈利记录 |
//+------------------------------------------------------------------+
void ResetDailyProfit()
{
datetime currentTime = TimeCurrent();
MqlDateTime dt;
TimeToStruct(currentTime, dt);

datetime todayMidnight = StringToTime( IntegerToString(dt.year) + "." +
IntegerToString(dt.mon) + "." +
IntegerToString(dt.day) + " 00:00:00");

if(lastResetTime < todayMidnight)
{
dailyProfit = 0;
lastResetTime = currentTime;
Print("每日盈利已重置");
}

UpdateDailyProfit();
}

//+------------------------------------------------------------------+
//| 更新每日盈利计算 |
//+------------------------------------------------------------------+
void UpdateDailyProfit()
{
double profit = 0;
datetime currentTime = TimeCurrent();
MqlDateTime dt;
TimeToStruct(currentTime, dt);
datetime dayStart = StringToTime( IntegerToString(dt.year) + "." +
IntegerToString(dt.mon) + "." +
IntegerToString(dt.day) + " 00:00:00");

for(int i = OrdersHistoryTotal() - 1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_HISTORY))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
if(OrderCloseTime() >= dayStart)
profit += OrderProfit() + OrderCommission() + OrderSwap();
}
}
}
dailyProfit = profit;
}

//+------------------------------------------------------------------+
//| 检查是否达到止盈目标 |
//+------------------------------------------------------------------+
bool CheckProfitTarget()
{
if(ProfitTarget <= 0) return false;

if(dailyProfit >= ProfitTarget)
{
Comment("每日止盈目标已达成: $", DoubleToStr(dailyProfit, 2));
return true;
}
return false;
}

//+------------------------------------------------------------------+
//| 检查回撤条件 |
//+------------------------------------------------------------------+
bool CheckDrawdown()
{
double equity = AccountEquity();
double balance = AccountBalance();
if(balance <= 0) return false;

double drawdownPercent = (balance - equity) / balance * 100;

if(drawdownPercent >= MaxDrawdown)
{
Comment("回撤限制已达: ", DoubleToStr(drawdownPercent, 2), "%");
return false;
}
return true;
}

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

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

//+------------------------------------------------------------------+
//| 平仓所有买单 |
//+------------------------------------------------------------------+
void CloseBuyPositions()
{
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber && OrderType() == OP_BUY)
{
OrderClose(OrderTicket(), OrderLots(), Bid, 3, clrNONE);
}
}
}
}

//+------------------------------------------------------------------+
//| 平仓所有卖单 |
//+------------------------------------------------------------------+
void CloseSellPositions()
{
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber && OrderType() == OP_SELL)
{
OrderClose(OrderTicket(), OrderLots(), Ask, 3, clrNONE);
}
}
}
}
//+------------------------------------------------------------------+
```

参数详解



| 参数 | 说明 | 推荐值 |
|------|------|--------|
| 初始手数 | 首笔交易手数 | 0.01 |
| 马丁格尔倍数 | 亏损后的手数倍数 | 1.5-2.0 |
| 最大订单数 | 最大连续马丁格尔订单数 | 3-5 |
| 随机指标K周期 | %K线周期 | 5-14 |
| 随机指标D周期 | %D线周期 | 3-5 |
| 超买/超卖线 | 阈值水平 | 80/20 |
| 止损点数 | 止损距离 | 40-60 |
| 止盈点数 | 止盈距离 | 60-100 |
| 移动止损 | 移动止损触发点 | 25-35 |
| 止盈目标 | 每日止盈目标金额 | 50-100 |
| 最大回撤 | 暂停交易的回撤百分比 | 25-35 |
| 最大点差 | 允许的最大点差 | 30-40 |

风险提示



马丁格尔策略具有固有风险。本EA包含多重保护机制:最大订单限制、回撤保护、止盈目标。请务必先在模拟账户测试。

安装步骤



1. 复制代码到MT4的MetaEditor(按F4)
2. 编译(F7)- 确保0错误
3. 将EA附加到图表(建议EURUSD、GBPUSD的M15/H1周期)
4. 在输入参数中调整设置
5. 启用自动交易

编译技巧



  • 对于4位报价经纪商,手动设置`pointMultiplier = 1`

  • 先用极小的InitialLot(0.01)测试

  • 密切监控回撤水平


  • 参考来源



    本文EA源码为自主编译,基于带随机指标过滤的马丁格尔策略原理。

    *如需更专业的优化版EA(含高级风险管理、多策略组合、完整回测报告),请查看我们的付费EA合集。订阅后可每周获取更新和独家交易工具。*