均值回歸策略:布林通道 + RSI 組合 MQL5 實作

📌 本文重點
實作均值回歸(Mean Reversion)策略:布林通道邊緣 + RSI 超買超賣共振訊號,含完整進出場邏輯與風險管理。

均值回歸策略邏輯

均值回歸假設:價格偏離平均值後,有回歸均值的傾向。布林通道下軌 + RSI 超賣 = 雙重確認的做多機會。

#include <Trade\Trade.mqh>
CTrade g_trade;

input int    InpBBPeriod   = 20;    // 布林通道週期
input double InpBBDeviation= 2.0;   // 標準差倍數
input int    InpRSIPeriod  = 14;    // RSI 週期
input double InpRSIOversold= 30.0;  // RSI 超賣門檻
input double InpRSIOverbought=70.0; // RSI 超買門檻
input int    InpATRPeriod  = 14;
input double InpATRMult    = 1.5;
input double InpRiskPct    = 1.0;
input int    InpMagic      = 20002;

int g_bbHandle = INVALID_HANDLE;
int g_rsiHandle= INVALID_HANDLE;
int g_atrHandle= INVALID_HANDLE;
datetime g_lastBar = 0;

int OnInit()
{
    g_bbHandle  = iBands(_Symbol, _Period, InpBBPeriod, 0, InpBBDeviation, PRICE_CLOSE);
    g_rsiHandle = iRSI(_Symbol, _Period, InpRSIPeriod, PRICE_CLOSE);
    g_atrHandle = iATR(_Symbol, _Period, InpATRPeriod);
    if (g_bbHandle==INVALID_HANDLE || g_rsiHandle==INVALID_HANDLE || g_atrHandle==INVALID_HANDLE)
        return INIT_FAILED;
    g_trade.SetExpertMagicNumber(InpMagic);
    return INIT_SUCCEEDED;
}

void OnDeinit(const int r) { IndicatorRelease(g_bbHandle); IndicatorRelease(g_rsiHandle); IndicatorRelease(g_atrHandle); }

void OnTick()
{
    datetime curBar = iTime(_Symbol, _Period, 0);
    if (curBar == g_lastBar) return;
    g_lastBar = curBar;

    double upper[2], mid[2], lower[2], rsi[2], atr[2];
    ArraySetAsSeries(upper,true); ArraySetAsSeries(mid,true);
    ArraySetAsSeries(lower,true); ArraySetAsSeries(rsi,true); ArraySetAsSeries(atr,true);

    if (CopyBuffer(g_bbHandle,  1, 0, 2, upper) < 2) return;
    if (CopyBuffer(g_bbHandle,  0, 0, 2, mid)   < 2) return;
    if (CopyBuffer(g_bbHandle,  2, 0, 2, lower) < 2) return;
    if (CopyBuffer(g_rsiHandle, 0, 0, 2, rsi)   < 2) return;
    if (CopyBuffer(g_atrHandle, 0, 0, 2, atr)   < 2) return;

    double close1 = iClose(_Symbol, _Period, 1);
    double close2 = iClose(_Symbol, _Period, 2);
    double ask    = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
    double bid    = SymbolInfoDouble(_Symbol, SYMBOL_BID);
    double pt     = SymbolInfoDouble(_Symbol, SYMBOL_POINT);

    int positions = CountPositions();

    // 做多條件:收盤從下軌以下回到上方 + RSI 超賣
    bool buySignal  = close2 < lower[1] && close1 >= lower[0] && rsi[1] < InpRSIOversold;
    // 做空條件:收盤從上軌以上回到下方 + RSI 超買
    bool sellSignal = close2 > upper[1] && close1 <= upper[0] && rsi[1] > InpRSIOverbought;

    if (buySignal && positions == 0)
    {
        double sl   = ask - atr[1] * InpATRMult;
        double tp   = mid[1]; // 目標:回歸中軌
        int slPips  = (int)((ask - sl) / pt);
        g_trade.Buy(CalcLots(slPips), _Symbol, ask, NormalizeDouble(sl,_Digits), NormalizeDouble(tp,_Digits), "MR Buy");
        Print("均值回歸買入!RSI=", rsi[1], " BB Lower=", lower[0]);
    }

    if (sellSignal && positions == 0)
    {
        double sl   = bid + atr[1] * InpATRMult;
        double tp   = mid[1];
        int slPips  = (int)((sl - bid) / pt);
        g_trade.Sell(CalcLots(slPips), _Symbol, bid, NormalizeDouble(sl,_Digits), NormalizeDouble(tp,_Digits), "MR Sell");
        Print("均值回歸賣出!RSI=", rsi[1], " BB Upper=", upper[0]);
    }
}

int CountPositions() { int n=0; for(int i=PositionsTotal()-1;i>=0;i--) if(PositionGetSymbol(i)==_Symbol&&PositionGetInteger(POSITION_MAGIC)==InpMagic) n++; return n; }
double CalcLots(int sp) { double r=AccountInfoDouble(ACCOUNT_BALANCE)*InpRiskPct/100.0; double tv=SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_VALUE); double ts=SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_SIZE); double pv=tv*(ts/SymbolInfoDouble(_Symbol,SYMBOL_POINT)); if(pv<=0||sp<=0) return 0.01; double l=r/(sp*pv); double mn=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN),mx=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MAX),st=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP); return NormalizeDouble(MathRound(MathMax(MathMin(l,mx),mn)/st)*st,2); }

策略適用市場

  • ✅ 適合:橫盤震盪、範圍明確的市場
  • ❌ 不適合:強趨勢市場(價格可能持續偏離)

建議搭配趨勢過濾器(如 ADX < 25 代表無趨勢)限制只在震盪市場交易。


本文由 James Lee 撰寫。內容僅供教育目的,不構成投資建議。

Similar Posts

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *