「このペアは東京時間よりロンドン時間のほうが動く」

といった話を聞いたことがあっても、

実際にどれくらい違うのかを自分のチャートで確認する手段はありませんでした。

この記事では、東京・ロンドン・NYの各セッションの平均値幅(pips)を、

複数の通貨ペアで横並びに比較表示するインジケーターを作ります。

この記事で分かること
・セッションごとの時間帯を判定する方法(日付をまたぐNY時間を含む)
・自分のチャートに表示していない通貨ペアのデータを取得する方法
・通貨ペアごとに異なるPoint/Digitsを正しく扱う方法
・複数ペア×複数セッションの比較表をパネル表示する実装

設計思想: なぜ「値幅」で比較するのか

まず、このインジケーターが何を計算するのかを整理します。

既刊2記事との違い

記事 対象 見ている指標
既刊seasonality-indicator.md 単一ペア 時間帯別の陽線出現率
既刊spread-logger-indicator-mt4.md 単一ペア スプレッド(売買コスト)の記録
本記事 複数ペア セッション別の値幅(高値-安値)

「値幅」を選んだ理由

補足
・陽線出現率は方向のクセを見る指標
・スプレッドはコストを見る指標
・値幅(レンジ)は『そのセッションがどれだけ動いたか』という
・勢いそのものを見る指標で、この3つはどれも別の視点

3つのセッションの定義

セッション サーバー時間(例) 日付をまたぐか
東京 9時〜15時 またがない
ロンドン 16時〜24時 またがない
NY 21時〜翌5時 またぐ
注意
・この時間帯はブローカーのサーバー時間を基準にした一例
・サーバーのGMTオフセットはブローカーごとに異なるため
・実際の東京・ロンドン・NY時間とズレる場合がある
・入力パラメータで必ず自分のブローカーに合わせて調整すること

セッション判定とデータ取得の下準備

複数の通貨ペアを扱うための下準備を行います。

日付をまたぐセッションの判定

bool IsInSession(int hour, int startHour, int endHour)
  {
   if(startHour < endHour)
      return(hour >= startHour && hour < endHour);
   else
      return(hour >= startHour || hour < endHour); // 日付をまたぐ場合(NY等)
  }

開始時刻が終了時刻より大きい場合(21時〜5時等)は、

日付をまたぐセッションとして、OR条件で判定します。

SymbolSelectで他の通貨ペアのデータを有効にする

if(!SymbolSelect(sym, true))
  {
   Print("シンボルを選択できません: ", sym);
   continue;
  }

チャートに表示していない通貨ペアのデータを取得するには、

事前にSymbolSelectでMarket Watchに追加しておく必要があります。

シンボルごとのPoint/Digitsを取得する

double PointAdjustFor(string symbol)
  {
   double point = MarketInfo(symbol, MODE_POINT);
   int digits = (int)MarketInfo(symbol, MODE_DIGITS);
   if(digits == 3 || digits == 5)
      point *= 10;
   return(point);
  }
注意
・_Point/_Digitsは、このインジケーターが適用されているチャート自身の
・通貨ペアの値しか返さない
・他の通貨ペアを計算するときは
・必ずMarketInfo(symbol, MODE_POINT)の
・ように対象のsymbolを指定して取得する
・(既刊currency-strength-meter-indicator-mt4.mdと
・同じ落とし穴)

セッションごとの平均値幅を計算する

過去N日分のH1バーから、セッションごとの平均値幅を計算します。

連続する同一セッションのバーをまとめて1回分とする

int j = i;
while(j < totalBars && IsInSession(TimeHour(iTime(symbol, PERIOD_H1, j)), startHour, endHour))
  {
   sessionHigh = MathMax(sessionHigh, iHigh(symbol, PERIOD_H1, j));
   sessionLow  = MathMin(sessionLow,  iLow(symbol, PERIOD_H1, j));
   j++;
  }

セッション時間帯に該当するH1バーが連続している間は、

同じ1回のセッションとしてまとめて高値・安値を追跡します。

1回分の値幅をpipsに変換して積算する

sumRange += (sessionHigh - sessionLow) / point;
dayCount++;

見つかったセッションの回数(dayCount)が

InpLookbackDaysに達するまで、

過去に向かって繰り返します。

平均値幅を返す

if(dayCount == 0)
   return(0);
return(sumRange / dayCount);

該当するセッションが1回も見つからない場合は0を返し、

比較表には「データ不足」として扱います。

複数ペア×複数セッションの比較表を表示する

計算結果を、通貨ペアごとに1行のパネルで表示します。

カンマ区切りの入力文字列を配列に分割する

string symbols[];
int symCount = StringSplit(InpSymbols, ',', symbols);

InpSymbolsに”EURUSD,GBPUSD,USDJPY”のように入力すると、

StringSplitで通貨ペアごとの配列に分割できます。

1行に3セッション分の値幅を並べて表示する

