この記事でわかること
- リペイントとは何か、なぜバックテスト結果が信用できなくなるのか
- リペイントが起きる3つの原因と、それぞれの見分け方
request.security()でリペイントを防ぐ正しいコードの書き方
「バックテストでは勝率80%だったのに、実際に動かしたら全然シグナルが違う」。マルチタイムフレームのインジケーターを自作した人が必ず一度はぶつかるのが、このリペイントという問題です。
原因の多くはrequest.security()の使い方にあります。この記事では、リペイントが起きる仕組みと、そのまま貼り付けて使える回避コードを解説します。Pine Script v5・v6の両方で共通する内容です。
📊 この記事はTradingViewユーザー向けです
TradingViewをまだ使っていない方は、まず無料アカウントを作成してみましょう。登録は30秒、クレジットカード不要です。
無料でTradingViewを始める →リペイントとは何か
リペイントとは、過去のバーに表示されるシグナルや値が、後から書き換わってしまう現象のことです。
チャートを開いた瞬間は「3本前のローソク足で買いシグナルが出ていた」ように見えるのに、時間が経ってチャートを開き直すと、そのシグナルが消えていたり別の位置に移動していたりします。これでは、バックテスト(過去検証)で見えていた好成績が、リアルタイムでは再現できません。
やっかいなのは、リペイントするインジケーターほど過去チャート上では「よく当たっているように見える」点です。未来の情報を使って過去を描いているので、当たって見えるのは当然なのです。
リペイントが起きる3つの原因
| 原因 | 内容 |
|---|---|
| 未確定バーの値を使う | 形成途中のローソク足の終値などを条件に使うと、確定までに値が変わる |
| 上位足の未来参照 | request.security()で上位足の未確定・未来の値を持ってきてしまう |
| 未来関数の使用 | ZigZagやfractalなど、確定に数本先の情報が必要な描画 |
この中で最も多く、かつ気づきにくいのが2番目のrequest.security()によるものです。順番に見ていきます。
request.security()でリペイントする典型例
下位足のチャートに日足の200EMAを重ねたい、というよくあるケースで考えます。まずはリペイントしてしまう書き方です。
//@version=5
indicator("MTF EMA(リペイントする悪い例)", overlay=true)
// lookahead_on を単体で使うと未来を参照してしまう
htfEma = request.security(syminfo.tickerid, "D",
ta.ema(close, 200),
lookahead=barmerge.lookahead_on)
plot(htfEma, color=color.red, linewidth=2)
barmerge.lookahead_onを単体で指定すると、TradingViewは「その日足が最終的に確定したときの値」を、日足がまだ始まったばかりの過去バーにまで先取りして表示します。つまり未来の値を過去に持ち込んでいる状態です。過去チャートでは完璧なラインに見えますが、リアルタイムでは同じ値になりません。
リペイントを防ぐ正しい書き方
Pineの公式コミュニティ(PineCoders)で確立されている定番パターンが、lookahead_onと1本前オフセット[1]の組み合わせです。
//@version=5
indicator("MTF EMA(リペイントしない正しい例)", overlay=true)
htf = input.timeframe("D", "上位足")
emaLen = input.int(200, "EMA期間", minval=1)
// [1] で「1本前に確定した上位足の値」を取得する
htfEma = request.security(syminfo.tickerid, htf,
ta.ema(close, emaLen)[1],
lookahead=barmerge.lookahead_on)
plot(htfEma, color=color.orange, linewidth=2)
ポイントはta.ema(close, emaLen)[1]の部分です。「1本前に確定した上位足の値」だけを参照するため、履歴(過去バー)でもリアルタイムでも常に同じ確定値を使うことになり、リペイントしなくなります。
lookahead_onと[1]は必ずセットで使うのがコツです。lookahead_off単体だと、今度は上位足の途中経過の値が下位足に少しずつ漏れてくるケースがあり、これも見た目のズレの原因になります。
TradingViewで実際に試してみよう
この記事の内容はTradingViewで今すぐ実践できます。無料プランでも十分対応できます。
▶ TradingViewを無料で開くクレジットカード不要・いつでも解約OK
安全なラッパー関数にしておく
毎回この書き方を思い出すのは大変なので、リペイントしないrequest.security()を関数にまとめておくと安全です。
//@version=5
indicator("安全なMTF参照", overlay=true)
// リペイントしない上位足参照ラッパー
// _off に 1 を渡すと確定値だけを参照する
f_secure(_tf, _expr) =>
request.security(syminfo.tickerid, _tf, _expr[1],
lookahead=barmerge.lookahead_on)
htf = input.timeframe("60", "上位足")
htfHigh = f_secure(htf, high)
htfLow = f_secure(htf, low)
plot(htfHigh, "上位足の高値", color=color.teal)
plot(htfLow, "上位足の安値", color=color.red)
こうしておけば、複数の上位足の値を参照するときも1行で安全に呼び出せます。マルチタイムフレーム分析の実装全般はマルチタイムフレーム分析の完全ガイドで詳しく解説しています。
リペイントしているか確認する方法
自分のインジケーターがリペイントするかどうかは、以下の方法で確認できます。
- チャートを再読み込みする:ブラウザを更新して、過去のシグナル位置が動かないか目視する
- バーリプレイで比べる:バーリプレイ機能で「そのバーがリアルタイムだったとき」の表示と、確定後の表示を見比べる
barstate.isconfirmedを条件に入れる:シグナルを確定足だけに限定すると、未確定バーでのちらつきを防げる
ストラテジーの信頼性を検証する手順はバックテストの実行方法やstrategy()での本格的な設計ガイドもあわせて確認してください。
こんな場合はリペイントを気にしなくてよい
- 現在の時間足だけで完結するインジケーターの場合:
request.security()を使わず、確定足の値だけを見るなら基本的にリペイントは起きません - 描画・視覚確認だけが目的で、売買判断に使わない場合:厳密な再現性が不要なら、多少のズレは実用上問題になりません
- あえてリアルタイムの途中経過を見たい場合:現在進行中の上位足の動きを監視したいなら、確定値ではなくリアルタイム値を使う方が目的に合います
まとめ
- リペイントは過去のシグナルが後から書き換わる現象で、バックテスト結果を信用できなくする
- 最大の原因は
request.security()の未来参照。lookahead_onと[1]をセットで使うと防げる - 安全なラッパー関数を用意し、確定足を参照する習慣をつけるとハマりにくい
リペイントを避けた正しいコードをAIに書かせたい場合は、Pine Scriptを正確に書かせるプロンプトの書き方で「リペイントしない実装で」と指定するコツを解説しています。上位足を扱う本格的なインジケーター開発では、有料プランの上位足データや複数チャート表示が役立ちます。
TradingViewを始めるなら、まずは無料プランで試してみましょう。有料プランへのアップグレードで、さらに高度な機能が使えます。
