Repository navigation
Releases
Releases · PyneSys/pynecore
v6.13.5
Features
- Python 3.15 support: the import hook accepts the
fullnameargument Python 3.15 passes tosource_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_lazyon imports, an optionalDictComp.value). Wheels are built for cp315 as well.
Fixes
- History in an
else ifcondition (#118): a history reference in anelse ifcondition (and in a laterswitchcase, which compiles to the same chain) now advances on every bar theifstatement runs, as on TradingView. It used to advance only on the bars that reached the condition. Anifnested inside anelseblock 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
naobject (#118): reading or writing a field of annauser-defined object orchart.point, and copying one, now stops the script withNaFieldError, as on TradingView. This covers a field read through a method, a nested field,na(t.v), anarray.new<T>(1)element and an object history before its first bar. PyneCore used to answerna(and a write raised a rawAttributeError). A method called on thenaitself still runs, and annadrawing still answersnathrough its getters. A field namedtypeno longer collides with thena's own type. ta.highest/ta.lowestwith a non-positive length (#118): they and theirbarsforms now stop the script on a zero or negative length, also when the length is a series, like every othertabuiltin 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.indexofandarray.includesno longer find an infinity. request.securitytuples 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'sreturnand forrequest.security_lower_tf. It used to raise "too many values to unpack". A tuple-valued lower-timeframe read in a function'sreturnis now transposed into columns.request.securitytuple read through a function: a read whose expression calls a tuple-returning function, with the security call in another function'sreturn, now defaults to onenaper element. Unpacking it raised "na is not iterable" before the first higher-timeframe bar closed.request.securitywith a gaps mode in a variable: agapsvalue chosen at runtime (gapout = gap ? barmerge.gaps_on : barmerge.gaps_off) no longer raisesNameErrorwhen 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.jsonno 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
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_lowandask_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-naask. - 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.
- The writer adds the columns on the first bar that carries
bidandaskseries: they are real on every bar with a quoted ask.bidis the bar's close andaskits ask close. On bars without an ask both stayna, 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 intrademode.- 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_datalets a provider declare anExtraData, 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-datacovers 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 insyminfo. - 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
Fixes
- Pine semantics, measured on TradingView (#111):
- The date components of an
natime —year,month,dayofmonth,hour,minute,second,dayofweek,weekofyear— read it as the Unix epoch in the requested time zone, as TradingView does, instead of returningna. With the exchange zoneAmerica/New_York,year(na)is 1969 anddayofmonth(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 raisingInvalid 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.securitycalls sit in functions called from many places no longer fails to load withsecurity 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 254request.securitycalls through the functions it calls. The global scope has no bound, as on TradingView.
- The date components of an
- Security instantiation: a call made under a parameter, import,
except,matchor class name that matches a function holdingrequest.securityis no longer rebound to that function's per-call-site copy. - Inner functions reading a variable of
main: they getmain's value at every call site, also when the calling function binds the same name as a parameter or local. Such callers receivemain'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 aSyntaxErrorthat asks for an explicit receiver. - CLI:
pyneno 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 existsuntil 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
Fixes
- Pine semantics, measured on TradingView (#107):
bool()of a constant number is exact:bool(1e-10)istruein 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)isna, notfalse. In v6 mode it staysfalse. forloops: abyof zero or below stops the script with an error, also on a descending loop, becausebyis the size of the step and the direction comes fromfromandto. A negative step used to be flipped and the loop ran. Anastep is no error: the loop runs once and ends.forloops with a fractional counter compare the end with Pine's tolerance, sofor i = 1.0 to 0.1 by 0.3runs four times, not three.pine_loopgains anentry=Truemode (entering,enter(by)) that evaluates a v6 loop header in TradingView's order:from,to,by, thentobefore every iteration, every read oftobeing 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_tickmoved afterbehind_chart, so the 10th positional argument ismax_bars_backand the 13thdefault_qty_value.
- Legacy OHLCV files: the bar count leaves out the phantom gap records the reader skips, so
last_bar_indexmatches the last bar the script sees. - Edge profile: the gate accepts the names PyneComp generates for a
varblock 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 underPYNE_EDGE_STRICT=1). pyne robustnesscoin-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
SystemExitorKeyboardInterruptraised 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.comerror.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.
- 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
v6.13.1
Features
- Coin-flip backtests run in parallel and stop early. The coin-flip tests that run a backtest per draw (
timing, anddirectionwith--exact) take two new options ofpyne robustness:--jobs/-jruns that many backtests at the same time, each in its own process (default: the CPU count minus one). Worker processes can startrequest.securitycontexts, use the sametimenowas the main backtest and do not rewrite the script's TOML.--early-stop edge|better|offstops the test once its verdict is clear:edge(default) once it is clear whether the real rules beat 95% of the random draws,betteronce it is clear whether they beat half of them,offruns 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 gainsplannedandstop_reason.- From Python,
direction_test,timing_testandcoinflip_testtakejobsandearly_stop(defaultjobs=1,early_stop='off'). With more than one job,make_runnermust be picklable.
Fixes
- Coin-flip direction test: a flipped entry exposed the broker-side sign through
strategy.position_size,strategy.opentrades.size()andstrategy.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--exactinstead; the JSON result gains avalidflag and writesnullfor 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), default0.0keeps 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
Upgrade notes
- Input calls must be
main()parameter defaults. An input is identified by itsmain()parameter name, which is its key in the script TOML and in runtime overrides. An input call anywhere else (module level, the body ofmain(), 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-locatedSyntaxError. Onlylib.input(...)andlib.input.<kind>(...)are checked, so a script's own function namedinputis left alone. - Recompile scripts that reassign an input with
:=. PyneComp before 6.0.80 compiled a globalx := input.*()(orx += input.*()) with the input call left in the body ofmain(), which the rule above now rejects. PyneComp 6.0.80 emits it as its ownmain()parameter; recompile such scripts. - Edge profile 2026.11.1: the Edge gate rejects Python's builtin
round(), which rounds half to even, unlike Pine'smath.round.
Features
- Strategy robustness tests:
pyne robustnessand the newpynecore.analysispackage. 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-tradesit analyses a trade CSV written bypyne runinstead, 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-leveragecaps 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):directionkeeps 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--exactas a full backtest per draw;timingre-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) anddocs/cli/robustness.md(command reference).
syminfo.prefix(symbol)andsyminfo.ticker(symbol)also work as functions of another symbol.syminfo.tickeris 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=trueevery read isna(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.
- At a timeframe higher than the script's, the run stops before its first bar, as on TradingView. With
request.security():- On a 24/7 market,
nD/nMhigher-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,
fortargets,casecaptures, container literals, arguments bound to a user function's parameters), and rebinding a former alias no longer keeps unrelated statements in the context.
- On a 24/7 market,
- A typeless
nain a bool position is the boolna. In anarray<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 typelessna, sox == falsewas false even in a two-state script andstr.tostringgave"NaN". It is nowfalseby default, and Pine v4/v5's third state underna_bool=True. - Plot ids no longer collide. A
plotand aplotcandle/plotbarwith 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 abgcolor/barcolor/fill/hlineid 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_1Dread.P_1Das the file extension and failed with aFileNotFoundError. Only a known data extension (.ohlcv,.csv,.txt,.json) now counts as one, everywhere a data path is derived from a name (pyne rundata and--securityvalues,pyne benchmark,pyne data convert-toandaggregate, 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.crossoverandta.crossunderare described as they behave:crossover/crossundercompare against the last bar where both sources were defined, andcrossis 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
Features
- The documentation ships inside the package. The build copies the repository's
docs/into the wheel aspynecore/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'sdocs/in an editable install. display.pine_screeneris 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.*andtext.format_*are bit sets, as on TradingView. Thedisplay.*constants were sequential integers, so sums collided (display.data_window + display.pane == display.price_scale), andtext.format_bold + text.format_italicconcatenated 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 flooringx + 0.5, which rounded0.49999999999999994up to1. The10**-pscale of a rounding with a precision comes from the portablepine_mathkernel instead of the platform libm.ta.almacomputes 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.roundandmath.powfollows TradingView's parser. A constantmath.roundrounds to 15 decimal places first; with a positive precision it scales the whole number, so a constantmath.round(1.245, 1)is1.3where a series one is1.2. A constantmath.powfolds with fdlibm's pow without the exponent tail, which is one ulp off StrictMath in places (math.pow(10, -4)embeds9.999999999999999e-05), tested against 452 measured constants. The native fold matches, including fractional precisions. request.securityin libraries: a library context's clone dropped prologue statements that only an exported function read (for example a capturedclosehistory), so the context failed with aNameError. Export detection also recognises an aliasedexportimport.- 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
reprdoes.
Changes
- The
--vizstream writes adisplayvalue and a drawing'stext_formattingas 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
Native transform
- The AST transform pipeline now runs in Rust. Every transform step has a native port in
rust/, built into the wheels aspynecore.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_BACKENDselects the backend:native(the default) runs every step that has a current native port natively,pythonruns the pure-Python pipeline,verifyruns 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, orPYNE_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.sumandta.smafollow 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,nabars and changing lengths. On annabar, 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.cogcomputes its numerator newest-first over the window and its denominator with themath.sumengine, as TradingView does; it was one ulp off on nearly every bar.ta.sarfollows TradingView's reference computation: the initial trend is decided byclose > 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.securitywithlookahead_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.securityin libraries: a library'srequest.securitycontext 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 - 9007199254740992now folds to0, as on TradingView, instead of1.
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.securitypage describes where the closed bars of alookahead_oncontext come from.
v6.11.1
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.securitychild process and the profiler used the whole registry, so a later script in the same process ran themain()of an earlier script's libraries on every bar, merged theirrequest.securitycontexts 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.securityon 24/7 symbols: on a round-the-clock session the23:59:59end-of-day marker was taken literally, so the last intraday bar of a day was assigned to the next day and dailyrequest.securityvalues on intraday charts were shifted by one bar. Such a session now ends at midnight. (#98) - Live trading – exits on spot holdings: a
strategy.exitre-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
Profiler
pyne run ... --profileprofiles a backtest: after the run it prints a summary and writesoutput/<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 Nsets 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.securitycontexts 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.monitoringwith a compiled Cython extension that ships in the wheels. It needs Python 3.13+ and has no pure-Python fallback: without it,--profileends with an error message instead of a traceback.--profileis refused together with--liveand--broker.
Pyne Code
- Methods declared in
@udtclass bodies can now carry state. A method in a class body is Pine'smethod m(Type this); ata.*call, aSeriesor aPersistentinside 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, andClass.m(obj)as well asmethod_callby name reach the same method. A stateful method with a decorator other than@staticmethodis 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.pyscript 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 annDsource counts asntrading days on a multi-day target. time("D", timeframe_bars_back=N)no longer raisesIndexErrorwhen the symbol info has no session template: every calendar day then counts as a trading day.- Import order: importing
pynecore.core.script(orpynecore.core.profiler) beforepynecore.libno longer raisesImportError. - Line numbers of generated code: code emitted by the transformers now runs at the source line it serves. Inlined
math.*andarray.getbodies no longer carry the line numbers of the library files,request.securityslice clones no longer runmain()'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 userequest.securityor are strategies.
Documentation
- New Profiling page, profiling options in the
pyne runreference andprofile_pathin theScriptRunnerreference. - New reference pages for global functions,
footprintandvolume_row, plusmatrix.delete, the fullplot()parameters and the missingrequestparameters. - Fixed examples that failed to run (
PersistentSMA initializer,matrix.new/map.new/chart.pointcalls, missing imports, conditionalplot(), na checks, enum arguments,lookaheadpassed positionally asgaps, and more). - Corrected descriptions:
lookahead_onas the period built so far,strategy.closefill 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
@methodspelling; 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.