CHAZLE

how it works
← Back to console
System overview

An LLM proposes. Plain code decides. You approve. Nothing else executes.

Chazle is an approve/reject trade-proposal engine for algorithmic options and prediction-market trading. A Claude orchestrator and four sub-agents do the analysis; a deterministic guard layer the model cannot talk its way around vets every proposal against hard caps; and no trade reaches a broker without your tap. The guard is the point of the whole thing.

“An LLM is a phenomenal analyst and a bad fuse. It is never the fuse here.”

End to end

The pipeline

A proposal travels a fixed path: a scheduled review wakes the orchestrator, its sub-agents gather and disagree, the orchestrator synthesizes proposals, the guard fuses them, survivors queue for your tap, and only what you approve — after a second guard check — ever touches a broker. Every step is written to an append-only ledger.

Schedulercode · periodic

Triggers a review every few hours during market hours (08:00–18:00 ET, weekdays). Deliberately periodic, not event-driven — the goal is more deliberation per trade, not reacting fastest to a headline.

wakes
Sub-agents + adversary → orchestratorClaude · analysis

Four focused sub-agents (news, sentiment, technical, positions) run concurrently and make the case for trades. Then an adversary agent argues the case against — why each idea won't work. The orchestrator (the "father") weighs both sides, synthesizes conservative proposals, and lowers confidence where the case is contested. It can only propose; it is never handed anything that executes.

proposals
Guard layer — the fuseplain code · NOT a prompt

A pure function checks each proposal against hard caps: per-trade size, daily loss limit, daily deployment, per-symbol / total / per-asset-class exposure, and the kill switch. Anything over a cap is turned away and logged as a capped row before it ever reaches your queue.

only what passes
Pending queue → your approvalyou decide

Survivors wait for you to approve or reject. On approval the guard re-checks against live state, so a queue of individually-fine proposals can't collectively bust a cap.

on approval
Execution → append-only ledgercode · connector

Plain code places the order through the asset class's connector (mock by default), records the fill, and writes every event — proposal, cap rejection, intent, fill — to a tamper-evident JSONL log.

The intelligence layer

What AI models the system uses

Chazle runs entirely on Anthropic's Claude models, matched to each job: the heavy synthesis runs on the most capable model, web-search reconnaissance on a mid-tier model, cheap reasoning-over-known-data on the fastest, and a dedicated skeptic that argues the bear case. With no API key present the whole pipeline still runs in mock mode — crafted illustrative proposals, zero model calls.

Orchestrator · the brain
Claude Opus 4.8
claude-opus-4-8

Synthesizes the sub-agents' often-conflicting findings into conservative, right-sized proposals, lowering confidence and stating the disagreement where they diverge. Runs with adaptive extended thinking at high effort, constrained to a JSON-schema output, with the system prompt prompt-cached. Read-only tools only — the model is never handed anything that can execute a trade.

adaptive thinkinghigh effortjson-schema outputprompt cachingread-only
News sub-agent
Claude Sonnet 4.6
claude-sonnet-4-6

Cross-references recent material news per symbol, distinguishing corroborated reporting from a single unconfirmed source. Uses the server-side web-search tool (capped at 4 searches per cycle), so it needs a model that supports it.

web searchlow effort
Sentiment sub-agent
Claude Sonnet 4.6
claude-sonnet-4-6

Gauges retail/social vs analyst sentiment and — crucially — flags where they diverge, because that divergence is itself a signal. Also web-search-enabled.

web searchlow effort
Technical sub-agent
Claude Haiku 4.5
claude-haiku-4-5

Reads the technical setup — trend, key levels, volatility regime — from price context we already hold, and states what would invalidate the read. No web search needed, so it runs on the fastest, cheapest model.

reasons over known data
Positions sub-agent
Claude Haiku 4.5
claude-haiku-4-5

Looks at the current book — positions, exposures, room left under the caps, daily P&L — and flags concentration or room-remaining concerns that should shape or veto new proposals. Also Haiku, also no web search.

reasons over known data
Adversary · the skeptic
Claude Sonnet 4.6
claude-sonnet-4-6

Runs last, over the other four's findings, with a single job: argue the strongest case against each trade — the underweighted risk, the crowded-trade objection, the disconfirming read of the same evidence. The orchestrator must engage it head-on, because a proposal that survives a real attempt to kill it is worth more than one that was never challenged. Reasons over the assembled findings, no web search — the value is the counter-argument, not fresh data.

argues the bear caselow effortno web search
Resilience & ordering

The four gathering sub-agents run concurrently, so their wall-clock is the slowest single agent, not the sum; the adversary then runs once over their combined findings. Each call has a hard timeout and one retry, and any agent that stalls or fails is simply dropped — the orchestrator synthesizes from what it has rather than letting one slow step wedge the cycle.

