Skip to content

Releases: hoox-sh/axis

AXIS v2.27.0

Choose a tag to compare

@github-actions github-actions released this 01 Nov 01:22
d6a3f09

[2.27.0] — 2026-11-01

Changed

  • Pre-eval call scanning is single-pass (O(n) instead of O(n²)). scanAllCallSites (src/editor/pine-call-params.ts) used to call findCallSite per (, and findCallSite re-scanned from offset 0 — so idle pre-eval stalled for seconds on real scripts (measured ~2.9 s at 2 000 lines, ~22 s at 4 000). Open parens are now stacked and each call is parsed when its closer is reached. Same source, both lint passes on: 5.9 s → 43 ms at 2 000 lines and 22.3 s → 120 ms at 4 000. Unclosed calls are skipped (mid-edit input is not linted).

  • Vendored PYNE wheel bumped to pynescript-0.6.28. PYNE 0.6.27 / 0.6.28 only touch the VS Code extension and the version string, so the Python runtime, compiler, and language server are identical to 0.6.26; the wheel, the three hard-coded /vendor/ paths, and the engine docs now agree. The builtins catalog (src/editor/data/pyne-builtins.json, 941 entries) is byte-identical to PYNE's builtin_metadata.json.

Added

  • Pine symbol navigation: definition, references, rename, occurrence highlight. One local index (src/editor/symbol-index.ts) parses functions / methods, parameters, type and enum bodies, variables (incl. var / varip and tuple destructuring), loop bindings and import … as alias, then answers all four features without a Pro API round trip — so they work in Pyodide and offline. F12 (or Alt-click) jumps to the declaration, Shift-F12 lists every reference in a CodeMirror panel, F2 renames the declaration plus its references, and the cursor symbol is highlighted automatically. Scope is indentation-based like Pine, so f(n) and g(n) resolve separately and a parameter rename stays inside its function. Rename refuses builtins, builtin namespaces, keywords, invalid identifiers, names that would collide or shadow in an affected scope, and type / enum members. Also in the command palette.

  • Offline PYNE lint parity: W002 and W104. localPreevaluate now warns on a pre-v5 //@version pragma (W002) and on kwargs Pine v6 removed (W104) — when= on strategy.* order calls and transp= on drawing / plot calls (plot*, fill, bgcolor, barcolor, hline, line.*, box.*, label.*, table.*, polyline.*, linefill.*). color.new / color() keep a valid transp and are never flagged. Previously these came only from POST /lsp/diagnostics, so Pyodide / offline mode missed them. New setting preevalV6Kwargs (default on) with a Settings → Editor toggle; preevalVersionWarn now covers both W001 and W002.


Desktop installers (Tauri 2). CLI tarball, worker bundle, and checksums attach from the Release workflow.

AXIS v2.26.0

Choose a tag to compare

@github-actions github-actions released this 31 Oct 15:08
59bf314

[2.26.0] — 2026-10-31

Changed

  • While ranging, chart paints stay tip-update only. Overlay/bgcolor/OHLC smart-apply skips full-series setData, marker setMarkers defers until the gesture ends, PaneManager.setData and non-fit setDataToChart no-op, and the existing slide-replace deferral stays. Tip updates continue. Cuts the post-slide-defer panFrame max spike (~133 ms at 15k bars) from engine plot/marker writes during pan.

  • Live forming-tip apply skips O(n) align / overlay rebuild and store sync. When executionContext === 'tip', applyPrecomputedChart reuses the last aligned overlay payloads (tip-patch only), skips setLastRun / updateIndicatorStore / drawings when unchanged, and reuses store.bars for the cached OHLCV axis. Append and snapshot still take the full path. Bridge tip deltas reuse cached input descriptors. Aimed at G1 apply ≤ 8 ms on a 5k-bar tip.

  • Live window-slide setData defers while the chart is ranging. Tip series.update stays on; a full-window replace during pan/zoom was the ~84 ms panFrame spike under live ticks. The pending slide flushes on the next frame after the gesture. PaneManager.isRanging() exposes the gesture for the paint path.

  • chart.lag v1 latency rings are a recent window of 120 samples and skip the first 16 after connect/reset. Cold-start / one-off spikes no longer pin live p95 forever in the old 240-cap ring. Field names (queueWait, tickToApply, …) stay stable. Pan and long-task rings share the 120 window but not the warmup skip.

  • Rolling-window slides defer the full-series setData to one animation frame. Forming ticks stay on series.update. A slide still needs a replace (LWC update cannot drop the rolled-off head), but the replace is coalesced to one rAF and no longer flushes a wasted tip update immediately before setData, so the per-minute 5k-bar hit leaves the tick/apply path. Without requestAnimationFrame the replace stays synchronous.

Added

  • MCP logs.debug.get / logs.debug.clear. Reads or clears the rotating axis.debug.log.* localStorage ring written when AXIS debug mode is on (VITE_AXIS_DEBUG or localStorage.axis.debug).

  • chart.lag v1.queueWait is recv → apply:start. On the worker path that is batch:post → bridge entry, so a live G1 read can separate main-thread queueing from the apply span. Live ticks also skip the O(n) internal-hole scan and debug-pin clear when pins are off.

  • CLI 0.3.6. @hoox-sh/axis-cli default PYNE URL is the hosted Pro API (https://pynescript.online); AXIS_PYNE_URL still overrides it. The stale defaultPyneUrl test now matches the shipped default.

  • Pine hover/autocomplete refreshed against PYNE 0.6.25. src/editor/data/pyne-builtins.json re-synced from pyne (builtin_metadata.json, 88 improved docs incl. line.fill / linefill.new). Library annotation cards now render the description verbatim as Markdown (headings, code fences, blockquotes, tables, links, images, italic/strikethrough and two-space line breaks), matching PYNE's hover. //@description on library shows a library card. Card sections use PYNE's **Fields:** / **Parameters:** / **Returns:** shape with - \name`: desc` bullets.

Fixed

  • Shortcuts help no longer steals typing keys. app.show-shortcuts was bound to bare Shift-? / Shift-/, so typing / (Shift+7 on many layouts) or ? opened the shortcuts overlay — the editor could not type //. The binding now requires a real modifier (Alt-Shift-? / Alt-Shift-/) and is skipped while focus is inside an editor/input/dialog.

  • Series live no longer storms full snapshots (~6/s) into chart setData. Debug ring showed 228 wire.series.full + 200 setData in ~40s: gap recovery cleared its backoff on every full, then unsubscribe+subscribed again. Series gaps now resume (scripts still fresh-subscribe); sliding fulls after the first snapshot tip-merge only; backoff clears only after a continuous delta.

  • Live candle paint no longer clamps or repairs OHLC. PaneManager.appendBar no longer calls repairOhlc on forming ticks — exchange OHLC is clean ~99.9% of the time, and clamping could alter real values. Non-finite ticks are dropped instead.

  • sanitizeSeriesDataForSetData removed entirely. No donor fill, no OHLC fill-from-neighbor, no clamping — bad rows are simply dropped before setData. A minimal filterValidCandleRows guard drops rows with non-finite time/OHLC or duplicate times (last write wins) so LWC never throws Value is null. repairSeriesPayload, setCandleData, and setVolumeData pass data straight to Lightweight Charts.

  • Live script input-change resubscribe no longer stays idle/released. An input edit (e.g. Momentum Length 10→100) unsubscribes then re-subscribes on the same id; the PYNE idle/released ack arriving during awaitSnapshot used to dropScriptLocal the half-open replacement (wiping appliers / ctx), so the next eval re-subscribed forever and never rejoined while Bollinger/SMA stayed live. In-flight script subscribe now ignores that ack (same idea as gap-resync holdUntilFull), clears the inbox on release, and re-binds applier state when the new full lands.

  • Venue live owns candle paint — engine series snapshots/deltas no longer setData or overwrite forming bars. Worker tips were painting stub OHLC (tiny volume, disconnected opens) on top of Binance candles — visual gaps — and 5k setData on every engine full stalled scripts (websocket · cooldown / stop updating). Replica stays on the wire for indicators; chart tail stays venue.

  • Engine-owned mode no longer repaints the full window from PYNE on every tick. The full diff branch called setDataToChart(proBars), replacing every candle — including venue-closed bars whose OHLC PYNE could report collapsed or stale. Now engine bars are merged into the venue window via mergeEngineSnapshot: existing times keep venue's OHLC, only new times from the engine are appended, and the chart is only repainted when the merged window actually changed. Closed candles no longer get collapsed on a new candle.

  • Live start / scheduleRerun re-subscribes every visible non-library script, including idle/released. PYNE unsubscribe acks (idle + message released via live-wire release→unsubscribe) no longer leave EMA/Momentum blank while Bollinger stays on the wire: multiplex bypasses stream-eval backoff for released scripts, clears their retry state, and on stream-route live start kicks an immediate (0-debounce) scheduleRerun. scriptHistoryBars uses the stable chart window so a 1-bar length flicker cannot change history_bars and bounce subs.

  • Price chart printing pipeline refactored. store.appendBar splits into applyCandlePoint / applyPriceLineTint / updateVolumeBar for a simple O(1) live-tick path; setCandleData / setVolumeData extract full-history setData out of manager-access.setDataToChart so both full loads and live slides share one code path. Test doubles / managers without the new methods fall back to inline painting. appendBar no longer bumps chartDataGen on a live slide — ChartHost's chartDataGen effect responded with fit:true → fitContent(), resetting zoom/position on every new candle. appendLiveBar already paints slides with fit:false, so ChartHost does not need to repaint.

  • Live candle open no longer jumps when historical data arrives. store.appendBar preserves the candle's open once it has started forming: a historical close for the same bar updates high/low/close only, so the open set by the venue's first tick is not overwritten.

  • store.appendBar now writes live-tick OHLC to store.bars instead of a discarded unwrap() copy. Object.assign(unwrap(tip), payload) mutated a deep copy returned by Solid's unwrap(), so live ticks never persisted updated OHLC/high/low/close into store.bars. A full setData (window slide at capacity, engine mergeEngineSnapshot, etc.) then painted stale OHLC from the store — old candles collapsed/lost their OHLC when a new candle arrived. Both the OHLC write and the closed = true flag now go through setStore('bars', index, ...) so the store is the source of truth for both series.update (live tip) and setData (full window).

  • Engine deltas no longer overwrite venue OHLC on forming candles. store.appendBar now accepts a via parameter ('worker' | 'venue'). When the engine (via: 'worker') updates a forming tip (same bar time, tip not closed), venue's OHLC values are preserved — engine deltas may have slightly different values due to computation/rounding, and overwriting them caused candles to collapse/change OHLC on every engine tick. Venue ticks (via: 'venue' or undefined) always overwrite — they are the source of truth for forming candles. Threaded through appendLiveBar → appendBar → store.appendBar.

  • Indicator live pushes no longer shift right on a new bar. alignApplySeries head-padded shorter series with nulls when the engine evaluated the previous window, shifting every indicator value right by one position instead of extending onto the new bar. On venue live pushes (non-session, no engine clamp), shorter series now tail-extend by holding the last value forward (extendSeriesToWindow), so plots like MA ribbons extend onto the new candle instead of shifting. Session runs and free-tier windowBars clamps keep the existing head-pad / tail-pin behavior.

  • Chart view no longer resets (zoom/position) on each new candle. appendBar no longer bumps chartDataGen on a window slide. ChartHost's chartDataGen effect previously responded with fit:true → fitContent(), which reset the viewport on every new candle. appendLiveBar already paints slides with fit:false via scheduleSlideReplace, preserving the view.

  • Candle rendering no longer merges bars or hides bodies. LWC conflation (`enableConflat...

Read more

AXIS v2.25.3

Choose a tag to compare

@github-actions github-actions released this 20 Oct 13:20
e0ef21a

[2.25.3] — 2026-10-20

Fixed

  • A remote dataset document that is already gone is no longer reported as a failure. The dataset flush deletes the remote dataset documents; removing one that was never pushed (or was pushed before the sink existed) answered "not found", and the flush reported it as a write failure. Removing a document that is already gone is the state the caller wanted, so it is silent now.
  • The reconnect test no longer counts another suite's sockets. The reconnect suite counted every socket the shared WebSocket mock had seen, so a reconnect timer another test file left behind could land in it and make the suite count one socket too many (a CI-only flake). The suite now counts only the sockets its own client opened, and waits for the reconnect instead of guessing the delay.

Desktop installers (Tauri 2). CLI tarball, worker bundle, and checksums attach from the Release workflow.

AXIS v2.25.2

Choose a tag to compare

@github-actions github-actions released this 20 Oct 11:13
ab974f1

[2.25.2] — 2026-10-20

Added

  • The dataset flush reaches the engine. AXIS caches the venue's OHLCV twice: in the local dataset store and in the engine's series store (the shared venue cache behind GET /data/series). Flushing only the local side left the engine serving the old — stale, holed — window, and the live path mirrors the engine's copy. Settings → Data → "Flush dataset manager" now also drops the engine's series for every key AXIS knows about (per key — the engine's store is shared), and the MCP exposes it as engine.flush.

Fixed

  • A dataset write that was queued before a reset no longer repopulates the store. The suite resets the shared singletons between files, and a write that was still queued when the reset ran landed in the next file's mirror — a dataset the next file never created, which then failed its "the mirror is empty" assertion (order-dependent, CI-only). A queued write now carries the store's generation and is dropped when the store was reset underneath it.
  • A live apply keeps the script's input descriptors. The socket payload carries the values, not the run's metadata, so a live apply replaced the run's input list with an empty one (the Results panel and the Script Settings modal read it from there). The descriptors of the script's last full run are carried over.
  • A live script subscription now covers the chart's whole window. The socket subscribed the script with the series depth — the short live snapshot (500 bars), which is right for the chart's own OHLCV (the chart is painted from REST) but wrong for a script: the engine's script state was 500 bars deep, so every live apply repainted the indicator over the last 500 bars only, blanked the rest of the window and collapsed the script's band fill with it. The script subscription now asks for the chart's own depth (capped by the Rolling window).
  • A script's input values now reach the engine. The live socket subscribed the script without its inputs, so the engine evaluated it with the Pine defaults: changing an input (a Keltner channel length, an EMA period) repainted the chart with the new values and the next live tick overwrote them with the old ones — the change looked like it did nothing. The values now travel with the subscribe frame, and a changed value re-subscribes (the engine keys its session on the inputs).

Desktop installers (Tauri 2). CLI tarball, worker bundle, and checksums attach from the Release workflow.

AXIS v2.25.1

Choose a tag to compare

@github-actions github-actions released this 20 Oct 01:56
119c77b

[2.25.1] — 2026-10-20

Added

  • An uncaught error now carries its stack. "Uncaught error: Value is null" named no series and no frame; the first three stack frames are appended to the log line, so the next occurrence is attributable.
  • The chart audits its own time axis. After every apply, the co-painted series and the volume are checked against the candle axis; a series that holds a bar the candles do not have is named in the log (time axis drift: price/overlay_x: 1 row(s) outside the candle axis …).

Fixed

  • The engine's live snapshot no longer replaces the chart's window. The engine's live series is a short tail (the socket subscribes with ~500 bars), and the chart painted that tail as a replace: a freshly loaded 20 000-bar dataset was thrown away and the chart showed the engine's stale 500-bar window — with the engine's holes in it. The snapshot is now merged into the chart's window: it fills the holes the chart has, extends the tail when the engine is ahead, and never shortens the chart's own history.
  • The volume series carries exactly the bars the candles carry. The volume skipped a bar only when its open/close were not finite, while the candle series rejects a bar whose high/low are not finite too — so the volume could hold a bar time the candles did not have. The library's shared time scale is the union of every series' rows, and each renderer asks its own series for the bar at the rendered index: a volume row at a bar the candles dropped makes the candlestick renderer fail with Value is null and no other clue. Both series now use the same rule.
  • A live tick lands on the same bar in the candle and the volume series. The candle took its time from the mapped bar while the volume took the tick's own time, so a rejected tick could leave the two series one bar apart.
  • The co-painted series land on the candle axis. The script series were clamped to the candles' range, so a row at a bar time the candles do not hold (a venue hole, a stale window) survived and extended the shared time scale past the candles — the render then failed with Value is null on every paint. The clamp now drops the rows the candles do not have, and the overlay lines are projected onto the candle axis (one row per candle, a Pine na whitespace row where the script had no value).
  • The new chart-window tests no longer leak state into the suite. The test files that exercise the Rolling window left a small window in the shared store, so a later test file that loaded bars got them sliced — order-dependent failures on CI (and a coverage-gate drop). The store is now restored after each file.
  • A dataset repaint no longer paints over a chart that moved on. The progressive repaint of a completed backfill ran on a timer: if the chart had switched to another symbol in the meantime, the repaint put the old series on the chart (and the store's bars with it). The repaint is now dropped when the chart no longer shows that series.
  • Three test files no longer leak state into the suite. The dataset store, the provider session and the worker's proxy rate-limit buckets are module singletons: a dataset cached by one test file made the next file's backfill find the range already complete (and skip the walk), a venue left behind made the next file label its chart with the wrong exchange, and the worker's per-IP rate-limit bucket made a later worker test answer 429 instead of 404. The state is now reset per file.

Desktop installers (Tauri 2). CLI tarball, worker bundle, and checksums attach from the Release workflow.

AXIS v2.25.0

Choose a tag to compare

@github-actions github-actions released this 20 Oct 00:43
7fe4de9

[2.25.0] — 2026-10-20

Added

  • AXIS Settings → Data can flush the dataset manager. A new “Dataset manager” section (next to the exchange keys) wipes every cached OHLCV dataset — bars and metadata — from the memory mirror, the local IndexedDB cache, and the remote dataset stores (git / cloud), behind a confirm step and with a result line (Dataset manager flushed · N datasets removed). Chart bars stay until the symbol is reloaded.

Changed

  • The Rolling window is now the chart window, and it slides instead of growing. Settings → Rolling window used to bound only what the engine was asked to evaluate: the chart itself kept every bar a live tick appended (up to the 100k safety rail) and only the next dataset repaint re-capped it. The window is now one number (chartWindowLimit) shared by the load paths, the live append path, the background catch-up and the engine input, and a new bar at the cap drops the head instead of extending the series. The status-bar inline edit and the Settings page both re-slice the chart through the same helper, so the setting and the painted window can no longer disagree. ROLLING_WINDOW_MAX is now HISTORY_BARS_MAX (100k) — the setting cannot promise a window the chart would refuse to paint.
  • A live tick is applied through one audited path. store.appendBar returns a verdict (ignored / updated / appended / slid) and chart/live-append.ts is the only place a live bar reaches the chart. A rejected tick no longer moves the price card, no longer evaluates alerts, and no longer overwrites a dataset bar.
  • The chart data session follows the chart window. The engine session was provisioned with a fixed 5 000 bars while the chart window is 10 000, so the engine's store could only serve half the configured window. The depth is part of the session identity now, and changing the Rolling window re-provisions the session.
  • A dataset write no longer re-repairs the whole series. The stored series is already repaired, so a page write merges the page in one linear pass instead of rebuilding a map, re-sorting and re-sanitizing every bar.

Fixed

  • The dataset repair no longer deletes venue bars (the phantom-gap loop). The phase-aware snap moved an off-phase bar onto the nearest grid point even when that slot already held a bar; the dedupe then dropped one of the two — losing a real venue bar, shifting another bar's OHLCV onto the wrong candle, and leaving a hole the gap detector reported as fillable. The DSM chased that hole on every round and the job always ended with “Partial: N gaps remain”. The snap is now collision-safe (a bar only moves onto a free slot, and keeps its own timestamp when both neighbours are taken), never snaps onto a non-positive time, and the repair is idempotent.
  • The backfill walk stays on one grid. The walk advanced its cursor by one second, so a venue that answers “the newest bar with open time ≤ endTime” returned a bar one second off its own grid — a one-second hole in the dataset, which is a phantom gap the job then chased. The cursor now advances by one step (one duplicated bar per page, dropped by the merge).
  • A widened backfill window is fetched by the running walk. A second startBackfill for the same series widens the job's window while the walk runs; the walk now re-reads the bound on every page instead of keeping the one it was launched with.
  • A zero Rolling window falls back to the default. 0 used to mean "no cap" for the chart as well, which could grow the series without limit; the engine keeps its "no cap" escape hatch, the chart stays bounded.
  • Monthly intervals count calendar months. The expected-bar count used a fixed 30-day step, so the density floor was unreachable for most windows and a 1M job could never report "complete".
  • A session venue's Friday closure is no longer chased as a gap. The weekend classifier excused only gaps whose every missing slot was a Saturday or Sunday, so a venue closed from Friday's close to Monday's open produced a fillable gap: every job burned all six gap rounds on a bar the venue does not have. A missing Friday bar now belongs to that closure; a missing Monday bar is still chased.
  • The dataset job no longer re-walks a complete dataset. The pre-walk completeness check validated the series without the venue calendar class, so a session venue's weekend closure counted as a hole and every job re-walked the whole range.
  • A failed dataset read can no longer truncate the dataset. A sink that could not be read looked exactly like an empty dataset, so the merge base was empty and the last backfill page replaced the whole series. The sinks now distinguish “absent” from “broken”, and a write against an unreadable sink is refused instead of applied.
  • A dataset write is atomic and serialized. The sink replace was a clear-then-write (a window where the dataset did not exist at all, and a concurrent write could be clobbered); it is now a single record put. Writes are serialized per key, so two concurrent writers (a backfill page and the debounced live-bar write) can no longer drop each other's bars. The memory mirror evicts LRU instead of FIFO, so the hottest series is no longer the first to be dropped.
  • A closed candle is immutable in the dataset. A stale REST page carrying the partial (forming) values of a candle the venue had already closed could overwrite the final bar; a closed bar now wins against a forming one.
  • The live tail survives a dataset repaint. The dataset store only learns about a live bar when the Data Manager source is active, so a background backfill slice landing in between repainted the chart from a series that was one bar behind and the forming candle vanished. The repaint now merges the chart's live tail back in and re-caps the result to the window.
  • A late tick cannot rewrite history. A tick for a bar behind the tip was appended behind it, which put the series out of order and broke every index-based zip. Such a tick is now dropped, and a closed tip is never reopened by a forming tick.
  • The forming tip is replaced, not merged. Solid's store setter merges an object into the existing node, so a tick without a volume kept the previous volume and the chart drew a stale histogram. The tip payload is normalised before the path-set.
  • A slid window no longer drifts the indicator pane. When the window slides, the candles move forward by one bar while the script series do not (the engine has not re-run yet); the prune left them one row short, which shifts the pane's logical range against the price pane. The pruned series are padded back to one row per candle with Pine na whitespace rows.
  • A Pine na is a gap, not a zero. A non-finite plot value was coerced to 0, which draws a line at the axis; it now becomes a whitespace row (an omitted value). null is still coerced to 0 because the library rejects it.
  • Strategy fills follow the bar, not a stale index. The engine stamps bar_index relative to the window it evaluated, and the chart window slides (it drops its head on every new candle) — so an index pointed at a different bar in the current series: every entry/exit marker landed one bar early and the PnL was computed from the wrong bars. The absolute bar_time is now the authority (the index is the fallback), and the report is built from the run's bars rather than the live store.
  • plotshape() markers survive the apply that paints them. The owner-scoped series cleanup also cleared the script's shape and trade markers — and the apply path calls it on the price pane right after painting them, so plotshape() on every non-overlay script (RSI/MACD sub-panes) never rendered, and "Add Indicator" lost its markers before they could be re-homed. The series cleanup is now series-only; the detach path still clears the markers.
  • Two scripts that plot the same series name no longer collide. The marker id was plot-scoped (<plot>_<index>), so the price-pane merge kept only one owner's markers and the rest vanished. The id is owner-scoped now.
  • The legacy plots[] fallback is length-checked. A plots[] array of a different length than the chart window was index-zipped, mis-plotting every value with no error and no visual cue. It now goes through the same alignment as the named series.
  • A re-owned indicator's bgcolor band stays on its own pane. The re-own path left the band on the price pane, where the removal loop (which only inspects the target pane) never removed it — a permanent duplicate band and a leaked series.
  • A destroyed pane releases its drawing layer. The script DrawingLayer (SVG overlay, DOM listeners, a reference to the removed series) outlived the pane; a recreated pane with the same id got the stale layer back and projected drawings against a disposed scale.
  • A bar replay cannot leak the future. The bgcolor bands were built from the full series and had no clamp, so they extended the shared time scale past the replayed candles — the user could scroll into the colours of bars the replay had not reached. The bands are clamped to the painted range now.
  • A bar-colour repaint updates the painted range. The overlay clamp used a stale range after the candles were re-tinted, so it either dropped the newest overlay points or kept rows that no longer had candles.
  • A hidden script no longer fires its alerts. The Pine alert notification ran before the "hide / detach after the engine returned" check.
  • A live alert sees the bar it fired on. The alert context was built from the store, which is appended after the alert hook — so a condition on bar.open / bar.high / bar.low saw the previous candle. The tick's own bar is part of the context now.
  • plotshape() follows Pine's defaults. The marker position is no longer inferred from the arrow direction (Pine's default is location.abovebar whatever the style), and the plot tit...
Read more

AXIS v2.24.8

Choose a tag to compare

@github-actions github-actions released this 19 Oct 20:46
4fb146c

[2.24.8] — 2026-10-19

Fixed

  • The desktop release pipeline works again. The changelog extraction read the file with the Windows default codepage (cp1252) and died on the typographic quotes in the release notes, and the matrix jobs raced on creating the GitHub Release (HTTP 422 — Release.tag_name already exists) — the tag shipped without its installers.
  • The feed chip says what the data path is, not a stale state word. A live engine feed read degraded whenever only the engine’s own copy confirmed the close, so the chip disagreed with the engine plane (ws / open) while the data was flowing. It now reads live and flags the weak tier as live·unverified; a feed that never came up keeps its state word plus the time it has been stuck (connecting · 45s). The opened panel lists the facts behind it — state, freshness, verification tier, progress, the engine hop and the series identity.
  • The session delta merge keeps the engine’s bar-time axis. A merged engine-owned payload described only the tail, so the apply path fell back to an index join — which displaces the plot whenever the engine’s stored series has gaps (a missed minute). The axis is spliced with the tail like the plot columns.

Desktop installers (Tauri 2). CLI tarball, worker bundle, and checksums attach from the Release workflow.

AXIS v2.24.7

Choose a tag to compare

@github-actions github-actions released this 19 Oct 19:41
a2a4efb

[2.24.7] — 2026-10-19

Added

  • GitHub Release notes use the CHANGELOG version section (scripts/generate-changelog.py --extract) instead of the generic “Desktop build of AXIS (Tauri 2)” stub.
  • MCP connect / session chip sits on the command bar trailing edge (very top row, above Studio). Overlay indicator plots stack above price candles (setSeriesOrder); Layers lists Scripts above Panes.
  • Topbar Market after Venue. Binance (and OKX / Bybit / MEXC) can switch Spot, USDT-M, and COIN-M. BTCUSDT on Binance was always spot (api.binance.com / stream.binance.com); USDT-M uses fapi / fstream, COIN-M uses dapi / dstream. Engine series keys send PYNE/CCXT names (swap / future).

Fixed

  • CI is green again. The coverage gate ran the test files in parallel, so files sharing module state raced (clampPointsToPaintedRange) and every run since 2026-10-15 failed on a flaky assertion. The gate now runs serialized (--max-concurrency=1), and the engine-owned live plane (wire, series subscription, snapshot paint, close verification) got real unit coverage — the coverage floor (89%) was below it and is met again.
  • Pyodide compile mode no longer crashes on alert() / alertcondition(). The wheel hands the compiled __alerts back as an empty float array, and the runtime’s or [] on it raised ValueError: The truth value of an empty array is ambiguous — the whole compile run failed in the browser (Pyodide has no Numba, so this path is the norm there). The runtime now normalizes the packed event containers without truthiness checks.
  • A broken engine WebSocket no longer re-opens on every run. An abnormal close (1002 protocol error, 1006, 1011) after the socket opened now marks the shared /ws/run client dead for the cool-down, so the next runs go straight to REST instead of re-connecting to an endpoint that accepts the handshake and then drops every frame. Normal closes (1000 / 1001) keep the client reusable.
  • Interval switch no longer paints a stale cache tail plus the live candle. Switching 5m → 1m used to show yesterday’s last cached bar, a hole, then the current price because live started before venue pages closed cache→now. The load path now fills that tail before startLive.
  • Internal holes in the painted window are filled from the venue. A 1m cache that already reached “now” still left a ~2h blank (133 bars); load now fetches those ranges before live starts.
  • /ws/live subscribes a 500-bar tail instead of the full 5k window. A 5000-bar 1m full timed out; the chart stays on the REST/DSM window and the socket can go live.
  • Transient /ws/live errors no longer disconnect() the client. Snapshot timeout, empty snapshot, and subscribe/connect errors still fall back to venue+REST and debounce subscribe, but stopped stays false so the socket can stay up or reconnect. Only reset() / stopLive stop the client.
  • /ws/live snapshot wait is 120s, and a script miss no longer cools the series wire. History ensure of a painted 5m window often exceeds 30s; a Keltner (script) snapshot-timeout used to fail() the whole socket so the HUD stayed degraded · venue+REST.
  • Plot fills tail-align when the engine series is shorter than the chart. Adding Keltner (or any fill()) on a 5000-bar cache plus DSM/forming bars no longer leaves the band 2–3 candles left of the last bars.
  • Worker and engine API keys survive a new tab, PWA update, and browser restart. The secret vault was memory + sessionStorage only, and persist stripped apiKey from the app-state JSON — so the first hard reload after that migration (or the 2.24.6 update) looked like the app deleted the keys. They now also live in a dedicated localStorage bag (axis.secret-vault.v1), still out of workspace snapshots and the main persist payload. Exchange CEX keys stay session-only.
  • /ws/live no longer dies on Redesign2 auth timeout. Browser sockets cannot send Authorization, so the client now puts api_key on the upgrade URL (the engine already accepts that handshake) and does not block connect() on auth.ok. The auth frame is still sent. A close rejects any leftover auth waiter.

Desktop installers (Tauri 2). CLI tarball, worker bundle, and checksums attach from the Release workflow.

AXIS v2.24.6

Choose a tag to compare

@github-actions github-actions released this 18 Oct 19:01
833f0d6

Desktop build of AXIS (Tauri 2).

AXIS v2.24.5

Choose a tag to compare

@github-actions github-actions released this 17 Oct 00:52
d946a42

Desktop build of AXIS (Tauri 2).