Active development

Smart Trader

A trading system is not a strategy. It is the integration of market data, behavior intelligence, decision logic, risk gates, and a UI that lets a human — or an automated supervisor — trust what the system is doing.

View on GitHub ← All work

Overview

A decision-intelligence trading system: market structure, behavior signals, and explicit risk gates.

Most trading prototypes fail the same way: they conflate signal with action. Smart Trader was built to keep them apart — to model market structure honestly, to expose every decision with its reasons, to bound risk per trade and per regime, and to survive the boring parts of being a real product.

What I built

  • A FastAPI backend with a continuous trading loop, SQLite-backed durable state, and a vanilla-JS dashboard served as static files.
  • A multi-provider market data layer with explicit fallback (Wallex → CoinGecko → CoinCap), normalized candle schema, and per-provider health.
  • A SignalEngine that combines trend, momentum, mean-reversion, breakout channels with ADX gating, ATR-based stops, regime scaling, and behavior intelligence as an additional weighted channel.
  • A risk model that sizes positions against equity, applies per-trade and per-regime caps, and rejects actions that would breach daily loss limits.
  • A staged deployment (production + staging subdomains, separate systemd services, separate ports, separate databases) and a deliberate non-modification contract with the live trading engine.

Why it matters

Smart Trader is the project where the constraint of "must run for real, every day, with real money" stops being optional. It taught me that a clean strategy is not the deliverable — the deliverable is the system that survives contact with a live market, bad data, and an operator who has to sleep.

Problem

Most trading prototypes fail the same way: they conflate signal with action. Smart Trader was built to keep them apart — to model market structure honestly, to expose every decision with its reasons, to bound risk per trade and per regime, and to survive the boring parts of being a real product.

Architecture

Market providers        Ingestion           Decision              Execution          Presentation
───────────────         ─────────           ────────              ─────────          ───────────
Wallex        ──┐                         ┌─▶ trend_channel       ┌─▶ sizing          ┌─▶ FastAPI
CoinGecko     ──┼──▶ MarketDataProvider──▶├─▶ momentum_channel  ──┼─▶ risk gates    ──┼─▶ /api/*
CoinCap       ──┘    (normalized candles) ├─▶ meanrev_channel    ──┤  └─▶ position     └─▶ static/*
                        regime detection   ├─▶ breakout_channel   ──┤
                        ATR / ADX / VR     ├─▶ behavior_intel   ───┘
                                            └─▶ aggregate + reasons

                            SQLite ◀── decision logs / trade events / account snapshots
                            nginx ◀── /api/* → :8000 │ static/* │ /dashboard

Engineering decisions

  • Multi-provider market data with explicit fallback and per-provider health — never trust a single feed.
  • Behavior intelligence as a weighted channel, not a black box override.
  • Risk gates before every position change, not after.
  • Regime-aware thresholds so the same signal in a high-volatility regime cannot trigger the same action as in a low-volatility one.
  • Hard separation between strategy logic (trading_logic.py) and infrastructure (web_app.py, owner_api.py) — the strategy can be reasoned about in isolation.
  • Staging subdomain with its own service, port, and database; no shared state with production.
  • No silent refactors of the live engine. Add new endpoints and tables additively, never mutate the contract that production depends on.

What changed during development

  • A first dashboard tried to do too much in one screen. It was rebuilt into a layered structure: landing for first impression, dashboard for the human in operations, app for power use.
  • Initial risk gating was per-decision only. It became clear the system needed per-day loss limits and per-regime caps — without them, a low-quality streak drained equity even when individual trades were "correct".
  • The market data layer initially assumed one provider would always be available. The first live outage made the fallback contract non-negotiable.
  • A version of behavior intelligence was bolted onto the engine without an interface. It became ungovernable. The new version is a bounded weighted channel with explicit scoring.

Technical implementation

  • Python 3.12 with FastAPI for HTTP, sqlite3 for durability, vanilla JS + CSS for the UI.
  • SignalEngine with DecisionContext, StrategyParams, Account, Position dataclasses.
  • MarketDataProvider interface with WallexProvider as primary and HTTP-based fallback providers.
  • Behavior engine as a separate module with explicit score, bias, and provider list fields on the decision context.
  • systemd services: smarttrader-api (HTTP), smarttrader-bot (decision loop); staging duplicates on different ports and databases.
  • nginx reverse proxy: /api/* → :8000, static/* served from disk, /dashboard served from disk.
  • GitHub Actions deploy-prod / deploy-stg with rollback artifact, DB backup, schema ensure, restart, and smoke tests.
  • Telegram integration for outbound notifications.

Product implications

  • A trading product is as much about trust as about edge. Operators must be able to answer "what is the system doing right now, and why?" in one screen.
  • The boring parts of a trading product — observability, rollback, staged deploys, additive endpoints — are what separate a prototype from a system.
  • Risk gates must be visible. A trade that the system did not take is more important than a trade it did.
  • A UI is a contract. Once shipped to operators, every behavior change must respect that contract or signal a migration.

Lessons learned

  • Product thinking changes engineering. Architecture exists because of a user, business, or system problem, not because a technology is fashionable.
  • Behavior intelligence without an interface is technical debt waiting to happen. Every non-core module deserves a typed contract.
  • Operators are users too. The system has to be operable, not just correct.
  • Refusing a trade is a first-class product action, not the absence of one.
  • Staging is a feature, not a courtesy. A second environment catches the failures you did not know to test for.

Current state

Active development. The decision engine, provider abstraction, behavior intelligence, and UI are in production. The additive-only deployment contract with the running services is honored. The next iteration focuses on better behavior modeling, broader provider coverage, and explicit risk visualizations for operators.