Summary: 专业的随机震荡指标交叉MT4 EA,当%K线与%D线在超买超卖区域交叉时开仓。包含移动止损、保本管理、时间过滤和完整的仓位管理功能,源码可直接编译。




# 随机震荡指标交叉EA - 完整MQL4源码

本文提供一个基于随机震荡指标(Stochastic Oscillator)交叉策略的完整自动交易EA。随机震荡指标由George C. Lane在20世纪50年代开发,通过比较收盘价与一定周期内价格区间的关系来判断市场动能。本EA在超买或超卖区域检测%K线与%D线的交叉信号进行交易。

策略逻辑



EA使用标准随机震荡指标,可配置%K周期、%D周期和减速因子。当随机指标跌破超卖线(默认20)且%K线上穿%D线时开多单;当随机指标突破超买线(默认80)且%K线下穿%D线时开空单。附加功能包括移动止损、保本止损、时间过滤和点差控制。

完整MQL4代码



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

//--- 输入参数
input double LotSize = 0.1; // 固定手数
input int KPeriod = 5; // %K周期
input int DPeriod = 3; // %D周期
input int Slowing = 3; // 减速因子
input int Overbought = 80; // 超买线
input int Oversold = 20; // 超卖线
input int StopLoss = 60; // 止损点数
input int TakeProfit = 120; // 止盈点数
input bool UseTrailingStop = true; // 启用移动止损
input int TrailingStart = 25; // 移动止损启动点数
input int TrailingStep = 15; // 移动止损步长
input int BreakEvenPips = 30; // 保本触发点数(0=禁用)
input bool UseTimeFilter = false; // 启用时间过滤
input int StartHour = 8; // 开始交易小时
input int EndHour = 20; // 结束交易小时
input int MaxSpread = 35; // 最大允许点差
input int Slippage = 10; // 最大滑点
input int MagicNumber = 202413; // EA魔术号
input bool CloseOpposite = true; // 反向信号时平仓反向单

//--- 全局变量
double stoMainCurr = 0, stoMainPrev = 0;
double stoSignalCurr = 0, stoSignalPrev = 0;
int pointMultiplier = 10;
datetime lastBarTime = 0;

//+------------------------------------------------------------------+
//| EA初始化函数 |
//+------------------------------------------------------------------+
int OnInit()
{
// 检测平台位数
if(Digits == 3 || Digits == 5)
pointMultiplier = 10;
else if(Digits == 2 || Digits == 4)
pointMultiplier = 1;

// 参数验证
if(Overbought <= Oversold)
{
Print("错误: 超买线必须大于超卖线");
return(INIT_PARAMETERS_INCORRECT);
}
if(KPeriod < 1 || DPeriod < 1 || Slowing < 1)
{
Print("错误: 随机指标周期参数无效");
return(INIT_PARAMETERS_INCORRECT);
}
return(INIT_SUCCEEDED);
}

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

