Skip to Content
Storage & persistence

Storage & persistence

There is no application server, so all run state lives in your browser. This page is the complete inventory: what is stored, under which key, and how to clear it.

Two mechanisms are in use — localStorage for small UI preferences, and IndexedDB for things that can grow large.

localStorage — UI preferences

All keys are versioned and prefixed trad:. They are written defensively: a full or unavailable localStorage degrades the feature, it does not break the app.

KeyHolds
trad:ui-theme:v1Selected theme
trad:workspace:v1Which workspace is active (chart / optimizer)
trad:sidebar-tab:v1Selected sidebar tab
trad:sidebar-width:v1Sidebar width
trad:mobile-pane:v1Which phone pane was last open (chart / sidebar)
trad:sidebar-tab:v1Shared by the main sidebar and the optimizer entry point
trad:backtest-h:v1Height of the backtest panel
trad:history-depth:v1How many bars of chart history to load
trad:user-strategies:v1Strategies you created in the editor
trad:user-symbols:v1Symbols you added to the picker
trad:user-timeframes:v1Timeframes you added
trad:optimizer-config-collapsed:v1Whether the optimizer config column is collapsed
trad:analysis-view:v1Analysis display preferences
trad:analysis-job:<jobId>Per-job Analysis state — comparison, selected trial, page
trad:opt-detached-sort:v1Detached window sort
trad:opt-detached-filter:v1Detached window filters
trad:opt-detached-selected:v1Detached window selection
trad:opt-detached-tab:v1Detached window active tab
trad:binance-ban-until:v1Binance rate-limit ban marker
Per-job Analysis state is namespaced by job id, so two runs never share it

The job id is part of the key, not part of the value: trad:analysis-job:<jobId>. Two runs therefore write two records instead of overwriting one another, and each keeps its own comparison, selected trial and page.

The same property is what makes targeted cleanup possible. Deleting the key for one job leaves every other job’s Analysis state untouched, so one stale run can be cleared on its own rather than by wiping the table.

Optimizer drafts

Optimizer configuration is drafted before a run starts, keyed by a normalized strategy-source identity — so returning to the same source restores your draft, while an edited source starts clean. The stored draft retains method, budget, seed, exclusions, ranges and choices.

IndexedDB — caches and saved runs

trad:bars:v1 — the candle cache

Wraps Vela’s BinanceProvider. Its getBars() override is the only interception point, because that is Vela’s sole public fetch entry.

Object storesbars, meta
Cache keyprovider + symbol + timeframe + session
meta recordearliest / latest ms, byte size, created and last-accessed timestamps
Hard cap~500 MB total, LRU-evicted
Max key age30 days

On a hit the requested range is sliced out of the cached bars; on a partial hit the missing range is fetched and merged. A failed write is logged and the data is still returned — a full quota degrades caching, it does not break charting.

How the cache key is composed, and what a partial hit saves you

The key is four parts — provider, symbol, timeframe and session — so all four have to match for a hit. The key names a series, not a range: the span you asked for is not part of it.

That is what makes a partial hit cheap rather than a wasted read. A request wider than the cache holds still finds what is there: the covered part is sliced out of the cached bars, only the missing range is fetched, and the two are merged. You keep the bars you already have and pay once for the gap.

What 'degrades, does not break' means when a write fails

A failed write is logged and the bars are still returned to the caller. Charting is downstream of the fetch, not of the write, so a full quota or a rejected transaction costs you the cache and not the chart.

The consequence is that nothing looks broken: the next request for the same range simply fetches again. The same contract holds for localStorage — those keys are written defensively, so an unavailable or full store degrades the feature rather than the app.

trad:optimizer-jobs:v1 — saved runs

Object storesoptimization_jobs (keyPath: "id"), optimizer-jobs-index (keyPath: "id")
ContentsRun configuration, search space, every completed trial, metrics, status
Restored onPage reload — “latest saved results” reappear

The index store exists so the results list can be read without deserialising every job body.

What is not stored

  • No market-data keys or API credentials — every endpoint used is a public, keyless read.
  • No telemetry, and no upload of runs. Nothing is sent anywhere except the endpoints in Endpoints.
  • No optimizer or backtest data leaves the machine. If you sign in, the only thing that crosses the network is your display name and contact phone.

In-page events

Two custom DOM events coordinate surfaces that share no state object:

EventMeaning
trad:theme-changeThe theme changed; charts and the editor re-theme
trad:optimizer-snapshotA run produced a new snapshot; Analysis coalesces it on an animation frame

Analysis subscribes to trad:optimizer-snapshot rather than polling, and coalesces bursts with requestAnimationFrame so a fast run does not thrash the renderer.

Resetting

From the UI — the bar cache exposes a “Clear cache” control, which wipes both trad:bars:v1 object stores.

By hand — in DevTools → Application → Storage:

  • Local Storage → clear the trad: keys you want gone.
  • IndexedDB → delete trad:bars:v1 and/or trad:optimizer-jobs:v1.

Deleting is scoped: the reset path removes only the project’s own keys and databases. An unrelated same-origin key is preserved, and that is asserted by the browser test suite rather than assumed.

What each reset route actually removes

The three routes have different scopes, so which one you use is the decision:

  • “Clear cache” in the bar cache wipes both trad:bars:v1 object stores — bars and meta — and nothing else. Saved runs are not touched.
  • Local Storage is cleared per key, so the trad: keys you want gone can go without disturbing the rest.
  • IndexedDB is deleted per database: trad:bars:v1 and/or trad:optimizer-jobs:v1. The two are independent, which is what lets you reclaim a cache without losing runs.
Last updated on