fundamental_arb_exec dossier — does the short-horizon Black-Scholes edge survive executable entry prices, OOS, net?
Status: FALSIFIED under the pre-registered rule (results 2026-09-14, commit 7542179). Executable fills remove the fundamental_arb_short edge; not promoted. Pre-registered 2026-09-14 as a separate hypothesis, after the fundamental_arb_short post-hoc diagnostics pointed to a stale-price artifact and before any fundamental_arb_exec result was computed.
Pre-registration
Frozen with the G4c checks in tests/g4c/. Nothing below may change after a result exists; a change means a new, separately registered hypothesis.
Hypothesis. For Polymarket markets that ask whether an asset's price will be above, below, or touch a strike by a date, a Black-Scholes fair probability (archived fundamental_arb pricer, realized volatility) identifies mispriced YES prices. Taking the model's side when the gap is large earns a positive net return out of sample and beats a no-skill benchmark under the same costs.
This is a limited test. Volatility is realized volatility for every asset. The archived bot preferred ATM implied volatility for equities; that variant is not tested here.
Universe.
- Markets parsed by the archived parser
archive/oddsoddy/strategies/fundamental_arb/fundamental_arb_strategy/scanner.py::parse_market, imported verbatim. endDatebetween 2026-04-10T00:00:00Z and 2026-08-21T09:07:27Z,closedtrue, and a clean resolution:oddsx.data.markets.resolve_outcomegives YES = 0 or 1.- Sides
above,below,touch_above,touch_below.betweenis excluded (the archived config disabled it). - Symbols: BTC/USD, ETH/USD, SOL/USD, AAPL, MSFT, GOOGL, AMZN, NVDA, TSLA, META, SPY.
- Strike sanity: 0.5 ≤ strike / spot at decision ≤ 2.0. This removes index-level strikes mapped to SPY and non-price "index" markets.
Decision time. One decision per market.
t_dec = max(first_trade + 10 minutes, end − 1 hour), rounded up to the whole second.- Skip the market if
end − t_dec < 30 minutes. - Skip the market if no trade priced it in the 10 minutes before
t_dec.
YES price at decision. Price of trades in [t_dec − 10 min, t_dec), weighted by token_amount.
- YES is the first token in
clobTokenIds. - A trade whose outcome token is the second token is converted to YES price as
1 − price.
Entry (fill) price. The signal still uses the YES price above; the bet is filled at an executable price.
- Fill trades: the market's trades with the earliest timestamp in
[t_dec, t_dec + 10 min). fill_price: those trades' YES prices (second-token trades converted as1 − price), weighted bytoken_amount;fill_ts= that timestamp.- No fill trade in the window, or a fill price not strictly between 0 and 1: no fill. A signalled but unfilled market is not a bet; it is counted as
n_unfilled_signals.
Model inputs.
spot: the close of the last Alpaca 1-minute bar that ended at or beforet_dec. Crypto usesBTC/USD,ETH/USD,SOL/USD; stocks use the SIP feed with raw (unadjusted) prices.sigma: annualized standard deviation (ddof=1) of daily log returns between the closes of Alpaca daily bars that started within the 30 days beforet_decand ended at or beforet_dec(no partial bar); × √365 for crypto, × √252 for stocks.T: (end − t_dec) in days / 365.25.r= 0.05.p_modelcomes from the archived pricer:bs_prob_above(above),1 − bs_prob_above(below),bs_prob_touch_above,bs_prob_touch_below.
Signal. edge = p_model − yes_price.
- Buy YES if
edge > 0.08. - Sell YES (buy NO) if
edge < −0.08. - Otherwise no bet.
- 0.08 is the archived bot's 6% edge plus its cost buffer. It is fixed a priori, not tuned on TRAIN.
Returns. Hold to resolution, $1 notional per bet, at p = fill_price.
- Buy at price p:
(1 − p) / pif YES resolves, otherwise −1. - Sell at price p:
p / (1 − p)if NO resolves, otherwise −1.
Costs. oddsx.research.validation cost model on fill_price, spread 0.02.
fee_ratefrom the market's fee flags:feesEnabledfalse → 0.0;crypto_fees_v2→ 0.07;finance_prices_feesortech_fees→ 0.04; any other enabled fee type → 0.05.
Split. Calendar split on t_dec: TRAIN before 2026-07-01T00:00:00Z, TEST on or after it. TRAIN is reported, never used to choose anything.
Benchmark. The favorite null on the filled decisions, same costs, at fill_price. Buy YES when yes_price ≥ 0.5, otherwise sell YES. edge_vs_naive = strategy TEST mean net − benchmark TEST mean net.
Verdict rule (TEST, pooled).
INSUFFICIENT_DATAif TEST has fewer than 200 bets.VALIDATEDonly if all three hold: mean net > 0,edge_vs_naive> 0, and the 95% lower bound of mean net > 0. The lower bound comes from a cluster bootstrap: clusters are (symbol, end), 2000 resamples, seed 7.FALSIFIEDotherwise.
Also reported (no effect on the verdict).
- Per-cohort results by asset type and side.
- Median vs mean net return.
- The top-1% |net PnL| share.
Promotion. A VALIDATED verdict does not move anything into strategies/. Promotion is a separate operator decision.
Results
Computed by research/hypotheses/fundamental_arb_exec/run.py --results on runs/g4c/decisions.parquet (commit 7542179). Numbers are copied from registry/results/fundamental-arb-exec.json and runs/g4c/universe.json.
Universe accounting. 106,318 markets parsed in the window; 29,623 kept (one decision each), the same universe as fundamental_arb_short. Exclusions:
| reason | markets |
|---|---|
end − t_dec under 30 minutes | 33,872 |
| no trades | 26,595 |
no YES trade in the 10 minutes before t_dec | 8,061 |
| not cleanly resolved | 5,042 |
between side | 3,125 |
Fill accounting. 4,421 of the 29,623 decisions have a fill trade in [t_dec, t_dec + 10 min). Fill delay after t_dec: median 198 s, p10 17 s, p90 492 s. Signals: TRAIN 1,546 (627 filled, 919 unfilled), TEST 1,228 (516 filled, 712 unfilled). These signal counts equal fundamental_arb_short's bet counts, as they should: the signal is unchanged.
TRAIN vs TEST (pooled, $1 per bet at fill_price, net of costs unless stated).
| metric | TRAIN | TEST |
|---|---|---|
| n_decisions | 13,074 | 16,549 |
| n_bets (signalled and filled) | 627 | 516 |
| n_unfilled_signals | 919 | 712 |
| mean_gross | +0.2214 | −0.0988 |
| mean_net | +0.1125 | −0.2449 |
| median_net | +0.0386 | +0.0088 |
| Sharpe (bet-level) | +0.0261 | −0.1457 |
| favorite-null mean_net | +0.1043 | −0.0504 |
| edge_vs_naive | +0.0082 | −0.1945 |
| cluster-bootstrap 95% lower bound of mean_net | −0.1474 | −0.3901 |
| top-1% |net PnL| share | 0.2729 | 0.1434 |
TEST cohorts (reported, no vote). crypto|above: 515 bets, mean net −0.2456. crypto|touch_above: 0 bets. stock|above: 1 bet, mean net +0.1202.
Tails. In TEST the median is slightly positive (+0.009) while the mean is −0.245: many small wins are outweighed by full −1 losses. The largest 1% of bets carry about 14% of absolute PnL, so the loss is not a few outliers. In TRAIN the mean is about three times the median (+0.113 vs +0.039) and the largest 1% carry about 27% of absolute PnL; even TRAIN barely beats the favorite null (+0.008) and its lower bound is negative.
Caveats.
- Limited test: realized volatility for every asset. The archived bot's ATM implied volatility for equities was not tested.
- TRAIN never votes.
- TEST is effectively one cohort (crypto|above, 515 of 516 bets), so the pooled verdict says nothing about stocks or touch markets.
- The fill is the first trade after
t_dec, not an order-book quote. A signalled market with no trade in the next 10 minutes is dropped.
Post-hoc diagnostics (not part of the verdict)
Computed by the controller on 2026-09-14, after the verdict. None of this was pre-registered and none of it changes the verdict below. It bears on why the fundamental_arb_short edge disappears.
- No selection bias from unfilled signals. TEST signals valued at the stale signal price (fundamental_arb_short economics): filled n = 516, mean net +0.179; unfilled n = 712, mean net +0.120. The unfilled signals were not the better ones, so dropping them does not explain the loss.
- The price moves against the signal. Fill price minus signal price, in the bet's direction: median +0.044, mean +0.089. 67.4% of fills are worse than the signal price.
- Same bets, two prices. The 516 filled TEST bets earn +0.179 net at the stale signal price and −0.245 net at the executable fill.
- Fill speed does not save it. Mean net at signal price → at fill, by fill delay:
| fill delay | n | at signal price | at fill |
|---|---|---|---|
| 0–60 s | 104 | +0.295 | −0.129 |
| 60–300 s | 237 | +0.249 | −0.286 |
| 300–600 s | 175 | +0.014 | −0.258 |
- Conclusion for fundamental_arb_short (G4b). Its frozen-rule result (TEST mean net +0.145) is explained by stale YES prices. With executable entry the edge disappears.
Verdict
FALSIFIED
Under the pre-registered rule, TEST has 516 bets (above the 200 floor), and all three conditions fail: mean net −0.2449 ≤ 0, edge_vs_naive −0.1945 ≤ 0, and the cluster-bootstrap 95% lower bound −0.3901 ≤ 0. The protocol verdict is recorded as computed.
Plain conclusion: the short-horizon Black-Scholes signal has no executable edge on these markets. fundamental_arb_short's result was a stale-price artifact: the market price catches up to the model before a bet can be filled. Do not paper-trade or promote any fundamental_arb variant; nothing moves into strategies/.