Repository navigation
Lower strategy.default_entry_qty to the engine host's query and refuse the spellings TradingView refuses - #339
Conversation
… refused spellings are refused strategy.default_entry_qty(fill_price) is valid in TradingView and returns the order size the default quantity settings give at that price. Here it is refused whatever its arguments, with a message saying that PineForge does not support it yet, because the strategy host had no query for it; the engine host now has one (default_entry_qty(price)). Every spelling TradingView refuses (no price, two prices, a price given twice, an unknown argument name, a string, bool, color or array price) is refused by the same blanket error instead of at the offending argument. tests/test_strategy_default_entry_qty.py (new): - eleven accepted price spellings each lower to one call of the host query, and the plain series price is the call's argument; - eleven spellings TradingView refuses (its CE10115, CE10120, CE10123, CE10165 and CE10271 failure records) are refused at the offending line, two of them inside the arm of a switch; - native rows link the emitted code against the engine host: fixed sizing returns the configured units at a valid price and at a zero, negative or na price, a fractional size is returned unrounded, a zero size is zero, the result is a series, the price argument is evaluated once and not at all when a conditional price does not select it, a script variable named like the query does not hide it, and the script as it was reported (an EMA crossover sizing its entry with the query) builds and sizes its entries. tests/test_strategy_default_quantity_query.py (rewritten): it pinned the blanket refusal and the public contract row that described it. It now pins the contract row of the supported form (what the call lowers to, the paired engine it needs, the refusals and their codes, the earlier refusal as retired); the behaviour itself is pinned by the file above. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
…e the spellings TradingView refuses strategy.default_entry_qty(fill_price) used to lower to calc_qty(...), a function no host declares, so a script that read it did not build; the last change refused every call with PF-E5084 until the strategy host had a query. It now lowers to the engine host's default_entry_qty(price), which returns the order size the default quantity settings give at that price. Because the engine host provides the query, the name joins the host members a script identifier is renamed around, and the change needs an engine whose strategy host has the query. A call that has no value to lower is refused where TradingView refuses it, also inside the arm of a switch (where the support checker otherwise downgrades a refusal to a warning): no price, a second price, a price given both by position and by name, or an unknown argument name (the builtin argument binding, applied to this one strategy function, PF-E1088); and a string, bool, color or collection price (four new analyzer errors, PF-E2083 to PF-E2086, only for a type the syntax proves). PF-E5084, the blanket refusal, is retired: its catalog entry and pin stay (a code is never removed) and nothing emits it. diagnostics_catalog.json and the code pin gain the four new codes (generated, then their explanation and user message written); tests/test_diagnostic_notes.py declares them among the added codes. The public contract row and the CHANGELOG describe the supported form, and the CHANGELOG entry of the refusal is dropped, since both would ship in the same unreleased series. tests/test_named_array_constructors.py no longer lists strategy.default_entry_qty among the lowerings that must stay identical to the reference, since this one now differs by design. tests/test_strategy_default_entry_qty.py and the rewritten tests/test_strategy_default_quantity_query.py (the previous commit) go from red to green. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The keyword allow_switch_warning of _check_builtin_arguments was added with the default_entry_qty lowering but is never read: the body keeps passing allow_switch_warning=func.name in sigs.BUILTIN_FUNCTIONS to _err, and none of the three callers passes it. The signature is the one main has again. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
There was a problem hiding this comment.
Graphify reviewed this change.
No issues found by static checks — no coupling regressions or blocking issues in the code graph. That is not the same as safe to merge: see what was not checked.
Not checked: tests were not run; formal verification proved 0 of 3 changed function(s) (3 not verified).
Formal verification. PR-changed functions: 0/3 verified (0 proven, 0 may-equivalent, 0 distinguished) · 3 not verified (3 unsupported).
Not verified on this run: \_handle\_strategy\_call (unsupported), \_visit\_strategy\_call (unsupported), \_visit\_FuncCall (unsupported).
Graphify review — findings
Adds real support for strategy.default_entry_qty(fill_price), which now lowers to the engine strategy host's default_entry_qty(price) query instead of raising PF-E5084. It needs an engine whose strategy host provides that query, and the name is added to the reserved host member names so user code can't collide with it. The analyzer refuses the calls TradingView refuses: a missing price, a second price, a price given both by position and by name, and an unknown argument name (PF-E1088). A string, bool, color or collection price is also refused (PF-E2083 to PF-E2086), but only when the syntax proves the type, never when it is merely inferred. PF-E5084 stays in the diagnostics catalog but is no longer emitted. The changelog and PUBLIC_CONTRACT.md are updated to match.
No blocking issues surfaced. 5 lower-confidence candidates did not survive cross-model review.
Analysis details — impact, health, verification
Impact & health
Graphify review
Impact — 4561 functions depend on the 773 functions this change touches.
Health — grade A; 10 existing hotspot(s) in the area this change touches (pre-existing, not introduced here):
Analyzer— 101 callers, 15 callees (high)_generate()— 131 callers, 9 callees (high)SupportChecker— 29 callers, 34 callees (high)_generate_raw()— 29 callers, 9 callees (high)_generate()— 9 callers, 25 callees (high)_analyze()— 30 callers, 6 callees (high)_check()— 31 callers, 3 callees (high)resolve()— 25 callers, 3 callees (high)- …and 2 more
Verification — 4561 functions in the blast radius were not formally verified this run (proofs are advisory here).
Gate & verification
graphify gate
PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.
Advisory (not blocking):
- verification_scope: 3777 function(s) in the blast radius were not formally verified this run
Cross-repo impact
Cross-repo impact
This change impacts 2 symbol(s) across 1 peer repo(s) — blast radius over derived cross-repo edges:
- pineforge-4pass/pineforge-hpo —
python/pineforge_hpo/transpile.py(via import_moniker, heuristic),tests/python/test_transpile.py(via import_moniker, heuristic)
1 peer repo(s) in this group are not yet indexed — cross-repo impact may be incomplete.
Cross-repo linkage is derived (SCIP-moniker matches are sound; name/contract matches are heuristic) and ADVISORY — it never affects this check's conclusion.
Formal verification
Could not verify: Could not verify \_handle\_strategy\_call.
The verifier did not have enough to check \_handle\_strategy\_call, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: method — first parameter is `self`, which needs a constructed instance (not synthesizable)
Could not verify: Could not verify \_visit\_strategy\_call.
The verifier did not have enough to check \_visit\_strategy\_call, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: method — first parameter is `self`, which needs a constructed instance (not synthesizable)
Could not verify: Could not verify \_visit\_FuncCall.
The verifier did not have enough to check \_visit\_FuncCall, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: method — first parameter is `self`, which needs a constructed instance (not synthesizable)
Summary
strategy.default_entry_qty(fill_price)is valid in TradingView: it returns the order size that the strategy's default quantity settings give at a price. Here it lowered tocalc_qty(...), a function no engine host declares, so every script that read it failed to build; the integration of the pending compiler fixes then refused every call withPF-E5084until the host had a query.It now lowers to the engine host's
default_entry_qty(price)(added by pineforge-engine #751). The spellings TradingView refuses are refused here too, at the offending argument or call, also inside the arm of a switch:PF-E1088, the existing argument binding ofnz/fixnan/request.currency_rate, applied to this one strategy function);PF-E2083toPF-E2086), only where the syntax proves the type.The generated host-member registry gains
default_entry_qty, so a script variable with that name is renamed instead of hiding the host's member.PF-E5084is retired: its catalog entry and its pin stay (a code is never removed) and nothing emits it. The public contract row and the CHANGELOG describe the supported form.Needs
An engine whose strategy host provides
default_entry_qty(pineforge-engine #751, merged). Without an engine the native rows skip, as for every native test.Tests
tests/test_strategy_default_entry_qty.py(new, 41 tests) andtests/test_strategy_default_quantity_query.py(rewritten), added first and red on main (41 failed, 1 passed: the refusal of an unknown function was already right), green with the change (42 passed). The new file: eleven accepted price spellings lower to one call of the host query; eleven refused spellings are refused at the offending line (two inside a switch arm); native rows link the emitted code against the engine host: fixed sizing returns the configured units at a valid price and at a zero, negative or na price, a fractional size is returned unrounded, a zero size is zero, the result is a series, the price argument is evaluated once and not at all when a conditional price does not select it, a script variable named like the query does not hide it, and the script as reported (an EMA crossover sizing its entry with the query) builds and sizes its entries. The rewritten file used to pin the blanket refusal and its contract row; it now pins the contract row of the supported form.The expected values are TradingView's own answers (exported trades of probe scripts that encode the query's answer in the entry quantity, and TradingView's failure records for the refused spellings), not outputs of this code.
Also adapted:
tests/test_diagnostic_notes.py(declares the four new codes).Evidence
Every run is on Linux aarch64 against engine
d9e787e3(it includes #751).nz,fixnan,request.currency_rateand the strategy signatures: the same ids pass on main and on this change (4,070 passed, 49 skipped on both). The 49 skipped rows are rows whose private fixtures are not in the checkout (tests/test_host_member_names.py25,tests/test_diagnostic_codes.py22,tests/test_e2e_lane_compositions.py2). Among them are the checks that compare the generated host-member registry with the engine's header; the namedefault_entry_qtyis shown to exist by the native rows instead, which compile and link against the real host header.strategy.position_entry_name, which this change does not touch.PF-E5084) and its control gives the same result.Not in this change
intdeclaration is accepted (TradingView refuses it): there is no assignment-type check in the transpiler yet.Part of #295 (item 7).
🤖 Generated with Claude Code