「このペアは東京時間よりロンドン時間のほうが動く」
といった話を聞いたことがあっても、
実際にどれくらい違うのかを自分のチャートで確認する手段はありませんでした。
この記事では、東京・ロンドン・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でのコンパイル・動作確認手順
実際に動かすまでの手順です。
- MetaEditorで新規インジケーター「SessionRangeComparisonIndicator」を作成し、上のコードを貼り付けてコンパイルする
- 「0 error(s), 0 warning(s)」になることを確認する
- 任意のチャートに適用し、InpSymbolsに指定した通貨ペアがMarket Watchに自動で追加されることを確認する
- 数十秒待ち、各通貨ペア・各セッションの平均値幅(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で
通貨ペアごとに異なる桁数を正しく扱う設計にしています。
既刊の陽線出現率・スプレッド記録とは別の『値幅』という視点から、
どの時間帯にどの通貨ペアが動きやすいかを確認できるツールとしてまとめました。
くわしい自動化の開示方針は編集方針ページもご覧ください。
コメント