Skip to content

v6.8.0

Latest

Choose a tag to compare

@wallneradam wallneradam released this 04 Aug 22:50

This release is about numerical fidelity. Most of the rolling machines in ta.*, the
math.sum accumulator, the string formatters and Pine's float comparison rule were
measured against TradingView and reimplemented to match it bit for bit, so a script's
numbers now line up with the chart instead of merely being close. Alongside that, the
OHLCV v2 format learned the symbol's tick grid, atomic file publication works on Windows,
and several strategy-emulator and accounting bugs are fixed.

Because so much of this changes the last digits of computed values, the notes start with
what will look different after upgrading.

Behavior changes

  • Comparison operators in scripts are now tolerant. TradingView compares floats with
    an absolute 1e-10 tolerance at every magnitude, boundary included. <, >, <=, >=,
    == and != in Pyne code are rewritten to that rule by the new
    FloatToleranceTransformer, which replaces NeGuardTransformer in the pipeline.
    Integers keep exact semantics. Conditions that sat on a razor's edge may now flip.
  • ta.* results change in the last digits. Every machine listed under Fixes below
    now reproduces TradingView's own arithmetic. Golden-value or snapshot tests written
    against previous PyneCore output will need to be refreshed.
  • str.tostring and str.format print different digits. Both start from the shortest
    decimal that reads back as the same double, so
    str.tostring(0.1, "#.####################") gives 0.1 and not 0.10000000000000000555.
  • Plot CSV output is full precision. The writer's default float format is the shortest
    round-trip string instead of 8 significant digits, so exported values carry more digits
    than before. The plot writer also echoes the bar the script actually saw rather than the
    raw candle.
  • OHLCV v2 header is now minor 1. Files written by this release carry the tick grid in
    the previously reserved eight bytes. Readers accept any minor at or below their own, so
    6.8.0 reads older v2 files, but PyneCore 6.7.x cannot read files written by 6.8.0.
  • OHLCVWriter takes the tick grid as two integers. The mintick: float keyword is
    replaced by minmove: int / pricescale: int, so fractional grids like 25/1000 or 1/32
    stay exact. set_tick_info() lets a provider declare the grid after construction.
  • Persistent += accumulates naively. The transformer no longer turns augmented
    assignment into a Kahan sum; TradingView accumulates plainly, and the compensated
    version was a systematic divergence in every compiled var x = 0.0; x += y.
  • The private _alpha parameter of ta.ema is gone. ta.rma no longer rides on
    ta.ema; it runs Wilder's own step.

Features

  • Pine's tolerant float comparison implemented from a single definition in
    core/pine_compare.py, at both the operator layer and in the builtins measured to
    compare tolerantly (ta.rising, ta.falling, ta.cmo, ta.mfi, ta.percentrank,
    array.includes, array.indexof, array.lastindexof, array.percentrank). Builtins
    measured to be exact are deliberately left exact. Adds EPSILON-aware lower/upper bound
    helpers.
  • OHLCV v2 stores the symbol's tick grid in the header. On write, a price off the grid
    promotes its column from an f32 delta to an absolute f64; on read, a delta-decoded price
    within the f32 encoding error of a grid point is snapped onto it, so 65399.99 comes
    back exactly instead of 65399.990000000224.
  • Atomic file publication on Windows. The new core/_file_io module opens files with
    delete sharing, publishes through FileRenameInfoEx POSIX-style renames with an
    os.replace fallback, and provides one exclusive file lock over flock/LockFileEx.
    The run command's download lock is no longer a no-op off POSIX.
  • Volume clean-up is decided per feed: OHLCVReader exposes lossless_volume and
    ScriptRunner accepts it, so a v2 file's absolute f64 volume is used as served. The
    security child reads it from its own feed, since a context can run on a different file
    and format than the chart.
  • The demo OHLCV generator produces more realistic markets: clustered volatility, trend
    variation, gaps, wicks, mintick rounding and range-based volume with quieter weekends.
  • Library reference documentation updated: chart points, lookahead modes, request
    fallbacks, namespaces, plus new splits.md and ticker.md.

