ATR 指標應用:動態止損與波動率過濾器

📌 本文重點
深入應用 ATR(平均真實波幅):動態止損計算、波動率過濾、倉位大小調整,讓你的 EA 自動適應不同市場環境。

什麼是 ATR?

ATR(Average True Range,平均真實波幅)衡量市場的平均波動幅度,是設定動態止損最常用的指標。ATR 高代表市場波動大,止損應放寬;ATR 低代表市場平靜,止損可收緊。

取得 ATR 值

int g_atrHandle = INVALID_HANDLE;
input int InpATRPeriod = 14;          // ATR 週期
input double InpATRMultiplier = 2.0;  // 止損倍數

int OnInit()
{
    g_atrHandle = iATR(_Symbol, _Period, InpATRPeriod);
    if (g_atrHandle == INVALID_HANDLE) return INIT_FAILED;
    return INIT_SUCCEEDED;
}

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

double GetATR(int index = 1)
{
    double atr[1];
    ArraySetAsSeries(atr, true);
    if (CopyBuffer(g_atrHandle, 0, index, 1, atr) < 1) return 0;
    return atr[0];
}

ATR 動態止損

bool OpenBuyWithATRStop()
{
    double atr   = GetATR(1);      // 使用已完成K線的ATR
    if (atr <= 0) return false;

    double ask   = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
    double sl    = ask - atr * InpATRMultiplier;   // 止損 = 進場價 - 2*ATR
    double tp    = ask + atr * InpATRMultiplier * 2; // 止盈 = 進場價 + 4*ATR(1:2)

    Print("ATR=", DoubleToString(atr, _Digits),
          " | SL=", DoubleToString(sl, _Digits),
          " | TP=", DoubleToString(tp, _Digits));

    MqlTradeRequest req = {}; MqlTradeResult res = {};
    req.action   = TRADE_ACTION_DEAL;
    req.symbol   = _Symbol;
    req.volume   = 0.1;
    req.type     = ORDER_TYPE_BUY;
    req.price    = ask;
    req.sl       = NormalizeDouble(sl, _Digits);
    req.tp       = NormalizeDouble(tp, _Digits);
    req.deviation= 10;
    req.magic    = 11111;
    return OrderSend(req, res);
}

ATR 波動率過濾器

// 計算 ATR 相對大小(與過去平均比較)
bool IsVolatilityNormal()
{
    double atrValues[20];
    ArraySetAsSeries(atrValues, true);
    if (CopyBuffer(g_atrHandle, 0, 1, 20, atrValues) < 20) return true;

    double currentATR = atrValues[0];
    double avgATR = 0;
    for (int i = 1; i < 20; i++) avgATR += atrValues[i];
    avgATR /= 19;

    double ratio = currentATR / avgATR;

    // 波動率太高(超過平均的2倍):新聞時段,跳過
    if (ratio > 2.0) { Print("波動率過高(", DoubleToString(ratio,2), "x),跳過"); return false; }

    // 波動率太低(低於平均的0.5倍):市場無方向,跳過
    if (ratio < 0.5) { Print("波動率過低(", DoubleToString(ratio,2), "x),跳過"); return false; }

    return true;
}

// ATR 動態調整倉位大小
double CalcLotsWithATR(int fixedRiskPips = 0)
{
    double atr    = GetATR(1);
    double point  = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
    int    slPips = (fixedRiskPips > 0) ? fixedRiskPips : (int)(atr / point * InpATRMultiplier);

    double balance   = AccountInfoDouble(ACCOUNT_BALANCE);
    double riskAmt   = balance * 0.01; // 1% 風險
    double tickVal   = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
    double tickSz    = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
    double pointVal  = tickVal * (tickSz / point);

    if (pointVal <= 0 || slPips <= 0) return 0.01;
    double lots = riskAmt / (slPips * pointVal);
    double minL = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
    double maxL = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
    double step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
    lots = MathMax(MathMin(lots, maxL), minL);
    return NormalizeDouble(MathRound(lots/step)*step, 2);
}

ATR 追蹤止損

void TrailStopWithATR()
{
    double atr = GetATR(1);
    if (atr <= 0) return;

    for (int i = PositionsTotal()-1; i >= 0; i--)
    {
        if (PositionGetSymbol(i) != _Symbol) continue;

        ulong  ticket  = PositionGetInteger(POSITION_TICKET);
        int    posType = (int)PositionGetInteger(POSITION_TYPE);
        double curSL   = PositionGetDouble(POSITION_SL);
        double bid     = SymbolInfoDouble(_Symbol, SYMBOL_BID);
        double ask     = SymbolInfoDouble(_Symbol, SYMBOL_ASK);

        double newSL;
        if (posType == POSITION_TYPE_BUY)
        {
            newSL = bid - atr * InpATRMultiplier;
            if (newSL <= curSL) continue; // 只往有利方向移動
        }
        else
        {
            newSL = ask + atr * InpATRMultiplier;
            if (curSL > 0 && newSL >= curSL) continue;
        }

        MqlTradeRequest req={}; MqlTradeResult res={};
        req.action   = TRADE_ACTION_SLTP;
        req.symbol   = _Symbol;
        req.position = ticket;
        req.sl       = NormalizeDouble(newSL, _Digits);
        req.tp       = PositionGetDouble(POSITION_TP);
        OrderSend(req, res);
    }
}

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

Similar Posts

發佈留言

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