Skip to content

v6.13.0

Choose a tag to compare

@wallneradam wallneradam released this 08 Nov 12:42
· 23 commits to main since this release

Upgrade notes

  • Input calls must be main() parameter defaults. An input is identified by its main() parameter name, which is its key in the script TOML and in runtime overrides. An input call anywhere else (module level, the body of main(), another function's parameter default) got no id, never reached the settings file and silently ran with its default. Such a call is now a line-located SyntaxError. Only lib.input(...) and lib.input.<kind>(...) are checked, so a script's own function named input is left alone.
  • Recompile scripts that reassign an input with :=. PyneComp before 6.0.80 compiled a global x := input.*() (or x += input.*()) with the input call left in the body of main(), which the rule above now rejects. PyneComp 6.0.80 emits it as its own main() parameter; recompile such scripts.
  • Edge profile 2026.11.1: the Edge gate rejects Python's builtin round(), which rounds half to even, unlike Pine's math.round.

Features

  • Strategy robustness tests: pyne robustness and the new pynecore.analysis package. They test how much of a backtest result could be luck. The command runs the backtest once, records its closed trades and writes the results as CSV files (<script>_robustness.csv, _montecarlo.csv, _costs.csv, _periods.csv, _coinflip_<mode>.csv; the whole result as JSON with --json). With --from-trades it analyses a trade CSV written by pyne run instead, in currency, since that file has no stops.
    • Every trade is measured in R from its initial stop, read from the bound exit orders while the backtest runs.
    • Summary statistics, a Wilson interval of the hit rate, a block bootstrap of the mean (--block week|month|trade), a trade-sequence Monte Carlo (--mc-method bootstrap|shuffle) of the running total and of a compounding account with ruin and target shares (--capital, --risk, --ruin, --target), extra-cost sensitivity with the break-even cost (--cost-bps), and year / month / weekday breakdowns (--periods).
    • --max-leverage caps the account Monte Carlo's position at a multiple of the account: a trade whose stop is too close to risk the set share within the limit risks the limit times its stop distance instead, and the report counts the capped trades. There is no limit without the option.
    • Coin-flip tests (--coinflip direction|timing|both|none): direction keeps every entry and flips a coin for its side (stop and limit entries swap, bound exits mirror around the fill), by default by pairing the trades of one flipped backtest, with --exact as a full backtest per draw; timing re-enters the real trades at random flat moments with their own stop distance and holding time.
    • Partial exits are merged into one record per fill.
    • The strategy engine hooks the coin flips need are inert outside a robustness run.
    • Documentation: docs/robustness.md (guide with a worked example) and docs/cli/robustness.md (command reference).
  • syminfo.prefix(symbol) and syminfo.ticker(symbol) also work as functions of another symbol. syminfo.ticker is the symbol without its exchange prefix; the chart's bare ticker still resolves to the chart's own security data.

Fixes

  • request.security_lower_tf():
    • At a timeframe higher than the script's, the run stops before its first bar, as on TradingView. With ignore_invalid_timeframe=true every read is na (each element of a tuple expression included), no child process is spawned and no data feed is required. A timeframe resolved at run time is checked when it resolves.
    • Lower-timeframe contexts get a sliced copy of main() like every other context, so chart-only statements no longer run once per intrabar or kill the child on the context's own bars.
    • A request.security() context reading another context's lower-timeframe array no longer hangs when the lower-timeframe feed has no intrabar in one of its periods, or ends before its last one. The consumer gets the intrabars the feed holds for its period (an empty array for a missing one), as the chart's own read does.
  • request.security():
    • On a 24/7 market, nD / nM higher-timeframe periods were counted in weekdays. They now follow the symbol's own schedule.
    • A developing batch on a D/W/M chart reads the first record of a close-time tie, matching the per-bar path.
    • A security child that dies reports the exception that ended it, with the script location, in the chart's error.
    • The context slice follows aliases through every binding form (unpacking, walrus, for targets, case captures, container literals, arguments bound to a user function's parameters), and rebinding a former alias no longer keeps unrelated statements in the context.
  • A typeless na in a bool position is the bool na. In an array<bool> element (array.new_bool, set, push, fill, unshift, insert), a bool variable, parameter, declared return or ternary arm, a bool map value, a bool matrix element or a bool UDT field, it stayed the typeless na, so x == false was false even in a two-state script and str.tostring gave "NaN". It is now false by default, and Pine v4/v5's third state under na_bool=True.
  • Plot ids no longer collide. A plot and a plotcandle / plotbar with the same title (in either order) shared one plot meta, a plot titled like a candle channel key overwrote that column, and a plot titled like a bgcolor / barcolor / fill / hline id took over that record. A name already taken is now numbered like a duplicate plot title (X 0).
  • Data names with a dot: pyne run SCRIPT bybit_BTCUSD.P_1D read .P_1D as the file extension and failed with a FileNotFoundError. Only a known data extension (.ohlcv, .csv, .txt, .json) now counts as one, everywhere a data path is derived from a name (pyne run data and --security values, pyne benchmark, pyne data convert-to and aggregate, the data converter, the runner and the currency provider). A bare name resolves to its first existing data file, and a missing one reports "Data file not found".
  • Broker:
    • A retryable transport error while reading positions inside a hedging-mode dispatch no longer halts the bot; the dispatch parks and retries, since nothing was sent.
    • On a venue that closes a position under the position's own deal id (Capital.com), a parent fill arriving after an attach reject no longer settles the defensive-close marker, which kept the trade out of the book and dropped the real close fill as a duplicate.
    • A filled entry that the script re-places and the pyramiding cap drops keeps its exit brackets while its position is open; they used to be cancelled, leaving the position unprotected.
    • Exit coverage is no longer reconciled while fills are settling or queued, so the bracket of a fresh pyramid leg or a fill pushed during the position read is not cancelled as stale or reported as an external reduction.
    • Orphan TP and SL legs left by an earlier run count as one exit against the held size and are cancelled together; summing them used to cancel the adopted position's stop.
    • Flat-close cleanup also cancels journal-only bracket legs whose key carries a TP / SL suffix, instead of leaving them live.
  • Broker log noise: routine events no longer log warnings. A warm restart resuming the run's own position, the routine pyramid fill wait (a single warning only on a stall), an exit-leg cancel explained by its OCA sibling's fill, native fail-safe recovery, bars re-delivered after a venue outage, and an external reduction while our own close is still filling are logged at INFO or reported once. A trail-price limitation warns once per exit.

Documentation

  • ta.cross, ta.crossover and ta.crossunder are described as they behave: crossover / crossunder compare against the last bar where both sources were defined, and cross is not the same as either.
  • Page descriptions improved across the documentation; every docstring cross-reference resolves.

Build

  • Release wheels build in parallel jobs per platform.