Quantitative finance · 2026

Simple Stock Trader

The same strategy object runs live and in the backtest.

Most retail trading systems fail at the seam between research and execution: the backtest runs one implementation and the live engine runs another, and the two quietly diverge. This platform has one implementation. The identical strategy and risk-manager objects are instantiated by the live loop and by the event-driven backtester, so a result that reproduces in research reproduces in production or the bug is in the data, not the code.

Status
Production
Domain
Algorithmic trading · Risk systems
Role
Architecture, strategy design, risk engine
Year
2026

Stack

  • Python
  • C++ · pybind11
  • R
  • Tauri
  • FastAPI
  • SQLite
  • asyncio

An algorithmic foreign-exchange platform with two mechanical strategies, conviction-scaled position sizing, drawdown circuit breakers, an event-driven backtester, and crash-safe auto-flatten.

01

Two strategies that hedge each other

A range-breakout scalper trades session breakouts gated by volatility-regime percentile, compression, trend agreement, relative volume and a spread cap.

Its paired counterpart fades exactly the breakouts those gates reject — the false moves the scalper is designed to avoid. A third, slower trend-continuation system runs on the hourly frame across crosses and metals. The pairing means the filter that costs the first strategy an entry is the signal that gives the second one.

02

Risk as layers, not a stop-loss

Position size is a function of a multi-layer conviction score rather than a fixed fraction, and it sits underneath drawdown circuit breakers and per-strategy cooldowns.

Above all of it are the operational safeties that matter more than any edge: a kill switch, a heartbeat watchdog, and crash-safe auto-flatten so an engine that dies does not leave a position open behind it.

03

One implementation, and what that costs

Sharing strategy and risk objects between the live loop and the backtester removes an entire class of silent divergence, but it constrains the design: every strategy must be written against an event interface that a historical replay can satisfy, which rules out convenient shortcuts like reaching for the current wall-clock time or querying a broker mid-decision.

The discipline is the point. Anything a strategy cannot get from the event stream is something the backtest could not have known either.

04

C++ where it earns its keep

Indicator hot paths — rolling volatility, channel extremes — are compiled C++ bound in-process through pybind11.

Everything else stays in Python, where it can be read and changed. The boundary is drawn at measured cost, not at preference.

05

The adapter is the seam

Live broker, an optional second platform, and the backtest all sit behind one adapter interface.

That is what makes the shared-implementation claim enforceable rather than aspirational: the engine cannot tell which one it is talking to, so there is nowhere for live-only behaviour to hide. Adding a venue is implementing an interface; it is not touching a strategy.

06

Safety before edge

The features that took the most care are the ones that produce no return: a watchdog that flattens on heartbeat loss, notifications tiered by severity so a critical event is not buried among informational ones, and state persisted such that an engine restarting mid-session recovers its own positions rather than discovering them.

An edge that survives one bad disconnection is worth more than a better edge that does not.

Pipeline

One implementation, two drivers

Stage Can reject

  1. 01

    Adapter

    Event stream

    Live or replay — engine cannot tell

  2. 02

    Strategy

    Signal

  3. 03

    Gates

    Filtered signal

    Regime · volume · spread

  4. 04

    Conviction

    Position size

  5. 05

    Risk

    Approved order

    Drawdown breaker · cooldown

  6. 06

    Execution

    Fill + state

The mathematics

What the model
actually computes.

Equations and every symbol defined. Fitted parameters — shares, tapers, elasticities, reconciliation bands — stay in the configuration they belong to.

01

Conviction-scaled position size

Size is a function of how many independent conditions agree, floored at zero and capped by the risk layer above it — never a fixed fraction.

κ = Σg wg · 1[g] Q = κ·R·E λ·ATR · θdd
κ
Conviction score from the weighted gates
1[g]
Indicator that gate g passed
R
Risk fraction per trade
E
Account equity
ATR
Average true range — the volatility unit
λ
Stop distance in ATR units
θdd
Drawdown throttle, 0 when a breaker has tripped

Sizing in volatility units rather than price units is what keeps risk per trade constant across instruments and regimes. The drawdown throttle multiplies rather than subtracts, so a tripped breaker takes size to zero instead of to something small.

Skills exercised

What the build
actually demanded.

Against the corpus · 15 systems

  • Stack breadth 7
  • Design decisions 6
  • Pipeline stages 6
  • Decision gates 2

This system Corpus median

Strategy

  • Session-breakout and mean-reversion system design
  • Volatility-regime classification and signal gating
  • Paired strategies that monetise each other’s rejections
  • Multi-timeframe trend continuation across FX and metals

Risk

  • Multi-layer conviction scoring driving position size
  • Drawdown circuit breakers and per-strategy cooldowns
  • Spread and slippage modelling shared with the backtester
  • Kill switch, heartbeat watchdog and crash-safe flatten

Engineering

  • Event-driven backtesting that reuses live code paths
  • C++ indicator kernels bound in-process via pybind11
  • Async broker adapters behind one interface
  • Native desktop packaging from a web UI

What it establishes

  • One implementation

    Live and backtest share strategy and risk objects

  • Layered conviction sizing

    Under drawdown breakers and cooldowns

  • Crash-safe

    Kill switch · heartbeat watchdog · auto-flatten

  • Native desktop

    Rust shell, one binary per platform

  • Venue-agnostic

    Live, alternate platform and replay behind one interface