//+------------------------------------------------------------------+
//| EA报价处理函数 |
//+------------------------------------------------------------------+
void OnTick()
{
// 新K线检查(每根K线只交易一次)
if(!IsNewBar())
return;

// 时间过滤检查
if(!IsTradingTime())
return;

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

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

// 检查数据有效性
if(stoMainCurr == EMPTY_VALUE || stoMainPrev == EMPTY_VALUE)
return;

// 管理现有持仓
ManagePositions();

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

//+------------------------------------------------------------------+
//| 检查是否为新K线 |
//+------------------------------------------------------------------+
bool IsNewBar()
{
if(lastBarTime == Time[0])
return false;
lastBarTime = Time[0];
return true;
}

//+------------------------------------------------------------------+
//| 计算随机震荡指标值 |
//+------------------------------------------------------------------+
void CalculateStochastic()
{
// 主线%K - MODE_MAIN = 0, 信号线%D - MODE_SIGNAL = 1
stoMainCurr = iStochastic(Symbol(), 0, KPeriod, DPeriod, Slowing, MODE_SMA, 0, MODE_MAIN, 0);
stoMainPrev = iStochastic(Symbol(), 0, KPeriod, DPeriod, Slowing, MODE_SMA, 0, MODE_MAIN, 1);
stoSignalCurr = iStochastic(Symbol(), 0, KPeriod, DPeriod, Slowing, MODE_SMA, 0, MODE_SIGNAL, 0);
stoSignalPrev = iStochastic(Symbol(), 0, KPeriod, DPeriod, Slowing, MODE_SMA, 0, MODE_SIGNAL, 1);
}

//+------------------------------------------------------------------+
//| 检查买入信号: 超卖区域 + %K上穿%D |
//+------------------------------------------------------------------+
bool IsBuySignal()
{
// 当前值在超卖区域
if(stoMainCurr >= Oversold)
return false;

// %K上穿%D(前一根%K <= 前一根%D,当前%K > 当前%D)
bool crossUp = (stoMainPrev <= stoSignalPrev && stoMainCurr > stoSignalCurr);

return crossUp;
}

//+------------------------------------------------------------------+
//| 检查卖出信号: 超买区域 + %K下穿%D |
//+------------------------------------------------------------------+
bool IsSellSignal()
{
// 当前值在超买区域
if(stoMainCurr <= Overbought)
return false;

// %K下穿%D(前一根%K >= 前一根%D,当前%K < 当前%D)
bool crossDown = (stoMainPrev >= stoSignalPrev && stoMainCurr < stoSignalCurr);

return crossDown;
}

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

// 止损计算
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, LotSize, price, Slippage, sl, tp, "Stoch Crossover", MagicNumber, 0, clrNONE);

if(ticket < 0)
Print("开仓失败. 错误码: ", GetLastError());
else
Print("开仓成功. 订单号: ", ticket);
}

//+------------------------------------------------------------------+
//| 管理现有持仓 - 移动止损和保本止损 |
//+------------------------------------------------------------------+
void ManagePositions()
{
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
// 保本止损管理
if(BreakEvenPips > 0)
{
double bePoints = BreakEvenPips * Point * pointMultiplier;

if(OrderType() == OP_BUY)
{
double profitPips = (Bid - OrderOpenPrice()) / Point / pointMultiplier;
if(profitPips >= BreakEvenPips && OrderStopLoss() < OrderOpenPrice())
{
OrderModify(OrderTicket(), OrderOpenPrice(), OrderOpenPrice(), OrderTakeProfit(), 0, clrNONE);
Print("保本止损已触发 BUY #", OrderTicket());
}
}
else if(OrderType() == OP_SELL)
{
double profitPips = (OrderOpenPrice() - Ask) / Point / pointMultiplier;
if(profitPips >= BreakEvenPips && (OrderStopLoss() > OrderOpenPrice() || OrderStopLoss() == 0))
{
OrderModify(OrderTicket(), OrderOpenPrice(), OrderOpenPrice(), OrderTakeProfit(), 0, clrNONE);
Print("保本止损已触发 SELL #", OrderTicket());
}
}
}

// 移动止损管理
if(UseTrailingStop && TrailingStart > 0 && TrailingStep > 0)
{
double trailStartPoints = TrailingStart * Point * pointMultiplier;
double trailStepPoints = TrailingStep * Point * pointMultiplier;

if(OrderType() == OP_BUY)
{
double profitPips = (Bid - OrderOpenPrice()) / Point / pointMultiplier;
if(profitPips >= TrailingStart)
{
double newSL = Bid - trailStepPoints;
if(newSL > OrderStopLoss())
{
OrderModify(OrderTicket(), OrderOpenPrice(), newSL, OrderTakeProfit(), 0, clrNONE);
Print("移动止损已更新 BUY #", OrderTicket());
}
}
}
else if(OrderType() == OP_SELL)
{
double profitPips = (OrderOpenPrice() - Ask) / Point / pointMultiplier;
if(profitPips >= TrailingStart)
{
double newSL = Ask + trailStepPoints;
if(newSL < OrderStopLoss() || OrderStopLoss() == 0)
{
OrderModify(OrderTicket(), OrderOpenPrice(), newSL, OrderTakeProfit(), 0, clrNONE);
Print("移动止损已更新 SELL #", OrderTicket());
}
}
}
}
}
}
}
}

