Repository navigation
Description
Summary
Three valid v6 scripts translate and build, but an argument of a strategy command reaches the engine as a fixed value that differs from the Pine value. Each case has a control that differs in one place and trades as documented.
| # | Pine form | Emitted argument | Result |
|---|---|---|---|
| 1 | strategy.order('Long', strategy.long) with no qty |
qty = 0 |
no orders (0 trades instead of 68) |
| 2 | q = switch mode with 'Fixed' => fixedQty (an input.int) and => na, then qty = q |
int q, q = na<int>() |
no orders (0 trades instead of 71) |
| 3 | strategy.risk.allow_entry_in(dir == 'Long' ? strategy.direction.long : strategy.direction.short) with dir from input.string |
direction 0 (both) |
shorts are opened (71 trades instead of 35 long) |
Versions
pineforge-codegen 1.9.1, pineforge-engine v1.9.1 (fc0a2df), Apple clang 17.0.0, macOS 15.8 arm64. Each script was built as is and run on BTCUSDT 1h bars from 2024-01-01 to 2024-03-01 (1,441 bars), initial capital 10000 through strategy_set_override, no commission or slippage. Codegen main at c1cc532 (after #290), run from source with the same engine, emits the same arguments and gives the same results.
Reproduction
All scripts share this header:
//@version=6
strategy('repro', default_qty_type = strategy.percent_of_equity, default_qty_value = 10)
fast = ta.ema(close, 9)
slow = ta.ema(close, 21)
1. strategy.order() without qty
if ta.crossover(fast, slow)
strategy.order('Long', strategy.long)
if ta.crossunder(fast, slow)
strategy.order('Short', strategy.short)
Generated:
strategy_order(std::string("Long"), true, 0, na<double>(), na<double>(), "", 0);
strategy_order(std::string("Short"), false, 0, na<double>(), na<double>(), "", 0);0 trades. Control, the same calls with qty = na: strategy_order(std::string("Long"), true, na<double>(), ...), 68 trades, first order 0.02288 (10% of equity).
2. switch returning an int input or na, used as qty
mode = input.string('Default', 'Size', options = ['Default', 'Fixed'])
fixedQty = input.int(1, 'Fixed qty')
q = switch mode
'Fixed' => fixedQty
=> na
if ta.crossover(fast, slow)
strategy.entry('Long', strategy.long, qty = q)
if ta.crossunder(fast, slow)
strategy.entry('Short', strategy.short, qty = q)
Generated:
int q = 0;
// ...
q = fixedQty; // 'Fixed' branch
q = na<int>(); // default branch
strategy_entry(std::string("Long"), true, na<double>(), na<double>(), q, "", "", 0, -1);0 trades with the default 'Default' mode. Control, q = mode == 'Fixed' ? fixedQty : na: double q, q = (((mode == std::string("Fixed"))) ? (fixedQty) : (na<double>()));, 71 trades, first order 0.02288. With input.float instead of input.int the switch form also gives 71 trades.
3. strategy.risk.allow_entry_in() with a direction chosen from an input
dir = input.string('Long', 'Direction', options = ['Long', 'Short'])
strategy.risk.allow_entry_in(dir == 'Long' ? strategy.direction.long : strategy.direction.short)
if ta.crossover(fast, slow)
strategy.entry('Long', strategy.long)
if ta.crossunder(fast, slow)
strategy.entry('Short', strategy.short)
Generated: set_pine_risk_direction(0);, the value for "both directions", although dir is read with get_input_string("Direction", std::string("Long")) just before.
71 trades, 35 long and 36 short. Control, strategy.risk.allow_entry_in(strategy.direction.long): set_pine_risk_direction(1);, 35 trades, all long.
Expected
- 68 trades as in the control. A
strategy.order()call without aqtyargument uses the strategy's default order size fromdefault_qty_typeanddefault_qty_value(default_qty_typeanddefault_qty_value, position sizing); theqtyparameter defaults to na (strategy.order()). Checked on TradingView (BINANCE:BTCUSDT 1h): the script and its control give the same trade list; the first order, on bar 60, is 10% of equity. - 71 trades as in the control. A
qtyof na also means the default order size (position sizing). An int na should stay na when it is passed to theqtyparameter. Checked on TradingView (BINANCE:BTCUSDT 1h): theswitchform and its control give the same trade list; the first order is 10% of equity. - 35 long trades and no short, as in the control, with the default input. The argument is an "input" value, which a parameter that takes "simple" values accepts (qualifiers,
strategy.risk.allow_entry_in()). If only literal directions can be supported, a translation error would be safer than allowing both directions. Checked on TradingView (BINANCE:BTCUSDT 1h): the script and its control give the same trade list, all long: 35 trades from bar 0 to bar 1440, none short.
Workaround
- Pass
qty = naexplicitly, or a computed quantity. - Use the ternary form of the control, or
input.floatfor the fixed quantity. - Pass a literal direction. We have no tested workaround that keeps the choice as an input.
Activity
luisleo526 commentedon Nov 1, 2026
Thank you for the detailed report. We are checking each case against TradingView's behaviour and our source, and will post a result for each case in this issue by the end of 2026-11-03 (UTC). If a case needs longer, we will say so here with the reason.
luisleo526 commentedon Nov 5, 2026
The ignored direction restriction is tracked in this known issue. All released versions are affected: a non-literal positional argument can lose an intended long-only or short-only restriction. Pass the literal direction constant positionally as a workaround;
value =is a separate hard error on 1.10.0. Fixed in the next release.luisleo526 commentedon Nov 6, 2026
Status: all three cases are fixed on main: the argument of each strategy command now reaches the engine as the Pine value, not as a fixed one. The fixes ship in the next release. Read the note under the table before you compare trade counts.
Checked with the code of main at 5b72a0c and the engine at d398279, on Linux aarch64: each script of the issue and its control were translated, the generated C++ was built against the engine and run on BTCUSDT 1h bars from 2024-01-01 to 2024-03-01 (1,441 bars), with initial capital 10000 and no commission or slippage, as in the report. "Before" is what the report measured on 1.9.1. The TradingView values are the ones the report gives.
strategy.order()withoutqtyswitchreturning anintinput orna, used asqtystrategy.risk.allow_entry_in()with a direction chosen from an inputNote on the first bars: PineForge returns a value for
ta.emaearlier than TradingView does (the diagnostic PF-W1505 that these scripts print), so a script that trades from its first bar makes trades in the first 21 bars that TradingView cannot make. The numbers above start trading at bar 21 (the engine'sstrategy_set_trade_start_time, set to the open of bar 21), the first bar on which TradingView can produce a crossover of the two EMAs (its 21 bar EMA has its first value on bar 20). The same scripts trading from bar 0 give 71, 74 and 37 trades for cases 1, 2 and 3; 3, 3 and 2 of them are filled before bar 21, and the rest are the 68, 71 and 35 above. The script and its control give the same trade list in both ways.Not run: x86 and macOS.
luisleo526 commentedon Nov 8, 2026
Update: today's check confirms all three argument-handling fixes on main, with the separate moving-average start-up difference described below.
Checked on 2026-11-08 on codegen main at
7d7681dcwith engine main at755e6658, on the 1,441-bar BTCUSDT 1h sample. Each script and its control give identical trade lists, both from bar 0 and when trading starts at bar 21.strategy.order()withoutqty08261a24)switchreturning anintinput orna, used asqty08261a24)strategy.risk.allow_entry_in()with a direction chosen from an input08261a24)The moving-average values agree from the first bar where TradingView has a value; before that PineForge returns a number where TradingView has
na. This is a separate known start-up difference that we are working on. Starting trading at bar 21 excludes that window. Trading from bar 0 gives 71, 74 and 37 trades for cases 1, 2 and 3, including 3, 3 and 2 fills before bar 21. The 68 and 71 counts above are control counts, not TradingView totals printed by the issue. Case 3 also reportsPF-W1503for partial runtime support.This is fixed on main and ships in 1.11.0, whose publish target is 2026-11-10. This issue will be closed when that release is published.
luisleo526 commentedon Nov 9, 2026
Fixed in v1.11.0: all three cases (the strategy command arguments that were lowered to fixed values) are in the v1.11.0 release. Closing.