//+------------------------------------------------------------------+
//| MACD_Quantitative_EA.mq4 |
//| Copyright 2025, Expert Advisor Developer |
//| https://yourwebsite.com/ |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, Your Name"
#property link "https://yourwebsite.com/"
#property version "1.00"
#property strict
// 输入参数
input int FastEMA = 13; // 快速EMA周期
input int SlowEMA = 29; // 慢速EMA周期
input int SignalSMA = 9; // 信号线周期
input double LotSize = 0.1; // 固定交易手数
input int StopLoss = 300; // 止损点数(黄金波动大)
input int TakeProfit = 600; // 止盈点数
input int TrailingStart = 100; // 启动跟踪止损的盈利点数
input int TrailingStop = 80; // 跟踪止损点数
input bool UseTrailingStop = true;
input bool UseMACDAreaSignal = true;
input bool UseMACDAreaClose = true;
input double MinMACDArea = 0.0002; // 更高阈值
input int AreaLookback = 5;
input int MaxTrendBars = 10; // 更长趋势判断
input int DivergenceLookback = 30;
input int CrossAngleLookback = 3;
input double DistanceThreshold = 0.0003;
input int AccelerationLookback = 5;
// 新增:K线形态识别参数
input bool UseKPatternFilter = true;
// 新增:多指标参数
input int FastMAPeriod = 20;
input int SlowMAPeriod = 50;
input int RSI_Period = 14;
input int StochasticPeriod = 8;
input int ATRPeriod = 20;
input int BBPeriod = 20;
input double BBDeviation = 2.5;
input int SRPeriod = 100;
// 全局变量
int ticket = 0;
//+------------------------------------------------------------------+
//| 初始化函数 |
//+------------------------------------------------------------------+
int OnInit()
{
Print("MACD量化交易EA已加载");
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| 主函数 OnTick |
//+------------------------------------------------------------------+
void OnTick()
{
if (OrdersTotal() > 0)
{
if (UseMACDAreaClose)
CheckMACDAreaClose(); // 检查MACD面积是否扩大,决定是否平仓
ManageTrailingStop(); // 管理跟踪止损
}
if (OrdersTotal() == 0)
{
int signal = 0;
// 原有MACD逻辑
signal = GetMACDSignal();
if (signal == 0 && UseMACDAreaSignal)
signal = GetMACDAreaSignal();
if (signal == 0)
signal = GetMACDTrendSignal();
if (signal == 0)
signal = DetectMACDTopBottom();
if (signal == 0)
signal = DetectMACDCombinedDivergence();
if (signal == 0)
signal = IsMACDPeakOrValley();
// 新增:多指标信号增强(趋势、动量、波动率、价格行为)
int multiSignal = GetMultiIndicatorSignal();
if (signal == 0 && multiSignal != 0)
signal = multiSignal;
// K线形态过滤器
if (UseKPatternFilter)
{
if (IsPinBar(1)) // 前一根 K 为 Pin Bar
{
if (iClose(NULL, 0, 1) < iOpen(NULL, 0, 1)) signal = -1;
else signal = 1;
}
if (IsEngulfing(1)) // 前一根 K 为 Engulfing
{
if (iClose(NULL, 0, 1) > iOpen(NULL, 0, 1)) signal = 1;
if (iClose(NULL, 0, 1) < iOpen(NULL, 0, 1)) signal = -1;
}
if (IsInsideBar(1)) // 前一根 K 为 Inside Bar
{
if (iClose(NULL, 0, 0) > iHigh(NULL, 0, 1)) signal = 1;
if (iClose(NULL, 0, 0) < iLow(NULL, 0, 1)) signal = -1;
}
if (IsHarami(1)) // 前一根 K 为 Harami
{
if (iClose(NULL, 0, 1) < iOpen(NULL, 0, 1) && iClose(NULL, 0, 0) > iClose(NULL, 0, 1)) signal = 1;
if (iClose(NULL, 0, 1) > iOpen(NULL, 0, 1) && iClose(NULL, 0, 0) < iClose(NULL, 0, 1)) signal = -1;
}
}
if (signal == 1)
OpenBuyOrder(); // 做多
else if (signal == -1)
OpenSellOrder(); // 做空
}
}
//+------------------------------------------------------------------+
//| 获取MACD金叉/死叉信号 |
//+------------------------------------------------------------------+
int GetMACDSignal()
{
double macdMain = iMACD(NULL, 0, FastEMA, SlowEMA, SignalSMA, PRICE_CLOSE, 0, 0);
double macdSignal = iMACD(NULL, 0, FastEMA, SlowEMA, SignalSMA, PRICE_CLOSE, 1, 0);
double macdPrevMain = iMACD(NULL, 0, FastEMA, SlowEMA, SignalSMA, PRICE_CLOSE, 0, 1);
double macdPrevSignal = iMACD(NULL, 0, FastEMA, SlowEMA, SignalSMA, PRICE_CLOSE, 1, 1);
if (macdPrevMain < macdPrevSignal && macdMain > macdSignal)
{
if (IsMACDCrossWithSteepAngle() && GetMACDLineDistanceFromSignal() > DistanceThreshold)
return 1;
}
if (macdPrevMain > macdPrevSignal && macdMain < macdSignal)
{
if (IsMACDCrossWithSteepAngle() && GetMACDLineDistanceFromSignal() > DistanceThreshold)
return -1;
}
return 0;
}
//+------------------------------------------------------------------+
//| 获取MACD面积缩小信号(优化版:连续5根K线) |
//+------------------------------------------------------------------+
int GetMACDAreaSignal()
{
double hist[5] = {0, 0, 0, 0, 0};
for (int i = 0; i < 5; i++)
{
hist[i] = iMACD(NULL, 0, FastEMA, SlowEMA, SignalSMA, PRICE_CLOSE, 2, i);
}
bool shrinkingNegative = true;
for (int i = 0; i < 4; i++)
{
if (MathAbs(hist[i]) >= MathAbs(hist[i + 1]))
{
shrinkingNegative = false;
break;
}
}
if (shrinkingNegative && hist[0] < 0 && hist[1] < 0 && hist[2] < 0 && hist[3] < 0 && hist[4] < 0)
return 1;
bool shrinkingPositive = true;
for (int i = 0; i < 4; i++)
{
if (hist[i] <= hist[i + 1])
{
shrinkingPositive = false;
break;
}
}
if (shrinkingPositive && hist[0] > 0 && hist[1] > 0 && hist[2] > 0 && hist[3] > 0 && hist[4] > 0)
return -1;
return 0;
}
//+------------------------------------------------------------------+
//| 获取MACD趋势信号 |
//+------------------------------------------------------------------+
int GetMACDTrendSignal()
{
double hist[5] = {0, 0, 0, 0, 0};
for (int i = 0; i < 5; i++)
{
hist[i] = iMACD(NULL, 0, FastEMA, SlowEMA, SignalSMA, PRICE_CLOSE, 2, i);
}
bool allAboveZero = true;
for (int i = 0; i < 5; i++)
{
if (hist[i] <= 0)
{
allAboveZero = false;
break;
}
}
if (allAboveZero)
return 1;
bool allBelowZero = true;
for (int i = 0; i < 5; i++)
{
if (hist[i] >= 0)
{
allBelowZero = false;
break;
}
}
if (allBelowZero)
return -1;
return 0;
}
//+------------------------------------------------------------------+
//| 判断MACD是否出现顶部/底部形态 |
//+------------------------------------------------------------------+
int DetectMACDTopBottom()
{
int trendBars = CountMACDBarTrend();
double currentHist = iMACD(NULL, 0, FastEMA, SlowEMA, SignalSMA, PRICE_CLOSE, 2, 0);
double prevHist = iMACD(NULL, 0, FastEMA, SlowEMA, SignalSMA, PRICE_CLOSE, 2, 1);
if (currentHist > 0 && prevHist > 0 && currentHist < prevHist && trendBars >= MaxTrendBars)
return -1;
if (currentHist < 0 && prevHist < 0 && currentHist > prevHist && trendBars >= MaxTrendBars)
return 1;
return 0;
}
//+------------------------------------------------------------------+
//| 检测MACD面积与价格背离 |
//+------------------------------------------------------------------+
int DetectMACDAreaDivergence()
{
double priceHigh = iHigh(NULL, 0, iHighest(NULL, 0, MODE_HIGH, DivergenceLookback, 0));
double priceLow = iLow(NULL, 0, iLowest(NULL, 0, MODE_LOW, DivergenceLookback, 0));
double areaHigh = CalculateMACDArea(DivergenceLookback);
double areaLow = CalculateMACDArea(DivergenceLookback);
if (Bid > priceHigh && areaHigh < CalculateMACDArea(DivergenceLookback - 5))
return -1;
if (Ask < priceLow && areaLow > CalculateMACDArea(DivergenceLookback - 5))
return 1;
return 0;
}
//+------------------------------------------------------------------+
//| 检测MACD柱连续上涨/下跌与价格背离 |
//+------------------------------------------------------------------+
int DetectMACDBarDivergence()
{
double priceHigh = iHigh(NULL, 0, iHighest(NULL, 0, MODE_HIGH, DivergenceLookback, 0));
double priceLow = iLow(NULL, 0, iLowest(NULL, 0, MODE_LOW, DivergenceLookback, 0));
int trendBars = CountMACDBarTrend();
if (Bid > priceHigh && trendBars < DivergenceLookback - 3)
return -1;
if (Ask < priceLow && trendBars < DivergenceLookback - 3)
return 1;
return 0;
}
//+------------------------------------------------------------------+
//| 检测MACD面积和根数双重背离(最强信号) |
//+------------------------------------------------------------------+
int DetectMACDCombinedDivergence()
{
int areaSignal = DetectMACDAreaDivergence();
int barSignal = DetectMACDBarDivergence();
if (areaSignal == -1 && barSignal == -1)
return -1;
if (areaSignal == 1 && barSignal == 1)
return 1;
return 0;
}
//+------------------------------------------------------------------+
//| 检测MACD柱是否出现“山峰”或“谷底”形态 |
//+------------------------------------------------------------------+
int IsMACDPeakOrValley(int lookback = 5)
{
double hist[5] = {0, 0, 0, 0, 0};
for (int i = 0; i < 5; i++)
hist[i] = iMACD(NULL, 0, FastEMA, SlowEMA, SignalSMA, PRICE_CLOSE, 2, i);
if (hist[1] < hist[2] && hist[2] > hist[3] && hist[0] < hist[2])
return 1;
if (hist[1] > hist[2] && hist[2] < hist[3] && hist[0] > hist[2])
return -1;
return 0;
}
//+------------------------------------------------------------------+
//| 判断MACD线与信号线交叉是否角度陡峭 |
//+------------------------------------------------------------------+
bool IsMACDCrossWithSteepAngle(int lookback = 3)
{
double angle = 0;
for (int i = 0; i < lookback - 1; i++)
{
double currentMain = iMACD(NULL, 0, FastEMA, SlowEMA, SignalSMA, PRICE_CLOSE, 0, i);
double currentSignal = iMACD(NULL, 0, FastEMA, SlowEMA, SignalSMA, PRICE_CLOSE, 1, i);
double nextMain = iMACD(NULL, 0, FastEMA, SlowEMA, SignalSMA, PRICE_CLOSE, 0, i + 1);
double nextSignal = iMACD(NULL, 0, FastEMA, SlowEMA, SignalSMA, PRICE_CLOSE, 1, i + 1);
angle += MathAbs(currentMain - currentSignal) - MathAbs(nextMain - nextSignal);
}
return MathAbs(angle) > 0.0001;
}
//+------------------------------------------------------------------+
//| 计算MACD线与信号线之间的平均距离(用于衡量趋势强度) |
//+------------------------------------------------------------------+
double GetMACDLineDistanceFromSignal(int lookback = 5)
{
double sum = 0;
for (int i = 0; i < lookback; i++)
{
double macd = iMACD(NULL, 0, FastEMA, SlowEMA, SignalSMA, PRICE_CLOSE, 0, i);
double signal = iMACD(NULL, 0, FastEMA, SlowEMA, SignalSMA, PRICE_CLOSE, 1, i);
sum += MathAbs(macd - signal);
}
return sum / lookback;
}
//+------------------------------------------------------------------+
//| 判断是否为 Pin Bar 形态(反转信号) |
//+------------------------------------------------------------------+
bool IsPinBar(int shift = 0)
{
double open = iOpen(NULL, 0, shift);
double close = iClose(NULL, 0, shift);
double high = iHigh(NULL, 0, shift);
double low = iLow(NULL, 0, shift);
double range = high - low;
double body = MathAbs(open - close);
double upperWick = high - MathMax(open, close);
double lowerWick = MathMin(open, close) - low;
if (body == 0) return false;
if (upperWick > 2 * body && lowerWick < body) return true;
if (lowerWick > 2 * body && upperWick < body) return true;
return false;
}
//+------------------------------------------------------------------+
//| 判断是否为 Engulfing 形态(吞没形态,反转信号) |
//+------------------------------------------------------------------+
bool IsEngulfing(int shift = 1)
{
double open1 = iOpen(NULL, 0, shift + 1);
double close1 = iClose(NULL, 0, shift + 1);
double open2 = iOpen(NULL, 0, shift);
double close2 = iClose(NULL, 0, shift);
if (open2 < close2 && close2 > open1 && close2 > close1 && open2 < close1) return true;
if (open2 > close2 && close2 < open1 && close2 < close1 && open2 > close1) return true;
return false;
}
//+------------------------------------------------------------------+
//| 判断是否为 Inside Bar 形态(震荡/突破) |
//+------------------------------------------------------------------+
bool IsInsideBar(int shift = 1)
{
double high1 = iHigh(NULL, 0, shift + 1);
double low1 = iLow(NULL, 0, shift + 1);
double high2 = iHigh(NULL, 0, shift);
double low2 = iLow(NULL, 0, shift);
return (high2 <= high1 && low2 >= low1);
}
//+------------------------------------------------------------------+
//| 判断是否为 Harami(孕线)形态(反转信号) |
//+------------------------------------------------------------------+
bool IsHarami(int shift = 1)
{
double open1 = iOpen(NULL, 0, shift + 1);
double close1 = iClose(NULL, 0, shift + 1);
double open2 = iOpen(NULL, 0, shift);
double close2 = iClose(NULL, 0, shift);
double high1 = iHigh(NULL, 0, shift + 1);
double low1 = iLow(NULL, 0, shift + 1);
double high2 = iHigh(NULL, 0, shift);
double low2 = iLow(NULL, 0, shift);
return (high2 <= high1 && low2 >= low1);
}
//+------------------------------------------------------------------+
//| 开多单 |
//+------------------------------------------------------------------+
void OpenBuyOrder()
{
double sl = Ask - StopLoss * Point;
double tp = Ask + TakeProfit * Point;
ticket = OrderSend(Symbol(), OP_BUY, LotSize, Ask, 3, sl, tp, "MACD Buy", 0, 0, clrGreen);
if (ticket < 0)
Print("多单开仓失败,错误码: ", GetLastError());
else
Print("【多单开仓】价格: ", DoubleToStr(Ask, Digits), " 止损: ", DoubleToStr(sl, Digits), " 止盈: ", DoubleToStr(tp, Digits));
}
//+------------------------------------------------------------------+
//| 开空单 |
//+------------------------------------------------------------------+
void OpenSellOrder()
{
double sl = Bid + StopLoss * Point;
double tp = Bid - TakeProfit * Point;
ticket = OrderSend(Symbol(), OP_SELL, LotSize, Bid, 3, sl, tp, "MACD Sell", 0, 0, clrRed);
if (ticket < 0)
Print("空单开仓失败,错误码: ", GetLastError());
else
Print("【空单开仓】价格: ", DoubleToStr(Bid, Digits), " 止损: ", DoubleToStr(sl, Digits), " 止盈: ", DoubleToStr(tp, Digits));
}
//+------------------------------------------------------------------+
//| 管理跟踪止损 |
//+------------------------------------------------------------------+
void ManageTrailingStop()
{
for (int i = 0; i < OrdersTotal(); i++)
{
if (!OrderSelect(i, SELECT_BY_POS)) continue;
if (OrderSymbol() != Symbol()) continue;
double openPrice = OrderOpenPrice();
double currentPrice = (OrderType() == OP_BUY) ? Bid : Ask;
double profitPoints = (OrderType() == OP_BUY)
? (currentPrice - openPrice) / Point
: (openPrice - currentPrice) / Point;
if (UseTrailingStop && profitPoints >= TrailingStart)
{
double newStopLoss = 0;
if (OrderType() == OP_BUY)
newStopLoss = currentPrice - TrailingStop * Point;
else
newStopLoss = currentPrice + TrailingStop * Point;
if (newStopLoss != OrderStopLoss())
{
if (!OrderModify(OrderTicket(), openPrice, newStopLoss, OrderTakeProfit(), 0, clrGreen))
Print("跟踪止损更新失败,错误码: ", GetLastError());
else
Print("【跟踪止损更新】新止损: ", DoubleToStr(newStopLoss, Digits));
}
}
}
}
//+------------------------------------------------------------------+
//| 检查MACD面积是否缩小(用于平仓) |
//+------------------------------------------------------------------+
void CheckMACDAreaClose()
{
for (int i = 0; i < OrdersTotal(); i++)
{
if (!OrderSelect(i, SELECT_BY_POS)) continue;
if (OrderSymbol() != Symbol()) continue;
double currentArea = CalculateMACDArea(AreaLookback);
if (currentArea == 0) continue;
int type = OrderType();
if (type == OP_BUY)
{
if (MathAbs(currentArea) < MinMACDArea || IsMACDAreaShrinking(AreaLookback))
{
if (!OrderClose(OrderTicket(), OrderLots(), Bid, 3, clrRed))
Print("平仓失败,错误码: ", GetLastError());
else
Print("【MACD面积衰减】多单平仓 @ ", DoubleToStr(Bid, Digits));
}
}
if (type == OP_SELL)
{
if (MathAbs(currentArea) < MinMACDArea || IsMACDAreaShrinking(AreaLookback))
{
if (!OrderClose(OrderTicket(), OrderLots(), Ask, 3, clrRed))
Print("平仓失败,错误码: ", GetLastError());
else
Print("【MACD面积衰减】空单平仓 @ ", DoubleToStr(Ask, Digits));
}
}
}
}
//+------------------------------------------------------------------+
//| 判断MACD柱状图连续上涨/下跌的根数 |
//+------------------------------------------------------------------+
int CountMACDBarTrend(int maxBars = 20)
{
int count = 0;
double prevHist = iMACD(NULL, 0, FastEMA, SlowEMA, SignalSMA, PRICE_CLOSE, 2, 0);
for (int i = 1; i < maxBars; i++)
{
double currentHist = iMACD(NULL, 0, FastEMA, SlowEMA, SignalSMA, PRICE_CLOSE, 2, i);
if ((prevHist > 0 && currentHist > prevHist) || (prevHist < 0 && currentHist < prevHist))
{
count++;
prevHist = currentHist;
}
else
break;
}
return count;
}
//+------------------------------------------------------------------+
//| 计算MACD面积(Histogram的累计值) |
//+------------------------------------------------------------------+
double CalculateMACDArea(int bars = 5)
{
double area = 0;
for (int i = 0; i < bars; i++)
{
double hist = iMACD(NULL, 0, FastEMA, SlowEMA, SignalSMA, PRICE_CLOSE, 2, i);
area += hist;
}
return area;
}
//+------------------------------------------------------------------+
//| 判断MACD面积是否连续缩小(用于趋势衰减判断) |
//+------------------------------------------------------------------+
bool IsMACDAreaShrinking(int bars = 5)
{
double areas[];
ArrayResize(areas, bars);
for (int i = 0; i < bars; i++)
{
areas[i] = 0;
for (int j = 0; j <= i; j++)
{
double hist = iMACD(NULL, 0, FastEMA, SlowEMA, SignalSMA, PRICE_CLOSE, 2, j);
areas[i] += hist;
}
}
if (areas[0] > 0)
{
for (int i = 0; i < bars - 1; i++)
if (MathAbs(areas[i]) <= MathAbs(areas[i + 1]))
return false;
return true;
}
else if (areas[0] < 0)
{
for (int i = 0; i < bars - 1; i++)
if (MathAbs(areas[i]) <= MathAbs(areas[i + 1]))
return false;
return true;
}
return false;
}
//+------------------------------------------------------------------+
//| 获取最近N根K线的支撑位 |
//+------------------------------------------------------------------+
double GetSupportLevel(int lookback)
{
double minLow = iLow(NULL, 0, iLowest(NULL, 0, MODE_LOW, lookback, 0));
return minLow;
}
//+------------------------------------------------------------------+
//| 获取最近N根K线的阻力位 |
//+------------------------------------------------------------------+
double GetResistanceLevel(int lookback)
{
double maxHigh = iHigh(NULL, 0, iHighest(NULL, 0, MODE_HIGH, lookback, 0));
return maxHigh;
}
//+------------------------------------------------------------------+
//| 自定义 iROC 函数(Rate of Change) |
//+------------------------------------------------------------------+
double Custom_iROC(string symbol, int timeframe, int period, int shift)
{
if (shift + period >= Bars(symbol, timeframe)) return 0;
double current = iClose(symbol, timeframe, shift);
double prev = iClose(symbol, timeframe, shift + period);
if (prev == 0) return 0;
return (current / prev - 1.0) * 100;
}
//+------------------------------------------------------------------+
//| 综合多个技术指标信号(趋势、动量、波动率、价格行为) |
//+------------------------------------------------------------------+
int GetMultiIndicatorSignal()
{
double maFast = iMA(NULL, 0, FastMAPeriod, 0, MODE_SMA, PRICE_CLOSE, 0);
double maSlow = iMA(NULL, 0, SlowMAPeriod, 0, MODE_SMA, PRICE_CLOSE, 0);
double adx = iADX(NULL, 0, 14, PRICE_CLOSE, MODE_MAIN, 0);
double adxUp = iADX(NULL, 0, 14, PRICE_CLOSE, MODE_PLUSDI, 0);
double adxDown = iADX(NULL, 0, 14, PRICE_CLOSE, MODE_MINUSDI, 0);
double rsi = iRSI(NULL, 0, RSI_Period, PRICE_CLOSE, 0);
double stochMain = iStochastic(NULL, 0, StochasticPeriod, 3, 3, MODE_SMA, 0, MODE_MAIN, 0);
double stochSignal = iStochastic(NULL, 0, StochasticPeriod, 3, 3, MODE_SMA, 0, MODE_SIGNAL, 0);
double roc = Custom_iROC(NULL, 0, 14, 0); // ✅ 使用自定义函数
double atr = iATR(NULL, 0, ATRPeriod, 0);
double bbUpper = iBands(NULL, 0, BBPeriod, BBDeviation, 0, PRICE_CLOSE, MODE_UPPER, 0);
double bbLower = iBands(NULL, 0, BBPeriod, BBDeviation, 0, PRICE_CLOSE, MODE_LOWER, 0);
double close = Close[0];
// 趋势判断
bool trendUp = (maFast > maSlow) && (adx > 20) && (adxUp > adxDown);
bool trendDown = (maFast < maSlow) && (adx > 20) && (adxDown > adxUp);
// 动量判断
bool momentumUp = (rsi < 70) && (stochMain > stochSignal) && (roc > 0);
bool momentumDown = (rsi > 30) && (stochMain < stochSignal) && (roc < 0);
// 波动率判断
bool highVolatility = (atr > iATR(NULL, 0, ATRPeriod, 1));
bool priceInBB = (close > bbLower && close < bbUpper);
// 支撑阻力位判断
double support = GetSupportLevel(SRPeriod);
double resistance = GetResistanceLevel(SRPeriod);
bool supportResistanceSignal = (MathAbs(close - support) < atr * 0.5) || (MathAbs(close - resistance) < atr * 0.5);
// 多头信号
if (trendUp && momentumUp && !highVolatility && !priceInBB && supportResistanceSignal)
return 1;
// 空头信号
if (trendDown && momentumDown && !highVolatility && !priceInBB && supportResistanceSignal)
return -1;
return 0;
}
是里面这些不能改变,听不见吗