Commit Graph

11 Commits

Author SHA1 Message Date
jaxperro 29a4bcca9e harden pipeline against dropped connections; publish fee-aware sharps
- get_json: catch OSError + http.client.HTTPException (RemoteDisconnected/
  IncompleteRead escaped the retry loop and killed last night's validate_timing
  run on a single dropped keep-alive).
- validate_timing: per-wallet retry+exclude guard so one bad wallet can't take
  out the whole selection; regenerated watch_sharps.json — the first fee-aware,
  resolved-only sharp list (10 copy-positive holders of 254).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 09:15:42 -04:00
jaxperro 08f87c589e paper-run realism: taker fees, resolved-only scoring, bounded collect, local runner
- copybot: model Polymarket taker fees (V2, since 2026-03-30: shares*rate*p*(1-p),
  sports 0.03) on every paper/live fill; track fees_paid in state + feed; settle
  P&L nets the entry fee. publish_feed now commits state + fills ledger with the
  feed (autostash rebase) so the local poller is the sole state writer.
- validate_timing: copy_pnl/held_pnl are fee-aware -> sharp selection now requires
  clearing real copy costs; conv stats + lead profile use resolved bets only.
- conviction_scan/skill: exclude res_t>now rows (early-sold positions in
  unresolved markets scored at curPrice - a mark, not an outcome; was ~5% of the
  June test window at a 72% pseudo-win rate).
- portfolio: skip unresolved rows (stake used to vanish from equity); missed bets
  of kind=open no longer KeyError - mark-to-market hypothetical P&L.
- collect: cap stale refreshes at STALE_CAP=2500/run (bulk-aged pool turned the
  daily refresh into a ~40h pull holding the DuckDB lock).
- Actions cron disabled: GitHub ran */5 every ~1.5-2.5h and the 10-min stale
  window skipped the rest -> 1 of ~104 qualifying buys copied. launchd --poll 60
  is now the runner; workflow stays as manual backstop.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-01 23:15:14 -06:00
jaxperro 4b9488be9d live: select sharps by copyability, not lead time (surfaces new holders)
Replace the blunt 24h lead gate with a copy-replay selection run on EVERY
conviction wallet: keep only those that are copy-positive AND have a
genuine hold-to-resolution edge (held_pnl>0, held win-rate>=55% over >=8
resolved held bets), active in 30d, with a light >=1h lead floor vs true
snipers. display_stats now splits copy P&L into scalp vs held legs and
uses fresh-positions resolution (clob fallback) so it's cheap enough to
run on all ~229 wallets.

Effect: drops scalper-traps whose win% looked great but lose copied
(ArbTrader, iohihoo, S888, ttj01) and the old lead gate's discards;
surfaces fast-resolving copy-positive holders it used to throw away
(raid3r, 0x6d1A94f4, 0xec1d07e5, mantaray, earthisgood, shisan...).
14 copy-positive holders now in watch_sharps.json.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 13:31:41 -06:00
jaxperro 66f4229e3d live: add authoritative Copy P&L to the sharp feed
display_stats now computes copy_pnl: what a flat-$50 copier of a wallet's
conviction bets ACTUALLY realizes since Jun 1 — replays their entries,
mirrors their exits, and settles held bets at AUTHORITATIVE clob
resolution (winner by token_id). This is the truth for copyability and
exposes scalpers whose position win% looks great but lose when copied
(ArbTrader 99.5% conv win but -$793 copy; iohihoo 88.7% but -$749).

