ある市場で売買し、別の市場でフィルターをかける戦略には、その 2 つ目の市場のバーが必要ですが、チャートの CSV には入っていません。こうしたスクリプトを動かすために PineForge のエンジンが必要とするもの、バーがないときの挙動、Docker イメージと MCP サーバーが用意するものを解説します。
2026-10-04 更新:リリース 1.1.0 以降、pineforge-release の Docker イメージは、
PINEFORGE_SYMBOL_FEEDSで指定した JSON インデックスから、request.security用の別シンボルのバーをインストールします。フィードは要求された時間足ごとに 1 本ずつ使い、集約はしません(エンジン #327、pineforge-release #23)。詳しくはエンジンの 1.1.0 の変更履歴をご覧ください。以下の本文は、2026-10-02 にリリース 1.0.1 を対象に公開したときのままです。
ある個別株を売買する戦略で、買うのは Nasdaq が上昇トレンドにあるときだけにしたいとします。TradingView ならこれは 1 行で書けて、そのまま動きます。TradingView はあらゆるシンボルのバーを持っているからです。しかし、手元の CSV で動くバックテスターが持っているのはチャートのバーだけです。そこで QQQ を求められたら、取れる道は 3 つしかありません。拒否するか、停止するか、それとも黙って間違った市場でフィルターを計算するかです。
PineForge が選ぶのは 2 つ目で、それもやむを得ないときだけです。エンジンに QQQ 自身のバーを渡せば、スクリプトは動きます。
2 つ目の市場を必要とする 1 行
次のスクリプトは、QQQ が 50 日移動平均を上回っているときだけロングします。
//@version=6
strategy("QQQ regime filter", overlay=true)
qqqUp = request.security("NASDAQ:QQQ", "D", close > ta.sma(close, 50))
fast = ta.sma(close, 20)
if ta.crossover(close, fast) and qqqUp
strategy.entry("L", strategy.long)
if ta.crossunder(close, fast)
strategy.close("L")
問題は 3 行目です。式 close > ta.sma(close, 50) は、QQQ 自身の日足、つまり QQQ の終値と QQQ の 50 日移動平均で評価されます。チャートのバーをいくら集約しても、これは作れません。少し前まで、PineForge はこのスクリプトを門前払いしていました。いまは、エンジンを動かす側が QQQ のバーを渡しさえすれば実行できます。
エンジンに必要なもの
エンジンの公開 C ヘッダーには、そのための呼び出しが 1 つあります(pineforge.h)。
PF_API int strategy_set_symbol_feed(pf_strategy_t s, const char* key, const char* timeframe,
const pf_bar_t* bars, const int64_t* close_ms, int n);
これは 3 つの要件として読めます。key はスクリプトが渡す文字列そのもの、つまり NASDAQ:QQQ です。これで、バーがそれを要求した呼び出しに結び付けられます。timeframe は要求された時間足でなければなりません。日足の要求には日足のバーが必要で、同じシンボルでも別の時間足のフィードでは代わりになりません。そして close_ms は、各バーにそれぞれの終了時刻を与えます。「シンボルのカレンダーは推測しない」からです。バーが教えてくれない限り、エンジンには Nasdaq がいつ引けるのかが分かりません。シンボルのセッション、通貨、ティックサイズといった情報は任意で、対になる別の呼び出しで渡します。
どちらの時計で見るか
終了時刻が重要なのは、2 つの市場の引けの時刻が違うからです。エンジンはチャートの各バーで、そのバーの終了時刻までに確定した相手シンボルの最後のバーを要求に渡します(lookahead を有効にした場合は、チャートのバーの開始時刻までに始まった最後のバー)(pineforge.h)。そのため、「チャートのセッション終了より後に確定する日足は、TradingView と同じく、チャート上で 1 日遅れて見える」ことになります(MTF ガイド)。
これは TradingView のルールであって、PineForge の手抜きではありません。エンジンのテストには、ドルインデックスを要求する金の日足チャートを TradingView からエクスポートしたデータが入っています。日曜の夕方に始まる金の日足は、月曜の 17:00 ET に確定します。ドルインデックスの月曜のバーが確定するのは、その 2 時間後の 19:00 ET です。そのため TradingView では、この金のバーが読むのは金曜のドルインデックスのバーで、月曜のバーは次の金のバーで届きます(フィクスチャのノート)。相手市場のバーを、確定する前にチャートのバーへ渡してしまうバックテスターは、未来を読んでいることになります。
バーがないとき
スクリプトのトランスパイル自体は通ります。ただし、この要求が「別シンボルのバー」を読むこと、そして「何もインストールされていなければ、値を読んだ箇所で実行がエラーで止まる」ことを伝える警告が出ます(codegen)。最初に値を読んだ時点で、実行は呼び出しを名指しするメッセージとともに止まります(ソース)。
request.security("NASDAQ:QQQ", "D", ...) at line 3: no data is pinned for this request, and its value was read
決してしないのは、QQQ の代わりにチャートのバーを読むことです(変更履歴)。間違った市場で計算したフィルターは、もっともらしく見えて何の意味もないエクイティカーブを生みます。だから、止まるほうが安全な失敗なのです。止まらないケースは 2 つあります。値が一度も読まれない要求(たとえば、オフのままの入力の裏にあるもの)と、値がプロット、アラート、テーブルにしか届かない要求です。後者は na になり、「トレードには影響しない」という警告が出ます(codegen)。
Docker イメージと MCP サーバーが用意するもの
いずれも、バックテストに使うのは 1 つの市場のバー、つまりチャートのバーだけです。
- Docker イメージは
strategy.pineとohlcv.csv1 つを受け取り(README)、そのハーネスは別シンボルのバーをインストールしません。QQQ のスクリプトはここで上のメッセージとともに止まり、イメージは終了コード 4 で終了します。 - ローカル MCP サーバーはこのイメージの上に作られていて、1 つの CSV でバックテストします。README にもそう書かれています。「別シンボルのバーはインストールしない……たとえば別シンボルへの
request.securityはエンジンではサポートされているが、ここでは、値がトレードに届きうる要求は実行を止める」(README)。 - ホスト型 MCP サーバーはシンボル、時間足、取引所を受け取り、その 1 つの市場のバーを自分で取得します。対応しているのは暗号資産だけなので、QQQ の例のような株式の戦略はそもそも実行できません(同じ README)。
ここで多くの人がはまる落とし穴があります。Docker イメージとローカルサーバーは、チャートがどの市場なのかをエンジンに伝えません。そのため、文字列で書いたシンボルは、チャート自身の市場を指していても別シンボルとして扱われます(カバレッジのノート)。チャートのシンボルには syminfo.tickerid と書いてください。これに対する上位足の要求なら、追加のデータは要りません。エンジンがチャートのバーから上位足のバーを組み立てるからです(MTF ガイド)。ただし制約が 1 つあります。米国株や CME 先物の日中足チャートでは、TradingView の日足は取引所自身の日足で、公式の終値を使っています。これはどう集約しても再現できません(pineforge.h)。
押さえておきたいこと
- チャート自身の市場でフィルターをかけるなら、
syminfo.tickeridを使ってください。手元の CSV だけでスクリプトが動きます。 - 別の市場でフィルターをかけるなら、エンジンを自分で動かし、実行前にその市場のバーを終了時刻つきでインストールしてください。エンジンリポジトリのテストハーネスが実例になります。スクリプトごとのリクエストマニフェストからバーをインストールしていて、マニフェストのフィールドは
scripts/run_strategy.pyが定義しています。 - TradingView と比べるなら、デフォルトの
lookaheadオフでは、自分の市場より後に引ける市場の日足は 1 日遅れて届くものと考えてください。これは TradingView の挙動で、エンジンもそれに合わせています。
2026-10-02 に、同日リリースの PineForge エンジンと codegen 1.0.1 で確認しました。この挙動が最初に入ったのは 1.0.0 です。
次に読むなら
- Claude や Cursor からバックテストする:2 つの MCP サーバーを、順を追って紹介します。
- PineForge をオフラインで動かす:手元のデータで Docker イメージを使います。
- 実務で見る Pine v6 の型システム:
request.securityの lookahead のデフォルトが、Pine のバージョンをまたいでどう変わってきたかも扱っています。