|
PineForge v0.2.0-1-gb2f747d
Deterministic PineScript v6 backtest runtime — C ABI reference
|
PineForge exposes two distinct multi-timeframe surfaces. They look similar from Pine but resolve through completely different runtime paths, and they have different rules:
| Surface | Pine call | Direction | Bars source |
|---|---|---|---|
| Upward (HTF) | request.security(sym, "60", expr) | target TF coarser than input | aggregated from input bars |
| Downward (LTF) | request.security_lower_tf(sym, "1", expr) | target TF finer than input | synthesized from each input bar's OHLC path |
The downward path is where PineForge diverges from TradingView. TV downloads a separate finer-resolution feed when you switch to a lower timeframe; PF does not. The input feed's resolution is the upper bound on what request.security_lower_tf can target — beyond that, no data exists. Sub-bars are synthesized on demand by walking the input bar's OHLC path. See Bar magnifier for the same idea applied to fill resolution.
For the upward path and the chart-aggregation rules, see also Timeframes.
Every backtest run takes two timeframe strings on run_backtest_full(...):
The runtime resolves them in order:
input_tf — what the bar feed actually is. If empty, the runtime computes the median delta between consecutive pf_bar_t::timestamp values and snaps to a canonical Pine TF.script_tf — what the strategy script believes it's running on. If empty, defaults to the resolved input_tf.script_tf > input_tf, the runtime aggregates input bars up to script-TF parents before each on_bar dispatch. The two timeframes are concatenated by an aggregator (ratio or calendar — see src/engine_security.cpp).The report exposes the resolved values and the ratio:
| Report field | Meaning |
|---|---|
input_tf_seconds | Resolved input TF (after auto-detect). |
script_tf_seconds | Resolved script TF (after defaulting). |
script_tf_ratio | script_tf_seconds / input_tf_seconds. |
needs_aggregation | 1 when ratio > 1, 0 otherwise. |
The full runnable harness — three tables walking the script_tf sweep, the input_tf/script_tf pair matrix, and the lower-TF synthesis ratio — is in tutorial/run_mtf.py. See the Worked example section below.
The lower-TF builtin returns an array of one value per synthesized sub-bar. The codegen pattern in generated.cpp is:
When configure_security_evaluators runs, the engine validates each registered lower-TF site against input_tf_:
lookahead and gaps must be off (TV does not expose them on this builtin and PF refuses to fake them).Violations raise at run-time with a precise diagnostic, e.g.:
For each input bar, the engine generates input_tf / target_tf synthetic sub-bars by walking the input bar's OHLC endpoints (the same path-sampling primitive the bar magnifier uses, see src/engine_lower_tf.cpp). Each synthetic sub-bar gets 1/ratio of the input bar's volume.
Per-run sub-bar counts surface in the report's security_feeds_total field. With a 15m chart and a "1" lower-TF target, expect:
tutorial/mtf/ ships two strategies side-by-side:
| File | Demonstrates |
|---|---|
tutorial/mtf/strategy_htf.pine + generated_htf.cpp | request.security (HTF SMA filter on a 15m chart). HTF is an input — sweepable without rebuild. |
tutorial/mtf/strategy_ltf.pine + generated_ltf.cpp | request.security_lower_tf("1", close) — intra-bar 1m range as an entry signal. |
tutorial/run_mtf.py | Three tables: script_tf sweep, input_tf/script_tf pair matrix, lower-TF synthesis ratio. |
Build:
Expected (excerpt):
Numbers depend on the OHLCV snapshot.