cleanup + calibration tracker

- Railway project magnificent-kindness DELETED (soft-delete, purges
  2026-07-10) — retired-infra fully gone; requirements.txt loses its
  Railway-detection rationale (stdlib core, optional extras noted).
- tests/test_check_book.py: the Option A orphan-fix regression harness
  moves into the repo (was session-local); 6 cases, no network/config.
- daily.sh: calibration step — one row/day of live-book vs backtest
  equity to live/history/calibration.csv, the series that sizes real
  money (day-zero row seeded: live $1,000 vs model $22,027).
- config.json.bak (untracked, stale copy of the leaked Alchemy key)
  deleted locally.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
jaxperro
2026-07-08 17:09:46 -04:00
parent a690323f68
commit d45255c48e
4 changed files with 226 additions and 2 deletions
+1
View File
@@ -28,6 +28,7 @@ The repo is authoritative; project memory has deeper history.
`jaxperro/trading/copybot-section.js`. `?botFeed=`/`?rmFeed=` query params
override feeds for local testing.
- **Daily pipeline** (Mac launchd 08:00, fires on wake — typically ~10:00)
appends one live-vs-model row/day to `live/history/calibration.csv` and
re-derives everything under the new rules: entries cursors now invalidate
daily, `_open_split` folds by the `redeemable` flag, `rtally` splits SOLD.
- **Backtest**: `portfolio.py` takes `--bank` (backtest.json `"bank"`).
+32
View File
@@ -54,6 +54,38 @@ echo "[daily] $(date '+%F %T') 5/7 floors: pin copy-bot p80 conviction floors ->
python3 sync_floors.py || echo "[daily] floor sync skipped"
echo "[daily] $(date '+%F %T') portfolio: cache-based \$1k book -> portfolio.json"
python3 portfolio.py || echo "[daily] portfolio skipped"
echo "[daily] $(date '+%F %T') calibration: live-book vs model, one row/day"
# The number that sizes real money (FINDINGS "The calibration experiment"):
# the measured ratio between the live paper book and the backtest of the SAME
# set. Live side from the bot-committed feed on GitHub (freshest truth; the
# local checkout can lag), model side from the portfolio.json just written.
python3 - <<'CALIB' || echo "[daily] calibration row skipped"
import csv, json, os, ssl, time, urllib.request
ctx = ssl._create_unverified_context()
live = json.load(urllib.request.urlopen(
"https://raw.githubusercontent.com/jaxperro/winning-wallet-finder/main/live/copybot_live.json",
timeout=30, context=ctx))
model = json.load(open("portfolio.json"))
row = {
"date": time.strftime("%F"),
"live_equity": round((live.get("cash") or 0) + (live.get("deployed") or 0)
+ (live.get("reserve") or 0), 2),
"live_realized": live.get("realized"), "live_open": live.get("open_count"),
"live_drift": live.get("ledger_drift"),
"model_equity": model.get("equity"), "model_realized": model.get("realized"),
"model_bank": model.get("bank"), "wallets": ",".join(live.get("wallets") or []),
}
os.makedirs("history", exist_ok=True)
path = "history/calibration.csv"
new = not os.path.exists(path)
with open(path, "a", newline="") as fh:
w = csv.DictWriter(fh, fieldnames=list(row))
if new:
w.writeheader()
w.writerow(row)
print(f"[daily] calibration: live ${row['live_equity']:,} vs model "
f"${row['model_equity']:,} -> {path}")
CALIB
echo "[daily] $(date '+%F %T') 6/7 dashboard"
python3 dashboard.py
mkdir -p history && cp watch_skilled.json "history/watch_$(date '+%Y%m%d').json" 2>/dev/null
+5 -2
View File
@@ -1,2 +1,5 @@
# Winning Wallet Finder — no third-party deps, Python 3 stdlib only.
# This file exists so Railway/Nixpacks detects a Python app.
# Core system: Python 3 stdlib only — nothing to install for the finder,
# the daily pipeline, or the PAPER copy bot.
# Optional extras, installed only where used:
# py-clob-client==0.34.6, web3==7.16.0 -> live trading (fly.Dockerfile has them)
# duckdb -> live/ cache + wide/ scanner
+188
View File
@@ -0,0 +1,188 @@
"""Regression tests for the Option A orphan fix (HANDOFF 2026-07-08):
_record_untracked_buy, _synth_bet, check_book (record/conds self-correct +
guarded boot cash heal). Run: python3 tests/test_check_book.py — needs no
network, no cache, no config; stubs the engine.
"""
import json
import os
import sys
import tempfile
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), ".."))
import copybot # noqa: E402
_TMP = tempfile.mkdtemp(prefix="copybot_test_")
FILL_LOG = os.path.join(_TMP, "fills_test.jsonl")
FEED = os.path.join(_TMP, "feed_test.json")
class StubEx:
live = False
def __init__(self):
self.fills = []
class StubEngine:
def __init__(self, state):
self.state = state
self.ex = StubEx()
self.persisted = 0
def persist(self):
self.persisted += 1
def open_exposure(self):
return sum(p["cost"] for p in self.state["my_pos"].values())
def mkbot(state):
cfg = {"watch": [{"wallet": "0xabc", "name": "TestSharp"}],
"bankroll_usd": 1000.0,
"feed_path": FEED, "fill_log": FILL_LOG}
eng = StubEngine(state)
bot = copybot.Copybot(cfg, eng, filt=None)
bot.here = "" # fill_log paths are absolute already
return bot, eng
def base_state():
return {"my_pos": {}, "their_pos": {}, "bets": {}, "conds": {},
"adjustments": [], "missed": [], "lag_recent": []}
def approx(a, b, tol=0.02):
assert abs(a - b) <= tol, f"{a} !~ {b}"
fails = []
def case(name):
def deco(fn):
try:
if os.path.exists(FILL_LOG):
os.remove(FILL_LOG)
fn()
print(f"PASS {name}")
except AssertionError as e:
print(f"FAIL {name}: {e}")
fails.append(name)
return deco
@case("drain debits cash; untracked buy gets record+conds+ledger line")
def t1():
st = base_state()
bot, eng = mkbot(st)
# position exists (engine added it), fill drained under ANOTHER trade's loop
st["my_pos"]["T2"] = {"shares": 80.0, "cost": 40.0, "title": "Orphan Mkt",
"outcome": "Yes", "wallet": "0xabc", "cond": "0xcond2"}
eng.ex.fills.append({"side": "BUY", "token": "T2", "shares": 80.0, "price": 0.5})
cash0 = st["cash"]
buys = bot._drain_fills()
assert len(buys) == 1
fee = buys[0]["fee"]
approx(st["cash"], cash0 - 40.0 - fee)
bot._record_untracked_buy(buys[0])
b = st["bets"]["T2"]
assert b["status"] == "open" and b["their_price"] is None
assert b["name"] == "TestSharp" and b["wallet"] == "0xabc"
assert st["conds"]["T2"] == "0xcond2"
lines = [json.loads(l) for l in open(FILL_LOG)]
assert any(l.get("token") == "T2" and l.get("side") == "BUY" for l in lines)
approx(bot.ledger_drift(), 0.0)
@case("check_book synthesizes record + backfills conds, drift stays 0")
def t2():
st = base_state()
bot, eng = mkbot(st)
st["my_pos"]["T3"] = {"shares": 100.0, "cost": 45.0, "title": "Synth Mkt",
"outcome": "No", "wallet": "0xabc", "cond": "0xcond3"}
# cash WAS debited (cost + the same estimated fee) — drained-but-not-recorded
fee_est = copybot.taker_fee(100.0, 0.45, bot.fee_rate)
st["cash"] -= 45.0 + fee_est
drift = bot.check_book()
b = st["bets"]["T3"]
assert b["status"] == "open" and b["their_price"] is None
approx(b["fee"], fee_est)
assert st["conds"]["T3"] == "0xcond3"
approx(drift, 0.0)
assert eng.persisted > 0
@case("boot heal: never-debited orphan matched by drift -> cash debited, drift 0")
def t3():
st = base_state()
bot, eng = mkbot(st)
st["my_pos"]["T4"] = {"shares": 72.0, "cost": 36.35, "title": "Hanyi Liu Redux",
"outcome": "Yes", "wallet": "0xabc", "cond": "0xcond4"}
cash0 = st["cash"] # cash NEVER debited — the crash orphan
drift = bot.check_book(heal_cash=True)
fee_est = st["bets"]["T4"]["fee"]
approx(st["cash"], cash0 - 36.35 - fee_est)
approx(drift, 0.0)
lines = [json.loads(l) for l in open(FILL_LOG)]
assert any(l.get("healed") for l in lines)
@case("boot heal REFUSES a drift that matches nothing")
def t4():
st = base_state()
bot, eng = mkbot(st)
st["my_pos"]["T5"] = {"shares": 80.0, "cost": 40.0, "title": "Innocent Mkt",
"outcome": "Yes", "wallet": "0xabc", "cond": "0xcond5"}
fee_est = copybot.taker_fee(80.0, 0.5, bot.fee_rate)
st["cash"] -= 40.0 + fee_est # this position's cash is fine
st["cash"] += 10.0 # unrelated $10 drift from somewhere else
cash0 = st["cash"]
drift = bot.check_book(heal_cash=True)
assert st["cash"] == cash0, "healed cash it should not have touched"
approx(drift, 10.0)
@case("real _record_lag record (their_price set) is never a heal candidate")
def t5():
st = base_state()
bot, eng = mkbot(st)
st["my_pos"]["T6"] = {"shares": 80.0, "cost": 40.0, "title": "Lagged Mkt",
"outcome": "Yes", "wallet": "0xabc", "cond": "0xcond6"}
st["bets"]["T6"] = {"token": "T6", "wallet": "0xabc", "name": "TestSharp",
"outcome": "Yes", "title": "Lagged Mkt",
"their_price": 0.48, "my_price": 0.5, "slippage_pct": 0.04,
"shares": 80.0, "cost": 40.0, "fee": 0.6,
"opened": 1, "status": "open",
"exit_price": None, "pnl": None, "settled": None}
st["cash"] -= 40.6 # this position's cash was properly drained
# fabricate an UNRELATED drift that HAPPENS to equal this bet's cost+fee
st["cash"] += 40.6
cash0 = st["cash"]
drift = bot.check_book(heal_cash=True)
assert st["cash"] == cash0, "debited a vouched position"
approx(drift, 40.6)
@case("two orphans: both healed iteratively? no — only exact-match heals")
def t6():
st = base_state()
bot, eng = mkbot(st)
# two never-debited orphans of different sizes: drift = sum, matches
# NEITHER individually -> heal must refuse both (visible drift > 0)
st["my_pos"]["T7"] = {"shares": 80.0, "cost": 40.0, "title": "Orphan A",
"outcome": "Yes", "wallet": "0xabc", "cond": "c7"}
st["my_pos"]["T8"] = {"shares": 50.0, "cost": 30.0, "title": "Orphan B",
"outcome": "No", "wallet": "0xabc", "cond": "c8"}
cash0 = st["cash"]
drift = bot.check_book(heal_cash=True)
assert st["cash"] == cash0, "healed on a non-matching drift"
assert drift > 70, f"drift should stay visible, got {drift}"
for f in (FILL_LOG, FEED):
if os.path.exists(f):
os.remove(f)
print()
print("FAILURES:", fails or "none")
sys.exit(1 if fails else 0)