價格行為(Price Action)EA:支撐壓力與 K 線型態識別

📌 本文重點
用 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 撰寫。內容僅供教育目的,不構成投資建議。

Similar Posts

發佈留言

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