trades need sudo privileges
AI-powered algorithmic trading system for Indian markets. Multiple AI agents collaborate during market hours — researching, screening, debating bull vs bear, analyzing, and executing trades — all through a plugin architecture where every layer is independently swappable.
Paper trading first. Real money when it's ready.
flowchart LR
subgraph Agents
R[Research Agent]
SC[Screener Agent]
DB[Bull + Bear<br/>Debaters]
AN[Analysis Agent]
EX[Execution Agent]
end
subgraph Core
MA[Master Agent]
EB[EventBus]
end
subgraph Brokers
GR[Groww<br/>DATA]
KT[Kite<br/>EXECUTION]
end
MA -- orchestrates --> R & SC & DB & AN & EX
R -- research:complete --> EB
SC -- screened --> EB
DB -- debate:argument --> EB
AN -- analysis:complete --> EB
EB -- trade:requested --> EX
GR -- market data --> EB
EX -- place_order --> KT
KT -- executes --> NSE[NSE / BSE]
Every arrow is an event on the EventBus. Agents never import each other — the MasterAgent orchestrates the pipeline. Subscribe price data from Groww, execute trades via Kite. Or use one broker for both.
The MasterAgent runs a phase-based pipeline during IST market hours:
sequenceDiagram
participant S as Scheduler
participant M as Master
participant R as Researcher
participant SC as Screener
participant Bull as Bull Debater
participant Bear as Bear Debater
participant J as Consensus Judge
participant A as Analyst
participant E as Executor
S->>M: PRE_MARKET (9:00)
M->>R: research task
R->>M: findings (news, social, filings)
S->>M: OPENING (9:15)
M->>SC: screen universe
SC->>M: top 5 picks
loop For each pick
M->>Bull: argue bull case
M->>Bear: argue bear case
Bull->>M: round 1 argument
Bear->>M: round 1 argument
Bull->>M: round 2 rebuttal
Bear->>M: round 2 rebuttal
M->>J: evaluate debate
J->>M: verdict (strong_buy/buy/hold/sell)
end
M->>A: deep analysis (if verdict strong)
A->>M: signals (sentiment + technical)
M->>E: trade signal
E->>M: executed (or queued for approval)
| Agent | What it does |
|---|---|
| ResearchAgent | Scans news, filings, social media via Provider plugins. LLM extracts market-moving insights |
| ScreenerAgent | Fetches quotes from Groww, applies quantitative filters (momentum, volume spikes), LLM ranks qualitatively |
| DebateAgent (x2) | Bull and bear agents argue with evidence. Multi-round: initial case → rebuttal. System prompts force each side |
| ConsensusEngine | Neutral "judge" LLM evaluates both sides. Produces verdict: strong_buy / buy / hold / sell / strong_sell |
| AnalysisAgent | Runs all analyzers (sentiment, technical) in parallel on confirmed picks |
| ExecutionAgent | LLM decides order type, sizing, timing. Human approval by default (auto_execute = false) |
| MasterAgent | The brain. Subscribes to all events, orchestrates the pipeline, makes final go/no-go calls |
| Phase | IST | Activity |
|---|---|---|
| PRE_MARKET | 9:00–9:15 | Research overnight news |
| OPENING | 9:15–9:30 | Screen for opportunities, start debates |
| MORNING | 9:30–12:00 | Execute trades, monitor positions |
| AFTERNOON | 12:00–14:00 | Re-evaluate, scan new opportunities |
| CLOSING | 14:00–15:30 | Close intraday positions |
| POST_MARKET | 15:30–16:00 | Daily summary report |
Skips weekends and NSE holidays automatically.
graph TB
subgraph Core
ENG[Engine]
EB[EventBus]
PR[PluginRegistry]
CFG[Config]
end
subgraph Agents
MA[MasterAgent]
RES[Researcher]
SCR[Screener]
DBT[Debaters<br/>Bull + Bear]
ANL[Analyst]
EXA[Executor Agent]
SCHED[MarketScheduler]
COST[CostTracker]
MEM[SharedState]
end
subgraph Plugins
BRK[Brokers<br/>Groww · Kite]
PRV[Providers<br/>News · Social]
ANA[Analyzers<br/>Sentiment · Technical]
LLM[LLM Client<br/>Claude · GPT · Gemini]
STR[Strategies<br/>MA Crossover · Custom]
EXE[Executors<br/>Paper · Live]
INT[Interfaces<br/>API · WebSocket]
end
ENG --> EB & PR & CFG
PR --> BRK & PRV & ANA & LLM & STR & EXE & INT
MA --> RES & SCR & DBT & ANL & EXA
MA --> SCHED & COST & MEM
BRK <--> EB
MA <--> EB
RES <--> EB
SCR <--> EB
INT <--> EB
Everything is a plugin. Agents are plugins too — they register under the "agent" category and communicate via EventBus. The MasterAgent coordinates; individual agents are independently swappable.
The EventBus is the contract. All communication is events:
| Event | Payload | Emitted By |
|---|---|---|
market:tick |
symbol, price, timestamp | Broker, Backtester |
market:quote |
symbol, price, volume, ohlc | Broker |
signal:generated |
Signal | Analyzers |
agent:research:complete |
symbols, findings | ResearchAgent |
agent:screened |
symbols, picks | ScreenerAgent |
agent:debate:argument |
position, symbol | DebateAgent |
agent:debate:complete |
symbol, consensus | MasterAgent |
agent:analysis:complete |
symbols, signals | AnalysisAgent |
agent:trade:requested |
TradeSignal | MasterAgent |
agent:trade:executed |
TradeResult | ExecutionAgent |
agent:trade:pending |
signal, pending_count | ExecutionAgent |
schedule:phase_change |
phase, old_phase, time | Scheduler |
flowchart LR
subgraph "BrokerRole.DATA"
G[Groww]
end
subgraph "BrokerRole.EXECUTION"
K[Kite / Zerodha]
end
G -- "market:tick<br/>market:quote<br/>historical" --> EB[EventBus]
EB -- trade:signal --> K
K -- order execution --> NSE[NSE / BSE]
- DATA — market quotes, historical candles, WebSocket streaming
- EXECUTION — order placement and management
- BOTH — everything
Agents don't know which broker handles what. They call data_broker for data and executor for trades. Routing is automatic.
HTTP + WebSocket server for external tools (Claude Code, dashboards, mobile apps):
GET /status — all agent states + market phase
GET /signals — latest analysis signals
GET /consensus/:symbol — debate result for a symbol
GET /pending — pending trades awaiting approval
GET /cost — LLM cost report
POST /task — submit task {"type": "research|screen|debate|analyze"}
POST /trade/approve/:idx — approve a pending trade
POST /trade/reject/:idx — reject a pending trade
WS /ws — real-time event stream
# Submit a research task
curl -X POST http://localhost:8080/task \
-H "Content-Type: application/json" \
-d '{"type": "screen", "symbols": ["RELIANCE", "TCS", "INFY"]}'
# Check agent status
curl http://localhost:8080/status
# Approve a pending trade
curl -X POST http://localhost:8080/trade/approve/0Each agent can use a completely different LLM provider, model, and API key:
flowchart TB
subgraph "LLM Router"
R[Router]
end
subgraph "Agent → Model mapping"
MA[Master Agent] -- "claude-opus-4-6<br/>Anthropic API" --> R
DB[Debaters] -- "claude-opus-4-6<br/>Anthropic API" --> R
RS[Researcher] -- "gpt-4o-mini<br/>OpenAI API" --> R
SC[Screener] -- "gpt-4o-mini<br/>OpenAI API" --> R
AN[Analyst] -- "claude-opus-4-6<br/>Anthropic API" --> R
end
R --> A1[api.anthropic.com]
R --> A2[api.openai.com]
R --> A3[local LLM / any endpoint]
Configured via env vars — no code changes needed:
# Default for all agents
AI_BASE_URL=https://api.openai.com/v1
AI_MODEL=gpt-4
AI_API_KEY=sk-...
# Master uses Claude Opus for final decisions (different provider + key)
AI_MASTER_BASE_URL=https://api.anthropic.com/v1
AI_MASTER_MODEL=claude-opus-4-6
AI_MASTER_API_KEY=sk-ant-...
# Debaters also use Claude (creative argumentation)
AI_DEBATER_MODEL=claude-opus-4-6
# Research + screening use cheaper models
AI_RESEARCHER_MODEL=gpt-4o-mini
AI_SCREENER_MODEL=gpt-4o-miniPattern: AI_{AGENT}_BASE_URL, AI_{AGENT}_MODEL, AI_{AGENT}_API_KEY. Falls back to defaults. debater_bull and debater_bear both resolve to DEBATER config.
Multiple agents making LLM calls adds up fast. Built-in cost tracking:
- Daily budget: configurable limit (default $5/day)
- Per-agent budgets: master gets $2, others get $1
- Token tracking: every LLM call logged with cost estimate per model
- Auto-gating: agents skip LLM calls when budget exhausted, degrade gracefully
- Per-model pricing: knows token costs for Claude Opus, Sonnet, GPT-4, GPT-4o, GPT-4o-mini
# Check current spend
curl http://localhost:8080/costThe PaperExecutor simulates trade execution without risking real money:
- Tracks capital, positions, and P&L
- Validates capital for buys, position existence for sells
- Logs every trade with order IDs (
PAPER-XXXXXXXX) ExecutionAgentdefaults toauto_execute=false— trades queue for human approval via the API
Switch to live by implementing a LiveExecutor that routes to Kite's place_order(). The agent pipeline stays identical.
The backtester replays historical data through the same EventBus. Strategies are 100% agnostic:
flowchart LR
CSV[CSV / JSON<br/>Historical Data] --> BT[Backtester]
BT -- "market:tick<br/>market:quote" --> EB[EventBus]
EB --> S[Strategy]
S -- trade:signal --> EB
EB --> BT
BT --> R[BacktestResult<br/>P&L · Win Rate<br/>Drawdown · Sharpe]
Same strategy class, same events, same code path. Live or backtest.
══════════════════════════════════════════════════
Backtest Report: ma_crossover
══════════════════════════════════════════════════
Period: 2024-04-01 → 2025-03-07
Initial: ₹1,00,000
Final: ₹1,03,420
Total P&L: ₹3,420 (+3.4%)
Trades: 12 (Win: 7, Loss: 5)
Win Rate: 58.3%
Max Drawdown: 4.2%
Sharpe Ratio: 1.14
══════════════════════════════════════════════════
sudo-trade/
├── src/
│ ├── core/ # Engine, EventBus, PluginRegistry, Config, Logger
│ ├── agents/ # Multi-agent AI system (12 files)
│ │ ├── master.py # MasterAgent — orchestrates everything
│ │ ├── researcher.py # ResearchAgent — news + social scanning
│ │ ├── screener.py # ScreenerAgent — stock screening
│ │ ├── debater.py # DebateAgent — bull/bear argumentation
│ │ ├── consensus.py # ConsensusEngine — neutral judge
│ │ ├── analyst.py # AnalysisAgent — deep analysis
│ │ ├── executor.py # ExecutionAgent — trade execution
│ │ ├── scheduler.py # MarketScheduler — IST phases + holidays
│ │ ├── memory.py # AgentMemory + SharedState
│ │ └── cost.py # LLMCostTracker
│ ├── brokers/ # Broker plugins + instrument map + rate limiter
│ │ ├── groww.py # Groww: REST + WebSocket, TOTP auth, rate-limited
│ │ ├── kite.py # Kite Connect (stub)
│ │ ├── instruments.py # Instrument CSV cache + symbol resolution
│ │ └── rate_limiter.py # Token-bucket per-second + per-minute limits
│ ├── analysis/ # Analyzers (sentiment, technical)
│ │ └── sentiment.py # LLM-powered market sentiment analyzer
│ ├── llm/ # LLM client + router
│ │ ├── client.py # OpenAI-compatible client via aiohttp
│ │ └── router.py # LLMRouter — per-agent model/endpoint/key
│ ├── strategy/ # Strategy plugins (signals in, trade decisions out)
│ ├── execution/ # Executors
│ │ └── paper.py # PaperExecutor — simulated trades, P&L tracking
│ ├── providers/ # Data sources
│ │ └── news.py # RSS news (MoneyControl, Economic Times)
│ ├── interfaces/ # API + WebSocket interface
│ │ └── api.py # HTTP + WS API for external agents
│ ├── backtesting/ # Backtest engine, data loaders, fill simulator
│ └── __main__.py # Entry point — wires everything together
├── scripts/
│ ├── groww_cli.py # CLI: quote, stream, holdings, positions, historical
│ └── backtest_cli.py # CLI: run backtests with formatted reports
├── configs/
│ └── default.toml # Runtime config (agents, brokers, API, LLM budgets)
├── .github/
│ └── workflows/ci.yml # GitHub Actions: lint + test on push/PR
├── data/ # Runtime data (gitignored except reference/)
├── docs/ # Broker API specs, strategy notes
├── logs/ # Structured JSON logs (gitignored)
└── tests/ # 39 tests — agents, brokers, backtester, events
# Clone
git clone https://github.com/myselfshravan/sudo-trade.git
cd sudo-trade
# Install dependencies (Python 3.13+)
uv sync
# Configure credentials
cp .env.example .env
# Edit .env with your API keys# Groww (required for live data)
GROWW_API_KEY=your_api_key
GROWW_API_SECRET=your_secret # or use TOTP below
GROWW_TOTP_SECRET=your_base32_totp # from Groww 2FA setup
# LLM — default for all agents
AI_BASE_URL=https://api.openai.com/v1 # any OpenAI-compatible endpoint
AI_MODEL=gpt-4 # model name at that endpoint
AI_API_KEY=your_key # or OPENAI_API_KEY / ANTHROPIC_API_KEY
# Per-agent LLM overrides (optional — see "Per-Agent LLM Routing" section)
# AI_MASTER_BASE_URL=https://api.anthropic.com/v1
# AI_MASTER_MODEL=claude-opus-4-6
# AI_MASTER_API_KEY=sk-ant-...
# AI_RESEARCHER_MODEL=gpt-4o-mini
# Agent system
AGENT_DAILY_BUDGET_USD=5.0 # daily LLM spend limit
AGENT_AUTO_EXECUTE=false # require manual trade approval
# Engine
SUDO_TRADE_MODE=paper # paper | liveuv run python -m srcThis starts all agents, the market scheduler, and the API server at http://localhost:8080.
# Get a quote
uv run python -m scripts.groww_cli quote RELIANCE
# Batch quotes
uv run python -m scripts.groww_cli quotes RELIANCE INFY TCS
# Stream real-time ticks via WebSocket
uv run python -m scripts.groww_cli stream RELIANCE INFY
# Portfolio
uv run python -m scripts.groww_cli holdings
uv run python -m scripts.groww_cli positions
# Historical candles
uv run python -m scripts.groww_cli historical RELIANCE --interval 1d --days 30# Run MA crossover backtest on sample data
uv run python -m scripts.backtest_cli \
--data data/reference/sample_RELIANCE.csv \
--strategy ma_crossover \
--capital 100000
# Custom MA windows
uv run python -m scripts.backtest_cli \
--data data/reference/sample_RELIANCE.csv \
--strategy ma_crossover \
--short 10 --long 30# Trigger a stock screen
curl -X POST http://localhost:8080/task \
-d '{"type": "screen", "symbols": ["RELIANCE", "TCS"]}'
# Start a debate on a stock
curl -X POST http://localhost:8080/task \
-d '{"type": "debate", "symbols": ["RELIANCE"]}'
# Check what agents are doing
curl http://localhost:8080/status
# See pending trades
curl http://localhost:8080/pending
# Approve trade #0
curl -X POST http://localhost:8080/trade/approve/0# Run tests (39 tests)
uv run pytest
# Lint
uv run ruff check .
# Format
uv run ruff format .
# Add a dependency
uv add <package>
# Add a dev dependency
uv add --dev <package>CI runs automatically on every push/PR via GitHub Actions (lint + test).
A strategy subscribes to events and emits trade signals. This exact code runs on live data and backtester replay:
class MyStrategy:
name = "my_strategy"
def __init__(self, events: EventBus):
self._events = events
async def start(self) -> None:
self._events.on("market:tick", self._on_tick)
async def stop(self) -> None:
self._events.off("market:tick", self._on_tick)
async def _on_tick(self, symbol: str, price: float, timestamp: datetime, **_):
if should_buy:
signal = TradeSignal(
action=TradeAction.BUY,
symbol=symbol,
quantity=1,
confidence=0.8,
reasoning="my reason",
timestamp=timestamp, # use event timestamp, not datetime.now()
)
await self._events.emit("trade:signal", signal=signal)Register it: engine.add("strategy", MyStrategy(events))
- Python 3.13 with
uvfor package management - asyncio everywhere — async by default
- aiohttp for HTTP (broker REST APIs, LLM calls, API server)
- growwapi for Groww broker integration
- pyotp for TOTP-based authentication
- ruff for linting + formatting
- pytest + pytest-asyncio for testing
- GitHub Actions for CI