Calibrated certainty

Confidence per proposal

Every proposal carries one of three confidence labels, assigned by the orchestrator and calibrated to how much the sub-agents agree and how good the evidence is — not to how exciting the idea sounds. It's a read on conviction, written for the human who has to judge it.

low
Thin or conflicting evidence. Sized at the low end deliberately, if proposed at all.
moderate
A reasonable setup with a real caveat — e.g. the sub-agents split. The sensible default.
high
Reserved for genuinely corroborated, low-ambiguity setups. Should be rare.

How it's set. Each of the four sub-agents attaches its own confidence to every finding. The orchestrator weighs them and, crucially, lets disagreement pull confidence down — when retail sentiment is bullish but analysts are cooling, that divergence is named explicitly in the proposal's dissent field and the proposal lands at moderate, not high. A system that always reports unanimous high confidence is one to distrust.

Confidence is advisory — never a key to the fuse

The guard layer never reads confidence. It changes no cap, gates no execution, and influences no sizing in code — it only informs your approve/reject decision and shows as a chip on the proposal card. The proof is built into the demo: an intentionally oversized high-confidence proposal is still turned away as capped. A confident model does not buy its way past a plain function.

The fuse

The guard layer

This is the part the whole architecture exists to protect. It's a pure function — given a proposal, a snapshot of current exposure, and the caps, it returns a decision. No I/O, no state, no network, deterministic, and critically not a prompt. The LLM produces proposals; this code decides whether they may execute, and a model cannot talk its way around a plain function.

It runs in two places. At propose() time it pre-checks every proposal and turns away anything already over a cap, writing the capped ledger row that proves the cap was enforced before the trade reached your queue. Then at approve() time it re-checks authoritatively against live state — so queuing a hundred individually-fine proposals cannot collectively bust a cap. Each is re-evaluated the moment you try to approve it.

Every check the guard runs, in order — it collects all violations rather than stopping at the first, so the ledger shows you exactly why something was blocked:

  1. SanityFail closed on malformed proposals: empty symbol, non-positive quantity, price, multiplier, or notional are violations, not exceptions to shrug off.
  2. Kill switchWhen engaged, nothing executes. Full stop — one flip halts all execution.
  3. Symbol blocklistA symbol you removed may not trade — the agents stop proposing it and the guard rejects anything for it until you un-block it.
  4. Trading modeStock mode blocks options; options mode blocks equities. Prediction markets (isolated pool) trade in both. Enforced here, not just suggested to the model.
  5. Per-trade cap (global)Gross order notional may not exceed the global per-trade ceiling.
  6. Per-trade cap (per asset class)A tighter per-trade ceiling for options and prediction markets specifically.
  7. Daily loss limitOnce today's realized loss has reached the limit, new risk is blocked. The point of the limit is to stop, not to allow one more.
  8. Daily notional capBounds newly-deployed capital per day. Closing trades deploy nothing, so they never consume the daily budget.
  9. Per-symbol exposure capPost-trade exposure to a single symbol may not exceed its ceiling.
  10. Total exposure capTotal open exposure across all positions may not exceed the portfolio ceiling.
  11. Per-asset-class exposure capEach asset class has its own isolated exposure pool — a bad week in one can't bleed into another.
  12. Pending-queue capBounds how many proposals can pile up awaiting your review.
Two conventions worth knowing

Landing exactly on a cap is allowed — a check rejects only when the resulting value would exceed the cap. The daily loss limit is the deliberate exception (it blocks at >=). And de-risking is never blocked: a closing or reducing trade adds zero new deployed notional, so the deployment and exposure caps never stand in the way of getting out of a position.

Hard limits

The caps

Caps are plain data, loaded once from the environment at startup. The guard reads them; the LLM never sees or sets them. Changing a cap is a deliberate config-and-restart action, never something that happens mid-conversation. These are the shipped defaults — tune them in .env.

LimitDefaultWhat it bounds
Per-trade notional (global)$500Largest single order, gross
Daily loss limit$1,000Realized loss that halts new risk for the day
Daily notional cap$2,000Newly-deployed capital per day
Total exposure$5,000Open exposure across the whole book
Per-symbol exposure$1,500Open exposure to any one symbol
Pending queue25Proposals awaiting review at once

Per-asset-class pools isolate risk by instrument type, so a bad stretch in prediction markets can't bleed into the equities/options book:

Asset classPer-tradePool exposure
Equity$500$4,000
Option$400$2,000
Prediction market$200$1,000

Operator controls

Controls & alerts

Three operator controls sit alongside the kill switch — all persisted on disk, all enforced by the guard in code, none of them something the model can talk its way around — plus a notification layer that pushes decisions to you the moment they're needed.