//+------------------------------------------------------------------+
//| 检查当前是否在允许交易时间内 |
//+------------------------------------------------------------------+
bool IsTradingTime()
{
if(!UseTimeFilter) return true;

datetime now = TimeCurrent();
MqlDateTime dt;
TimeToStruct(now, dt);

int hour = dt.hour;
return (hour >= StartHour && hour < EndHour);
}

//+------------------------------------------------------------------+
//| 检查点差是否在限制范围内 |
//+------------------------------------------------------------------+
bool IsSpreadOK()
{
if(MaxSpread <= 0) return true;

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

//+------------------------------------------------------------------+
//| 统计买单数量 |
//+------------------------------------------------------------------+
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, Slippage, 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, Slippage, clrNONE);
}
}
}
}
//+------------------------------------------------------------------+
```

参数详解



| 参数 | 说明 | 推荐值 |
|------|------|--------|
| 固定手数 | 每笔交易手数 | 0.01 - 1.0 |
| %K周期 | %K线计算周期(随机指标计算周期) | 5, 9, 14 |
| %D周期 | %D线平滑周期 | 3, 5 |
| 减速因子 | %K线减速平滑因子 | 3, 5 |
| 超买线 | 超买阈值(通常为80) | 75-85 |
| 超卖线 | 超卖阈值(通常为20) | 15-25 |
| 止损点数 | 止损距离 | 40-80 |
| 止盈点数 | 止盈距离 | 80-160 |
| 启用移动止损 | 是否启用移动止损功能 | true/false |
| 移动启动点数 | 开始移动止损的盈利点数 | 20-30 |
| 移动步长 | 止损距离当前价格的步长 | 10-20 |
| 保本点数 | 移动止损到开仓价的触发点数 | 25-40 |
| 启用时间过滤 | 是否限制交易时段 | true/false |
| 开始小时 | 开始交易时间(服务器时间) | 0-23 |
| 结束小时 | 停止交易时间 | 0-23 |
| 最大点差 | 允许的最大点差 | 20-40 |
| 滑点 | 最大滑点容忍度 | 10 |
| 魔术号 | EA唯一标识 | 任意不重复数字 |
| 反向平仓 | 新信号时平掉反向持仓 | true/false |

安装步骤



1. 复制代码到MT4的MetaEditor(按F4)
2. 创建新的EA(文件 > 新建 > 智能交易系统)
3. 将所有默认代码替换为上方完整代码
4. 按编译按钮(F7)- 确保底部错误选项卡为0个错误
5. 关闭MetaEditor,从导航器将EA拖拽到图表上
6. 在输入参数选项卡中调整参数
7. 启用自动交易(绿色三角形按钮或Alt+T)

策略原理解析



随机震荡指标由两条线组成:%K线(主线)和%D线(信号线)。%K线测量收盘价相对于一定周期内价格区间的位置,计算公式为:%K = (当前收盘价 - 最低价) / (最高价 - 最低价) × 100。%D线是%K线的移动平均线。

交易逻辑:
  • 买入信号:随机指标跌破20(超卖区)且%K线上穿%D线

  • 卖出信号:随机指标升破80(超买区)且%K线下穿%D线


  • 编译与修改技巧



    常见编译问题:
  • 确保代码顶部有`#property strict`

  • 对于4位报价平台,检查pointMultiplier设置是否正确

  • 确保MagicNumber不与其他EA冲突


  • 如何自定义策略:
  • 调整KPeriod、DPeriod和Slowing可改变指标灵敏度

  • 根据不同货币对特性修改超买超卖线(部分货币对更适合75/25)

  • 启用UseTrailingStop并调整TrailingStart保护盈利

  • 在OnTick()中添加移动平均线等额外确认指标## 参考来源


  • 本文EA源码为自主编译,策略基于经典的随机震荡指标交叉方法论。该震荡指标由George C. Lane在20世纪50年代开发。代码已在MT4 build 1420+环境下测试编译通过。

    *如需更专业的优化版EA(含多周期分析、机器学习优化、完整回测报告),请查看我们的付费EA合集。订阅后可每周获取更新和独家交易工具。*