Skip to content

Releases

v6.13.5

Choose a tag to compare

@wallneradam wallneradam released this 14 Nov 11:31

Features

  • Python 3.15 support: the import hook accepts the fullname argument Python 3.15 passes to source_to_code, the Edge gate finds a lambda under its 3.15 symbol-table name, and the Python name tables include the 3.15 builtins and standard library modules. The native transform reads the 3.15 AST (is_lazy on imports, an optional DictComp.value). Wheels are built for cp315 as well.

Fixes

  • History in an else if condition (#118): a history reference in an else if condition (and in a later switch case, which compiles to the same chain) now advances on every bar the if statement runs, as on TradingView. It used to advance only on the bars that reached the condition. An if nested inside an else block keeps its gaps, as on TradingView.
  • History of arrays, matrices and maps (#118): a[1] of an array, matrix or map is now the object as it was when the previous bar closed, a shallow copy whose elements stay shared, as on TradingView. It used to be the live object, already holding the current bar's changes. This also holds for a history read of a function result (f()[1]) and inside functions called with a collection in some places and a scalar in others. The history of a user-defined object stays a reference.
  • Fields of an na object (#118): reading or writing a field of an na user-defined object or chart.point, and copying one, now stops the script with NaFieldError, as on TradingView. This covers a field read through a method, a nested field, na(t.v), an array.new<T>(1) element and an object history before its first bar. PyneCore used to answer na (and a write raised a raw AttributeError). A method called on the na itself still runs, and an na drawing still answers na through its getters. A field named type no longer collides with the na's own type.
  • ta.highest / ta.lowest with a non-positive length (#118): they and their bars forms now stop the script on a zero or negative length, also when the length is a series, like every other ta builtin and like TradingView. They used to return the source value.
  • Comparing infinities (#117): == and != are false whenever an operand is infinite, and two infinities of the same sign are neither equal nor ordered, as on TradingView; an infinity still orders against a finite value. array.indexof and array.includes no longer find an infinity.
  • request.security tuples unpacked into fewer names (#114): [a, b] = request.security(...) reading a three-value tuple now takes the first two values, also when the read is another function's return and for request.security_lower_tf. It used to raise "too many values to unpack". A tuple-valued lower-timeframe read in a function's return is now transposed into columns.
  • request.security tuple read through a function: a read whose expression calls a tuple-returning function, with the security call in another function's return, now defaults to one na per element. Unpacking it raised "na is not iterable" before the first higher-timeframe bar closed.
  • request.security with a gaps mode in a variable: a gaps value chosen at runtime (gapout = gap ? barmerge.gaps_on : barmerge.gaps_off) no longer raises NameError when the script loads; the mode is applied when the context first signals.
  • Live plots after a missing bar close: when a live bar opened before the previous one closed, the leftover plot ids made the new bar's plots appear as new plots ("Plot 1"). They are cleared at the bar open now.
  • Lib registry across machines: the generated lib_types.json no longer records routes for standard-library callables, whose route depended on how the interpreter was built.

Performance

  • Native transform data: the native transform no longer embeds and parses its JSON data files at run time; a build script compiles them in as static tables.

Project

  • A contributing guide and a pull request template state the rules for issues and pull requests: one problem per issue, one fix per PR, a failing repro and a test. A daily job flags and later closes pull requests that no longer merge into main. Issues must be opened from the issue forms; issues created without one are closed automatically.

v6.13.4

Choose a tag to compare

@wallneradam wallneradam released this 12 Nov 09:47

Features

  • Bid/ask data in OHLCV files: a feed that quotes both sides can now store the ask side in the record itself. OHLCV v2 gains four columns, ask_open, ask_high, ask_low and ask_close. Each is stored as a delta from the bid price, so the record grows from 36 to 52 bytes, and only in files that hold an ask.
    • The writer adds the columns on the first bar that carries OHLCV.ask. Bars written earlier read back with an all-na ask.
    • Readers expose has_ask, and CSV/JSON exports include the ask columns.
    • Aggregation folds the ask side like the bid side, but only for periods where every source bar quotes it.
  • bid and ask series: they are real on every bar with a quoted ask. bid is the bar's close and ask its ask close. On bars without an ask both stay na, as on TradingView's bar timeframes.
  • Spread costs in the strategy simulator: the new strategy(spread_type=...) setting is a PyneCore extension. On a bid-priced feed, a buy then pays the spread.
    • strategy.spread.none (default): the simulator works as before.
    • strategy.spread.trade: buys fill at the ask, sells at the bid, and every order is triggered by the side it trades on. Buy limits and buy stops trigger when the ask reaches them and fill at their own level. Sell orders trigger on the bid.
    • strategy.spread.opposite: stops trigger on the opposite side. A buy stop triggers on the bid and fills one spread above its level. A sell stop triggers on the ask and fills one spread below. Limits work as in trade mode.
    • Trailing stops follow the side that triggers them.
    • The spread of a bar comes from its ask side. A bar without an ask uses syminfo.avg_spread, and with neither, no spread is charged.
  • Opt-in provider data: ProviderPlugin.extra_data lets a provider declare an ExtraData, a label plus a help text. It describes the per-bar data the provider fetches only on request, such as an ask side rebuilt from tick history. Front-ends can then offer the switch where it exists. --extra-data covers this kind of data, and an ask side delivered with the bars is always stored.
  • Continued downloads keep the ask side: continuing a file that already stores the ask side keeps fetching it, so the new bars no longer get an empty ask. A truncated download starts over.

Fixes

  • Symbol info between runs: optional provider details (avg_spread, taker_fee, maker_fee) that the current symbol lacks no longer keep the value of an earlier run in syminfo.
  • Live trading: after the boundary watchdog has closed a bar slot because the feed stalled, ticks the feed still delivers for that slot are dropped. They used to reach the script runner, which then gave misleading warnings about intra-bar updates and bar-close-only execution. The drop is reported once per slot at INFO level.

v6.13.3

Choose a tag to compare

@wallneradam wallneradam released this 11 Nov 14:10

Fixes

  • Pine semantics, measured on TradingView (#111):
    • The date components of an na time — year, month, dayofmonth, hour, minute, second, dayofweek, weekofyear — read it as the Unix epoch in the requested time zone, as TradingView does, instead of returning na. With the exchange zone America/New_York, year(na) is 1969 and dayofmonth(na) is 31; with "UTC" they are 1970 and 1.
    • timestamp(dateString) recognizes a month word by its first three letters, in any letter case: "1 Janv 2017 00:00:00", "Sept" or "Septembre" parse like their English abbreviation instead of raising Invalid date format. When a string holds several month words, the last one wins. Month words TradingView rejects ("Févr", "Mai", "Juin") are still rejected.
    • A script whose request.security calls sit in functions called from many places no longer fails to load with security instantiation exceeded 64 function clones. The fixed limit is replaced by TradingView's own per-function bound: a function called from elsewhere may reach at most 254 request.security calls through the functions it calls. The global scope has no bound, as on TradingView.
  • Security instantiation: a call made under a parameter, import, except, match or class name that matches a function holding request.security is no longer rebound to that function's per-call-site copy.
  • Inner functions reading a variable of main: they get main's value at every call site, also when the calling function binds the same name as a parameter or local. Such callers receive main's value as an extra capture parameter and pass it along the whole call chain, matching TradingView. Calling a capture-taking method with an unpacked receiver raises a SyntaxError that asks for an explicit receiver.
  • CLI: pyne no longer crashes on import with Typer 0.29, which removed the internals plugin options were built on. The minimum Typer version is now 0.19 (#109).
  • Broker: after a crash or power loss, the restarted bot takes over the dead instance's run at once instead of failing with Active run_id already exists until the old heartbeat expired (5 minutes). Each run instance holds a process lock for its run; when the lock proves the previous owner gone, its run is closed and its orders are adopted.

Performance

  • OHLCV downloads publish bars in batches instead of syncing every bar to disk. On ext4 this removes about 16 s per 1000 bars. A crash loses at most the last 4096 unpublished bars, which a continued download fetches again.

v6.13.2

Choose a tag to compare

@wallneradam wallneradam released this 09 Nov 14:22

Fixes

  • Pine semantics, measured on TradingView (#107):
    • bool() of a constant number is exact: bool(1e-10) is true in v5 and v6, as on TradingView. The constant is folded at transform time; a series or input value keeps the 1e-10 tolerance of the runtime cast.
    • Under the v4/v5 three-state bool (na_bool), bool(na) is na, not false. In v6 mode it stays false.
    • for loops: a by of zero or below stops the script with an error, also on a descending loop, because by is the size of the step and the direction comes from from and to. A negative step used to be flipped and the loop ran. A na step is no error: the loop runs once and ends.
    • for loops with a fractional counter compare the end with Pine's tolerance, so for i = 1.0 to 0.1 by 0.3 runs four times, not three.
    • pine_loop gains an entry=True mode (entering, enter(by)) that evaluates a v6 loop header in TradingView's order: from, to, by, then to before every iteration, every read of to being the same call site. PyneComp emits it for a header that calls something with an effect or state; code compiled without it runs as before.
    • strategy() takes its positional arguments in Pine's order: calc_on_every_history_tick moved after behind_chart, so the 10th positional argument is max_bars_back and the 13th default_qty_value.
  • Legacy OHLCV files: the bar count leaves out the phantom gap records the reader skips, so last_bar_index matches the last bar the script sees.
  • Edge profile: the gate accepts the names PyneComp generates for a var block initializer, for the call-history alias of an outer series and for a renamed loop counter, so compiled scripts no longer get "not Pine" diagnostics (errors under PYNE_EDGE_STRICT=1).
  • pyne robustness coin-flip tests:
    • An early stop no longer hangs: each worker has its own pipe, and workers get a shared 5 s deadline before they are killed. A worker that dies during a draw raises an error naming the draw and its exit code, and an error inside a draw reaches the caller with the worker's traceback.
    • Stopped workers no longer print an ignored SystemExit, and a stop during a draw takes effect at the next bar instead of after the stop timeout.
  • Native transformer: a SystemExit or KeyboardInterrupt raised inside a host callback is re-raised instead of making the transform fall back silently to the Python steps.
  • Broker:
    • A partial close that takes the parent's whole position leaves the native stop in place. The stop update used to hit the already closed deal (cTrader POSITION_NOT_FOUND, Capital.com error.not-found.dealId) and fell back to a degrading fail-safe; a close that did not fill would have left the parent without its stop.
    • The log line for a partial bracket that keeps its classification names the actual cause.

v6.13.1

Choose a tag to compare

@wallneradam wallneradam released this 08 Nov 21:24

Features

  • Coin-flip backtests run in parallel and stop early. The coin-flip tests that run a backtest per draw (timing, and direction with --exact) take two new options of pyne robustness:
    • --jobs / -j runs that many backtests at the same time, each in its own process (default: the CPU count minus one). Worker processes can start request.security contexts, use the same timenow as the main backtest and do not rewrite the script's TOML.
    • --early-stop edge|better|off stops the test once its verdict is clear: edge (default) once it is clear whether the real rules beat 95% of the random draws, better once it is clear whether they beat half of them, off runs every backtest. The test is checked after 30 backtests and every 10 after that, with a 99% Wilson interval, always on the first draws in draw order, so the result does not depend on the number of workers. The verdict says when and why the test stopped, and the JSON result gains planned and stop_reason.
    • From Python, direction_test, timing_test and coinflip_test take jobs and early_stop (default jobs=1, early_stop='off'). With more than one job, make_runner must be picklable.

Fixes

  • Coin-flip direction test: a flipped entry exposed the broker-side sign through strategy.position_size, strategy.opentrades.size() and strategy.closedtrades.size(). A script deriving its bracket from that sign placed no exit, so the flipped backtest barely traded and the test reported a meaningless "beats 100%". These now carry the sign of the entry the script placed; money and prices stay the real ones. The fast path gives no verdict when fewer than 90% of the real trades pair with the flipped backtest and points to --exact instead; the JSON result gains a valid flag and writes null for the undefined percentile and share.
  • Broker:
    • A transient venue read fault during the startup reconcile (for example an execution index still behind a fill that landed during the restart) no longer ends the run before its first bar. The reconcile retries with a doubling wait capped at 60 s; other errors still propagate.
    • On an inverse market, pyramid legs of the same contract count book different base quantities, so a FIFO close of one leg could leave another a remainder below one contract, which no exit can trade. A plugin can now report the smallest tradable quantity (BrokerPlugin.least_tradable_qty(price), default 0.0 keeps every remainder). A remainder below it is spread over the other open trades with its entry cost, so the trades still add up to the venue position and the closes book the P&L of the fills.
    • The flat-book orphan exit sweep logs at INFO instead of WARNING, and a failed open-orders read during the reduce-coverage check reports through the shared read-outage throttle instead of warning on every reconcile pass.

Build

  • A failed gate or wheel build job cancels the whole release run instead of letting the other jobs run to their end.

v6.13.0

Choose a tag to compare

@wallneradam wallneradam released this 08 Nov 12:42

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.

v6.12.1

Choose a tag to compare

@wallneradam wallneradam released this 03 Nov 21:07

Features

  • The documentation ships inside the package. The build copies the repository's docs/ into the wheel as pynecore/docs/, and the sdist includes it, so an installed PyneCore carries the documentation of its own version. pynecore.docs_path() returns that directory, or the source tree's docs/ in an editable install.
  • display.pine_screener is available; it was missing, so a compiled script using it failed at run time.

Fixes

  • Sharpe and Sortino ratios follow TradingView's monthly rule. They used to be annualized daily returns of a per-bar equity curve, which matched nothing TradingView reports. Each closed trade's profit is now booked to the UTC month it closed in, the open profit to the last bar's month, monthly returns are measured against equity rolled forward from the initial capital, the excess return is taken over risk_free_rate / 12, and neither ratio is annualized. The monthly ledger covers the whole run, beyond the closed-trade history cap. Reproduced bit for bit on 16 TradingView strategy reports.
  • display.* and text.format_* are bit sets, as on TradingView. The display.* constants were sequential integers, so sums collided (display.data_window + display.pane == display.price_scale), and text.format_bold + text.format_italic concatenated to "bolditalic". Both now carry TradingView's measured bit values; + is a union and - a difference, adding a flag that is already set or removing one that is not changes nothing, and the result keeps its type. (#102)
  • math.round: the integer rounding compares the fraction against 0.5 instead of flooring x + 0.5, which rounded 0.49999999999999994 up to 1. The 10**-p scale of a rounding with a precision comes from the portable pine_math kernel instead of the platform libm.
  • ta.alma computes its Gaussian weights, and the strategy report its price rounding, with the portable kernels instead of the platform libm, whose results differ per system.
  • Constant folding of math.round and math.pow follows TradingView's parser. A constant math.round rounds to 15 decimal places first; with a positive precision it scales the whole number, so a constant math.round(1.245, 1) is 1.3 where a series one is 1.2. A constant math.pow folds with fdlibm's pow without the exponent tail, which is one ulp off StrictMath in places (math.pow(10, -4) embeds 9.999999999999999e-05), tested against 452 measured constants. The native fold matches, including fractional precisions.
  • request.security in libraries: a library context's clone dropped prologue statements that only an exported function read (for example a captured close history), so the context failed with a NameError. Export detection also recognises an aliased export import.
  • Live runs settle broker fills before disconnecting. A stop signalled while the live stream waited for the next bar disconnected a plugin that is both provider and broker before the runner settled the fills of already-dispatched orders, so a fresh entry could be left without its exit. The settle now runs before the teardown, once per stop, and a lost broker connection ends it with a warning instead of an uncaught error.
  • Broker restarts and operator resumes:
    • An exit re-declared after a restart is no longer re-sent under the client order ids of a dispatch that has already filled or closed; it goes out under fresh ids. Previously an idempotent venue returned the filled leg as if it had just been placed and the engine could halt on an unplaceable defensive close.
    • Legs of an earlier dispatch of the same exit that still rest on the venue are cancelled before the fresh bracket goes out, instead of resting next to it until a reduce-only venue refused the new leg.
    • Startup adoption recovers the real parent entry id from the run's live entry rows too, so on inverse contracts an operator resume restores a stop the operator removed.
    • Run-scoped journal queries no longer filter by symbol spelling (chart ticker vs. venue wire name), so on Bybit perpetuals an operator resume finds the exit legs and restores a removed take-profit or stop.
    • A rejected synthetic close no longer logs an ERROR next to its owner's own handling; the owner picks the level.
  • Plugin config files stay out of Git: the config/ directory of a workdir gets a .gitignore, as its files can hold API keys and broker credentials.
  • Native transform: a float that is an exact tie between two shortest spellings is written with the even last digit, as CPython's repr does.

Changes

  • The --viz stream writes a display value and a drawing's text_formatting as the list of its flag names in bit order ([] for none) instead of a single name. Its format version is now 2.

Tests

  • The security ring test checks the child's results only after the child exits, so a slow child no longer fails it. (#103)
  • The source-location checks skip the PEP 649 prologue and __annotate__ code objects on Python 3.14.

v6.12.0

Choose a tag to compare

@wallneradam wallneradam released this 30 Oct 15:11

Native transform

  • The AST transform pipeline now runs in Rust. Every transform step has a native port in rust/, built into the wheels as pynecore.transformers._native. It produces the same tree as the Python steps: each port is checked against its Python step, and the wheels and the release are gated on that parity. Measured over 1065 real-world Pyne code files, the whole transform (analysis and lowering) took 40.5 s natively against 98.0 s in Python. The transform runs when a script or library is compiled into its bytecode cache, not on every bar, so this shortens the first run and the run after an edit.
  • PYNE_AST_BACKEND selects the backend: native (the default) runs every step that has a current native port natively, python runs the pure-Python pipeline, verify runs both and fails on any difference. A Python step edited after its port was made is detected and runs in Python, with one warning per process. Without the extension (a platform with no binary wheel, or PYNE_BUILD_PURE=1), every step runs in Python as before.
  • The same pipeline builds for WebAssembly, so the transform can run where there is no Python interpreter.
  • Imported modules are no longer executed at compile time. The transform used to import modules to classify the functions a script calls from them, and resolving a dotted name imported every package on the way, so a library reached through a package ran during compilation and registered before the script's own imports. Callees are now routed from what the module's source binds, without running it.

Fixes

  • math.sum and ta.sma follow TradingView's new engine: TradingView replaced its rolling-sum engine between 2026-07-31 and 2026-10-29, and its output differs from the previous one from the first bars on. PyneCore now uses the same compensated rolling sum (the residue of a large value leaving the window stays in the compensation, as on TradingView), validated bit for bit on 396045 TradingView bars across lengths 2 to 300, spikes, na bars and changing lengths. On an na bar, a growing length now admits values only up to the window's edge. Results of these builtins and of indicators built on them can differ in the last digits from the previous release.
  • ta.cog computes its numerator newest-first over the window and its denominator with the math.sum engine, as TradingView does; it was one ulp off on nearly every bar.
  • ta.sar follows TradingView's reference computation: the initial trend is decided by close > close[1] (a tie starts short), bar 1 runs a full step instead of returning the start value, the two-bar clip reads two bars back only from bar 2 on, and comparisons are tolerant like Pine's.
  • request.security with lookahead_on: closed higher-timeframe bars of a same-symbol context were built by folding chart bars, which is not the real bar when the chart timeframe does not divide the requested one ("5" on a 3-minute chart), when the chart starts inside a period or has a gap in it, or on D/W/M charts. Closed bars now come from the context's own data, and the chart bars build only the developing bar. A period the context's data has no bar for is skipped, so [1] steps over it. (#100)
  • request.security in libraries: a library's request.security context could run the script's same-numbered context instead of its own, and a script context and a library context with the same group number were merged, which failed with "proved to name the same feed" when their data differed. Each context now runs in the module that declared it.
  • Constant folding of Pine ints: a Pine int is a double, but constant integer expressions were folded with exact Python ints. 9007199254740993 - 9007199254740992 now folds to 0, as on TradingView, instead of 1.

Build

  • Platform wheels carry the native transform, built with setuptools-rust as an optional extension; the sdist carries the Rust sources. Every wheel runs the native-versus-Python parity tests before it can be published.

Documentation

  • The request.security page describes where the closed bars of a lookahead_on context come from.

v6.11.1

Choose a tag to compare

@wallneradam wallneradam released this 28 Oct 15:24

Fixes

  • Libraries of other scripts in the same process: the library registry is process-wide and keeps the library of every script the process has imported. The runner, the request.security child process and the profiler used the whole registry, so a later script in the same process ran the main() of an earlier script's libraries on every bar, merged their request.security contexts and profiled their files (failing when such a file no longer existed). They now use only the libraries the script imports, directly or through another library. (#98)
  • Daily request.security on 24/7 symbols: on a round-the-clock session the 23:59:59 end-of-day marker was taken literally, so the last intraday bar of a day was assigned to the next day and daily request.security values on intraday charts were shifted by one bar. Such a session now ends at midnight. (#98)
  • Live trading – exits on spot holdings: a strategy.exit re-issued on a later bar could resize a bracket that had been sized to the fee-netted spot holding back to the script's gross quantity, so its stop could sell more than the bot held. A whole-row exit is now capped at what its entry id holds, and at its working quantity once the bracket's own legs have filled. An entry cycle counts as consumed when the net ledger reaches its quantity, the venue truncated the remainder, or all its venue orders are filled. On a live broker a pending same-side entry with the same id is folded into the filled target, so the single exit leg covers the holding plus the add. Backtests are not affected.
  • Compile errors: the CLI no longer adds the fixed "Check the variable declaration and spelling" hint to every structured compilation error; the compiler's own message is shown on its own.

Documentation

  • PyneComp links point to the compiler page.

v6.11.0

Choose a tag to compare

@wallneradam wallneradam released this 25 Oct 14:53

Profiler

  • pyne run ... --profile profiles a backtest: after the run it prints a summary and writes output/<script>_profile.json. The summary splits the run's time into categories (script, libraries, engine, output, data, security, runner) and lists the slowest lines with their time, share of the script's time, execution count, average and code. --profile-top N sets the number of listed lines (default 15). A function's def line is marked as per-call setup.
  • Per-line and per-block data: the profile records the time and execution count of every line of the script and its libraries, and the time of every loop and conditional block. A line's share is measured against script_ns, the time spent inside the script's or its libraries' code including everything they call, so nested user calls are not counted twice.
  • request.security contexts are profiled too: each security child process measures its own lines and script time and sends them back to the runner. The profile lists every context with its data source, timeframe, bars and script time.
  • Programmatic use: ScriptRunner(profile_path=...) writes the same profile. The profile is written only for a run that completes.
  • The profiler is built on sys.monitoring with a compiled Cython extension that ships in the wheels. It needs Python 3.13+ and has no pure-Python fallback: without it, --profile ends with an error message instead of a traceback. --profile is refused together with --live and --broker.

Pyne Code

  • Methods declared in @udt class bodies can now carry state. A method in a class body is Pine's method m(Type this); a ta.* call, a Series or a Persistent inside one used to fail at run time. State belongs to the call site, a call site inside a loop keeps one instance per site and rolls back its builtins per bar, and Class.m(obj) as well as method_call by name reach the same method. A stateful method with a decorator other than @staticmethod is rejected at transform time.
  • Type inference types an unannotated receiver of a class-body method as its class.
  • Edge profile 2026.10.2 admits undecorated class-body methods.
  • The demo.py script created with a new workdir is marked @pyne edge.

Fixes

  • Weekly and monthly aggregation: aggregating to monthly targets was always rejected, because a month was measured as a fixed number of seconds. Weekly and monthly targets now accept an intraday source that tiles the day, 1D, or the same calendar unit with a dividing multiplier; combinations whose bars straddle a period boundary (1W -> 1M, 2D -> 1M, 7 -> 1W) are rejected. An intraday source bar is assigned to its day/week/month period by its last instant, cut short by a session end inside the bar, and an nD source counts as n trading days on a multi-day target.
  • time("D", timeframe_bars_back=N) no longer raises IndexError when the symbol info has no session template: every calendar day then counts as a trading day.
  • Import order: importing pynecore.core.script (or pynecore.core.profiler) before pynecore.lib no longer raises ImportError.
  • Line numbers of generated code: code emitted by the transformers now runs at the source line it serves. Inlined math.* and array.get bodies no longer carry the line numbers of the library files, request.security slice clones no longer run main()'s last line in their prologue, and Series/Persistent rewrites and security write/signal/wait blocks no longer fall back to the def line. This fixes debugger stepping, tracebacks and per-line timing.
  • Code prepended to a function or module goes after the docstring, so __doc__ survives and the docstring is no longer evaluated on every call.
  • Scripts that start with a from __future__ import now compile when they use request.security or are strategies.

Documentation

  • New Profiling page, profiling options in the pyne run reference and profile_path in the ScriptRunner reference.
  • New reference pages for global functions, footprint and volume_row, plus matrix.delete, the full plot() parameters and the missing request parameters.
  • Fixed examples that failed to run (Persistent SMA initializer, matrix.new / map.new / chart.point calls, missing imports, conditional plot(), na checks, enum arguments, lookahead passed positionally as gaps, and more).
  • Corrected descriptions: lookahead_on as the period built so far, strategy.close fill timing, numeric na as a native NaN, the closed-bar rule and source priority of currency rates, the current security process model and extra-field number parsing.
  • The FreqTrade signal pattern no longer derives entry signals from closed trades.
  • The edge profile's class and method rules are documented, with an example and the equivalent @method spelling; the function isolation page describes how state reaches a class-body method.
  • The contributing guide covers the call-site routing and registry steps for new lib functions.