Trading mode
A three-way switch: ALL (default), STOCK (options blocked), or OPTIONS (equities blocked). Prediction markets trade in both. The mode steers what the orchestrator proposes and is re-checked by the guard at approval — flip modes and stale queue items of the excluded class can no longer execute.
Symbol blocklist
Remove any symbol from trading with one tap: its pending proposals are purged immediately, the agents stop analyzing it, and the guard rejects anything for it — until you explicitly un-block it. Every block, purge, and un-block is a ledger row.
Notifications
Browser push (works with the console closed), Telegram, and email — each channel switches on when its config is present. You're notified the moment a proposal reaches your queue, on fills and failures, on kill-switch flips, and when the daily loss limit trips.
Forecasts — an expectation, never a promise

Each proposal can carry the orchestrator's forecast: a target price, a horizon in days, and — crucially — the condition that invalidates the idea. Notifications and cards word it as an expectation ("expect ~$230 within 7 days, moderate confidence; invalid below the 50-day"), never as a certainty, so you can size a holding period with honest inputs. A forecast that can't be invalidated is a promise, and this system doesn't make promises.

Watchlists & the insight layer

Point the desk, read the book

Four operator tools sit around the queue. Every one of them is read-only or steering-only — they choose what the desk looks at and help you read what it found. None of them can approve, size, or execute a trade; the guard and your tap remain the only path to a position.

Watchlists
Save named baskets of symbols (up to 50 each) and mark one active. The review cycle — manual or scheduled — then analyzes that universe instead of the built-in default. Steering only: the blocklist, caps, and approval gate still apply to everything a watchlist surfaces.
Performance
An equity curve of cumulative realized P&L and a daily-executions strip, both derived purely from the append-only ledger — sage above the zero line, red below. It reports what happened; it never predicts or advises.
Daily brief
A short, scannable news digest for your watched and held symbols, written by the brief agent from cross-referenced sources. It's a market scan framed as expectations-with-risk — never a buy/sell directive — and refreshes at most once a day.
Ask Chazle
A read-only assistant grounded in a live snapshot of your own engine — the caps and how much is used, the pending queue, your positions, a forecast. It explains state and reasoning; asked to act, it points you back to the console. Admin-only, because it spends API tokens.
Still not the fuse

The assistant and the brief are the same kind of thing as the orchestrator: phenomenal analysts, never fuses. They read the snapshot, summarize news, explain a cap — and stop. Nothing they output moves money. The equity curve and executions come straight off the ledger, so the numbers you read are the numbers that happened, not a model's estimate of them.

The differentiator

Options trading

Options are the core differentiator — and the asset class the guard treats most carefully, because their leverage makes notional easy to underestimate. They run through the same propose → guard → approve → log loop as everything else, with option-specific economics.

Real contract economics. Options carry a 100× contract multiplier, so the guard sizes against true exposure: notional is quantity × limit_price × 100. One contract at $3.20 is $320 of notional, not $3.20 — and the per-trade and pool caps are measured against that, never the per-contract premium. Their pool is the tightest of the three ($400 per trade, $2,000 exposure) and fully isolated, so an options drawdown can't bleed into the equities or prediction-market books.

The shipped mock set includes a worked example: a covered-call — sell one slightly-OTM weekly call to harvest premium into elevated implied volatility — sized at one contract ($320 notional), comfortably under the $400 option per-trade cap.

Execution is not wired yet

Today, options proposals flow through the full guard-and-approval loop but settle on the mock connector — no real option order is placed. The live OptionsConnector is a deliberate fail-loud stub that raises rather than fake a fill, because the Robinhood Agentic MCP is equities-only at launch. Going live means choosing, funding, and testing a real options API (Tradier / Tastytrade / IBKR) behind the same connector interface.

Contract model in place — broker wiring is the remaining gap

Each option proposal now carries a first-class contract — strike, expiration, and put/call right — validated at construction and re-checked by the guard. Positions key on full contract identity, so two contracts on the same underlying never net against each other, and the connector can render the standard OCC symbol for a broker. What's left before live options trading is purely the broker adapter: choosing, funding, and testing a real options API behind the (still fail-loud) connector above.

State machine

A proposal's lifecycle

Every proposal is only ever a request. It becomes a position only after the guard passes it and you approve it. Here is every state it can land in:

pending
Passed the pre-check; waiting for your tap.
executed
You approved, the guard re-checked, and it filled.
rejected_by_user
You rejected it from the queue.
rejected_by_guard
Failed the guard re-check at approval time (live state had moved).
capped
Blocked by the guard before it ever reached the queue.
failed
Approved and passed the guard, but execution errored — no position taken.

The boundary

Execution & connectors

One connector interface, swappable adapters, everything defaulting to a mock connector so the full propose → guard → approve → execute → log loop runs end to end without credentials or real money. An adapter goes live only when you fund the account and wire it in config.

