價格行為(Price Action)EA:支撐壓力與 K 線型態識別
📌 本文重點
用 MQL5 實作價格行為策略:動態支撐壓力計算(擺動高低點)、錘子線、看漲/看跌吞噬、十字星型態識別,以及結合支撐壓力的完整進場邏輯。
用 MQL5 實作價格行為策略:動態支撐壓力計算(擺動高低點)、錘子線、看漲/看跌吞噬、十字星型態識別,以及結合支撐壓力的完整進場邏輯。
擺動高低點(Swing High / Low)計算
bool IsSwingHigh(int index, int lookback=3)
{
double h = iHigh(_Symbol,_Period,index);
for (int i=1; i<=lookback; i++)
{
if (iHigh(_Symbol,_Period,index+i) >= h) return false;
if (iHigh(_Symbol,_Period,index-i) >= h) return false;
}
return true;
}
bool IsSwingLow(int index, int lookback=3)
{
double l = iLow(_Symbol,_Period,index);
for (int i=1; i<=lookback; i++)
{
if (iLow(_Symbol,_Period,index+i) <= l) return false;
if (iLow(_Symbol,_Period,index-i) <= l) return false;
}
return true;
}
void GetSwingLevels(double &highs[], double &lows[], int maxLevels=5, int searchBars=100)
{
ArrayResize(highs,0); ArrayResize(lows,0);
for (int i=3; i<searchBars && ArraySize(highs)<maxLevels; i++)
if (IsSwingHigh(i)) { int n=ArraySize(highs); ArrayResize(highs,n+1); highs[n]=iHigh(_Symbol,_Period,i); }
for (int i=3; i<searchBars && ArraySize(lows)<maxLevels; i++)
if (IsSwingLow(i)) { int n=ArraySize(lows); ArrayResize(lows,n+1); lows[n]=iLow(_Symbol,_Period,i); }
}
bool NearLevel(double price, double level, int tolerancePips=10)
{
return MathAbs(price-level) <= tolerancePips * SymbolInfoDouble(_Symbol,SYMBOL_POINT);
}
K 線型態識別
// 錘子線:下影線長、實體小
bool IsHammer(int index)
{
double o=iOpen(_Symbol,_Period,index), h=iHigh(_Symbol,_Period,index);
double l=iLow(_Symbol,_Period,index), c=iClose(_Symbol,_Period,index);
double body=MathAbs(c-o), lowerWick=MathMin(c,o)-l;
double upperWick=h-MathMax(c,o), range=h-l;
if (range==0) return false;
return lowerWick>=range*0.6 && body<=range*0.3 && upperWick<=range*0.1;
}
// 看漲吞噬:陰線後出現更大陽線
bool IsBullishEngulfing(int index)
{
double o1=iOpen(_Symbol,_Period,index), c1=iClose(_Symbol,_Period,index);
double o2=iOpen(_Symbol,_Period,index+1), c2=iClose(_Symbol,_Period,index+1);
return c2<o2 && c1>o1 && o1<=c2 && c1>=o2;
}
// 看跌吞噬:陽線後出現更大陰線
bool IsBearishEngulfing(int index)
{
double o1=iOpen(_Symbol,_Period,index), c1=iClose(_Symbol,_Period,index);
double o2=iOpen(_Symbol,_Period,index+1), c2=iClose(_Symbol,_Period,index+1);
return c2>o2 && c1<o1 && o1>=c2 && c1<=o2;
}
// 十字星:開盤與收盤幾乎相同
bool IsDoji(int index, double threshold=0.1)
{
double body=MathAbs(iClose(_Symbol,_Period,index)-iOpen(_Symbol,_Period,index));
double range=iHigh(_Symbol,_Period,index)-iLow(_Symbol,_Period,index);
return (range>0) && (body/range < threshold);
}
結合支撐壓力的完整進場邏輯
#include <Trade\Trade.mqh>
CTrade g_trade;
input int InpTolerance = 15;
input double InpRiskPct = 1.0;
input int InpMagic = 50001;
datetime g_lastBar = 0;
int OnInit() { g_trade.SetExpertMagicNumber(InpMagic); return INIT_SUCCEEDED; }
void OnTick()
{
datetime curBar = iTime(_Symbol,_Period,0);
if (curBar == g_lastBar) return;
g_lastBar = curBar;
double swingHighs[], swingLows[];
GetSwingLevels(swingHighs, swingLows, 5, 100);
double ask=SymbolInfoDouble(_Symbol,SYMBOL_ASK);
double bid=SymbolInfoDouble(_Symbol,SYMBOL_BID);
double pt=SymbolInfoDouble(_Symbol,SYMBOL_POINT);
int ah=iATR(_Symbol,_Period,14);
double atrBuf[2]; ArraySetAsSeries(atrBuf,true);
CopyBuffer(ah,0,0,2,atrBuf); IndicatorRelease(ah);
double atr=atrBuf[1];
if (CountPositions() > 0) return;
// 在支撐附近出現看漲訊號 → 做多
for (int s=0; s<ArraySize(swingLows); s++)
{
if (NearLevel(bid, swingLows[s], InpTolerance))
{
if (IsHammer(1) || IsBullishEngulfing(1))
{
double sl=swingLows[s]-atr, tp=bid+atr*3;
g_trade.Buy(CalcLots((int)((bid-sl)/pt)), _Symbol, ask,
NormalizeDouble(sl,_Digits), NormalizeDouble(tp,_Digits), "PA Buy");
break;
}
}
}
// 在壓力附近出現看跌訊號 → 做空
for (int r=0; r<ArraySize(swingHighs); r++)
{
if (NearLevel(ask, swingHighs[r], InpTolerance))
{
if (IsBearishEngulfing(1) || (IsDoji(2) && iClose(_Symbol,_Period,1) < iOpen(_Symbol,_Period,1)))
{
double sl=swingHighs[r]+atr, tp=ask-atr*3;
g_trade.Sell(CalcLots((int)((sl-ask)/pt)), _Symbol, bid,
NormalizeDouble(sl,_Digits), NormalizeDouble(tp,_Digits), "PA Sell");
break;
}
}
}
}
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,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 撰寫。內容僅供教育目的,不構成投資建議。