【MQL5】iATR関数の使い方|ATR(ボラティリティ)でSL・ロット・トレールを自動調整するサンプルコード

【MQL5】iATR関数の使い方|ATR(ボラティリティ)でSL・ロット・トレールを自動調整するサンプルコード MQL5リファレンス

iATR関数は、ATR(Average True Range=平均的な値幅)の値をMQL5プログラムから取得する関数です。ATRは「その相場がいま1本でどれくらい動いているか」をボラティリティとして数値化する指標で、買い/売りのサインそのものではなく、SL・TPの幅、ロットの大きさ、トレールの追従幅を相場に合わせて自動調整する“物差し”として使います。この記事では、iATRの構文と「ハンドル+CopyBufferの2段階」、そしてコピーしてすぐ動く4つのサンプル(ATRの取得・ATRでSL/TPを決める・ATRでロットを逆算・ATRトレーリング)を丁寧に解説します。

iATR(ATR)とは

ATRは、各足の「真の値幅(前日終値とのギャップも含めた高安幅)」を一定期間で平均した指標です。値が大きいほど荒い相場、小さいほど穏やかな相場を表します。重要なのは、ATRは方向(上がる/下がる)を示さないこと。「どれだけ動くか」だけを測るので、トレンド系・逆張り系のサインと組み合わせ、リスク管理の部品として使うのが王道です。MQL5では iATR() でこの値を取得します。

前提:値の取り出しは「2段階」
MQL5のインジケーターは「① OnInit() でハンドルを作る → ② OnTick()CopyBuffer() により値を取り出す」の2段階です(詳しくは iMAの記事 を参照)。iATRも同じ作法で、バッファ番号は 0 の1本だけです。

iATR() の構文と引数

int  iATR(
   string           symbol,      // 通貨ペア(_Symbol で現在のペア)
   ENUM_TIMEFRAMES  period,      // 時間足(PERIOD_CURRENT 等)
   int              ma_period    // 期間(一般的に 14)
);

戻り値はハンドル(整数のID)です。RSIやMAと違い、適用価格の引数はありません(高安を使うため)。取り出したATRの値は「価格の値幅」である点に注意してください(後述)。

サンプル①:現在のATRを取得して表示する

ATR(14) の現在値をチャート左上に表示します。ATRは価格の値幅なので、pips換算もあわせて出してみます。

コピペOK

int atrHandle = INVALID_HANDLE;

int OnInit()
{
   // ① ハンドルを1回だけ作る(期間14)
   atrHandle = iATR(_Symbol, PERIOD_CURRENT, 14);
   if(atrHandle == INVALID_HANDLE)
   {
      Print("iATRハンドルの作成に失敗: ", GetLastError());
      return(INIT_FAILED);
   }
   return(INIT_SUCCEEDED);
}

void OnTick()
{
   double atr[];
   ArraySetAsSeries(atr, true);              // [0]を最新足にそろえる
   if(CopyBuffer(atrHandle, 0, 0, 2, atr) < 2) return;

   double atrPrice = atr[0];                 // 値幅(価格)
   double atrPips  = atrPrice / _Point / 10; // pips換算(5/3桁口座の目安)

   Comment("ATR(14) = ", DoubleToString(atrPrice, _Digits),
           "  (約 ", DoubleToString(atrPips, 1), " pips)");
}

void OnDeinit(const int reason)
{
   IndicatorRelease(atrHandle);              // 後始末
}
pips換算は口座の桁数に注意
多くのFX口座は「1pip = 10 ポイント」(5桁・3桁表示)なので _Point / 10 で割ります。一方、ゴールドのような銘柄では「pips」の定義がブローカーで異なります。迷ったらATRは“価格の値幅”のままSLやロット計算に使うのが安全です。

サンプル②:ATRでSL・TPの幅を決めて発注する

「いつも固定で20pips」ではなく、ATR × 倍率でSL/TPを決めると、荒い相場では広く・穏やかな相場では狭く、自動で適応します。確定足のATRを使います。

コピペOK(デモ口座で動作確認用)

#include <Trade\Trade.mqh>
CTrade trade;
input double InpLot    = 0.10;
input double InpSlMult = 1.5;   // SL = ATR × 1.5
input double InpTpMult = 2.0;   // TP = ATR × 2.0

void OpenBuyWithAtrStops()
{
   double atr[];
   ArraySetAsSeries(atr, true);
   if(CopyBuffer(atrHandle, 0, 0, 2, atr) < 2) return;

   double atrVal = atr[1];                   // 確定足のATR(価格の値幅)
   double slDist = atrVal * InpSlMult;
   double tpDist = atrVal * InpTpMult;

   double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   double sl  = NormalizeDouble(ask - slDist, _Digits);
   double tp  = NormalizeDouble(ask + tpDist, _Digits);

   trade.Buy(InpLot, _Symbol, 0.0, sl, tp);  // ATR基準のSL/TP付きで買い
}

