Verification status. Public Binance Spot prices and candles can stream live, without credentials. That feed is read-only and is not connected to strategy evaluation or portfolio accounting. The bundled strategy results use synthetic or replay data. The production authenticated account and order lifecycle is not qualified: see the live-gate report. No strategy profitability is claimed. Trading can lose all of your capital. Nothing here is financial advice, and the project makes no profit claims of any kind.
Trading Crew is an open-source Python/FastAPI application that models a "crew" of 50 small, typed, deterministic agents. Together they take a market-data replay through data QA, feature engineering, regime detection, sizing, a hard-coded risk engine, paper fills, settlement, reconciliation and honest reporting. A dashboard shows every step, and an append-only ledger and trace record every decision.
Repository topics: dashboard, python, risk-management, websocket, market-data.
Verification scope: automated tests and local drills cover application behavior and offline execution contracts. They do not establish production readiness, exchange order capability, or profitable performance. The current gate-by-gate status and evidence are maintained in reports/LIVE_GATES.md.
The D4 replay now reports causal trend, volatility and spread labels and groups closed paper outcomes by the regime at entry. This report is descriptive only: it does not route strategies, and the bundled synthetic sample is too short to establish an edge.
The project is built with a risk-officer mindset. Capital preservation comes first, risk controls fail closed, and every number on the dashboard is either a real output of the code running on the bundled sample data or clearly labelled as decorative or synthetic. When something is missing, the app says so ("NOT CONFIGURED", "DISABLED_BY_POLICY", "CALENDAR_MISSING"). It never fills the gap with plausible-looking fake data.
Trading Crew command center dashboard
git clone https://github.com/CodesbyFebin/Trading-Crew-app.git
cd Trading-Crew-app
scripts/run_all.shThe script creates a virtual environment, runs tests and safety drills, runs the bundled replay, and serves the dashboard
on http://127.0.0.1:8795. If no session tokens are configured, generated local-only tokens are saved to
var/session.env (mode 600, gitignored). No exchange keys or network trading connection are used. The optional default market feed connects only to Binance public market-data endpoints.
See Quickstart, safety architecture, end-to-end lifecycle coverage, known limitations, and the 17-page dashboard screenshots.
grk.mp4
- What this is (and is not)
- Safety architecture
- Mode ladder
- Desks
- The 50 agents
- Trading skills catalog
- Shared memory
- Model router (advisory only)
- JEV, the advisory assistant
- Downloads (desktop and Android)
- Quickstart
- Configuration and environment variables
- API reference
- Testing and drills
- Reference results (synthetic data)
- Audit summary and known limitations
- Project layout
- Contributing, security, license
- Disclaimers
It is:
- A reproducible research and paper-trading harness. Given the same input file it produces the same fills, the same fees
and the same report, down to the last decimal. All money math uses Python
Decimal, never floats. - A reference implementation of trading safety controls: a fail-closed risk engine, immutable file-based limits with tamper detection, a kill switch, a promotion ladder that requires signed owner approval, and an append-only audit trail enforced by database triggers.
- A multi-agent pipeline in which every "agent" is ordinary deterministic Python with metrics (calls, errors, average latency, SLA breaches, queue depth) that the dashboard shows live.
- A dashboard (FastAPI and vanilla JavaScript) with 19 pages, including a Control Room and a reference-inspired Showcase wired to the app's run, agent, risk, audit, memory, skills and paper-fill APIs: Command Center, Reactor, Floor, Crew Pool, Tickets & Trace, Markets, Research, India D5, Risk Console, Connectors, Approvals, Reports, Backtests, Memory, Skills, LLM Routing and JEV.
It is not:
- A trading bot you can point at an exchange. Live adapters exist only as stubs that raise before any network code could run.
- An AI that decides trades. No large language model (LLM) ever makes a trade or risk decision. The optional LLM gateway can only produce advisory text notes. A test proves that no LLM code path can reach the order, risk or mode modules.
- A profitable strategy. The sample strategy is a placeholder (an EMA crossover gated by a regime filter). On the bundled synthetic sample it loses money after costs (see Reference results).
The safety design rests on one principle: hard limits live in code and files outside any model, and every failure mode blocks the trade instead of allowing it.
Every order intent passes through RiskEngine.check() before a paper fill can happen.
- Fail closed. The check runs with a timeout. If it times out, raises an exception or returns the wrong type, the order
is vetoed with a reason that starts with
fail-closed:. - At-limit counts as a breach. Comparisons use
>=, so an exposure exactly equal to a cap is rejected. - Decimal only. Non-Decimal numeric inputs are rejected, which caught a real bug during development (an empty
sum()returned anint). - Limits come from
config/limits.jsonand are loaded read-only, together with their SHA-256 hash. ALimitsAuditorre-hashes the file and logsLIMITS_FILE_CHANGEDas a critical risk event when the file is modified. The API refuses any attempt to change limits at runtime and writes that attempt to the audit log.
The limits currently in config/limits.json:
| Limit | Value |
|---|---|
| max position (fraction of equity) | 0.02 |
| max gross exposure | 0.20 |
| per-venue cap | 0.10 |
| daily loss | 0.02 |
| weekly loss | 0.05 |
| max slippage | 20 bps |
| min liquidity (24h quote volume) | 100,000 USD |
| max open positions | 3 |
| post-loss cooldown | 1,800 s |
| withdrawals | disabled (false) |
| drawdown halt | 0.10 |
An operator or owner can engage the kill switch from the Risk Console or with POST /api/kill. It blocks new paper orders
and marks open tickets. Order submission and kill acknowledgment are serialized. This replay/paper build does not own a
live position or exchange connection, so the kill switch does not flatten external positions. The latest kill state is
recovered from the append-only audit journal across run IDs after a restart. Only the owner can clear it through
POST /api/kill/reset, which applies an HMAC-signed kill:reset approval and requires TOTP when configured.
Automatic daily-loss kill. When the risk engine sees a daily-loss breach (at-limit counts) it only returns a veto with
the reason daily loss limit breached; the risk worker has no side effects. The engine then engages the global kill on its
own event loop through the same engage_kill path as POST /api/kill: every desk is halted, open tickets are
cancelled/expired, and a KILL risk event plus an audit row (actor risk-engine:auto) are written to the store under the
run's run_id. Fills are serialised against kills: the kill state is re-checked under a lock immediately before any paper
fill is recorded, and a fill that loses the race is discarded. Neither a new trading day nor a process restart clears the
kill. The latest kill state is recovered from the append-only audit journal across run IDs. Only the owner can clear it
through POST /api/kill/reset, which persists an HMAC-signed kill:reset approval before clearing it and requires TOTP
when configured.
Daily loss is mark-to-market. Daily PnL = (cash + open positions marked to the fixture bid) − equity at the start of the
UTC day (the last mark before 00:00 UTC; the start equity on the first day). It therefore includes realized and unrealized
PnL and all fees. The limit is breached when -daily_pnl >= daily_loss_pct × min(start-of-day equity, current equity)
(at-limit counts, like every other limit). Two paths use the same pure function risk.daily_loss_breached:
the pre-trade risk check, and a per-tick open-position monitor in the engine that records a DAILY_LOSS_MTM risk event and
calls the same engage_kill (actor risk-engine:auto) even if no new order is attempted. The weekly check adds unrealized
PnL to realized PnL. On the bundled sample this changes nothing (Track 1 stays at −3.6518, 24 fills, 0 vetoes); tests in
tests/test_mtm_daily_loss.py crash a test copy of the fixture to prove an open, unclosed position triggers the kill.
Risk input validation. The risk engine rejects, with a fail-closed: ... reason, any non-Decimal, NaN or ±Infinity
value; negative notional, slippage, liquidity, gross or venue exposure; non-integer or negative position counts; venue
exposure larger than gross exposure; and a bad start-of-day equity. Timeouts, worker exceptions, malformed decisions,
a kill engaged while the check ran, and limits swapped in memory (digest check) all reject.
Owner approvals are HMAC-signed with a key read from the CREW_APPROVAL_HMAC_KEY environment variable. Without the key,
approvals cannot be signed. A signed approval is necessary but not sufficient: the promotion gates still apply.
The trace, ledger, risk events, audit log and shared memory are append-only. In SQLite, BEFORE UPDATE and BEFORE DELETE
triggers raise an error. In Postgres, a PL/pgSQL trigger function does the same. All writes go through a process-wide lock.
- Three roles: owner (view, research, approve, kill, mode change, config), operator (view, research, kill) and viewer (view).
- Tokens come from environment variables only and are accepted only in the
Authorization: Bearerheader. Tokens in the URL are rejected, and the UI removes them from the address bar. - The WebSocket authenticates with a first message
{"auth": "<token>"}sent within 5 seconds. - Optional TOTP (time-based one-time password) for owner actions: set
CREW_OWNER_TOTP_SECRETand send anX-TOTPheader. It is disabled when the variable is unset.
tests/test_brain.py::test_no_llm_path_reaches_order_risk_mode_code parses the source of every decision module (risk, modes,
kill switch, engine, connectors, desks, overseer, models, bus, store, agents, drills, skills, backtest) and fails if any of
them imports the LLM router, JEV or memory-read functions. The engine only writes deterministic facts to memory; it never
reads them back.
- No keys in code, chat or command-line arguments.
- Secrets live only in
.env, which is gitignored;.env.examplelists the variables with empty values. - The application needs no exchange keys at all, and no key may ever have withdrawal permission.
REPLAY → PAPER → LIVE_APPROVAL → LIVE_AUTO (crew/modes.py)
- Promotion moves one rung at a time, and each step has a pure gate function over recorded statistics (out-of-sample net expectancy, calibration, number of paper days, reconciliation drift, kill-drill result, slippage within model).
- Any promotion beyond PAPER also needs a valid signed owner approval.
- The API evaluates promotion evidence, signed owner approval and venue execution readiness. Setting
LIVE_ENABLED=truealone never makes order submission available. - The current build has no authenticated exchange account or live order adapter, so live execution is not configured and mode promotion fails closed.
- Any breach auto-demotes one rung. The auto-demotion drill shows LIVE_APPROVAL → PAPER → REPLAY.
| Desk | What | Data | Status |
|---|---|---|---|
| D4 | Crypto signals (Binance-style spot) | Strategy input: fixtures/binance_replay_sample.jsonl (synthetic); separate display feed: public Binance Spot REST + WebSocket |
PAPER/REPLAY only; public live feed is display-only and cannot affect signals, fills or PnL |
| D1 | Prediction markets (Polymarket-style) | synthetic fixture | NOT_APPROVED_BY_OWNER, locked to REPLAY |
| D2 | Equities night research (filings) | synthetic fixture | NOT_APPROVED_BY_OWNER, locked to REPLAY |
| D3 | Memecoin scanner (rug filters) | synthetic fixture | NOT_APPROVED_BY_OWNER, locked to REPLAY |
| D5 | India NSE equities (SEBI) | local NSE bhavcopy files from a separate archived project (optional) | ARCHIVED_RESEARCH_ONLY / NOT_APPROVED_BY_OWNER, checks-only replay |
D5 India runs no strategy. Its replay exercises only the risk, universe, timing and calendar checks, using labelled probe intents. Its locked "Gate 0" terms are:
- CNC (delivery) only and long-only; MIS, MTF, NRML, BO and CO products are rejected.
- Risk limits: 1% risk per trade; a 10% per-stock cap (exactly 10% counts as a breach); 2% daily, 3% weekly and 6% drawdown limits.
- Times (IST): entries stop at 15:00, square-off at 15:20, disable at 15:25.
- Blackout dates come from a versioned calendar. No verified calendar exists, so every entry is blocked
(
CALENDAR_MISSING).
A SEBI/go-live checklist blocks D5 from ever leaving REPLAY. It requires:
- a new approved strategy;
- broker algo-ID confirmation;
- a whitelisted static IP;
- an order rate below 80% of an assumed 10 orders per second threshold (to be verified against current circulars);
- signed owner 2FA;
- a verified audit trail;
LIVE_ENABLED, which is always false here.
When the archived project is absent (for example on a fresh clone or in CI), the D5 replay tests skip cleanly.
Each agent is an Agent object (crew/agents.py) with its own concurrency semaphore, SLA timeout and metrics. Agents run
plain Python functions. A state other than ENABLED means the agent refuses to run.
- DISABLED_BY_POLICY: the agent is intentionally unavailable under its current policy.
- NOT_CONFIGURED: a required external connector or authenticated account integration is not implemented or connected.
- NOT_APPROVED_BY_OWNER: the owner has not approved that venue.
- DISABLED_IDLE: no real work exists for it on the available data.
Names after S23 are marked *. They were inferred because the pasted blueprint was cut off, and they have not yet been
reconciled with the full blueprint.
The call counts below come from one reference paper day on the bundled sample, generated by running the engine. In that run, 32 of the 34 enabled agents were invoked:
- S34 (VetoClerk) runs only when the risk engine vetoes a trade, and there were 0 vetoes in this run.
- S15 (Backtester) runs on
/api/backtests/runandscripts/run_backtests.py.
Two disclosed caveats:
- S10 and S14 quorum. In D4 the quorum is "S10 and S14 agree on LONG". S10 computes its verdict from the EMA features, but it is evaluated only on an EMA cross-up, so it always agrees. The quorum is therefore effectively one independent check (S14). This is disclosed rather than "fixed", because changing it would alter the frozen Track 1 strategy.
- Post-run monitors. S11, S17, S29, S30, S31, S32, S33, S42 and S44 measure and record after the run. They are not in the trade decision path; the risk engine alone enforces the limits.
| ID | Name | Crew | Task | Status | Calls in reference paper day | Reason if not enabled |
|---|---|---|---|---|---|---|
| S01 | TapeWatcher | C1 | Trade/book stream (replay fixture) | ENABLED | 1 | |
| S02 | OracleSync | C1 | Pyth/Chainlink staleness checks | DISABLED_BY_POLICY | 0 | needs external data feed; no network in this build |
| S03 | PredictionBook | C1 | Prediction-market catalog (synthetic fixture) | ENABLED | 1 | |
| S04 | ChainStream | C1 | Solana/EVM mints and transfers | DISABLED_BY_POLICY | 0 | needs external data feed; no network in this build |
| S05 | DexScanner | C1 | DEX pairs (synthetic fixture) | ENABLED | 1 | |
| S06 | FilingsFeed | C1 | SEC filings (synthetic fixture) | ENABLED | 1 | |
| S07 | NewsWire | C1 | News / economic calendar | DISABLED_BY_POLICY | 0 | news/social only via deterministic event calendar per owner rule; feed not connected |
| S08 | SocialPulse | C1 | X/Telegram/Reddit velocity | DISABLED_BY_POLICY | 0 | news/social only via deterministic event calendar per owner rule; feed not connected |
| S09 | DataQA | C1 | Dedupe, schema, gaps, clock skew | ENABLED | 1 | |
| S10 | FeatureForge | C2 | Deterministic features | ENABLED | 87 | |
| S11 | VolModeler | C2 | Realized vol | ENABLED | 2 | |
| S12 | MonteCarlo | C2 | Seeded path draws for P(up) | ENABLED | 3 | |
| S13 | FairValue | C2 | Binary fair value, edge after fees | ENABLED | 3 | |
| S14 | RegimeDetector | C2 | Trend/chop regime tags | ENABLED | 86 | |
| S15 | Backtester | C2 | Replay engine | ENABLED | 0 | |
| S16 | Calibrator | C2 | Brier score | DISABLED_IDLE | 0 | no resolved outcomes exist in the D1 fixture, so there is nothing real to calibrate |
| S17 | ModelRegistry | C2 | Versioned model params | ENABLED | 1 | |
| S18 | EarningsReader | C3 | Transcript tone | DISABLED_BY_POLICY | 0 | LLM role; owner rule: LLMs never make trade/risk decisions and no LLM calls in this build |
| S19 | InsiderAnalyst | C3 | Form 4 cluster rules (synthetic fixture) | ENABLED | 3 | |
| S20 | FilingsAnalyst | C3 | 8-K red-flag rules (synthetic fixture) | ENABLED | 3 | |
| S21 | SectorAnalyst | C3 | Regulation mapping | DISABLED_BY_POLICY | 0 | LLM role; owner rule: LLMs never make trade/risk decisions and no LLM calls in this build |
| S22 | SentimentScorer | C3 | Sentiment scores | DISABLED_BY_POLICY | 0 | LLM role; owner rule: LLMs never make trade/risk decisions and no LLM calls in this build |
| S23 | TokenForensics | C3 | Deployer/rug rules (synthetic fixture) | ENABLED | 3 | |
| S24 | HolderAnalyst* | C3 | Holder concentration rules (synthetic) | ENABLED | 3 | |
| S25 | ThesisWriter* | C3 | Narrative synthesis | DISABLED_BY_POLICY | 0 | LLM role; owner rule: LLMs never make trade/risk decisions and no LLM calls in this build |
| S26 | MorningBrief* | C3 | Deterministic brief from report data | ENABLED | 1 | |
| S27 | Sizer* | C4 | Fixed-fraction sizing (Decimal) | ENABLED | 12 | |
| S28 | KellyCapper* | C4 | Fractional Kelly cap | ENABLED | 3 | |
| S29 | ExposureMonitor* | C4 | Gross/venue exposure | ENABLED | 1 | |
| S30 | LossGuard* | C4 | Daily/weekly loss tracking | ENABLED | 1 | |
| S31 | LiquidityGate* | C4 | Min liquidity check | ENABLED | 1 | |
| S32 | CorrelationGuard* | C4 | Correlated exposure | ENABLED | 1 | |
| S33 | LimitsAuditor* | C4 | Limits hash audit | ENABLED | 1 | |
| S34 | VetoClerk* | C4 | Records vetoes to trace | ENABLED | 0 | |
| S35 | OrderRouter* | C5 | Routes to paper filler | ENABLED | 12 | |
| S36 | PaperFiller* | C5 | Book-walk fills with fees+slippage | ENABLED | 24 | |
| S37 | BinanceLiveExec* | C5 | Live Binance orders | NOT_CONFIGURED | 0 | venue-specific authenticated order adapter and reconciliation are not implemented |
| S38 | PolymarketExec* | C5 | Live Polymarket orders | NOT_APPROVED_BY_OWNER | 0 | venue-specific authenticated order adapter and reconciliation are not implemented |
| S39 | BrokerExec* | C5 | Alpaca/IBKR orders | NOT_CONFIGURED | 0 | venue-specific authenticated order adapter and reconciliation are not implemented |
| S40 | SolanaExec* | C5 | Solana swaps | NOT_APPROVED_BY_OWNER | 0 | venue-specific signing and execution are not implemented |
| S41 | EVMExec* | C5 | EVM swaps | NOT_CONFIGURED | 0 | venue-specific signing and execution are not implemented |
| S42 | SlippageMonitor* | C5 | Realized vs modeled slippage | ENABLED | 1 | |
| S43 | Settler* | C6 | Position settlement | ENABLED | 1 | |
| S44 | PnLAccountant* | C6 | Net-of-cost PnL | ENABLED | 13 | |
| S45 | Reconciler* | C6 | Ledger vs positions | ENABLED | 1 | |
| S46 | WalletWatcher* | C6 | Wallet balances | NOT_CONFIGURED | 0 | authenticated account and wallet state are not connected |
| S47 | HonestReporter* | C6 | Generated honest report | ENABLED | 1 | |
| S48 | CostMeter* | C6 | Metered compute/LLM cost | ENABLED | 1 | |
| S49 | Notifier* | C6 | Telegram/Slack/email alerts | DISABLED_BY_POLICY | 0 | needs external data feed; no network in this build |
| S50 | Archivist* | C6 | Audit log export | ENABLED | 1 |
crew/skills.py provides 15 deterministic, pure-Python tools. They do no I/O, make no network calls, use no LLM and work in
Decimal throughout. Agents and API users call them; they never place orders or change risk state. They are listed on the
Skills page and can be run with POST /api/skills/{name}.
| Skill | Category | What it does |
|---|---|---|
sma, ema |
indicator | moving averages |
rsi |
indicator | Wilder RSI |
atr |
indicator | average true range |
realized_vol |
risk metric | sample standard deviation of returns |
max_drawdown |
risk metric | peak-to-trough fraction |
position_size |
sizing | fixed-fractional risk sizing rounded down to the lot step; returns 0 on invalid input (fail closed) |
kelly_fraction |
sizing | Kelly fraction, capped and never negative |
fee_slippage |
costs | walks the order book: average price, slippage versus best price, fee (the fee rate is an assumption to verify) |
round_to_step, dust_after_rounding |
exchange | lot-step rounding and the leftover dust |
brier |
calibration | Brier score |
reconcile |
accounting | ledger cash versus engine cash drift |
india_entry_window |
calendar | D5 IST entry and square-off window |
india_order_rate |
compliance | peak orders per second versus the assumed SEBI threshold |
crew/memory.py is one shared, append-only memory for all agents. Each fact records agent_id, topic, kind
(observation, metric, report, advisory or decision_record), content, provenance, a UTC timestamp, an advisory flag and a
SHA-256 hash.
- Storage: SQLite by default (
var/shared_memory.db). SetDATABASE_URLto a Postgres URL to use Postgres instead; free tiers such as Neon or Supabase work. UPDATE and DELETE are blocked by triggers in both backends. - Who writes: after each paper day, the deterministic agents S09, S34, S44, S45, S47 and S48 write what they measured.
- Advisory facts: facts with
kind="advisory"must also setadvisory=True. They come only from the LLM path and are never read by order, risk or mode code. - Access:
GET /api/memorysupportsq,topic,agent_idandlimit. The Memory page shows and searches the facts.
crew/llm_router.py is an OpenAI-compatible /chat/completions client with a fallback order, timeouts and a per-agent model
mapping (config/llm_routing.json). It is optional and off by default.
- Provider order: OpenRouter (
:freemodels) → Kilo AI Gateway → Groq → Cline. - Keys come only from env or
.env:OPENROUTER_API_KEY,KILOCODE_API_KEY(base URL overridable withKILOCODE_BASE_URL),GROQ_API_KEYwithGROQ_MODEL, andCLINE_API_KEYwithCLINE_MODEL. - Without any key the router reports NOT CONFIGURED and raises. It never fabricates a response.
- Model IDs were not invented. On 2026-10-20 they were checked against the public model lists (OpenRouter
https://openrouter.ai/api/v1/modelsand the Kilo gateway/models). The snapshot is saved inconfig/llm_sources.json, and a test asserts that every configured model ID appears in it. - Not verified: Cline's base URL comes from its docs, but it needs a key and no free tier was found. The Groq base URL was not re-verified, and its model must be supplied by you.
- Output is an
AdvisoryNote: text only, labelled advisory.
JEV is a chat panel (the pink "GROKBOT + JEV" page) backed by the model router.
- What it reads. JEV reads app state only through a fixed whitelist of read-only tools: summary, risk, audit, reports, memory, skills, desks, india and backtests. Each tool returns a JSON copy.
- What it writes. An operator or owner can save an answer to shared memory as an advisory note
(
save_note: trueneeds thejev_notepermission; a viewer gets 403 before any LLM call). Notes arekind=advisoryand are never read by order, risk, mode or kill code. - Usage limits (
config/llm_budget.json, enforced increw/llm_budget.pybefore any provider call): per-role calls per minute and per UTC day, total calls and tokens per day, and an optional cost cap. A call whose provider reports no usage is charged atmax_tokens. If a cost cap is set and any cost is UNKNOWN, further calls are refused because the spend cannot be bounded. Limited calls return statusLIMITED.GET /api/jev/budgetshows the counters. Questions are 1–2000 chars. - What it cannot do. It holds no reference to the engine, risk engine, mode controller, kill switch, connectors, approvals or keys. Requests to place orders, change modes or limits, approve anything or reveal keys are refused in code before any LLM call.
- With no key it shows NOT CONFIGURED together with the real read-only context it gathered, and it never invents an answer.
crew/research_ta.py writes LLM research text only: a market-analyst report, then a bull and a bear argument. The
prompt patterns are adapted from TradingAgents (Apache-2.0; see
third_party/tradingagents/NOTICE.md and its LICENSE). No TradingAgents code is imported, and its trader,
risk-debate and portfolio-manager roles, ratings, memory/reflection, TypeSafe post-screening and ChatGPT login were
not ported.
- Off by default. Enable with
CREW_RESEARCH_TA=1. Only operators and owners can run it (POST /api/research/ta). - Input. By default, facts computed from the bundled synthetic fixture, labelled
SYNTHETIC_REPLAY; news, sentiment and fundamentals areunavailable. Network data (yfinance, fromrequirements-research.txt, not in the core lock) needsCREW_RESEARCH_TA_NETWORK=1and the owner role, and is labelledUNVERIFIED_THIRD_PARTY. - LLM calls go through
LLMRouterafter anLLMBudgetreservation (3 calls per run, shared with JEV). No portfolio context is sent. - Output. Three
kind=advisorymemory facts andreports/research-ta-<symbol>-<time>.md, each marked "LLM opinion, not a signal". LLM text is quoted verbatim; any rating word stays inside the quote and there is no rating field. Nothing reaches orders, risk, modes, limits or the kill switch (tests/test_research_ta.py).
Paper execution only. Live order placement is disabled in every build. The Market page may show real public Binance Spot quotes; bundled run results remain synthetic replay data. Not financial advice.
| Platform | Download | Checksum |
|---|---|---|
| Windows 10/11 (x64, single .exe) | trading-crew-windows-x64.exe | .sha256 |
| Debian / Ubuntu (amd64) | trading-crew-linux-amd64.deb | .sha256 |
| Android 7+ (viewer) | trading-crew-viewer.apk | .sha256 |
All checksums are also in SHA256SUMS.txt. These links point to the v0.1.0 pre-release (GitHub's releases/latest skips pre-releases, so versioned links are used). All releases: Releases page. Builds come from .github/workflows/packaging.yml on a v* tag, which builds, smoke-tests and attaches the files.
Verify before running: sha256sum -c trading-crew-linux-amd64.deb.sha256 (Linux) or Get-FileHash trading-crew-windows-x64.exe -Algorithm SHA256 (Windows).
Desktop (.exe / .deb). One bundled app: the FastAPI server plus a native window (pywebview; it falls back to your browser if no webview is available). It listens on 127.0.0.1 only and starts in PAPER mode by default. An explicit LIVE_ENABLED=true is passed through, but this build has no live order adapter or authenticated account connection, so order submission remains unavailable. LLM/DB keys from the launching shell are ignored. On first run it generates random owner/operator/viewer tokens and an approval key in your per-user data folder (%APPDATA%\TradingCrew on Windows, ~/.local/share/trading-crew on Linux; file mode 0600 where supported). No secrets ship inside the build. Install the .deb with sudo apt install ./trading-crew-linux-amd64.deb, then run trading-crew (--headless for server only).
Android (.apk). The trading engine does not run on the phone. The app is a thin, read-only viewer for your own desktop instance on your LAN: start the desktop app with trading-crew --lan --port 8790, get the viewer token with trading-crew --print-viewer-token, then enter http://<your-PC-IP>:8790/ and that token in the app. Use only the viewer token on a phone, never the owner token. Traffic is plain HTTP on your LAN, so use a network you trust. Long-press Back to reset the connection.
Unsigned builds. Unless the release notes say otherwise, the .exe is not code-signed (Windows SmartScreen will warn), and the APK is a debug build or signed with the maintainer's own key (Android/Play Protect may warn about an unknown developer). Only install files whose SHA256 matches.
git clone https://github.com/CodesbyFebin/Trading-Crew-app
cd Trading-Crew-app
cp .env.example .env # optional: fill in tokens; leave LLM keys empty if you don't want JEV answers
scripts/run_all.sh # venv + pinned deps, pytest, drills, paper day, backtests, then serves 127.0.0.1:8795If you did not set tokens, run_all.sh generates throwaway ones and writes them to var/session.env (mode 600, gitignored).
It does not print them. Open http://127.0.0.1:8795/ and paste a token into the sign-in form.
cp .env.example .env # set CREW_*_TOKEN, CREW_APPROVAL_HMAC_KEY and POSTGRES_PASSWORD
docker compose up --build- The image is
python:3.13-slim, runs as non-root uid 10001 and contains no secrets. - Compose starts the app plus
postgres:17-alpineandredis:7-alpine. - Verified so far: the image builds, and the container serves the API on SQLite.
- Not yet verified: the app reaching Postgres inside Docker, and
docker composeend-to-end (see known limitations). - Redis is provisioned, but the runtime engine does not use a bus yet (only the drills do).
| Variable | Purpose | Default |
|---|---|---|
LIVE_ENABLED |
opt-in mode-promotion flag; not an order capability by itself | false |
CREW_OWNER_TOKEN / CREW_OPERATOR_TOKEN / CREW_VIEWER_TOKEN |
API role tokens | none (required) |
CREW_APPROVAL_HMAC_KEY |
signs owner approvals | none (approvals cannot be signed) |
CREW_OWNER_TOTP_SECRET |
optional TOTP for owner actions | unset = disabled |
DATABASE_URL |
Postgres for ledger and shared memory | unset = SQLite |
CREW_LEDGER_PATH |
persistent SQLite ledger/audit DB, including restart-safe kill state | var/trading_crew.db |
DB_CONNECT_TIMEOUT_S |
Postgres connect timeout | 10 |
CREW_MEMORY_PATH |
SQLite path for shared memory | var/shared_memory.db |
OPENROUTER_API_KEY, KILOCODE_API_KEY, KILOCODE_BASE_URL, GROQ_API_KEY, GROQ_MODEL, CLINE_API_KEY, CLINE_MODEL |
advisory LLM gateway | unset = NOT CONFIGURED |
INDIATRADER_ROOT |
path to the archived India project (D5 replay) | /workspace/indiatrader |
TEST_DATABASE_URL |
enables the Postgres tests | unset = skipped |
Files:
config/limits.json: hard risk limits (immutable at runtime).config/llm_routing.json: providers, fallback order and agent-to-model mapping.config/llm_sources.json: the verified model-list snapshot.
Every endpoint requires Authorization: Bearer <token>. Owner actions additionally need X-TOTP when TOTP is enabled.
JSON Schemas for all request bodies and typed models are in schemas/ (draft 2020-12).
| Method | Path | Role | Purpose |
|---|---|---|---|
| GET | /api/whoami |
view | your role |
| GET | /api/summary |
view | header stats (mode, paper PnL, fees, fills, kill state, LLM cost) |
| GET | /api/agents |
view | the 50 named roles (deterministic Python functions, not independent AI agents) with call metrics |
| GET | /api/desks, /api/india |
view | desk status; D5 Gate 0, SEBI checklist and replay |
| GET | /api/tickets, /api/trace |
view | ticket state machine history; append-only trace |
| GET | /api/pnl, /api/markets, /api/research |
view | paper PnL and equity curve; fills and QA; D1–D3 fixture output |
| GET | /api/reports, /api/reports/{name} |
view | generated reports |
| GET | /api/risk, /api/drills |
view | limits, hash, kill state, risk events, drills |
| POST | /api/drills/run |
operator | re-run the safety drills |
| GET/POST | /api/connectors, /api/connectors/{name}/test |
view | connector health and contract tests |
| GET/POST | /api/configs, /api/configs/rollback/{ver} |
owner | versioned config (risk limits are rejected) |
| GET/POST | /api/mode |
owner | mode ladder; promotions require evidence and signed approval; no live-evidence source is wired |
| POST | /api/kill |
operator | engage the kill switch |
| POST | /api/kill/reset |
owner | clear the kill with an HMAC-signed approval (and TOTP when configured) |
| GET/POST | /api/approvals |
owner | signed approvals |
| GET | /api/audit |
view | audit log |
| GET / POST | /api/backtests, /api/backtests/run |
view / owner | S15 grid backtests |
| GET | /api/memory |
view | shared memory |
| GET | /api/skills |
view | skills catalog |
| POST | /api/skills/{name} |
owner/operator (research) |
execute a deterministic local skill (argument JSON limited to 16 KiB) |
| GET | /api/llm |
view | router status and recent calls |
| POST | /api/jev |
view; save_note needs operator/owner |
ask JEV (advisory text only); rate- and budget-limited |
| GET | /api/jev/budget |
view | LLM usage counters and limits (no keys) |
| POST | /api/research/ta |
operator/owner (research) |
optional TA research desk; off unless CREW_RESEARCH_TA=1; LLM text only |
| GET | /api/v1/metrics |
view | Prometheus text format |
| GET | /api/candles?symbol=&interval= |
view | OHLC of the replay mid price plus paper fills |
| GET | /api/market-data, /api/market-data/candles |
view | live public Binance Spot quotes/candles; read-only and separate from execution |
| GET | /api/execution/status |
view | actual venue-client/account/order readiness; LIVE_ENABLED alone never marks execution ready |
| GET | /api/execution/gates |
view | server-computed live readiness checklist and evidence state |
| GET | /api/risk/utilisation |
view | per-limit utilisation (last check and peak) |
| WS | /ws/{summary,agents,trace,pnl} |
view | live updates; first message {"auth": token} (tokens in URLs are ignored) |
Every mutating route (/api/mode, /api/configs, rollback, /api/approvals, /api/kill, /api/drills/run,
/api/backtests/run, JEV notes) writes an audit row, including rejected mode changes. D4 responses (/api/summary,
/api/pnl, /api/markets, /api/candles, /api/book), reports and backtests carry a provenance block: source,
sha256, data_status: SYNTHETIC_REPLAY, verified_historical: false, UTC time range, labelled assumptions (fee rate
ASSUMED, slippage MODELLED from synthetic L2, funding NOT MODELLED) and limitations.
Exposure. All launchers bind to 127.0.0.1 by default (scripts/run_all.sh, docker-compose.yml maps
127.0.0.1:8795, the desktop app only uses 0.0.0.0 with --lan). The app speaks plain HTTP. If you expose it beyond
localhost, put it behind a TLS-terminating reverse proxy (for example Caddy or nginx) on a trusted network, and use long
random tokens; bearer tokens over plain HTTP on a LAN can be sniffed.
Run isolation (run_id).
- Every row in the trace, ledger, risk events and audit log carries the store's
run_id(a UUID). - Keys and indexes:
- SQLite: trace has the composite primary key
(run_id, trace_id); every table gets a unique index on(run_id, seq|trace_id)and an index onrun_id. - Postgres: the same indexes are added.
- SQLite: trace has the composite primary key
- All reads behind drift, wins and net PnL are scoped to the current
run_id, so two runs sharing one database cannot pollute each other. Tests run two concurrent paper days on one SQLite file (and on Postgres), and each matches an isolated run. - Migration. Older databases get
run_idadded withALTER TABLE ... ADD COLUMN ... DEFAULT 'legacy', so older rows stay readable under thelegacyrun. - Concurrent setup. Schema creation is serialised: an in-process lock with a SQLite busy timeout, and a Postgres advisory lock.
LLM metering.
- The router records
prompt_tokensandcompletion_tokensfrom each provider response'susagefield. - Cost is computed only from
config/llm_pricing.json. Those prices were copied on 2026-10-20 from the public OpenRouter and Kilo model-list APIs, and each entry records its source URL. All configured models are free (price 0). - Any other model, or a response without
usage, is costed as UNKNOWN and is never guessed. - The header's LLM cost combines the engine's 0 calls with the router's real totals.
Prometheus metrics: GET /api/v1/metrics.
- Requires a viewer token. In Prometheus, set
authorization: {type: Bearer, credentials_file: ...}; keep the server bound to localhost or behind a reverse proxy. - The registry is built fresh on each scrape from the current run's telemetry. Series:
crew_intents_submitted_total;crew_rejects_total{reason}(no_quorum, risk:<reason>, stale_data:<why>, kill_switch);crew_risk_breaches_total;crew_quorum_outcomes_total{outcome};crew_signal_to_decision_seconds(histogram of wall time from signal to ticket decision);crew_kill_switch_engaged;crew_mode_info{mode,run_id};crew_bus_dlq_size{source="drill"}. This comes from the last bus_chaos drill, because the runtime engine has no message bus.
- In the reference run: 86 signals, 74 rejected for no quorum, 12 intents submitted, 0 risk breaches.
Offline order book and feed guard (crew/orderbook.py).
L2Bookvalidates the book (sorted, positive, not crossed) and gives spread, depth (optionally within N bps of mid) and a VWAP fill-cost estimate with impact versus best price and versus mid. It is tested on the replay fixture.StalenessGuardblocks signal evaluation when a symbol's data gap exceedsmax_gap_s, when timestamps go backwards, or when exchange time drifts from local time by more thanmax_drift_s. The settings are inconfig/feed_guard.json(120 s and 5 s); they are not risk limits.- It is wired into the D4 replay. The bundled fixture has no gaps, so it never fires there and results are unchanged.
- A test injects a 10-minute gap and checks that evaluation is blocked.
- Live market-data feed. The Market page may connect to Binance public Spot REST and WebSocket endpoints without API keys. It is display-only; it is not an account feed, it does not enter the paper strategy pipeline, and live quotes are not used to calculate replay fills or PnL.
Charts.
- The Markets page shows live Binance Spot OHLC candles in TradingView Lightweight Charts™ v5.2.1 (vendored unmodified, Apache-2.0; see
web/vendor/lightweight-charts/NOTICE). Replay fills remain in a separate, explicitly synthetic panel. Showcase charts and paper KPIs are likewise separate. - The Risk Console has a limit-utilisation heatmap: value divided by limit for every real limit in
config/limits.json, at the last risk check and the peak over the run (≥ 100% means a breach, and at-limit counts).
.venv/bin/pytest -q tests # unit, integration and API tests
TEST_DATABASE_URL=postgresql://... .venv/bin/pytest -q tests # also runs the Postgres tests
/workspace/.pwvenv/bin/python -m pytest tests_e2e # Playwright smoke test of all 19 pages (needs playwright)
.venv/bin/python scripts/run_drills.py # safety drills; exits non-zero on failureWhat the test suite covers:
- the ticket state machine;
- risk at-limit breaches and fail-closed behaviour;
- limits tampering;
- signed approvals;
- the mode gates;
- the bus (leases, idempotency, dead-letter queue, backpressure);
- the append-only triggers;
- paper reconciliation;
- RBAC, header-only auth, WebSocket first-message auth and TOTP;
- shared memory, the router (no key, and fallback), JEV refusals and LLM isolation;
- the skills;
- agent wiring;
- the JSON Schemas.
- read-only public market-feed validation, candle aggregation, API authentication and allowlist enforcement.
CI (.github/workflows/ci.yml) runs pytest against a Postgres service (REQUIRE_POSTGRES=1 makes the job fail if the Postgres tests would be skipped), then the drills and a paper day.
Drills (crew/drills.py), all PASS in the latest run:
- kill_switch: blocks new paper orders and marks open tickets. No external positions can be flattened by this paper-only build.
- drawdown_halt: passes at 5% drawdown, blocks at 10%, and the block persists.
- auto_demotion: LIVE_APPROVAL → PAPER → REPLAY.
- limits_tamper: a modified limits file is detected.
- bus_chaos: duplicates, a simulated 429, lease timeouts, the dead-letter queue and backpressure are all handled.
- daily_loss_kill: runs the real paper day with the risk engine fed an at-limit daily loss from the third order intent;
the kill engages once, halts every desk, writes one
KILLrisk event and one audit row, no further fill is recorded, and an unsigned reset is refused.
These numbers come from the bundled synthetic replay sample. They demonstrate that the accounting is correct. They do not show an edge, and the sample is far too small to infer one.
D4 paper day (reports/examples/paper-day-example.md, regenerated by scripts/run_paper_day.py):
| Metric | Value |
|---|---|
| Input rows / kept after QA | 8,640 / 8,640 (0 duplicates, crossed, gaps or out-of-order rows) |
| Span | 2.999 days |
| Start → final equity (marked to bid) | 10,000 → 9,996.3482 USDT |
| Net PnL after fees and slippage | −3.6518 USDT |
| Fees paid | 3.5936 USDT (assumed 10 bps fee rate, unverified) |
| Average slippage | 0.50 bps |
| Fills / round trips / winners | 24 / 12 / 2 |
| Tickets settled / rejected (no quorum) / vetoed | 12 / 74 / 0 |
| Reconciliation drift | 0 |
| LLM tokens / LLM cost in the engine | 0 / $0.00 |
Historical in-sample grid example (superseded; retained only as a historical artifact, reports/examples/backtests-example.json):
| EMA fast/slow | Regime slope | Net PnL after costs (USDT) | Fills |
|---|---|---|---|
| 20/60 | 0.0001 | −3.6518 | 24 |
| 20/60 | 0.0005 | 0.0000 | 0 |
| 10/30 | 0.0001 | −10.9396 | 60 |
| 10/30 | 0.0005 | −5.7180 | 32 |
| 30/90 | 0.0001 | −1.9840 | 8 |
| 50/200 | 0.0001 | −0.5241 | 22 |
The current S15 evaluator instead runs all six fixed variants across independent chronological 60/20/20 train, validation and holdout windows. Feature warm-up uses only preceding rows, each fold starts with independent paper equity, and every fold includes fees, mark-to-market open positions, QA counts and reconciliation drift. The Backtests page displays all 18 variant/fold results and the source SHA-256. This remains a synthetic replay; no metric is evidence of an edge.
The full audit, with file and line references, is in reports/AUDIT.md. It lists 35 items:
- 23 fixable offline: fixes are recorded inline in the audit table.
- 8 blocked by needing real data, verified evidence, or credentials.
- 11 intentional safety stubs.
- 1 uncategorised: "Jev-Claw", which could not be identified.
Known limitations, stated plainly:
- Data. The optional read-only Binance public market feed provides live quotes/candles for display only. All strategy evaluation, fills, backtests and PnL remain synthetic or replayed. No account, private stream, order or testnet connection exists.
- Unverified assumptions.
- the 10 bps Binance fee rate;
- the 2% prediction-market fee;
- the SEBI threshold of 10 orders per second;
- no connector's venue documentation has been verified.
- India D5. No verified blackout calendar exists, so every entry is blocked.
- Quorum. The D4 quorum is effectively one independent lane (see the S10 note above).
- Redis. The Redis adapter has been tested only against fakeredis, and the runtime engine does not use it.
- Docker.
docker composeand app-to-Postgres networking inside Docker are not yet verified. - LLM cost metering has been tested only with mocked provider responses. No real provider call has been made, because no key is configured.
- Agent names. S24–S50 have inferred names.
Set CREW_LIVE_MARKET_DATA=true (default) to enable the read-only Binance Spot feed. No API key or exchange account is used. CREW_LIVE_MARKET_SYMBOLS accepts up to five symbols from BTCUSDT, ETHUSDT, BNBUSDT, SOLUSDT and XRPUSDT. The service bootstraps up to 500 one-minute candles per symbol over the public REST API, then maintains quotes and one-minute bars from the public combined WebSocket stream. It reconnects with backoff and exposes connection, message-age, stale-data, reconnect and REST-error status.
The feed is available at authenticated GET /api/market-data, GET /api/market-data/candles, and WS /ws/market_data. Its values are isolated from D4's replay engine, risk checks, fills and accounting. A connected price feed does not mean live trading, a verified strategy edge, or live portfolio data. Set CREW_LIVE_MARKET_DATA=false to disable it.
Current execution-gate evidence and blockers are recorded in reports/LIVE_GATES.md. The public feed is qualified for display only; it does not make the application live-trading ready.
crew/ engine, agents, overseer, risk, modes, kill switch, store, memory, bus, connectors, desks, skills,
backtest, llm_router, jev, api
web/ dashboard (index.html, app.js, css, icons)
config/ limits.json, llm_routing.json, llm_sources.json
fixtures/ synthetic replay sample and D1–D3 fixtures
schemas/ JSON Schemas (draft 2020-12), generated by scripts/gen_schemas.py
scripts/ run_all.sh, run_drills.py, run_paper_day.py, run_backtests.py, screenshots.py, gen_schemas.py
tests/ pytest suite; tests_e2e/ Playwright smoke tests
reports/ AUDIT.md and examples/ (runtime reports are gitignored)
docs/ MkDocs site (published to GitHub Pages)
- See CONTRIBUTING.md, SECURITY.md, and CODE_OF_CONDUCT.md.
- Report vulnerabilities privately; see SECURITY.md. Never post keys or tokens in issues.
- Code of conduct: Contributor Covenant 2.1.
- License: MIT, © 2026 CodesbyFebin.
- Sponsorship:
.github/FUNDING.ymlshows a Sponsor button. GitHub Sponsors must be enabled on the owner's account for it to work.
- Paper execution only. This software does not place real orders. It can connect to public, unauthenticated Binance Spot market-data endpoints for display.
- Not financial advice. Nothing in this repository is investment, legal or tax advice.
- No profit claims. No result here shows or implies future profitability. The reference results are negative after costs.
- Synthetic results. Every strategy, backtest, paper fill and PnL result comes from synthetic or replayed sample data; live prices are not used for those results.
- Your responsibility. If you modify this software to trade real money, you alone are responsible for compliance (including SEBI rules in India and the rules in your own jurisdiction) and for any losses. Trading can lose all of your capital.
