バックテストの結果が実運用の成績と大きくズレて、
手法そのものを疑ってしまったことはないでしょうか。
原因の1つとして見落とされがちなのが、
ブローカーが提供するヒストリカルデータ自体に欠落や、
出来高が0のまま記録されたバーが混ざっているケースです。
この記事では、MQL4のiVolume関数で出来高を1本ずつ確認する方法と、
CopyRates関数で四本値・出来高をまとめて配列取得し、
欠落の疑いを自動検出するスクリプトの作り方を解説します。
・iVolumeで出来高を1本ずつ取得する方法
・CopyRatesで四本値をまとめて配列取得する方法
・ヒストリカルデータの欠落を自動検出する設計
・MQL4におけるCopyRatesの立ち位置
なぜヒストリカルデータの欠落に気づく必要があるのか
まず、この確認がなぜ必要なのかを確認します。
欠落があるとバックテストの信頼性が落ちる
本来存在するはずのバーが抜けていたり、出来高0のまま記録されていたりすると、
ストラテジーテスターの計算がそのバーで不自然になり、結果全体の信頼性が下がります。
ブローカーを乗り換えた直後に起こりやすい
ヒストリーセンターにまだ全期間のデータがダウンロードされていない状態で、
古い日付のチャートを開くと、一部の期間だけデータが粗くなっていることがあります。
目視では気づきにくい
出来高0のバーやわずかな時刻のズレは、チャートを目で見ているだけでは気づきにくく、
スクリプトで機械的に検出する必要があります。
iVolumeで出来高を1本ずつ取得する
まずは最も基本的なiVolume関数の使い方を確認します。
iVolume関数の書式
long iVolume( string symbol, //通貨ペア(NULLで現在のチャートの通貨ペア) int timeframe, //時間足(0で現在のチャートの時間足) int shift //バーのシフト(0が現在のバー) );
戻り値の型はlongで、
そのバーのティックボリューム(約定を伴う価格変化の回数)を返します。
出来高が0のバーは「データが無い」合図
・ティックボリュームは実際の取引量ではない
・価格が何回動いたかの回数が記録される
・0は値動きが1度も記録されていないことを示す
iVolumeだけでは1本ずつしか確認できない
iVolumeはshiftを指定して1本ずつ呼ぶ関数のため、
まとまった本数を確認したい場合はループで回す必要があり、
配列として一括取得する方法も合わせて知っておくと効率的です。
CopyRatesで四本値と出来高をまとめて取得する
CopyRates関数の仕様を確認します。
CopyRatesの書式
int CopyRates( string symbol_name, //通貨ペア int timeframe, //時間足 int start_pos, //開始位置(シフト) int count, //取得する本数 MqlRates rates_array[] //取得結果を格納する配列 );
戻り値はコピーされた本数で、失敗した場合は-1を返します。
MqlRates構造体のメンバー
| メンバー | 型 | 内容 |
|---|---|---|
| time | datetime | バーの開始時刻 |
| open/high/low/close | double | 四本値 |
| tick_volume | long | ティックボリューム |
| real_volume | long | 実出来高(多くのFXブローカーでは0) |
| spread | int | スプレッド |
CopyRatesはMQL4で後から追加された関数である
MQL4は元々、
iOpen/iClose/iHigh/iLow/iVolumeという、
「1本ずつ取得する」関数群が主流でした。
CopyRatesはMQL5との構文統一のために後から追加された、
配列一括取得用の関数です。
既刊copyrates-mql5.mdとの違い
MQL5版の記事と何が違うのかを明確にします。
MQL5はCopyRatesが標準の取得方法
既刊記事で解説した通り、MQL5にはiOpen/iClose相当の単純な関数が無く、
CopyRates系が標準の取得方法として設計されています。
MQL4はCopyRatesが「追加の選択肢」
MQL4では今でもiOpen等が使えるため、
CopyRatesは「複数本をまとめて処理したいときに使う、
追加の選択肢」という位置づけになります。
本記事ならではの着眼点
本記事は関数の使い方そのものではなく、
CopyRatesで取得した配列を使ってヒストリカルデータの品質を検査するという、
一歩進んだ活用方法に焦点を当てています。
欠落バーを検出する設計
実際に検出ロジックを組み立てます。
出来高0のバーを数える
if(rates[i].tick_volume == 0) zeroVolumeCount++;
配列の各要素のtick_volumeが0であれば、カウンターを増やすだけの単純な判定です。
時刻の間隔が不自然に開いていないか調べる
long diff = rates[i].time - rates[i + 1].time; if(diff > periodSeconds * 2) gapCount++;
隣接するバーの時刻差が、その時間足本来の間隔の2倍を超えていたら、
欠落の疑いがあると判定します。
PeriodSecondsで時間足を秒数に変換する
PeriodSeconds()は指定した時間足を、
秒数(例: PERIOD_H1なら3600)に変換する関数で、
時間足ごとに期待される間隔を計算する際に使います。
実際に動くコード全体
ここまでの要素をまとめた、実際に動くスクリプトです。
//+------------------------------------------------------------------+
//| HistoricalDataGapCheck.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 script_show_inputs
input int CheckBars = 200; //確認する本数
void OnStart()
{
MqlRates rates[];
ArraySetAsSeries(rates, true);
int copied = CopyRates(_Symbol, PERIOD_CURRENT, 0, CheckBars, rates);
if(copied <= 0)
{
Print("CopyRatesに失敗しました。エラーコード=", GetLastError());
return;
}
int zeroVolumeCount = 0;
int gapCount = 0;
int periodSeconds = PeriodSeconds(PERIOD_CURRENT);
for(int i = 0; i < copied - 1; i++)
{
if(rates[i].tick_volume == 0)
zeroVolumeCount++;
long diff = rates[i].time - rates[i + 1].time;
if(diff > periodSeconds * 2)
{
gapCount++;
Print("欠落の疑い: ", TimeToString(rates[i + 1].time),
" と ", TimeToString(rates[i].time), " の間");
}
}
Print("取得本数=", copied,
" 出来高0の本数=", zeroVolumeCount,
" 欠落の疑いの箇所=", gapCount);
//iVolumeで直近5本だけを個別にも確認しておく
for(int s = 0; s < 5 && s < copied; s++)
{
Print("iVolume shift=", s, " volume=", iVolume(_Symbol, PERIOD_CURRENT, s));
}
}
MetaEditorでの実行手順
実際にMT4で動かすまでの手順を確認します。
- MetaEditorで新規スクリプト「HistoricalDataGapCheck」を作成し、上のコードを貼り付けてコンパイルする
- エラー・警告が0件であることを確認する
- 確認したい通貨ペア・時間足のチャートにスクリプトをドラッグして実行する
- 「エキスパート」タブで、取得本数・出来高0の本数・欠落の疑いの箇所数を確認する
- 欠落が多い場合は、ヒストリーセンターからデータを再ダウンロードして再確認する
よくあるエラーと注意点
実装でつまずきやすいポイントをまとめます。
CopyRatesが-1を返す
・通貨ペアや時間足の履歴が未取得の可能性がある
・一度チャートを開いてデータを読み込ませる
・GetLastError()で詳細なコードも確認する
ECN系ブローカーでtick_volumeの意味が違う
一部のブローカーでは出来高の記録方式が異なり、
0ではないものの実態を反映していないことがあるため、
0件かどうかだけで品質を断定せず、傾向として参考にする姿勢が安全です。
MQL5との違い
このコードのCopyRatesはMQL5と同じ書式です。
MQL5ではiVolumeの代わりに、
CopyTickVolumeという専用関数も用意されている点が、
MQL4との違いです。
よくある質問
ヒストリカルデータの確認について、よくある疑問に回答します。
欠落が見つかったらどう対処すればいいですか?
MT4の「ツール」→「ヒストリーセンター」から、
該当する通貨ペア・時間足のデータを一度削除し、
インターネット接続を確認した上で再ダウンロードすると改善することが多いです。
改善しない場合は、別のVPSやブローカーのサーバーから再取得することも検討してください。
CheckBarsの本数はどれくらいにすればいいですか?
バックテストで使う期間と同じ本数を確認するのが基本です。
数年分のバックテストを行う場合は、一度に大きな本数を指定せず、
期間を分けて複数回実行すると処理が軽くなります。
このチェックだけでバックテストの信頼性は保証されますか?
保証はできません。
本記事のチェックはヒストリカルデータの「明らかな欠落」を見つける一次確認に過ぎず、
スプレッドの再現性やスリッページなど、バックテストと実運用のズレには他の要因も関わります。
【MQL4】iVolumeとCopyRatesで過去の出来高・四本値をまとめて取得し、ヒストリカルデータの欠落を確認する方法について解説してみた:まとめ
バックテストと実運用の結果がズレる原因の1つに、ヒストリカルデータ自体の欠落があります。
iVolumeで出来高を1本ずつ確認する方法と、
CopyRatesで四本値・出来高をまとめて配列取得する方法を、
組み合わせることで、
出来高0のバーや時刻の不自然な間隔を機械的に検出できます。
CopyRatesはMQL4では後から追加された「追加の選択肢」
であり、既存のiOpen系関数と役割を分けて覚えておくと理解しやすくなります。
コメント