Skip to Content
Architecture

Architecture

IndicaView is assembled from an open-source stack rather than hand-rolled. The project contributes the workspace, the optimizer, the analysis surface and the orchestration. It does not contain a charting engine or a simulation engine.

Two runtimes, one set of strategies

TrackEntry pointEngine
Interactive (browser)app/index.htmlVela + Vela PineTS
Autonomous (terminal)python orchestrator/sweep.pypinets-cli subprocess
Prop-firmpython propfirm/evaluate.pyLuxAlgo Monte Carlo CLI
Three engines behind one strategy file

The three tracks are not variants of the same runtime. One runs a browser library inside the page, one drives a Python subprocess from the terminal, one calls a Monte Carlo CLI. Each has its own entry point and its own invocation style, and the engines are not interchangeable with one another.

What they do not each have is their own strategy. app/strategies/ is the single crossing point, and because it is the only thing the three share, a change of engine never requires a change of strategy file. The strategy you backtest in the browser is the file the sweep reads.

Repository layout

app/ the browser application index.html the shell — topbar, chart, editor, panels app.js bootstrap: chart, editor, run, trades idb_bar_cache.js persistent + throttled candle cache responsive.css breakpoints, phone layout, hit-area tokens tb-select.css themed select triggers auth.css sign-in and account-area styling share-card.css result-card presentation js/auth/ optional Supabase sign-in and My Account js/share/ client-side result-card export js/optimizer/ the optimizer (engine, workers, UI, algorithms) js/optimizer.bundle.js the concatenated build of the above table-pins.js measures real column widths for pinned columns overflow-menu.js collapses button groups into a kebab enhance-select.js upgrades native selects into themed controls market-bar.js responsive market controls and the phone sheet strategies/ the .pine sources vendor/ vendored browser bundles (git-ignored) __smoke_*.cjs browser test suites pinets_cli/ Python wrapper around the AGPL CLI orchestrator/ sweep, hyperopt, rank, report, derive_r_multiples propfirm/ evaluate, firms, sweep, rank, report tests/ pytest suites + offline fixtures examples/ ready-to-run YAML configs scripts/ vendor copy, installer, bundler docs-site/ this documentation site

The browser application

A static page. There is no bundler framework, no server-side application logic and no API of our own; IndicaView serves the page and the browser does the rest. The build step copies prebuilt vendor bundles into app/vendor/ and concatenates the optimizer modules into a single bundle.

Vendored dependencies

BundlePackageLicence
vela.global.js@luxalgo/velaApache-2.0 + attribution
vela-pinets.global.js@luxalgo/vela-pinetsAGPL-3.0
monaco/ and vs/monaco-editorMIT
echarts.min.jsechartsApache-2.0

app/vendor/ is git-ignored and regenerated by the postinstall hook. It is never committed, and it is never served from a reference directory.

The vendor directory is a build product, not source

Nothing under app/vendor/ is authored here. The build step copies prebuilt bundles in, and the postinstall hook regenerates the whole directory — which is the whole reason it is git-ignored, never committed, and never served from a reference directory.

The same build step does a second and quite different job: it concatenates the optimizer modules from app/js/optimizer/ into the single app/js/optimizer.bundle.js. Third-party bundles are copied in whole; the project’s own modules are joined together. Both land under the same tree, and neither is treated as interchangeable with the other. Note also that the table above lists monaco/ and vs/ as two directories for one package, and that three of the four entries are Apache-2.0 or MIT while vela-pinets.global.js is AGPL.

Themes

The application is dark-first. Theme is expressed through CSS custom properties, and switching it re-themes the chart, the editor and every panel from one source — the editor swaps its syntax token set rather than maintaining a second copy of the palette. A trad:theme-change event fans the change out to components that redraw on theme change.

One palette, swapped rather than duplicated

There is no second copy of the palette in the tree. The editor is the case worth naming: it swaps its syntax token set to match the active theme rather than carrying a palette of its own, which is what stops the two drifting apart over time.

Fan-out is explicit rather than ambient. A trad:theme-change event reaches the components that redraw on theme change, and that is how the chart and every panel follow one switch from one source instead of each polling the theme.

The optimizer

A Web Worker pool evaluating trials, with seven interchangeable algorithms behind a small interface. The UI never blocks: configuration, results and analysis all stay responsive while trials complete.

What separates the optimizer's three layers

Three layers, held apart for three different reasons:

  • Algorithms — seven of them, interchangeable because each sits behind the same small interface. The loop does not know which one it is driving.
  • Workers — the pool that evaluates trials. This is the layer that buys responsiveness: configuration, results and analysis all stay live while trials complete.
  • UI — configuration and results, sitting above the pool and never inside it.

In the repository these are separate modules under app/js/optimizer/ — engine, workers, UI, algorithms — and the build step concatenates them into js/optimizer.bundle.js. So the layering is a source structure, not a load-time one: the separation that actually costs something at runtime is the worker boundary.

See Optimizer for the algorithms, the worker-pool sizing model and the metrics.

The Python track

Scripts rather than a service. Each is a single-purpose entry point that reads a YAML config, calls out to a CLI across a process boundary, and writes artifacts under results/<run-id>/.

sweep.py ──► rank.py ──► report.py │ └─► derive_r_multiples.py ──► propfirm/evaluate.py

The AGPL tooling stays on the other side of that process boundary — see Architecture decisions.

State

No database, no server. Browser state is localStorage for preferences and IndexedDB for caches and saved runs; CLI state is files on disk. See Storage & persistence.

Data flow

Binance public klines │ ├─► Vela BinanceProvider ──► idb_bar_cache ──► chart │ (IDB, throttled) ├─► BacktestAdapter ──► PineTS ──► metrics ──► results / analysis │ └─► pinets_cli/dataset.py ──► Parquet cache ──► sweep / hyperopt

Every arrow crosses a process, package or storage boundary that the project controls, which is what makes each of them individually testable.

The boundary each arrow actually crosses

The three paths are not the same kind of crossing, and treating them as one is how the diagram gets misread.

  • Package and storage. Vela’s BinanceProvider hands bars to idb_bar_cache, which keeps them in IndexedDB and throttles on the way. The chart reads out of the cache, not off the wire.
  • Package, inside one runtime. BacktestAdapter runs the .pine through PineTS and returns metrics. No process is crossed and no file is read; this one happens entirely in the browser.
  • Process and file. pinets_cli/dataset.py fetches and caches to Parquet, and sweep and hyperopt read from there. This is the only path that leaves the browser, and the only one with a process boundary in it.

Different boundary types, but each one is a boundary the project owns — which is the actual claim. A crossing you do not control is a crossing you cannot test from one side.

What the project does not do

  • No live order execution. Nothing contacts a broker.
  • No server-side application backend. The browser application is a static page: IndicaView serves it, and all computation happens in the page or in Web Workers it spawns. There is no server-side state to deploy, secure or scale. The only network service involved is optional Supabase sign-in for a profile, and — when usage metering is switched on — a credit balance on that same account.
  • No multi-asset portfolio aggregation. Deferred.
  • No homegrown charting or simulation. Those are LuxAlgo’s, and the boundary is deliberate.

See Limitations for the verification boundaries.

Last updated on