string line = sym + "  東京:" + DoubleToString(tokyo, 1) + "pips  " +
              "ロンドン:" + DoubleToString(london, 1) + "pips  " +
              "NY:" + DoubleToString(ny, 1) + "pips";

実際に動くコード全体

ここまでの要素をまとめた、実際にMetaEditorでコンパイルできるコードです。

//+------------------------------------------------------------------+
//|                              SessionRangeComparisonIndicator.mq4  |
//|                     Copyright 2026, FX-EA System Project Creator |
//|                        https://creator.fx-ea-system-project.com/ |
//+------------------------------------------------------------------+
#property copyright "Copyright 2026, FX-EA System Project Creator"
#property link      "https://creator.fx-ea-system-project.com/"
#property version   "1.00"
#property strict
#property indicator_chart_window
#property indicator_buffers 0

input string InpSymbols        = "EURUSD,GBPUSD,USDJPY,AUDUSD"; //比較する通貨ペア(カンマ区切り)
input int    InpLookbackDays   = 10; //各セッションを何回分さかのぼって平均するか
input int    InpTokyoStart     = 9;  //東京セッション開始(サーバー時間)
input int    InpTokyoEnd       = 15; //東京セッション終了(サーバー時間)
input int    InpLondonStart    = 16; //ロンドンセッション開始(サーバー時間)
input int    InpLondonEnd      = 24; //ロンドンセッション終了(サーバー時間)
input int    InpNYStart        = 21; //NYセッション開始(サーバー時間、日付をまたぐ)
input int    InpNYEnd          = 5;  //NYセッション終了(サーバー時間、日付をまたぐ)
input int    InpPanelX         = 10; //パネルのX座標(ピクセル)
input int    InpPanelY         = 20; //パネルのY座標(ピクセル)

double PointAdjustFor(string symbol)
  {
   double point = MarketInfo(symbol, MODE_POINT);
   int digits = (int)MarketInfo(symbol, MODE_DIGITS);
   if(digits == 3 || digits == 5)
      point *= 10;
   return(point);
  }

bool IsInSession(int hour, int startHour, int endHour)
  {
   if(startHour < endHour)
      return(hour >= startHour && hour < endHour);
   else
      return(hour >= startHour || hour < endHour);
  }

double ComputeAverageSessionRange(string symbol, int startHour, int endHour, int lookbackDays)
  {
   double point = PointAdjustFor(symbol);
   int totalBars = iBars(symbol, PERIOD_H1);
   double sumRange = 0;
   int dayCount = 0;

   int i = 0;
   while(i < totalBars && dayCount < lookbackDays)
     {
      int hour = TimeHour(iTime(symbol, PERIOD_H1, i));

      if(IsInSession(hour, startHour, endHour))
        {
         double sessionHigh = iHigh(symbol, PERIOD_H1, i);
         double sessionLow  = iLow(symbol, PERIOD_H1, i);
         int j = i;
         while(j < totalBars && IsInSession(TimeHour(iTime(symbol, PERIOD_H1, j)), startHour, endHour))
           {
            sessionHigh = MathMax(sessionHigh, iHigh(symbol, PERIOD_H1, j));
            sessionLow  = MathMin(sessionLow,  iLow(symbol, PERIOD_H1, j));
            j++;
           }
         sumRange += (sessionHigh - sessionLow) / point;
         dayCount++;
         i = j;
        }
      else
        {
         i++;
        }
     }

   if(dayCount == 0)
      return(0);
   return(sumRange / dayCount);
  }

void ClearPanel()
  {
   for(int i = ObjectsTotal(0, -1, OBJ_LABEL) - 1; i >= 0; i--)
     {
      string name = ObjectName(0, i, -1, OBJ_LABEL);
      if(StringFind(name, "SRCI_") == 0)
         ObjectDelete(0, name);
     }
  }