Position win%/record/P&L stay on the cache (large 180d sample). Added a
clob resolver (_clob_winner) + activity-replay; only Kruto2027 (+1184),
S888, oliman2, fortuneking (+430) etc. are positive to copy.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 12:13:42 -06:00
jaxperro 8a07d0d365 live: drop sharps inactive >30d from the feed
validate_timing now filters the sharp list to wallets that have traded in
the last 30 days (last_trade from the /activity pull), so watch_sharps.json
only lists currently-active sharps — not just hidden client-side. Dropped
10 of 41 on this run (-> 31 active). The dashboard keeps its own 30d filter
as a safety net for daily-stale feeds.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 11:18:41 -06:00
jaxperro 8dc00e44b3 live: add 30-day conviction stats to the sharp feed
display_stats now emits conv30_win/won/lost/pnl — conviction (top-20%)
bets resolved in the last 30 days — for a new "30 Day Conv Record" column
on the dashboard alongside the lifetime record.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 10:57:22 -06:00
jaxperro ee3d3d1e9a live: add avg_bet (mean conviction stake) to the sharp feed
display_stats now emits avg_bet = average stake of the wallet's
conviction (top-20%) bets, for a new dashboard column after the P&L
columns.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 21:19:34 -06:00
jaxperro 37817d380a live: precompute all sharp-table stats into watch_sharps.json
validate_timing.py now writes name, conv_win, conv_won/lost, conv_pnl,
realized_pnl, last_conv_bet and last_trade per sharp, so the dashboard
reads everything from the feed in ONE request instead of 3 data-api
calls per wallet (no more rate-limit storms). Stats come from the cache
(survivorship-correct): conviction win%/record/P&L over ALL of the
wallet's top-20%-stake bets; realized P&L over the last 500 resolved
bets. Resolved P&L per bet = stake*(1-p)/p if won else -stake.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 15:51:18 -06:00
jaxperro 2ee8e79d00 rename timing "insider" verdict -> "last-minute" (honest label)
The lead-time gate in validate_timing.py is a COPYABILITY heuristic, not
proof of inside information: a short entry->resolution lead can be a
genuine insider OR just someone who trades fast-resolving markets (live
sports, hourly) well — indistinguishable, and irrelevant for copy
purposes since either way the window is too tight to mirror. Rename the
verdict accordingly. Gate unchanged (median lead >=24h to count as a
copyable sharp; 50 sharps). Docs updated to current p80 numbers
(218 profile matches, 62/83 forward-profitable, 50 sharps).

The genuine insider-detection scanner (insider.py, z-score / Bubblemaps
fingerprint) keeps its name — that's a different, correctly-named thing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 14:55:43 -06:00
jaxperro 1cbe1a67b9 conviction = per-wallet top-20% stake (p80), not flat $200
Swap the flat $200 conviction cutoff for a per-wallet percentile (top
20% of each wallet's own stake sizes) everywhere it was used:

- cache.py: canonical CONV_PCTILE=0.80 + conv_cutoff() helper (matches
  the dashboard's pctl: filter >0, sort, linear interp)
- conviction_scan.py: per-wallet quantile_cont(size,0.8) in SQL, was
  `size >= 200`
- validate_timing.py, pnl_focused.py: use cache.conv_cutoff

Rationale + validation: p80 reproduces flat-$200's win-rate lift on the
sharps while adapting to scale (a whale's $200 isn't conviction, a
minnow's is). Re-running the pipeline under p80: scan finds 218 profile
wallets (was 69), forward 62/83 profitable (p~0), +16% pooled ROI — edge
persists out-of-sample. Regenerated conviction_wallets.json /
watch_sharps.json; docs updated. skill.py/strategy.py/insider.py
untouched (score over all bets / size as copyability heuristic only).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 14:47:39 -06:00
jaxperro d116b25d52 live: conviction-profile scan + insider-vs-sharp timing validation
conviction_scan.py finds wallets whose high-conviction (>=$200) bets win on
uncertain (~0.4-0.6) markets — real edge, not favorite-riding — trained pre-June
and validated June (25/37 stayed profitable forward, p=0.024). validate_timing.py
applies the entry->resolution lead-time gate that separates copyable sharps from
uncopyable insiders: of 69 matches, 21 were insiders (lead <6h), leaving 23
validated copyable sharps (watch_sharps.json) now shown on jaxperro.com/trading.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 17:22:50 -06:00