趨勢跟蹤 EA:ADX 趨勢強度過濾器完整實作

📌 本文重點
學習使用 ADX(平均趨向指數)判斷趨勢強弱,結合移動平均線建立只在強趨勢中交易的趨勢跟蹤 EA,避免在橫盤市場頻繁虧損。

ADX 指標解析

ADX 由三條線組成:ADX 線(趨勢強度)、+DI 線(上升趨向)、-DI 線(下降趨向)。

ADX 值 市場狀態 建議
< 20 無趨勢(橫盤) 避免趨勢策略
20–25 趨勢形成中 謹慎進場
25–40 強趨勢 趨勢策略最佳環境
> 40 極強趨勢 可進場但注意回調

取得 ADX 三條線

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

input int    InpADXPeriod    = 14;
input double InpADXThreshold = 25.0;
input int    InpFastMA       = 10;
input int    InpSlowMA       = 30;
input double InpRiskPct      = 1.0;
input int    InpMagic        = 40001;

int g_adxHandle=INVALID_HANDLE, g_fastHandle=INVALID_HANDLE, g_slowHandle=INVALID_HANDLE;

int OnInit()
{
    g_adxHandle  = iADX(_Symbol, _Period, InpADXPeriod);
    g_fastHandle = iMA(_Symbol, _Period, InpFastMA, 0, MODE_EMA, PRICE_CLOSE);
    g_slowHandle = iMA(_Symbol, _Period, InpSlowMA, 0, MODE_EMA, PRICE_CLOSE);
    if (g_adxHandle==INVALID_HANDLE||g_fastHandle==INVALID_HANDLE||g_slowHandle==INVALID_HANDLE) return INIT_FAILED;
    g_trade.SetExpertMagicNumber(InpMagic);
    return INIT_SUCCEEDED;
}

void OnDeinit(const int r)
{
    IndicatorRelease(g_adxHandle);
    IndicatorRelease(g_fastHandle);
    IndicatorRelease(g_slowHandle);
}

bool GetADX(double &adx, double &plusDI, double &minusDI)
{
    double a[2],p[2],m[2];
    ArraySetAsSeries(a,true); ArraySetAsSeries(p,true); ArraySetAsSeries(m,true);
    if (CopyBuffer(g_adxHandle,0,0,2,a) < 2) return false;
    if (CopyBuffer(g_adxHandle,1,0,2,p) < 2) return false;
    if (CopyBuffer(g_adxHandle,2,0,2,m) < 2) return false;
    adx=a[1]; plusDI=p[1]; minusDI=m[1];
    return true;
}

完整趨勢跟蹤 EA

datetime g_lastBar = 0;

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

    double adx,plusDI,minusDI;
    if (!GetADX(adx,plusDI,minusDI)) return;

    double fast[3],slow[3],atr[2];
    ArraySetAsSeries(fast,true); ArraySetAsSeries(slow,true); ArraySetAsSeries(atr,true);
    if (CopyBuffer(g_fastHandle,0,0,3,fast)<3) return;
    if (CopyBuffer(g_slowHandle,0,0,3,slow)<3) return;
    int ah=iATR(_Symbol,_Period,14);
    CopyBuffer(ah,0,0,2,atr); IndicatorRelease(ah);

    Comment("ADX="+DoubleToString(adx,1)+" +DI="+DoubleToString(plusDI,1)+" -DI="+DoubleToString(minusDI,1));

    // ADX 過濾:只在強趨勢時進場
    if (adx < InpADXThreshold) return;

    double ask=SymbolInfoDouble(_Symbol,SYMBOL_ASK);
    double bid=SymbolInfoDouble(_Symbol,SYMBOL_BID);
    double pt=SymbolInfoDouble(_Symbol,SYMBOL_POINT);
    int pos = CountPositions();

    bool buySetup  = plusDI>minusDI  && fast[2]<=slow[2] && fast[1]>slow[1];
    bool sellSetup = minusDI>plusDI  && fast[2]>=slow[2] && fast[1]<slow[1];

    if (buySetup && pos==0)
    {
        double sl=ask-atr[1]*2, tp=ask+atr[1]*4;
        g_trade.Buy(CalcLots((int)((ask-sl)/pt)),_Symbol,ask,NormalizeDouble(sl,_Digits),NormalizeDouble(tp,_Digits),"ADX Buy");
    }
    if (sellSetup && pos==0)
    {
        double sl=bid+atr[1]*2, tp=bid-atr[1]*4;
        g_trade.Sell(CalcLots((int)((sl-bid)/pt)),_Symbol,bid,NormalizeDouble(sl,_Digits),NormalizeDouble(tp,_Digits),"ADX Sell");
    }
    if (pos>0 && adx < InpADXThreshold-5) CloseAll();
}

int CountPositions(){ int n=0; for(int i=PositionsTotal()-1;i>=0;i--) if(PositionGetSymbol(i)==_Symbol&&PositionGetInteger(POSITION_MAGIC)==InpMagic) n++; return n; }
void CloseAll(){ for(int i=PositionsTotal()-1;i>=0;i--){ if(PositionGetSymbol(i)!=_Symbol||PositionGetInteger(POSITION_MAGIC)!=InpMagic) continue; g_trade.PositionClose(PositionGetInteger(POSITION_TICKET)); } }
double CalcLots(int sp){ double r=AccountInfoDouble(ACCOUNT_BALANCE)*InpRiskPct/100.0,tv=SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_VALUE),ts=SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_SIZE),pv=tv*(ts/SymbolInfoDouble(_Symbol,SYMBOL_POINT)); if(pv<=0||sp<=0) return 0.01; double l=r/(sp*pv),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); }

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

Similar Posts

發佈留言

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