The line that matters

Chazle never hands execution tools to the LLM. The orchestrator only proposes; execution is invoked here, in plain code, after the guard passes and you approve — including, for the Robinhood path, calling the broker's place-order tool programmatically rather than letting the model call it. Before touching the broker the engine writes a fsync'd execution_intent row and removes the proposal from the durable queue, so a crash mid-execution can never double-fill.

Asset classConnectorStatus
Prediction marketsKalshi (RSA-PSS signed, idempotent)Implemented; CFTC-regulated; verify against the demo API before real money
EquitiesRobinhood Agentic MCPFail-loud stub — raises rather than fake a fill
OptionsTradier / Tastytrade / IBKRBroker-agnostic fail-loud stub
All of the aboveMock (default)Simulated fills — the loop runs with no credentials

The record

The append-only ledger

Every meaningful event is written to an append-only JSONL log — not just fills, but the proposal and its full reasoning, every cap rejection, every kill-switch flip, and every recorded P&L line. A trading system earns trust through the legibility and exactness of its record; money is tracked in Decimal everywhere, never floats, so no cents are silently lost.

The events you'll see: proposed, capped, execution_intent, executed, rejected_by_user, rejected_by_guard, execution_failed, kill_switch_engaged, kill_switch_released, pnl_recorded, symbol_blocked, symbol_unblocked, and mode_changed. The capped and rejected_by_guard rows are the whole point — they are proof the limits were enforced in code the model couldn't drift around.

The surface

HTTP API

The dashboard is a thin client over these endpoints. Humans sign in with a username and password (invite-only accounts, admin or read-only viewer) and get an HttpOnly session cookie plus a CSRF token; automation uses the bearer token (CHAZLE_API_TOKEN), which is treated as admin. Writes require admin; the whole surface can approve trades and flip the kill switch.

GET/usageGuard-rail meters: used vs limit for every cap
GET/statusPositions, exposure, kill switch, connector modes
GET/queuePending proposals awaiting your review
GET/ledgerRecent ledger entries, reverse-chronological
POST/proposalsSubmit a proposal (runs the guard pre-check)
POST/proposals/{id}/approveRe-check against live state, then execute
POST/proposals/{id}/rejectReject a proposal from the queue
POST/kill-switchEngage or release the kill switch
POST/modeSwitch trading mode (all | stock | options)
GET/blocklistSymbols currently removed from trading (DELETE /blocklist/{symbol} un-blocks)
POST/blocklistRemove a symbol (blocks + purges its queue entries)
POST/push/subscribeRegister this browser for push notifications (also /push/unsubscribe, /push/pubkey, /notify/status)
POST/auth/loginSign in (also /auth/logout, /auth/me, /auth/password)
GET/admin/*Admin portal: users, roles, sessions, auth log, system (admin role only)
POST/pnlRecord realized P&L (gives the daily loss limit teeth)
POST/cycleKick off one orchestrator review in the background
GET/cycle/statusPoll a running review cycle
GET/performanceRealized-P&L equity curve + daily activity, off the ledger
GET/watchlistsSaved symbol baskets + the active review universe
POST/watchlistsSave/replace a basket; set active with /watchlists/active; DELETE /watchlists/{name}
GET/briefLatest daily market brief (POST /brief/generate to refresh — admin)
POST/assistant/askAsk the read-only assistant about current state (admin)

Rationale

Why these choices

Every decision here follows from one priority: don't lose money, and keep control.

DecisionWhy
Approve / reject every trade (v1)The only mode that satisfies “don't lose money” and “I control access” on day one. Autonomy-within-limits is a v2 graduation, earned after weeks of watching proposal quality — not a starting position.
Periodic review, not event-driven“More deliberation per trade” is incompatible with an event listener that rewards reacting fast. A scheduled cycle gives the sub-agents time to cross-reference sources.
Guard in code, outside the LLMCaps and the kill switch must be enforced where the model literally cannot drift from them over a long session — a plain function, not a system prompt.
Guard re-checks at approvalQueuing 100 individually-fine proposals can't collectively bust a cap: each is re-evaluated against live state the moment you approve it.
Decimal money everywhereTrust comes from exact numbers. No binary float ever touches a dollar amount.
Dissent is first-classThe orchestrator surfaces sub-agent disagreement instead of averaging it away. A system that always reports unanimous high confidence is one to distrust.
Plain truth about risk

Even with hard caps, manual approval, and a kill switch, this system will lose money sometimes — that's true of trading, not a flaw in the design. The caps bound how much a bad stretch costs you; they don't prevent one. And this is a personal engine: pooling other people's money to trade on their behalf triggers real regulation (RIA / fund formation, a qualified custodian, CFTC/NFA exposure for the prediction-markets side) and is a separate legal track — not something to code around. None of this is legal advice.