ATR 指標應用:動態止損與波動率過濾器
📌 本文重點
深入應用 ATR(平均真實波幅):動態止損計算、波動率過濾、倉位大小調整,讓你的 EA 自動適應不同市場環境。
深入應用 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 撰寫。內容僅供教育目的,不構成投資建議。