Fixes

TradingView exactness

  • math.sum reproduces TradingView's rolling compensated accumulator, including the exact
    re-baseline rule (fixed-order Fast2Sum residue of the magnitude-shifted add). Length
    changes rebuild the window from history instead of restarting the warmup.
  • ta.sma drops its round(..., 15), which broke the match on 17451 of 22282 EURUSD 60m
    bars.
  • ta.ema steps with prev + alpha * (source - prev); ta.rma runs Wilder's own step.
    ta.atr, ta.rsi, ta.macd, ta.tsi and ta.dmi became exact with them.
  • ta.wma re-sums the whole window oldest-first every bar, and its na window is
    forward-filled rather than compacted. ta.hma became exact as a result.
  • ta.variance, ta.stdev and ta.correlation moved onto the measured expressions over
    the exact rolling-sum machine. ta.dev grows its buffer so lengths beyond the default
    max_bars_back stay addressable.
  • ta.linreg recomputes its window per bar and anchors the line the way TradingView does;
    the previous rolling update drifted to 1e-11.
  • ta.mfi forms the money ratio first, which fixes 38% of bars, and applies the comparison
    tolerance to the money-flow sums.
  • ta.stoch no longer clamps, so a source on the window high can round just above 100.
  • ta.rci rewritten to TradingView's pipeline: min-anchored tie clustering, 0-based
    average ranks and moment-form variances.
  • ta.percentile_nearest_rank and ta.percentile_linear_interpolation order their window
    tolerantly, so a window holding near-equal values returns the same element as
    TradingView.
  • ta.change drops both round(..., 14) calls; TradingView subtracts the raw doubles.
  • math.avg matches TradingView's two-branch sum: plain up to two arguments, Kahan with a
    flush from three on.
  • str.tostring / str.format: 16-digit fraction cap in plain notation, half-up for
    tostring and half-even for format, and a mask with required decimals drops a zero
    integer part, so "#.0" formats 0.5 as .5.
  • ta.cmo and ta.mfi no longer raise ZeroDivisionError on a flat source; they return
    na and 100 respectively, as measured.

Strategy and accounting

  • A limit order the bar gaps past no longer takes market slippage. It fills at
    max/min(limit, open), the price the intrabar walk would have given it, while a
    gap-triggered stop keeps its slippage (#74).
  • strategy.position_avg_price re-weights the previous average when a position is
    pyramided instead of dividing accumulated cost by the new size.
  • The cash_per_order commission is deducted from the closed trade's own profit and
    profit_percent, and the win/loss/even counters and gross classification wait for the
    final figure.
  • The big-money entry gate quantization gained decade-based grids, float32 offset
    membership and sub-1e5 inflation handling.

Data and runtime

  • ta.cross no longer short-circuits: crossover and crossunder each keep their own
    previous-bar relation, so evaluating one no longer leaves the other stale and misses the
    crossing on the next bar.
  • Legacy float32 volume restoration is applied only to legacy feeds. Running it on a v2
    file truncated real decimals (a Binance 30m volume of 9362.123462 came back as
    9362.12346) and diverged a session's ta.vwap for the rest of the day. The aggregator
    applies the restoration per source bar so a resampled file does not carry float32 dust
    into its sums.
  • PyCharm's enhanced numeric stubs are closed over arithmetic, so an arithmetic result
    stored in a Pyne-typed variable is no longer flagged. Nothing changes under pyright.

Performance

  • ta.wma, ta.linreg and ta.dev read their window through the new
    ReadOnlySeriesView.oldest_first() as raw list slices and cache length-only constants;
    the per-bar cost at length 500 drops about five-fold with a bit-identical accumulation
    sequence.
  • ta.percentile_nearest_rank / ta.percentile_linear_interpolation replace the per-bar
    full sort with a rolling window, 2.5x to 100x faster depending on length.
  • One ta.sma call drops from 1811 ns to 686 ns.