Files
winning-wallet-finder/archive/copyback.py
T
jaxperro 8958279ee8 cleanup: archive everything the live system no longer touches
- retired-infra/: Railway config, Mac launchd runner, GH-Actions cron
  (worker is Fly.io arn now; Actions cron last fired 2026-07-02)
- live-research/: the June selection experiments (strategy/followability/
  pnl_basket/pnl_focused/backtest_june/clean_test + outputs)
- us-venue/: the scrapped Polymarket-US listability probe (+ its
  env-gated ONLY_CONDS replay filter stays in portfolio.py — generally
  useful for subset replays)
- root sweeps hunt/huntwide/oos/copyback/watch.json -> archive/
- untracked logs/CSVs of dead experiments -> archive/local/ (gitignored)
- READMEs updated: Fly migration, geoblock gotcha, new file map

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 17:18:46 -04:00

177 lines
6.5 KiB
Python

#!/usr/bin/env python3
"""Backtest: copy the z-validated edge wallets' fresh entries from a start date,
weighted by edge strength, reinvesting 100% of profits (compounding).
Honest model:
- We copy each wallet's FIRST buy of a market entered on/after the start date,
filling at THEIR entry price (optimistic — ignores the seconds-to-minutes
lag you'd really face; that's the next thing to test forward).
- Outcome = how that market resolved (curPrice 1 won / 0 lost), or current
price if still open (marked to market).
- Sizing: stake = wallet_weight * BET_K * current_bankroll, capped by cash.
Bankroll = cash + open-position cost, so profits compound into bet size.
"""
import time
from collections import defaultdict
import smart_money as sm
START = "2026-05-30"
BET_K = 0.10 # aggressiveness: top-weight wallet risks ~weight*K of bankroll/bet
START_BANKROLL = 1000.0
# z-validated edge wallets (z-proportional weights — reward edge strength)
EDGE = [
("Famecesgoal", "0x0b0f92507bbc340762d38eca43eba1e11ee37af1", 9.6),
("JAMJAMJAM4", "0xe8c4d68aff65b38cac46987b9b65e01eb47d395d", 5.7),
("Domerina", "0xf4ad6aedf5475f2023912cd191eff5ec90ead00b", 5.3),
("MyLastStand", "0x419f32f30814030554f5df3e9f508ad7394ce853", 4.2),
]
def _parse(d):
return time.mktime(time.strptime(d.replace("Z", "")[:19],
"%Y-%m-%dT%H:%M:%S" if "T" in d else "%Y-%m-%d")) if d else 0
def entries_after(wallet, cutoff):
"""Earliest BUY (price, ts) per asset, for buys on/after cutoff."""
out = {}
off = 0
while off < 4000:
page = sm.get_json("/activity", {"user": wallet, "type": "TRADE",
"limit": 500, "offset": off})
if not page:
break
for t in page:
if t.get("side") == "BUY" and t.get("asset") and t.get("timestamp", 0) >= cutoff:
a = t["asset"]
if a not in out or t["timestamp"] < out[a][1]:
out[a] = (t.get("price", 0), t["timestamp"], t.get("title", "?")[:40])
off += 500
if len(page) < 500 or page[-1].get("timestamp", 0) < cutoff:
break
return out
def outcomes(wallet):
"""asset -> (curPrice, endTs) from open + closed positions."""
o = {}
for ep in ("/positions", "/closed-positions"):
off = 0
while off < 2000:
params = {"user": wallet, "limit": 50, "offset": off}
if "closed" in ep:
params.update(sortBy="TIMESTAMP", sortDirection="DESC")
else:
params["sizeThreshold"] = 0.0
page = sm.get_json(ep, params)
if not page:
break
for p in page:
if p.get("asset"):
o.setdefault(p["asset"], (p.get("curPrice", 0), _parse(p.get("endDate", ""))))
off += 50
if len(page) < 50:
break
return o
def main():
cutoff = _parse(START)
now = time.time()
tot_z = sum(z for _, _, z in EDGE)
weights = {w: z / tot_z for _, w, z in EDGE}
names = {w: n for n, w, z in EDGE}
print(f"Copy-trade backtest from {START} · start ${START_BANKROLL:,.0f} · "
f"reinvest 100% · BET_K={BET_K}")
print("weights (z-proportional):")
for n, w, z in EDGE:
print(f" {n:14} z={z:>4} weight={weights[w]*100:>4.1f}%")
# gather all copied bets
bets = []
for n, w, z in EDGE:
ent = entries_after(w, cutoff)
outc = outcomes(w)
for a, (price, ts, title) in ent.items():
if a not in outc or not (0 < price < 1):
continue
cur, end = outc[a]
resolved = end and end < now
bets.append({"w": w, "name": n, "ts": ts, "price": price,
"mark": cur, "res_ts": end or now, "resolved": bool(resolved),
"title": title})
print(f"\ncopied bets entered since {START}: {len(bets)}")
# discrete-event sim: enter at entry ts, free capital as bets resolve
events = []
for i, b in enumerate(bets):
events.append((b["ts"], 0, i)) # 0=enter
if b["resolved"]:
events.append((b["res_ts"], 1, i)) # 1=resolve
events.sort()
cash = START_BANKROLL
open_cost = 0.0
pos = {} # i -> (shares, stake)
wins = losses = skipped = 0
realized_pnl = 0.0
by_wallet = defaultdict(float)
for ts, kind, i in events:
b = bets[i]
if kind == 0: # ENTER
bankroll = cash + open_cost
stake = min(weights[b["w"]] * BET_K * bankroll, cash)
if stake < 1:
skipped += 1
continue
shares = stake / b["price"]
pos[i] = (shares, stake)
cash -= stake
open_cost += stake
else: # RESOLVE
if i not in pos:
continue
shares, stake = pos.pop(i)
payout = shares * (1 if b["mark"] >= 0.5 else 0)
cash += payout
open_cost -= stake
realized_pnl += payout - stake
by_wallet[b["name"]] += payout - stake
if b["mark"] >= 0.5:
wins += 1
else:
losses += 1
# mark any still-open copied bets to current price
open_val = 0.0
for i, (shares, stake) in pos.items():
mark = bets[i]["mark"]
open_val += shares * mark
by_wallet[bets[i]["name"]] += shares * mark - stake
equity = cash + open_val
unreal = open_val - open_cost
print(f"\n{'='*64}")
print(f" resolved copied bets: {wins+losses} ({wins}W / {losses}L"
f"{f' · {wins/(wins+losses)*100:.0f}% hit' if wins+losses else ''})")
print(f" still open (marked to market): {len(pos)} · skipped (no cash): {skipped}")
print(f"\n REALIZED P&L (locked, resolved bets only): ${realized_pnl:+,.2f}"
f" -> {realized_pnl/START_BANKROLL*100:+.1f}%")
print(f" UNREALIZED (open positions marked to current price): ${unreal:+,.2f}"
f" -> {unreal/START_BANKROLL*100:+.1f}%")
print(f" ending equity: ${equity:,.2f} ({(equity/START_BANKROLL-1)*100:+.1f}% "
f"over {(now-cutoff)/86400:.0f}d) — but {open_val/equity*100:.0f}% of it is UNREALIZED")
print(f"{'='*64}")
print(" P&L by wallet:")
for n, _, _ in EDGE:
print(f" {n:14} {by_wallet[n]:+,.2f}")
if __name__ == "__main__":
main()