An AI-driven market intelligence pipeline that ingests news, social, and smart-money data, turns it into scored trading signals, validates those signals against real price outcomes, and — once a signal has proven itself — routes it into a paper or live trading engine (crypto via Binance, equities via Alpaca) with a hard safety layer in front of any real money.
The system doesn't trust its own signals by default. Every signal is archived immutably, tracked against actual forward price movement, scored for calibration, and fed into a governance layer that can down-weight or fade an engine that stops earning its keep — the name comes from that mechanic, and from the trading sense of the word: betting against the crowd, not with it. Nothing gets promoted to live trading on vibes — only on measured, out-of-sample edge.
All of it is observable through a FastAPI dashboard and a Prometheus-backed status page.
| Layer | Engines |
|---|---|
| Ingestion & scoring | news_intelligence, sentiment_engine, smart_money, trend_detection, technical_engine, macro_regime_engine |
| Truth & validation | outcome_tracking_engine, signal_scoring_engine, backtesting_engine, replay_scheduler |
| Governance & adaptation | engine_governance_engine, adaptive_weighting_engine, noise_filter |
| Strategic memory | strategic_memory_engine, regime_transition_intelligence, narrative_lifecycle_engine, strategic_failure_analysis_engine |
| Feedback loop | signal_evaluation_engine, feedback_aggregator |
A dedicated engine consumes validated signals through a truth_router (which can invert or fade a signal based on its measured historical edge, not just its stated direction), sizes positions with configurable risk rules, and executes against Binance Futures or Alpaca. Real-money trading is gated behind:
TRADING_MODE=paper|live— defaults to paperMAX_DAILY_LOSS— hard drawdown kill switch- A Redis-backed manual kill switch (
trading:kill_switch) - Stop-loss / trail-stop on every position
- An out-of-sample validation gate before any engine's signals are allowed to touch live capital
A second, unrestricted paper-trading-engine-sim instance runs in parallel purely for data collection, with no gating — it exists to generate a clean comparison baseline against the gated live path.
- Runtime: Python (FastAPI, asyncio), PostgreSQL, Redis, Docker Compose (~20 services)
- Trading: Binance Futures + Alpaca via direct REST/WebSocket clients (
httpx/aiohttp), no broker SDK dependency - Bot / alerting: Discord (
discord.py) - Observability: Prometheus + a homepage-style status dashboard (
GRAFANA_PASSWORDis legacy naming from an earlier Grafana setup — no Grafana service ships today, see Setup) - AI: OpenAI (primary) with Anthropic Claude as fallback, cost-capped via a daily budget guard
- Local LLM: in-network Ollama for scheduled lessons/governance analysis
- Docker + Docker Compose v2 (
docker compose versionshould work — the old standalonedocker-composebinary also works as a fallback) - ~4GB free RAM for the full stack (the
ollamaservice is memory-hungry; skip it — see below — on smaller machines) git
Everything else (Postgres, Redis, Prometheus, etc.) runs in containers. You don't need Python installed locally unless you want to run pytest outside Docker.
git clone <this-repo-url>
cd fade
cp .env.example .envOpen .env and set the three variables that are actually required to start the stack:
| Variable | What it's for |
|---|---|
POSTGRES_PASSWORD |
Database password — pick anything, just not the placeholder |
GRAFANA_PASSWORD |
Admin password checked at preflight (see note below — no Grafana service currently ships, this is legacy naming for the status dashboard's basic auth path; set it anyway, preflight enforces it) |
DASHBOARD_TOKEN |
Bearer token for the /api/v1/dashboard/* endpoints — generate one with openssl rand -hex 32 |
Everything else in .env.example is optional and degrades gracefully:
- No
OPENAI_API_KEY/ANTHROPIC_API_KEY→ AI digest generation is skipped, everything else still runs - No
DISCORD_BOT_TOKEN→ the Discord bot container will fail to start; comment it out ofdocker-compose.ymlor just ignore its restart loop if you don't want the bot - No
BINANCE_API_KEY/ALPACA_API_KEY→ trading engines run with nothing to execute against (fine for watching signals only) - No news/social/market-data provider keys → those specific sources are silently skipped; the pipeline runs on whatever sources are configured
make preflight # checks Docker is running, .env is filled in, ports are free
make up # runs preflight, then docker compose up -d
make health # hits every service's health endpointmake preflight fails loudly and tells you exactly what's missing — run it on its own first if you want to iterate on .env without spinning containers up each time.
make logs SERVICE=api # tail one service
curl http://localhost:8000/health
curl -H "X-Dashboard-Token: <your DASHBOARD_TOKEN>" http://localhost:8000/api/v1/dashboard/summarymake up prints the URLs it started once containers are healthy:
- API:
http://localhost:8000 - Validation API:
http://localhost:8010 - Prometheus:
http://localhost:9091 - Status dashboard (
homepage):http://localhost:3001
All of these bind to 127.0.0.1 only by default — nothing is exposed outside your machine unless you explicitly set BIND_HOST in .env (e.g. to a Tailscale/VPN IP for remote/phone access) and uncomment the matching line in docker-compose.yml.
Trading defaults to paper mode — no real orders are possible until you deliberately edit .env. See Going live before you touch that switch.
The FastAPI dashboard at http://localhost:8000/dashboard is served without a token (it's static markup); every data call it makes is authenticated with DASHBOARD_TOKEN behind the scenes. Key read endpoints under /api/v1/dashboard/: summary, positions, trades, signals, router, engine-trust, finance, journal, backtest/summary. Send DASHBOARD_TOKEN as either header X-Dashboard-Token or query param ?token=.
- Create an app + bot at the Discord Developer Portal, copy the bot token into
DISCORD_BOT_TOKEN - Enable the bot's slash-command scope, invite it to your server, set
DISCORD_GUILD_IDand your ownDISCORD_ADMIN_USER_IDin.env docker compose up -d discord-bot(or it's already running as part ofmake up)
Slash commands: /help, /pnl, /positions, /stats, /market, /watch, /news, /hot, /flows, /macro, /regime, /router, /pipeline, /engine_health, /cost, /cluster, /kill (kill switch), plus region-scoped /us, /bist, /cide.
With TRADING_MODE=paper (the default), the paper-trading-engine service consumes validated signals and simulates fills with no real orders placed. Watch it work via /pnl and /positions in Discord, or GET /api/v1/dashboard/trades. A second paper-trading-engine-sim container runs unrestricted (no gating) purely to build a comparison baseline — don't mistake its numbers for the gated engine's.
Real orders only fire when all of these are true: TRADING_MODE=live in .env, valid BINANCE_API_KEY/BINANCE_API_SECRET (or Alpaca equivalents), and the signal has passed the out-of-sample validation gate. Before flipping this:
- Read
.claude/runbooks/deploy_production.md - Set
MAX_DAILY_LOSSto an amount you're fully prepared to lose - Know the kill switch:
redis-cli SET trading:kill_switch 1halts trading immediately, or use/killin Discord - Start with
BINANCE_TESTNET=trueand confirm the full loop (signal → order → fill → exit) before pointing at a funded account
make testCoverage today is concentrated in paper_trading_engine/ (portfolio, risk, execution, exit logic, reconciliation). The signal/engine pipeline under src/engines/ doesn't have automated tests yet — see Known gaps.
make preflightfails on a port: something else on your machine is already using 5432/6379/8000/8010/9091/3001. Stop it or change the host-side port indocker-compose.yml.docker compose uphangs onollama: it's the heaviest service and only needed for the weekly LLM lessons/governance job. Comment it out ofdocker-compose.ymlif you're on a small machine — nothing else depends on it at startup.- Discord bot restart-loops: almost always a missing/invalid
DISCORD_BOT_TOKEN. Checkmake logs SERVICE=discord-bot. - Dashboard returns 401:
DASHBOARD_TOKENisn't set (fails closed by design), or you're not sending it asX-Dashboard-Token/?token=. - Want remote access (phone, another machine): set
BIND_HOSTin.envto a reachable interface IP (Tailscale IP recommended over a public IP) and uncomment the matching${BIND_HOST}port line(s) indocker-compose.yml. Never bind0.0.0.0— Docker-published ports bypass your host firewall.
src/ FastAPI app, engines, providers, scheduler, Discord bot
paper_trading_engine/ Order routing, risk sizing, execution, live/paper trading
signal_edge_analyzer/ Standalone signal-edge analysis tooling
migrations/ SQL schema migrations
sql/ Ad-hoc / reporting SQL
scripts/ Operational scripts (preflight, health checks) + research scripts
monitoring/ Prometheus config, status homepage
docs/ Architecture and subsystem reference docs
tests/ pytest suite (currently paper_trading_engine only)
.claude/ Claude Code tooling: hooks, subagents, and runbooks used to
build and operate this project (see below)
This project was developed with Claude Code as a standing collaborator, and the .claude/ directory is included because the tooling is part of the story:
.claude/architecture.md— architecture reference kept in sync with the actual system.claude/runbooks/— step-by-step procedures for common changes (add a data source, debug the pipeline, deploy, analyze governance).claude/hooks/— enforcement, not documentation: a pre-tool-use hook blocks any edit that would silently flip live trading on or touch.envwithout explicit confirmation, and a post-edit hook statically checks embedded SQL against the actual migrations.claude/agents/— scoped subagents for mechanical work (log/SQL greps) and SQL-diff review
MIT — see LICENSE. Use, fork, and modify freely.
- Test coverage doesn't reach the signal/engine pipeline (
src/engines/), API routes, or the Discord bot — only the trading engine is under test. paper_trading_engine/engine.pyis a large single file (order routing, sizing, execution, and exit logic all live here); it's a reasonable target for decomposition.scripts/mixes standing operational scripts with one-off research scripts; not currently distinguished by naming or location.
This system can place real orders with real money. MAX_DAILY_LOSS, the Redis kill switch, and stop-loss/trail-stop are the last lines of defense, not the first — read .claude/runbooks/deploy_production.md before pointing this at a funded account, and never run it against more capital than you're prepared to lose entirely.
Disclaimer: this is not financial advice. Provided as-is under the MIT license, with no warranty (see LICENSE). Trading carries risk of loss; running this against real capital is entirely at your own risk.