2つのPineエンジンが食い違うとき——PineForgeとPyneCoreのクロス検証
各リリースはTradingViewとPyneCore両方へのパリティ・スイープを通します。最新スイープの数字と、なぜ「友好的な第二ソース」が最高のデバッグ道具なのか。
私たちが知る限り、TradingView の外で「実用品質」の Pine エンジンは二つだけです。うちのものと、PyneCore です。PyneSys の PyneComp が Pine を Python にトランスパイルし、PyneCore が OSS の Python ランタイムとして実行します。言語もランタイムも違うが、原材料は同じ Pine、守るべき API 定義も同じです。
PyneCore は 第二ソースのオラクルとして使っています。PineForge の各リリースは、TradingView の「取引リスト」CSV エクスポート と PyneCore の実行結果の 両方 に対してパリティ・スイープを回します。三者が揃えば結果を信じる。二対一なら外れた側にバグがありがちで、たいていはこちらです。
この記事は、最新スイープの数字がどう見えるか、という話です。
コーパス
公開スイープに載せている参照戦略は 50 本です(より広い 162 本規模のコーパス本体は非公開。このサブセットがトレード比較レポートに載ります)。各戦略について:
- PyneComp で Python にコンパイルし、カノニカル OHLCV 上で PyneCore が実行
- PineForge codegen で C++ にコンパイルし、同じ OHLCV 上で
pineforge-engineが実行 - 両方を TradingView の CSV エクスポートと diff。ウィンドウは
[OHLCV のスパン] ∩ [TV のエントリーが始まるスパン] ∩ [エンジンのエントリーが始まるスパン]
にクリップ - 一致度は 5 段階(excellent / strong / moderate / weak / minimal)で付与
見出しになりやすい話
50 本のうち 47 本は PineForge と PyneCore で同じティアに収まります。両エンジンとも TV の CSV に対して ≥95% のトレード一致で excellent を取ったのが 47 本。そのうち 2 本だけ PineForge が strong —— PineForge 側の コード差分の数え方がいくつかの境界ケースでティアに効くアーティファクトです。
ティアが食い違う 3 本は、どれも PineForge の方が上。つまり PyneCore が TradingView からずれる場面で、こちらはまだ TV に寄せられている、という読みができます。
一番気になるやつ
49-partial-exit-qty-percent が一番の隔たりです。この戦略は strategy.exit(qty_percent=...) で部分利確をはがし、ひと回りしたらまた建て直す——というサイクルを繰り返します。テストウィンドウでは:
TV trades (raw, in-window): 725
PineForge engine trades: 852 → 725 in-window → 725 matched (100% of TV)
PyneCore engine trades: 3297 → 2805 in-window → 582 matched (80.3% of TV)PyneCore のこの戦略に対するカウント差分は +74% です。PineForge は 0%。
デシル別メトリクスはこうです:
PineForge: count_delta 0.0000% · entry p90 0.0000% · exit p90 0.0004% · PnL p90 0.1321%
PyneCore : count_delta 74.1533% · entry p90 0.0000% · exit p90 1.0376% · PnL p90 (high)言い換えると、両エンジンとも エントリーは同じバーで撃っている(両方 entry p90 が 0% —— 入口はピタリ)。争点は 出口です。PyneCore は TradingView と同じ戦略に対しておおよそ 4 倍の出口 fill を出しています。おそらく、同じ qty_percent 節が TV のオーダ処理が「完了」と見なす前に 再武装してしまう、という類の隅です。
これは PyneCore の品質を値踏みしている話ではありません。Pine の strategy.exit() まわりの 隅のセマンティクスで、パリティ・スイープがこちらの実装を殴ってから TV に寄せました。バグ探しは双方向に流れます——PyneCore と TV が一致しているのに、こちらだけ外していた時期も何度もあります。
もう二本
06-liquidity-sweep と 07-scalping-strategy は、形は同じで規模だけ小さいです。どちらもエントリーは 100% 揃い、出口は PyneCore 側で 1〜2 ポイント台漂うのに対し、PineForge は 0.05% 未満に収まっています。トレール型の出口や部分約定の意味論は、Pine の参照実装ほど文書化されていません。どのエンジンも 観測できる約定挙動から逆算するしかありません。
PyneSys のコンパイル API
PyneSys はコンパイルを HTTP で公開しています。curl でそのまま叩けます:
curl -X POST https://api.pynesys.io/compiler/compile \
-H "Authorization: Bearer pyne_..." \
--data-urlencode 'script=//@version=6
strategy("ma cross", overlay=true)
if ta.crossover(close, ta.sma(close, 20))
strategy.entry("L", strategy.long)'応答は OSS の pynecore ランタイム向けの Python ファイルです:
"""
@pyne
This code was compiled by PyneComp v6.0.31 — the Pine Script to Python compiler.
Run with open-source PyneCore: https://pynecore.org
Compile Pine Scripts online at PyneSys: https://pynesys.io
"""
from pynecore.lib import close, script, strategy, ta
@script.strategy("ma cross", overlay=True)
def main():
if ta.crossover(close, ta.sma(close, 20)):
strategy.entry('L', strategy.long)
if __name__ == "__main__":
from pynecore.standalone import run
run(__file__)それを .py に落として pip install pynecore、OHLCV を指させれば 第二エンジンの出力が手に入り、PineForge と diff できます。
なぜこれが効くか
一台のバックテストエンジンが「このテスト窓で 12.4% でした」と言うとき、それが意味するのは そのエンジンがそう主張しているということだけです。比較対象がなければ、エンジンの言葉を信じるしかありません。
独立した二台が同じ脚本・同じデータで同じ結果を返すなら、はるかに強い証拠です。食い違えば、その食い違い自体が仮説になります——どちらかにバグがあるか、Pine のセマンティクスがあいまいな角か、テストデータにどちらも踏んでいないエッジがあるか。どれも知っておいて損はありません。
私たちは 毎リリースこう使っています。本物の資金を載せる戦略を出すなら、あなたも同じことをした方がいいです。
この記事に書いていないこと
- インジケータ単位のドリフト。 PineForge・PyneCore・TradingView の参照値どうしの一致は別レポートで追っています。エンジンリポの
benchmarks/results/indicator_comparison.mdにあります。 - 公開スイープに載っていない内部コーパスの ~110 本。 UDT メソッド、マルチタイムフレーム、
request.security、OCA 出口グループなど、Pine の隅を狙ったものです。パリティ・スイープ自体は内部では回しています。方法論をきちんと公開できたタイミングで、別記事を書く予定です。
自分でクロス検証の型を試す
- PineForge の無料 codegen API キーを取る
- PyneSys の API キーを取る
- Pine 戦略を一本決める。同じ OHLCV CSV で二本のパイプラインを走らせ、出てきたトレードリストを diff する。
二つのエンジン、一つのソース、突き合わせる出力は三つ。 全部一致なら踏み込みやすい。不一致なら、金を動かす前に何かを学べます。