Atlas
An AI-augmented algorithmic trading research platform built incrementally on Python asyncio. It runs two engines side by side: a Mechanical Trading Engine executing classical rule-based strategies, and a Vision Chart-Reading Pipeline that reads chart images against a stated discretionary methodology. Every signal, opinion, risk assessment, and order from either engine is persisted to a local SQLite journal so decisions are auditable after the fact, not just fast.
Architecture
layer 1: perception
market data · news/sentiment · portfolio state · vision chart packets
│
▼
layer 2: cognition
research agent (filter) → regime-aware strategy engine → risk agent (veto)
│
▼
layer 3: action
execution agent → broker adapter (Alpaca live stream, or
Interactive Brokers, behind one interface)
│
▼
every signal, opinion, risk assessment, order,
and portfolio snapshot → SQLite journal
A multi-timeframe regime detector classifies each symbol's market state (trending, ranging, breakout, uncertain) on an hourly timeframe with a multi-bar hysteresis so classification does not flap. A strategy engine hosts multiple strategies, each tagged with the regime it applies to, and only activates the one whose affinity matches the current classification. Every proposed trade then passes through a Research Agent (a Claude-backed news filter) and a Risk Agent before it can reach the broker. Execution itself is broker-agnostic by a factory pattern: the same interface builds either an Alpaca adapter (live bar streaming) or an Interactive Brokers adapter.
GuardrailsThe Risk Agent runs deterministic hard rules first (cooldown, position size, portfolio exposure, daily loss limit, open-position count), all checked with no model call, then an adversarial Claude review with veto power over anything the hard rules already passed. The structural invariant across the whole pipeline: no signal reaches the broker without a risk assessment already written to the journal.
Extension pathThe platform's current direction extends this into vision-based methodology agents: a reusable pipeline extracts a discretionary trading methodology from source material into a structured, agent-consumable format, renders deterministic multi-timeframe chart packets from the journal, and runs a vision model against the methodology to produce a structured setup analysis, reviewed by an adversarial critic agent before a trade plan is constructed.
Autonomy dialThe system follows an explicit autonomy dial, Level 0 (agents research and recommend, a human approves and executes manually) through Level 3 (fully autonomous within risk constraints, a daily summary to the human). It is still cited directly in the platform's own decision log to justify staying on paper trading until edge-validation clears, and the platform starts every new methodology at Level 0, proving itself before gaining independence.
Multi-model by design, not Claude-onlyThe Vision Chart-Reading Pipeline's model choice is explicitly swappable: the default run reads charts with a small local model and only double-checks with Claude, and dedicated comparison scripts in the repo benchmark Claude against Google's Gemma models for chart-reading accuracy. Claude (claude-sonnet-4-6) is used throughout the Mechanical Engine's Research and Risk agents specifically, not as a platform-wide single-model claim.
A purpose-built labeling tool lets a human turn a rendered chart into a verified ground-truth label, recomputing objective fields server-side rather than trusting whatever the browser submits, with every label versioned and never overwritten. A separate scorer then grades the vision model's chart reads against that human-labeled set on price, trend, regime, and support/resistance accuracy specifically, deliberately decoupled from trading P&L: the same evaluation-first discipline as Plumbline and AdClaim, applied to perception accuracy instead of SQL correctness or RAG faithfulness.
Architecture & Trade-offs
What was built, and what was deliberately chosen against.
Risk is structurally independent
The Risk Agent runs a separate system prompt and a separate model call from the strategy and research agents, with deliberately opposite incentives. If the same reasoning that proposes a trade also approved it, there would be no real risk management, just one agent agreeing with itself.
Deterministic rules gate the LLM, not the other way around
Hard risk rules (cooldowns, position limits, exposure caps, drawdown halts) are checked first, with zero API calls. The adversarial LLM review only runs on trades that already pass the deterministic gate, so a model call is never the only thing standing between a signal and a live order.
Local-first persistence, and the journal is the asset
SQLite over a hosted database: zero ops, single file, and every signal, opinion, risk assessment, order, and regime state is written before the next stage runs. The journal, not any specific strategy, is treated as the durable long-term output of the system.
Regime-gated strategy selection with hysteresis
Strategies declare which market regime they are designed for and only run when the detector's current classification matches, rather than one strategy running unconditionally. A multi-bar hysteresis on regime changes prevents the system from switching strategies on every noisy tick.
Honest backtesting discipline
FIFO per-lot accounting, forced mark-to-market of open positions at the end of every backtest window, and a standing principle that an edge appearing in one window and disappearing in another is not a real edge. The system stays in paper trading, with no live capital, until a strategy demonstrates a validated edge across multiple, non-overlapping windows.
Methodology as a hostable unit, not a hardcoded strategy
A discretionary trading methodology is extracted from its source material into a structured document, then consumed by a generic chart-rendering and vision-analysis pipeline. Adding a new methodology means writing a new structured document, not rewriting the pipeline that hosts it.
Found: a concurrency bug that was corrupting the eval numbers
Every candidate chart event ran as its own concurrent task, but all of them shared one simulated broker instance for the whole run, which matched a new trade's fill against whatever position happened to still be open from a completely unrelated event on the same symbol, purely by side and symbol, not chronology. Identical inputs could score a different win-rate and P&L depending only on which async task finished first, silently undermining the exact numbers used to judge a strategy. Fixed by giving each event its own broker instance and combining stats into a summary only after every task finished.
Stack
Python, asyncio, SQLite, Anthropic SDK, pandas
Status
Active research and development. Paper trading only, no live capital, pending a validated edge across multiple backtest windows.
This repository is private. Email bharath@bharathk.dev for access.
Back to work