趨勢跟蹤 EA:ADX 趨勢強度過濾器完整實作
📌 本文重點
學習使用 ADX(平均趨向指數)判斷趨勢強弱,結合移動平均線建立只在強趨勢中交易的趨勢跟蹤 EA,避免在橫盤市場頻繁虧損。
學習使用 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 撰寫。內容僅供教育目的,不構成投資建議。