Desk offlinelablab.ai × Alpaca hackathon

An options desk where the AI is the least-trusted component.

Deflow trades defined-risk option spreads on Alpaca, harvesting the gap between the volatility options are priced at and the volatility stocks actually deliver. Four agents propose. Twelve deterministic circuit breakers decide. No model ever produces a number that reaches the broker.

Circuit breakers
12
zero-LLM, fail-closed
Gate latency
microseconds
run risk_gate.py to measure
Defined risk
100%
no naked options, ever
Decisions logged
hash-chained
tamper-evident
The problem

Autonomous trading agents fail in three predictable ways.

Each of them is a structural property, not a tuning problem — so Deflow is built to make each one impossible rather than unlikely.

01Unbounded

The model sizes the trade

Most agents let a language model choose the strike, the width and the position size. A hallucinated number becomes an order. There is no layer that can say no, because the layer that would say no is the same one that made the decision.

02Undefined

The risk has no floor

Naked options and unhedged directional bets have no worst case. The position works for weeks and then one gap erases the account. Nothing in the system knows what the maximum loss is, because the structure does not have one.

03Unverifiable

The results cannot be checked

A log tells you what a system says it did. It does not tell you whether the story was edited afterwards, and it usually records only the fills — so the refusals, which are most of what a risk system does, leave no trace at all.

Under the risk-neutral measure, every vertical spread is worth exactly what it costs. Score a candidate at its own implied volatility and every trade prices at zero — the correct answer, and a useless one.

So Deflow scores each candidate twice, and trades the difference.

MeasureVolatilityWhat it tells you
risk-neutralimpliedWhat the market says it is worth — ≈ 0 EV, as arbitrage requires
physicalforecast realisedWhat it is worth if the stock keeps moving as it has been

The dollar gap between those two rows is the variance risk premium. Anything with non-positive expectancy under the physical measure is refused — however high its win rate.

A trade Deflow refuses
probability of profit79%
you keep$320 · 4 times in 5
you lose$1,680 · the fifth
expected value−$112
The desk

Four agents, and only one of them is a language model.

The reasoning layer is shown finished, priced candidates and returns exactly one integer index — bounds-checked. It cannot change a strike, a width, a premium or a size. A total model failure degrades to a deterministic ranker, not to a bad trade.

01

Macro & Volatility Analyst

What is the market doing?

Measures implied against a jump-robust forecast of realised volatility on eight liquid names. Emits a stance, a directional bias — and about half the time, a documented refusal.

regime + variance risk premium
02

Options Structurer

What trade expresses that?

Delta-targeted strikes, a width ladder scaled to spot, a hard liquidity floor, and position size taken from the risk gate's own sizer. Wing geometry is correct by construction.

8 priced, defined-risk candidates
03

Adversarial Risk Auditor

What is wrong with it?

Re-derives every Greek from scratch rather than trusting the structurer, then runs 1,000 jump-diffusion paths under two volatility measures. Has fatal-objection authority.

pass, or a veto
04

Execution Agent

Route it, or don't.

Re-runs the entire risk gate on the exact proposal being sent, then submits a multi-leg order through Alpaca's official CLI with an idempotent client order id.

mleg order on Alpaca
the only LLM in the system

Between stages 2 and 3, Featherless AI picks one candidate from the list — or abstains. Its entire output surface is { index, confidence, rationale }. A model that hallucinates index 9999 is ignored, not indexed with.

The risk gate

Twelve breakers. No network, no prompt, microseconds.

Single-digit microseconds per evaluation — the button below reports the real figure from this machine. risk_gate.py imports nothing but the standard library. Given the same proposal and the same book it returns the same verdict, forever. It fails closed on anything malformed, runs all twelve even after one fails so the audit trail stays complete, and has no code path that can widen a limit or increase a size.

01defined_risk_structure

every short covered by a long of the same right

02max_loss_2pct

≤ 2% of equity per trade

03trade_delta_bound

|net delta| ≤ 0.35

04probability_of_profit

65% win rate for credit; expectancy for debit