void DrawPanel()
  {
   ClearPanel();

   ObjectCreate(0, "SRCI_Title", OBJ_LABEL, 0, 0, 0);
   ObjectSetInteger(0, "SRCI_Title", OBJPROP_CORNER, CORNER_LEFT_UPPER);
   ObjectSetInteger(0, "SRCI_Title", OBJPROP_XDISTANCE, InpPanelX);
   ObjectSetInteger(0, "SRCI_Title", OBJPROP_YDISTANCE, InpPanelY);
   ObjectSetString(0, "SRCI_Title", OBJPROP_TEXT,
                   "セッション別平均値幅(過去" + (string)InpLookbackDays + "回分)");
   ObjectSetInteger(0, "SRCI_Title", OBJPROP_COLOR, clrWhite);

   string symbols[];
   int symCount = StringSplit(InpSymbols, ',', symbols);

   int row = 0;
   for(int s = 0; s < symCount; s++)
     {
      string sym = symbols[s];
      StringTrimLeft(sym);
      StringTrimRight(sym);
      if(sym == "")
         continue;

      if(!SymbolSelect(sym, true))
        {
         Print("シンボルを選択できません: ", sym);
         continue;
        }

      double tokyo  = ComputeAverageSessionRange(sym, InpTokyoStart,  InpTokyoEnd,  InpLookbackDays);
      double london = ComputeAverageSessionRange(sym, InpLondonStart, InpLondonEnd, InpLookbackDays);
      double ny     = ComputeAverageSessionRange(sym, InpNYStart,     InpNYEnd,     InpLookbackDays);

      string line = sym + "  東京:" + DoubleToString(tokyo, 1) + "pips  " +
                    "ロンドン:" + DoubleToString(london, 1) + "pips  " +
                    "NY:" + DoubleToString(ny, 1) + "pips";

      string objName = "SRCI_Row" + (string)row;
      ObjectCreate(0, objName, OBJ_LABEL, 0, 0, 0);
      ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_LEFT_UPPER);
      ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, InpPanelX);
      ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, InpPanelY + 16 * (row + 1));
      ObjectSetString(0, objName, OBJPROP_TEXT, line);
      ObjectSetInteger(0, objName, OBJPROP_COLOR, clrWhite);
      row++;
     }
  }

int OnInit()
  {
   return(INIT_SUCCEEDED);
  }

int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
   // 複数銘柄のヒストリカルデータを毎tick再計算するため、意図的に新バー判定はせず
   // 一定間隔(60秒)だけ再描画を間引く
   static datetime lastDraw = 0;
   if(TimeCurrent() - lastDraw < 60)
      return(rates_total);
   lastDraw = TimeCurrent();

   DrawPanel();
   return(rates_total);
  }

void OnDeinit(const int reason)
  {
   ClearPanel();
   ObjectDelete(0, "SRCI_Title");
  }
//+------------------------------------------------------------------+

60秒おきに最新のデータへ更新し、複数ペアの比較表を常に表示する構成です。

MetaEditorでのコンパイル・動作確認手順

実際に動かすまでの手順です。

  1. MetaEditorで新規インジケーター「SessionRangeComparisonIndicator」を作成し、上のコードを貼り付けてコンパイルする
  2. 「0 error(s), 0 warning(s)」になることを確認する
  3. 任意のチャートに適用し、InpSymbolsに指定した通貨ペアがMarket Watchに自動で追加されることを確認する
  4. 数十秒待ち、各通貨ペア・各セッションの平均値幅(pips)が表示されることを確認する

よくあるエラーと注意点

実装・運用でつまずきやすいポイントをまとめます。

適用直後は「データ不足」のまま0pipsが表示される

注意
・SymbolSelectで追加した直後は、ヒストリカルデータの
・ダウンロードが完了していないことがある
・この場合、iBars()が少なく、dayCountが0のまま0pipsが表示される
・数十秒〜数分待ってから再度確認すること

サーバー時間が実際の東京・ロンドン・NY時間とズレる

ブローカーのサーバー時間は、

GMT+2やGMT+3など各社まちまちです。

InpTokyoStart等のデフォルト値は一例に過ぎないため、

自分のブローカーの実際のサーバー時間を確認し、調整してください。

通貨ペアを増やすほど計算に時間がかかる

このインジケーターは1ペアごとに過去データをさかのぼって計算するため、

InpSymbolsに指定する通貨ペアを増やすほど、

1回の再描画にかかる時間が長くなります。

比較したいペアを絞ることを推奨します。

よくある質問

セッション別値幅比較インジケーターについて、よくある疑問に回答します。

このインジケーターは発注まで行いますか?

行いません。

チャートに比較表を表示するだけで、注文は一切出しません。

日足や週足の値幅と比較できますか?

このインジケーター自体はH1(1時間足)データを使っていますが、

iHigh/iLowのtimeframe引数を

PERIOD_D1やPERIOD_W1に変更すれば、

同じ考え方で日足・週足ベースの計算に変更できます。

4つより多い通貨ペアを比較できますか?

できます。

InpSymbolsにカンマ区切りで追加すれば、

StringSplitが自動で配列に分割するため、

通貨ペア数の上限はコード側にはありません。

ただし前述のとおり、増やすほど計算時間が長くなります。

【MQL4】東京・ロンドン・NY、どの時間帯がよく動く?複数通貨ペアの値幅を比較表示するインジケーターの作り方:まとめ

この記事では、東京・ロンドン・NYの各セッションの平均値幅(pips)を、

複数の通貨ペアで横並びに比較表示するインジケーターを解説しました。

SymbolSelectで他の通貨ペアのデータを有効にし、

MarketInfo(symbol, MODE_POINT)/MODE_DIGITSで

通貨ペアごとに異なる桁数を正しく扱う設計にしています。

既刊の陽線出現率・スプレッド記録とは別の『値幅』という視点から、

どの時間帯にどの通貨ペアが動きやすいかを確認できるツールとしてまとめました。

くわしい自動化の開示方針は編集方針ページもご覧ください。

関連記事