MQLノーリペイント設計の核心である「確定足参照」と「バッファ管理」をコード例付きで解説。リペイントしないインジケーターを作る技術でバックテスト詐欺を見抜き、自作EAの信頼性を高める。
・リペイントとは過去足のシグナルを書き換えること——バックテスト詐欺の根本原因
・ノーリペイントの核心:confirmed=true(確定足のみ参照)
・iCustom参照はバッファ[1](1本前の確定足)を使う
・バッファ[0](現在足)はリアルタイム変動するため使用禁止
・実装コード例をそのままコピーして使える
リペイントとは何か
リペイント(Repaint)とは、インジケーターが過去のバーのシグナルを後から書き換える現象を指します。チャート上では「この足でシグナルが出た」と見えても、実際にはリアルタイムでは存在せず、後から都合よく加筆されたものです。
バックテスト詐欺との関係
リペイントはバックテスト詐欺の根本原因です。リペイントするインジケーターをバックテストに使うと、実際には発生し得なかった「完璧なエントリーポイント」が過去チャートに表示されます。結果として、バックテストのパフォーマンスは異常に良く見えますが、実運用では一切再現されません。
「驚異の勝率95%」を謳うインジケーターの多くは、このリペイントを利用したものです。STARKが14年のトレードで培った原則:バックテスト結果を信じる前に、まずリペイントの有無を確認する。
| リペイントするインジケーターとしないインジケーターの違い | |
|---|---|
| リペイントあり | 現在足確定前にシグナルが出て、後から消える or 移動する |
| ノーリペイント | 前の足が確定してからシグナルが出て、以後変化しない |
| バックテスト影響 | リペイントあり→結果が過剰に良く見える |
| 実運用影響 | リペイントあり→バックテストと全く異なる結果になる |
ノーリペイント設計の3原則
原則1:確定足のみを参照する(confirmed=true)
MQL4ではiCustom関数でカスタムインジケーターを参照する際、最後の引数でシフトを指定します。シフト=0は現在足(未確定)、シフト=1は1本前の確定足です。ノーリペイント設計の第一原則は、常にシフト=1(確定済みの足)を参照することです。
「confirmed=true」という考え方は、現在形成中のバーを無視し、完全に確定したバーのみを意思決定に使うという設計思想です。
原則2:バッファ[1]を使う
インジケーターのバッファ設計でも同じ原則が適用されます。Buffer[0]は現在足(リアルタイム変動)、Buffer[1]は1本前の確定足です。シグナル判定には必ずBuffer[1]を使います。
原則3:OnCalculateでの条件分岐
インジケーター内部でも、計算ロジックを確定足のみに限定するコードが必要です。rates_total - prev_calculatedの処理で、未確定の現在バー(インデックス0)をスキップする設計にします。
MQL4実装コード例
EAからiCustomを正しく参照する
以下はEAでカスタムインジケーターのシグナルを参照する際の正しい実装例です。シフト=1で確定足を参照し、ノーリペイントを保証します。
// ノーリペイント参照の正しい実装
// iCustom(symbol, timeframe, indicator_name, param1, buffer_index, shift)
// 悪い例(リペイントする):シフト=0で現在足を参照
double signal_bad = iCustom(NULL, 0, "MyIndicator", 14, 0, 0);
// 正しい例(ノーリペイント):シフト=1で確定足を参照
double signal_good = iCustom(NULL, 0, "MyIndicator", 14, 0, 1);
// 複数バッファを使うインジケーターの場合
// buffer_index=0がメインバッファ、=1がサブバッファなど
double main_buf = iCustom(NULL, 0, "MyIndicator", 14, 0, 1); // メインバッファの確定足
double signal_buf = iCustom(NULL, 0, "MyIndicator", 14, 1, 1); // シグナルバッファの確定足
// エントリー条件(必ず確定足で判定)
if (signal_good > 0 && IsNewBar()) {
// エントリーロジック
}
インジケーター内部のノーリペイント設計
インジケーター本体でも、OnCalculate関数内で未確定バーへの書き込みを防ぐ設計が必要です。
// ノーリペイントインジケーターのOnCalculate実装例
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[])
{
// 計算の開始インデックス
// rates_total-1が最新バー(インデックス0)
// 最新の未確定バー(i=0)をスキップするため limit を rates_total-1 まで
int start;
if (prev_calculated == 0)
start = MaPeriod; // 初回計算
else
start = prev_calculated - 1; // 前回の最終バーから再計算(リペイント防止)
// インデックス0(現在形成中バー)への書き込みを禁止する場合
// ループをi=1から開始するか、i=0への書き込みをスキップする
for (int i = start; i < rates_total - 1; i++) {
// 確定済みバーのみ計算・書き込み
// ※ rates_total-1(最新バー)はスキップ
SignalBuffer[i] = CalculateSignal(i, close);
}
// 最新バーは EMPTY_VALUE を明示的にセット(未確定のため表示しない)
SignalBuffer[rates_total - 1] = EMPTY_VALUE;
return rates_total;
}
新しいバーの検知(IsNewBar)
EAでシグナルを判定する際は、必ず新しいバーが開いたタイミングのみで判定するロジックが必要です。毎ティックで判定するとリペイントの影響を受けやすくなります。
// 新バー検知関数(必須パーツ)
bool IsNewBar()
{
static datetime last_bar_time = 0;
datetime current_bar_time = iTime(NULL, 0, 0);
if (current_bar_time != last_bar_time) {
last_bar_time = current_bar_time;
return true; // 新しいバーが開いた
}
return false; // 同じバー内のティック
}
// OnTick内での使い方
void OnTick()
{
if (!IsNewBar()) return; // 新バー以外は何もしない
// 1本前の確定足でシグナルを確認
double signal = iCustom(NULL, 0, "MyIndicator", 14, 0, 1);
if (signal > 0) {
// 確定足でのシグナルのみでエントリー
}
}
リペイントを見抜く実践的チェック方法
チャート上でのリペイント確認
インジケーターをチャートに表示した状態で、過去に遡ってシグナルが変化していないか確認します。具体的には、チャートを動かさずに新しいティックが入ってきた際に、過去のシグナルが移動・消滅・追加されていないかを観察します。
バックテストとフォワードテストの乖離確認
バックテストで非常に高い成績が出ても、同じ設定でデモ口座(フォワードテスト)に移すと全く異なる結果になる場合はリペイントを強く疑うべきです。この乖離こそがリペイントの最も分かりやすいサインです。
| リペイント診断チェックリスト | |
|---|---|
| 過去シグナルが変化する | リペイントあり |
| BT勝率80%超でFT勝率50%未満 | リペイント疑い濃厚 |
| シフト=0で参照している | リペイントの可能性 |
| BTとFTの乖離が大きい | リペイント要確認 |
MT4 EA運用に推奨するブローカー
ノーリペイント設計のEAを実運用する際は、MT4対応でティックデータ品質が高いブローカーが不可欠です。XMはMT4を正式サポートし、デモ口座も豊富なためMQL4開発の検証環境として最適です。
MT4正式対応・デモ口座無制限・EA稼働率が高い安定したサーバー環境。ノーリペイント設計の検証から実運用まで一貫して使える。