05aggregate_risk_6pct

≤ 6% of equity at risk across the book

06symbol_concentration_3pct

≤ 3% in any one underlying

07portfolio_delta_bound

book |delta| ≤ 1.20

08max_open_positions

≤ 6 concurrent structures

09dte_window

7–60 days to expiry

10payoff_quality

credit ≥ 15% of wing width

11daily_drawdown_killswitch

halts new risk at −3% on the session

12vega_ceiling

|vega| ≤ 2.5 per $1,000 of equity

Fails closed

Every field is read with a pessimistic default. A missing max_loss is not zero, it is unbounded. NaN and infinity fail every comparison by design.

Never short-circuits

All twelve run even after one fails, because a veto naming only the first problem hides the rest from the audit trail.

Sizes the trade itself

max_contracts() derives position size from breakers 2, 5 and 6. The model never chooses size, and the gate can only shrink or refuse.

Coverage

Eight names, chosen for penny-wide markets.

A defined-risk desk lives or dies on being able to exit. The universe is selected for depth of option chain, not for interesting stories — and roughly half of every scan ends in a documented refusal to trade.

SPY
awaiting first scan
QQQ
awaiting first scan
IWM
awaiting first scan
NVDA
awaiting first scan
AAPL
awaiting first scan
MSFT
awaiting first scan
AMD
awaiting first scan
TSLA
awaiting first scan
Auditability

Results you can check, not results you have to believe.

Every decision — each analyst view, proposal, audit, gate verdict, order and exit — is appended as one line carrying the SHA-256 of the line before it. Edit or delete any historical entry and the chain breaks, and the API reports the exact index where.

GET /api/ledger/verify
{
"valid": true,
"entries": 1284,
"broken_at": null,
"detail": "Chain intact — every
entry hashes to its successor."
}

Tamper-evident

Modify entry 3 of 6 and verification reports broken_at: 3. Delete one and it reports the same. A log tells you what a system says it did; this tells you whether the story was edited afterwards.

Refusals included

Stand-downs, abstentions and vetoes are logged with the numbers that produced them. A desk that records only its fills cannot be audited — and for this strategy, the refusals are most of the behaviour.

Survives concurrency

Appends take an exclusive file lock and re-derive the head underneath it, so two processes sharing a data directory chain onto each other instead of forking. Verified with four concurrent writers.

Built on

All three Alpaca surfaces, each for what it is best at.

Trading API

Orders & market data

Written directly against the HTTP surface so the multi-leg payload is visible in one place: account, positions, daily bars, option-chain snapshots with NBBO and server-side Greeks.

Refuses to initialise against a non-paper endpoint.

Alpaca CLI

Default order route

The official Go binary is the interface an unattended agent actually gets deployed behind — its own 429/5xx backoff, its own credential resolution, and --dry-run to render the exact request without sending it.

Every order carries an idempotent client order id.

MCP server

Structured discovery

Alpaca's FastMCP server spoken as JSON-RPC over stdio with no SDK dependency, resolving tool names at runtime so an upstream rename cannot break the integration.

72 tools discovered; chains, contracts and account state.

Featherless AI

Bounded reasoning

Serverless open-model inference for the one stage that is allowed to be wrong. Qwen2.5-72B picks among finished candidates and explains the choice in English.

Any failure falls back to the deterministic ranker.

A real multi-leg order, as routed
alpaca order submit --order-class mleg --qty 4 --type limit \
  --limit-price -1.35 \
  --legs '[{"symbol":"SPY261016P00540000","ratio_qty":"1",
            "side":"sell","position_intent":"sell_to_open"},
           {"symbol":"SPY261016P00535000","ratio_qty":"1",
            "side":"buy","position_intent":"buy_to_open"}]'

Negative limit price because Alpaca quotes multi-leg packages net — positive is a debit paid, negative is a credit received.

Watch it refuse a trade.

The desk streams every decision live — the regime read on eight names, the open book with Greeks, and a button that fires a naked call at the running risk gate.

One command from a bare clone — python main.py