Chanakya v2: A 17-Agent Portfolio and Wealth Research Platform
Chanakya is a private research platform built around a real, personally-traded portfolio across the US and Indian markets. Seventeen specialized agents — news, sentiment, technicals, fundamentals, macro, a Bull/Bear/Judge debate, forecasting, risk, weekly allocation, and more — reason across everything at once. Underneath the market-intelligence layer sits a full personal-finance system: a double-entry ledger, tax-lot accounting, and net-worth tracking. The system is advisory only. It proposes and explains; a human decides.
Four months of daily development since the platform's first public write-up have taken it from thirteen agents to seventeen, and from a single deterministic forecasting model to a dedicated quant-research module built around techniques used in institutional portfolio management: dynamic correlation modeling, portfolio optimization, and a full battery of overfitting controls borrowed from published quantitative finance research.
This is a private, personal system with no public repository. What follows is architecture, math, and engineering detail — the shape of the system and how it reasons — not the specific signals, thresholds, or weights that make up its actual trading edge.
Architecture
| Layer | Detail |
|---|---|
| LLM | OpenAI as the primary provider, with Gemini configured as an automatic fallback if a call fails |
| Agents | 17, each an independent Kafka consumer group — News, Sentiment, Technical, Fundamentals, Macro, Events, Ontology, Synthesis, Debate, Forecast, Decision, Risk, Allocation, Journal, Discovery, Grading, and one more |
| Forecasting | A single context-synthesizing LLM call per symbol per horizon, graded by a multiclass correlation metric that can't be flattered by a lazy predictor |
| Decision composition | Nine weighted signals, composed by deterministic code |
| Execution | Advisory only — every real trade is entered into a database-enforced double-entry ledger with full tax-lot accounting |
| Data collectors | Eight independent sources: SEC EDGAR, yfinance (US + India), FRED, RSS/StockTwits, options chains, insider Form 4, 8-K transcripts |
| Observability | OpenTelemetry, Prometheus, Grafana, and Arize Phoenix for LLM-native tracing — prompt, completion, and cost visibility per call |
| Deployment | Entirely local. Never hosted, never public, by design |
Every agent runs as its own restartable Kafka consumer, subscribed to whatever topic feeds it. There is no orchestrator holding the pipeline together — the pipeline is an emergent property of topic names, which means any agent can be redeployed or scaled without the others noticing.
A financial record that can't go out of balance
Every real trade is entered by hand into a double-entry ledger, enforced at the database level: a Postgres trigger guarantees that every transaction's entries sum to zero. Tax-lot accounting sits on top of it — FIFO, HIFO, LIFO, or specific-lot identification, with anniversary-rule long/short-term determination and Form 8949 export. Net worth, cashflow, and bank-import matching round out a full personal-finance surface alongside the market-intelligence agents.
Keeping the financial record accurate matters more than any single trading signal. A broken balance is the one failure mode that actually costs something, and the database-level invariant catches every write automatically, without depending on a person remembering to review it.
Forecasting
A single LLM call synthesizes the full context for a symbol — fundamentals, peer comparables, technicals, macro regime, and time-bounded news — into a three-class directional call (up, down, or flat) per horizon. The "flat" class is volatility-scaled per symbol and horizon rather than a fixed percentage band:
where is a volatility estimate for symbol (from ATR or a GARCH(1,1) fit), is the horizon in days, and , , are fixed shaping constants. A volatile small-cap gets a wider "flat" band than a stable large-cap, and grading against the eventual close uses the same band the forecast was made against.
Grading uses a multiclass generalization of the Matthews correlation coefficient rather than raw accuracy. The two-class version:
ranges from to and lands at exactly for a classifier that always predicts the majority class. Gorodkin's 2001 generalization extends the same idea — correlation between predicted and actual class assignment — across a full confusion matrix, which is what makes it a meaningful bar for a three-class forecaster to clear.
A leakage guard on the underlying data means a backtested or replayed decision only ever sees news published before its own decision timestamp, verified by a dedicated test that plants future-dated articles and confirms they never enter the context window.
The quant research module
A dedicated module handles risk modeling, portfolio construction, and validation — separate from the forecasting agent, feeding the risk and allocation agents directly.
Correlation and covariance. An Engle (2002) two-step DCC-GARCH model, with the Aielli (2013) bias correction, gives a dynamic, time-varying correlation estimate rather than a single static snapshot:
is the diagonal matrix of per-asset volatilities from univariate GARCH(1,1) fits; updates each step from an exponentially-weighted mix of standardized-residual cross-products and its own lag. A Marchenko–Pastur-style random-matrix-theory cleaning pass sits underneath it, denoising the sample covariance matrix before it reaches an optimizer — sample covariance estimated from noisy return series is itself mostly noise past a certain eigenvalue rank.
Position sizing. A multivariate fractional-Kelly sizer, with a ridge-stabilized covariance inverse and a conservative fractional multiplier, sets position size from the classical Kelly fraction:
— the probability of a favorable outcome, , the net odds. Portfolio-level construction runs alongside it: Black-Litterman (a market-implied equilibrium prior, tilted by the forecast model's own calibrated views), Hierarchical Risk Parity, and minimum-variance optimization, via PyPortfolioOpt and riskfolio-lib, wired to a live "suggested rebalance" endpoint.
Validation. Purged walk-forward cross-validation keeps training and test windows from leaking into each other through overlapping labels. The Probabilistic Sharpe Ratio converts a point-estimate Sharpe ratio into the probability it genuinely exceeds a benchmark, given a finite sample:
The Deflated Sharpe Ratio applies the same idea after correcting for how many strategy variants were compared before one was selected — the more trials, the higher the bar a real Sharpe ratio has to clear before it reads as skill rather than selection bias. Rounding out the module: cross-sectional factor tools (rank normalization, winsorization, sector/beta neutralization), a Fama-French-style return attribution splitting realized returns into factor premia versus residual alpha, a Corwin-Schultz bid-ask spread estimator that works from OHLC bars alone, and SHAP explainability over the prediction model's gradient-boosted trees.
Every backtest number is cross-checked against two independent, well-known third-party libraries before it's trusted, a standing correctness check on the analytics layer itself.
Tail risk and portfolio limits
Parametric Value-at-Risk and per-position risk-contribution functions feed the risk agent alongside a correlation veto and a drawdown gate that halves position sizing at a 10% portfolio drawdown and blocks new risk entirely at 15%. The parametric VaR form:
is a normal-distribution estimate of the loss threshold expected to be exceeded only of the time. It sits next to a historical, distribution-free VaR estimate rather than standing alone — two independent estimates of the same risk carry more information than one confident one.
Position and sector limits follow a configurable Investing Policy — risk tolerance, position and sector bounds, time horizon — set once and revisable at any time, rather than a constant buried in application code.
Named Philosophy presets apply pre-built weighting stances to the same regime-by-agent Bayesian weight matrix that composes signal weights across five market regimes. A zero-cost historical replay engine answers "what would this policy have done historically" by replaying the deterministic parts of the pipeline against stored signal history, with no LLM calls involved. A change-preview step shows a policy edit's historical impact before it's committed, and automated sentinel checks watch for policy drift, a weight-tuner that's stopped moving, or a widening calibration gap. Four feedback loops run continuously: a weekly model-promotion gate, an A–F calibration scorecard, the regime-by-agent weight matrix itself, and a classifier trained on which human overrides turned out to be correct.
A walk-forward strategy tuner searches allocation and strategy parameters across a multi-year walk-forward split, with a promotion gate structurally identical to the model-registry gate — a parameter change only goes live if it beats the incumbent out of sample.
Cost and inference discipline
Chanakya runs on a twice-daily pre-market sweep rather than continuous polling, with an input-hash dedup layer that calls a model at most once per (agent, input) pair per day, and a global daily budget cap enforced in micro-dollar precision. A kill switch sits in front of every LLM-calling path, checked before any call goes out.
Reasoning and access control
Every reasoning transcript, decision, and mutating endpoint requires an authenticated session. CSRF validation fails closed if its Redis-backed token store becomes unreachable — a request without a verifiable token is rejected outright rather than waved through. The Kafka administration console, along with the LLM-tracing UI, is bound to loopback only.
Stack at a glance
| Backend | Python 3.12, FastAPI, SQLAlchemy 2 (async), Alembic — 122 migrations |
| Data layer | TimescaleDB (hypertables + compression), pgvector, a double-entry ledger with a Postgres-enforced balance invariant |
| Event bus | Kafka via Redpanda — 17 agents, each an independent consumer group |
| LLM | OpenAI as primary, Gemini as automatic fallback, a global kill switch in front of every call |
| Quant module | DCC-GARCH, RMT covariance cleaning, VaR/CVaR, Black-Litterman / HRP / min-variance optimization, purged walk-forward validation, PSR/DSR, an independent third-party backtest cross-check |
| Frontend | Next.js 15, TypeScript, ECharts, surfaces for cashflow, research chat, and decision-reasoning transcripts |
| Execution | Advisory only — every trade is entered by hand into the ledger |
| Tests | 4,139 backend (pytest), 1,067 frontend (vitest) |
| CI | GitHub Actions — ruff, pyright, biome, tsc, pytest, vitest, plus an AST-based architectural-convention gate |
| Observability | OpenTelemetry, Prometheus, Grafana, Arize Phoenix |
| Deployment | Entirely local |
| License | Proprietary, personal use only |
More deep dives in this series: the original Chanakya post for the debate mechanism and the deterministic decision/risk split, and SuperZen and BroSki for the same instinct at a smaller scale — one disciplined mechanism doing the work of a dozen special cases.