py-clob-client==0.34.6 + web3==7.16.0, the versions preflight_live.py
was validated against. Paper path stays import-lazy/unchanged.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Every drained buy fill is now recorded: the tok-match keeps _record_lag
(real lag/slippage), leftovers get a bet record synthesized from the fill
(_record_untracked_buy). check_book() asserts the book invariant after
every trade and at boot: my_pos token => bet record + conds entry + cash
debited. Records/conds self-correct; the cash leg heals only at boot and
only when ledger_drift matches one un-vouched position's cost+fee exactly
— anything else stays a visible ⚠ drift. my_pos entries now carry cond
(engine) and fills-ledger BUY lines carry token, so the book can always
self-attribute. reconcile_exits' drain no longer discards leftover buys.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The heartbeat/dashboard 'avg lag' was a lifetime accumulator that mixed the
retired 60s poll era into push mode, so a genuinely ~3s bot read as 48s.
lag_recent now keeps the last 24h of (ts, lag, slip); summary + feed report
the 24h average (n = lifetime copies, n24 = fills in window). Backfilled from
copybot_fills.jsonl on boot so it's populated immediately. Verified: 24h view
12s vs 48s lifetime — reflects real push execution.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Self-contained handoff for a fresh session: current state (Set D live, honest
P&L, ledger reconciled), the orphan/cash-debit root-cause with the proper fix
(Option A surgical: record every drained buy fill + invariant assert; Option B
architectural: derive cash from the book), Phase 1 status (6/8 done; 1.3 worker
deps + 1.8 live-money dashboard remain), and standing to-dos (rotate Alchemy
key, delete Railway).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The Set D restart surfaced a $36.35 ledger drift (self-check working). Two
residues from the _record_lag skip on ArbTrader (now dropped): (1) an orphan
Total-Sets position in my_pos whose cost was never debited from cash — the
book held a position it never paid for; removed as unpaid bug residue; (2) a
fully-sold ITF Wuning position still marked open — finalized to closed (pnl
+0.37). Drift back to 0.00. Paper book; no real money touched.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
FINDINGS: new sections on the realizedPnl backbone (killed 4 P&L bugs) and
the anti-survivorship abandoned-loser fold-in (PM /profit is itself biased;
our All-Time is the honest number where it reads below PM), plus the Set D
follow-set selection by simulation. README: follow set is now Set D (6
moderate-bet volume wallets); whale follow-all class retired.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Rebuilt the live follow set from the corrected data. Dropped fortuneking
(weak copy P&L), iohihoo (negative 30d form), and Stavenson/0x4bFb-whale/
ArbTrader (no longer qualify as sharps post-truth-scoring). Added 42021,
0xbadaf319, BikesAreTheBikes. All 6 clear the 2-month copy P&L signal + the
30d conviction form + copyable lead, and are moderate-bet (fit a $1k book,
0 missed). 30d backtest: $27,799 (+2680%, 279W/75L) vs the old set's $16k.
backtest.json matched so the dashboard replays the live set.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The replay selected trusted conviction bets + sold round-trips + chain
settlement, but abandoned losers (bets held to a decided outcome, never
redeemed — /positions at curPrice 0, not in /closed-positions, not trusted
rows on operator-resolved markets) fell out entirely, reading optimistic.
resolved_unredeemed() now pulls them; window_bets folds in the in-window,
conviction-size ones at their decided outcome (loser pays 0). Same anti-
survivorship correction as the sharps table's _open_split.
Impact on the follow-set 30d backtest is ~0 ($16,255->$16,260): those 8
wallets are clean, AND abandoned-loser survivorship is an all-time
ACCUMULATION (oliman2: 1123 losers lifetime, only 2 in any 30d window), so a
30d period metric structurally can't surface much of it. The fix guarantees
no in-window loss is dropped and keeps the backtest consistent with the
all-time sharps view where survivorship actually lives. Verified the fold-in
fires (oliman2's 2 in-window conviction losers identified).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Resolved-but-unredeemed positions (bets that lost, went to $0, never
redeemed) were sitting in Open P&L, breaking the All-Time+Open=PM reconcile
and — worse — hiding real losses. _open_split now routes decided positions
(curPrice pinned 0/1) into the REALIZED total at their outcome, leaving only
genuinely in-flight positions in Open P&L. This is the project's founding
survivorship insight applied to P&L: PM /profit under-counts abandoned
losers unevenly, so where our All-Time reads lower than PM, PM is biased and
ours is honest. oliman2 $181k->$20k (walked from $161k of losers),
JuiceFarm $380k->$32k; clean wallets unchanged (ewww1 $409k, KBO30 ~PM).
Win% now counts abandoned losers too. Verified: Coteykens/imwalkinghere/KBO30
now ~PM; oliman2/JuiceFarm correctly far below their flattering PM.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A position can land in my_pos without a bet record when its buy fill is
drained in a context where _record_lag doesn't fire (e.g. a second market on
the same event in one batch): cash-correct (open_count/deployed read my_pos)
but INVISIBLE in the feed table — the header showed '2 open / $75 deployed'
while only 1 bet rendered. write_feed now synthesizes a bet record from any
open my_pos position lacking one, so the count always equals what's shown.
Ledger was unaffected (drift -0.0) — display-only. my_pos entries now carry
'wallet' for proper name attribution on the synthesized record.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
_open_pnl(w) sums cashPnl over the wallet's current positions (full paging).
Surfaces the sells-winners-holds-losers risk that the realized track record
hides: All-Time (realized) + Open P&L reconciles to PM P&L. A wallet with a
strong realized record but a large negative open book flatters live-follow
experience — now visible instead of inferred from the PM gap.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The sharps table's P&L was reconstructed from won×entry×size and diverged
from PM /profit by up to 10x, with sign flips — four root causes found by
decomposing outliers against PM:
1. bets pull capped at 2000 rows/180d -> high-volume wallets truncated
(ewww1: 740 of 4088 positions cached -> k shown vs k real)
2. both-sides (hedged) markets: one-per-market dedup dropped the paired
side (suraxy: -k of dropped losing legs -> overcount)
3. initialValue=0 on big longshot winners -> mis-sized reconstruction
4. corrupt near-epoch res_t rows polluting sums
Fix: All-Time/Conv/30d P&L are now the sum of Polymarket's OWN realizedPnl
per closed position over FULL history (cache.closed_exits, extended to store
realized_pnl; smart_money.closed_exits captures it + relaxes the avg/tb
guard). This is the wallet's realized track record — what a copier mirroring
buy/sell/hold banks — and sums to PM. Immune to all four bugs (each asset is
its own realized row; no size/entry/res_t needed). Win% = share of closed
positions that made money. Verified vs PM: ewww1 1.00x, KBO30 0.0x->0.97x,
suraxy 2.7x->1.03x, sign flip fixed; oliman2 1.63x is correct-by-design
(realized track record vs PM's open-book unrealized marks). README gotcha 11
rewritten; dashboard tooltips reframed off the copy-ceiling language.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Per the data-completeness call: closed_exits pulls each wallet's ENTIRE
closed-position history, not a 180d window. smart_money.closed_exits returns
(exits, reached_end) — reached_end True only when the pull hits the true
start of history (empty/short page), so the cache's flag tells a
finished backfill from an interrupted one and a killed deep pull re-completes
instead of falsely reporting done. One-time deep cost, amortized by the
incremental cache (later runs page only new closes). Verified: imwalkinghere
full 65d lifetime (879 exits, its real age not a cap), complete=True, 2nd
call 0.00s.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The row cap was the wrong instrument — it could truncate a hyperactive
wallet's window (compromising data) while the real fix is bounding by DATE.
cache.closed_exits now pulls exactly the 180d window the bets cache scores,
so the exit overlay COMPLETELY covers every bet the sharps/backtest stats
touch. Tracks oldest_ts so an interrupted backfill re-completes instead of
falsely reporting done; once complete, refreshes only page new closes.
since_ts (180d/30d per caller) is the real bound — this fixes completeness
AND the runtime stall (the old since_ts=0 pulled all-history). Verified:
imwalkinghere full window in 9s, 2nd call 0.00s cached.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>