p_lower by ≥ 5 percentage points on broker-routed defined-risk options, exactly as it did on the
binary venue. In plain terms: we expect the same calibration gap that killed the binary edge to reappear
once the same confidence number is used to pick and size a vertical spread. We reject H0 only on
hard evidence (§3); until then a failure to reject — the gap persists — is the lab's expected and
fully successful result. No real order is placed this phase; the bankroll is fictional; every leg is a
dry_run=True broker echo.
This lab descends from the binary Edge Lab (edge_lab_service.py +
edge_lab.db): the dashboard that logged a binary observation, graded it after a fixed forecast
window, and bucketed the results by the AI's stated confidence band [50–60, 60–70, …, 90–100]. That lab
never shipped a standalone whitepaper because its finding was a number, not a method — the number that
the Monte-Carlo F7 lab later turned into the project's first headline conclusion. The closest methodological
ancestor in writing is the
TRAIL Lab methodology whitepaper, which fixed the
Wilson-lower-bound discipline this lab inherits, and the calibration-honesty section of the
hybrid_v1 whitepaper.
The binary Edge Lab's verdict is headline finding #1 of the pivot brief, quoted verbatim:
"Confidence calibration is broken in our direction. The AI's stated confidence consistently overstatesp_hatby 5–15 pts across the upper buckets (70–90). Wilson lower-boundp_lowercollapses below break-even once realistic friction (spread + slippage + missed fills) is applied." — WEBULL_PIVOT_STUB_PROMPT.md §1.4, finding 1
Two further findings frame why a new lab is warranted rather than a simple verdict of "the model is
overconfident, stop." Finding #3 — "b ≈ 1 is a lie our binary UI was telling. When you
derive real b from |target−entry| / |entry−stop|, most setups are 0.6–0.9 — meaning
we needed p ≥ 0.55 just to be Kelly-neutral" — says the binary contract's payoff geometry was
working against us before calibration even entered the picture. Finding #5 — the project's adopted conclusion
— says the response is not to keep predicting the coin flip but to "use the same predictive engines to
select an options structure whose payoff is shaped to match the predicted regime."
The Webull Edge Lab is the most literal test of that pivot. It changes one thing versus its binary
ancestor: the confidence number no longer sizes a one-touch binary bet — it picks a defined-risk vertical
spread with an explicit, geometry-derived payoff ratio b, and sizes it off p_lower.
The question is brutally simple: does shaping the payoff repair the calibration gap, or does the gap travel
with the model regardless of the instrument we wrap around it?
The Edge Lab's classifier is the thinnest of the five labs by design: it is the raw Candlevision /
ai_intelligence_engine.py verdict — a {direction, confidence} pair extracted from
the chart frame by vision_analyst.py / vision_extractor.py, with no exhaustion,
divergence, or ensemble fold layered on top. That deliberate thinness is the control: the Exhaustion,
Divergence, TRAIL and Hybrid labs each add structure on top of this same primitive, so the Edge Lab measures
what the bare stated confidence is worth on the options rail before anyone improves it. The classifier emits
the §2.2 regime block: {direction, strength, iv_rank, vol_regime, exhaustion:null, divergence:null}.
The selector (options_strategy_mapper.select_strategy(regime, ctx)) is restricted, for this lab,
to the directional defined-risk subset of the Periodic Table (WEBULL_PIVOT §3) — the elements whose
payoff is a clean monotone function of being right about direction:
| Regime read | Primary candidate | Alternates |
|---|---|---|
| Directional-bull, IV-rank mid/low | bull_call_spread | long_call, bull_put_spread |
| Directional-bull, IV-rank high | bull_put_spread (credit) | bull_call_spread |
| Directional-bear, IV-rank mid/low | bear_put_spread | long_put, bear_call_spread |
| Directional-bear, IV-rank high | bear_call_spread (credit) | bear_put_spread |
| Neutral / no conviction | floor — no structure funded | — |
With defined_risk_only=true (the binding default, WEBULL_PIVOT §6.2) the selector never returns a
naked or undefined-risk leg. Every candidate carries the §2.2 output: primary, alternates,
rationale, legs, max_loss, max_gain, breakeven.
The vertical's payoff ratio is the real b — b = max_gain / max_loss — read
straight off the leg geometry, which is exactly the honesty finding #3 demanded.
Every closed paper option-trade is persisted on the existing paper_trade_service.py trade
row (no parallel DB — WEBULL_PIVOT §6.3), extended with the workboard §2.4 columns:
webull_strategy_id, iv_rank_entry, iv_rank_exit, theta_paid_usd,
vega_at_entry, defined_risk, max_loss_usd, max_gain_usd,
legs_json, broker_client_order_id. The
/api/cv/paper/calibration endpoint gains a ?strategy_id= filter so the evaluation
buckets per options structure instead of averaging an edge in one element against a drag in another —
the whole point being to discover which element of the periodic table, if any, carries a calibrated edge.
The Wilson 95% lower bound (Wilson 1927) is computed per bucket exactly as in the binary lab:
Sizing and growth are evaluated by the F7 machinery
(Wilson lower-bound Kelly, ergodic Monte Carlo). The
break-even win rate for a defined-risk spread with payoff ratio b is
p* = 1/(1+b); the growth-optimal stake is fractional Kelly off p_lower, never
p_hat ("Kelly off a thin sample is how accounts blow up" — F7 README). The simulation is
additive (bankroll += payoff), reporting the time-average growth rate Peters (2019)
distinguishes from ensemble EV — the same evaluation that rendered the flat-to-negative verdict on the binary
phase. A positive ensemble EV with a ≤ 0 time-average growth rate is, per finding #2, still a fail.
p_lower by ≥ 5 percentage points — i.e. the binary
calibration gap (finding #1) carries over to the options rail unchanged.
We reject H0 only if, over a sample of ≥ 100 closed paper option-trades in at
least one webull_strategy_id bucket, the Wilson lower-bound
p_lower for that bucket exceeds the geometry-derived break-even threshold:
and the bucket's stated-confidence midpoint sits within 5 points of that same p_lower (the
calibration-gap clause). Both conditions must hold simultaneously: an edge that exists only because the AI
got lucky in one band, while its confidence remains miscalibrated, does not reject H0 — it just
means the geometry, not the prediction, paid. The ?strategy_id= bucketing is what lets us tell
those two apart.
sufficient at
n ≥ 50. We raise the bar to 100 for the options rail because the per-structure split multiplies the number of
buckets, and because options outcomes carry path-dependent theta/vega noise the binary contract did not.
Until a bucket clears 100 closed trades it is insufficient and the selector holds
defined_risk_only=true and floor sizing regardless of how flattering p_hat looks.
Per the lab's stance — inherited directly from the TRAIL Phase 1.5 study, where a 36% directional hit rate
was reported as a finding, not buried — a null-not-rejected result is a successful run. If after
hundreds of closed trades no bucket clears p* , the lab has produced a real, expensive-to-fake
piece of knowledge: wrapping a defined-risk spread around a miscalibrated confidence number does not, by
itself, manufacture an edge. That is worth knowing precisely because the pivot's central hope is that
payoff-shaping rescues the prediction layer. Three concrete things a sustained failure teaches:
vision_analyst.py), and no amount of structure selection will fix it. That redirects effort
to calibration of the engine, not the selector.defined_risk_only guard and floor sizing did their job — we learned the model has no
edge without paying to learn it. That is the entire reason the lab exists.dry_run=false broker response, or any leg bundle that is not defined-risk
while defined_risk_only=true. Either would mean the instrument measuring the edge is itself
broken. The smoke-test sign-off (backups/webulledge/SMOKE_TEST.md) exists to prove neither
happened.
Must-do:
bull_call_spread trades actually won? Below-diagonal means dampen tilt amplitude, exactly
as TRAIL Phase 1.5 §10 prescribed for the binary case.theta_paid_usd against realized R per bucket so a "win rate" isn't quietly
funded by carry.+0.8×stake on a correct sign and −1.0×stake on a wrong one — the
actual contract the original "80%" intuition came from — and re-derive the break-even there.Must-not:
p_lower > p* across many sessions and structures.defined_risk_only=false while any funded bucket has p_lower < p*.
Undefined-risk legs on a miscalibrated edge is the precise failure the pivot was built to escape.b and growth-optimal
sizing; break-even p* = 1/(1+b).)