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:
@@ -28,6 +28,7 @@ The repo is authoritative; project memory has deeper history.
|
|||||||
`jaxperro/trading/copybot-section.js`. `?botFeed=`/`?rmFeed=` query params
|
`jaxperro/trading/copybot-section.js`. `?botFeed=`/`?rmFeed=` query params
|
||||||
override feeds for local testing.
|
override feeds for local testing.
|
||||||
- **Daily pipeline** (Mac launchd 08:00, fires on wake — typically ~10:00)
|
- **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
|
re-derives everything under the new rules: entries cursors now invalidate
|
||||||
daily, `_open_split` folds by the `redeemable` flag, `rtally` splits SOLD.
|
daily, `_open_split` folds by the `redeemable` flag, `rtally` splits SOLD.
|
||||||
- **Backtest**: `portfolio.py` takes `--bank` (backtest.json `"bank"`).
|
- **Backtest**: `portfolio.py` takes `--bank` (backtest.json `"bank"`).
|
||||||
|
|||||||
@@ -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"
|
python3 sync_floors.py || echo "[daily] floor sync skipped"
|
||||||
echo "[daily] $(date '+%F %T') portfolio: cache-based \$1k book -> portfolio.json"
|
echo "[daily] $(date '+%F %T') portfolio: cache-based \$1k book -> portfolio.json"
|
||||||
python3 portfolio.py || echo "[daily] portfolio skipped"
|
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"
|
echo "[daily] $(date '+%F %T') 6/7 dashboard"
|
||||||
python3 dashboard.py
|
python3 dashboard.py
|
||||||
mkdir -p history && cp watch_skilled.json "history/watch_$(date '+%Y%m%d').json" 2>/dev/null
|
mkdir -p history && cp watch_skilled.json "history/watch_$(date '+%Y%m%d').json" 2>/dev/null
|
||||||
|
|||||||
+5
-2
@@ -1,2 +1,5 @@
|
|||||||
# Winning Wallet Finder — no third-party deps, Python 3 stdlib only.
|
# Core system: Python 3 stdlib only — nothing to install for the finder,
|
||||||
# This file exists so Railway/Nixpacks detects a Python app.
|
# 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
|
||||||
|
|||||||
@@ -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)
|
||||||
Reference in New Issue
Block a user