サンプル③:ATRからロットを逆算する(リスク%固定)

SL幅をATRで決めるなら、「1トレードで失う額を資金の◯%に固定」するロット計算と相性が抜群です。SLが広い相場では自動でロットが小さくなり、損失額が一定に保たれます。

コピペOK

input double InpRiskPct = 1.0;   // 1トレードのリスク(残高に対する%)
input double InpSlMult  = 1.5;   // SL = ATR × 倍率

double NormalizeLot(double lot)
{
   double mn = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
   double mx = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
   double st = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
   if(st > 0) lot = MathRound(lot / st) * st;
   if(lot < mn) lot = mn;
   if(lot > mx) lot = mx;
   return(lot);
}

double AtrRiskLot()
{
   double atr[];
   ArraySetAsSeries(atr, true);
   if(CopyBuffer(atrHandle, 0, 0, 2, atr) < 2) return(0.0);

   double slDist  = atr[1] * InpSlMult;                 // SL幅(価格)
   double tickVal = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
   double tickSz  = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
   if(slDist <= 0.0 || tickVal <= 0.0 || tickSz <= 0.0) return(0.0);

   double riskAmt    = AccountInfoDouble(ACCOUNT_BALANCE) * InpRiskPct / 100.0;
   double lossPerLot = (slDist / tickSz) * tickVal;     // 1ロットあたりの損失額
   return NormalizeLot(riskAmt / lossPerLot);
}

サンプル④:ATRトレーリングストップ

保有中の買いポジションのSLを、現在値 − ATR × 倍率で追いかけます。値動きが荒くなればトレール幅も自動で広がり、ノイズで切られにくくなります(チャンデリア・ストップの考え方)。

コピペOK(デモ口座で動作確認用)

input double InpTrailMult = 2.0;   // トレール幅 = ATR × 2.0

void AtrTrailing()
{
   double atr[];
   ArraySetAsSeries(atr, true);
   if(CopyBuffer(atrHandle, 0, 0, 2, atr) < 2) return;

   double dist = atr[0] * InpTrailMult;
   double bid  = SymbolInfoDouble(_Symbol, SYMBOL_BID);

   for(int i = PositionsTotal() - 1; i >= 0; i--)
   {
      ulong ticket = PositionGetTicket(i);
      if(!PositionSelectByTicket(ticket)) continue;
      if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
      if(PositionGetInteger(POSITION_TYPE) != POSITION_TYPE_BUY) continue;

      double curSL = PositionGetDouble(POSITION_SL);
      double tp    = PositionGetDouble(POSITION_TP);
      double newSL = NormalizeDouble(bid - dist, _Digits);

      // 利益方向にだけ動かす(SLを下げ戻さない)
      if(newSL > curSL && newSL < bid)
         trade.PositionModify(ticket, newSL, tp);
   }
}
これは学習用の最小サンプルです
エントリー条件・スプレッド確認・売りポジション側の処理などは省いています。実運用では、ストップレベル(最小SL距離)の確認や、確定足での更新なども検討してください。まずはデモ口座で動きを確認しましょう。

ATRを使うときの注意・落とし穴

① ATRは「方向」を示さない
ATRが上がっても、それは「動きが激しくなった」だけで、上昇か下落かは分かりません。エントリーの向きは iMAiRSI などサイン系で決め、ATRはあくまで幅・リスクの調整に使うのが正しい役割分担です。
② ATRは「価格の値幅」。pips・通貨額と混同しない
取り出した値はそのまま価格差です。SL価格=ask − ATR×倍率 のように価格にそのまま足し引きできます。pipsや損益額に直す時だけ、口座の桁数・ティック価値を使って換算してください。
③ お決まりの3点
ハンドルは OnInit() で1回だけ/CopyBuffer() の戻り値を必ずチェック/ArraySetAsSeries(配列, true) を忘れない。これはどのインジでも共通です。

まとめ

  • iATR() はATRのハンドルを返す。値は CopyBuffer() で取り出す(バッファ番号0・適用価格の引数なし)
  • ATRは「どれだけ動くか」だけを測る指標。方向は出さない=サイン系と役割分担する
  • 取り出した値は価格の値幅。SL/TPは 価格 ± ATR×倍率 でそのまま付けられる
  • ATRでSL幅を決め、リスク%固定でロットを逆算すると、損失額を一定に保てる(サンプル③)
  • トレール幅をATR連動にすると、荒い相場でノイズ切られしにくい(サンプル④)
タイトルとURLをコピーしました