mirror of
https://github.com/jaxperro/winning-wallet-finder.git
synced 2026-07-27 15:57:47 +00:00
audit fixes: pin-mirror to backtest, unpinned-floor warning, watch_sharps diff, class_pct guard
From the automated-writer clobber audit (3-agent workflow): - sync_floors now ACTUALLY mirrors floor_pin into backtest.json (the comment claimed it; the code never did — so a paper pin diverged from the backtest's dynamic p80, defeating calibration). Kruto 80 now consistent across all 3. - sync_floors warns loudly when about to revert an UNPINNED floor that looks hand-edited (>5 and >15%) — floor_pin is the only protected key, so a direct edit used to vanish silently at 08:00. - validate_timing logs the sharps add/drop delta and documents watch_sharps.json as generated-only (format kept a plain list — dashboard/discord read it so). - daily.sh warns if copybot.paper.json vs backtest.json class_pct desync. Audit verdict otherwise CLEAN: sync_floors is the only pipeline step that mutates a manual-knob file, and it round-trips the whole JSON touching only floor — all other knobs (caps, guard, depth_gate, follow set, band) survive. config.live.example.json is untouched by every automated writer (safe by neglect). No second floor_pin-class bug exists. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -52,6 +52,15 @@ python3 conviction_scan.py
|
||||
python3 validate_timing.py
|
||||
echo "[daily] $(date '+%F %T') 5/7 floors: pin copy-bot p80 conviction floors -> copybot.paper.json"
|
||||
python3 sync_floors.py || echo "[daily] floor sync skipped"
|
||||
# parity guard (2026-07-13 audit): class_pct lives in BOTH copybot.paper.json
|
||||
# and backtest.json with no generator — warn if they silently desync so the
|
||||
# paper book and backtest don't drift apart on sizing.
|
||||
python3 - <<'PY' || true
|
||||
import json
|
||||
p=json.load(open("copybot.paper.json")).get("follow",{}).get("class_pct")
|
||||
b=json.load(open("backtest.json")).get("class_pct")
|
||||
if p!=b: print(f"[daily] ⚠ class_pct DESYNC: paper {p} != backtest {b}")
|
||||
PY
|
||||
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"
|
||||
|
||||
@@ -30,6 +30,35 @@ import trust # noqa: E402
|
||||
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
PAPER = os.path.join(HERE, "copybot.paper.json")
|
||||
BACKTEST = os.path.join(HERE, "backtest.json")
|
||||
|
||||
|
||||
def mirror_pins_to_backtest(paper_ws):
|
||||
"""Keep backtest.json's floors on the SAME pins as the paper book so the
|
||||
three books stay comparable (the audit found the code never did this
|
||||
despite the comment claiming it — a floor_pin=80 on Kruto in paper vs a
|
||||
dynamic p80=125.61 in the backtest silently defeated the calibration)."""
|
||||
pins = {w["wallet"].lower(): w["floor_pin"]
|
||||
for w in paper_ws if w.get("floor_pin") is not None}
|
||||
if not pins:
|
||||
return
|
||||
try:
|
||||
bt = json.load(open(BACKTEST))
|
||||
except Exception:
|
||||
return
|
||||
touched = 0
|
||||
for w in bt.get("wallets", []):
|
||||
p = pins.get(w["wallet"].lower())
|
||||
if p is not None:
|
||||
w["floor_pin"] = round(float(p), 2)
|
||||
if w.get("floor") != round(float(p), 2):
|
||||
touched += 1
|
||||
w["floor"] = round(float(p), 2)
|
||||
if touched:
|
||||
tmp = BACKTEST + ".tmp"
|
||||
json.dump(bt, open(tmp, "w"), indent=2)
|
||||
os.replace(tmp, BACKTEST)
|
||||
print(f"[floors] mirrored {len(pins)} pin(s) into backtest.json")
|
||||
|
||||
|
||||
def trusted_p80(wallet, now=None):
|
||||
@@ -71,12 +100,19 @@ def main():
|
||||
continue # no trusted sized bets — leave as-is
|
||||
new = round(p80, 2)
|
||||
old = w.get("floor")
|
||||
# a big UNPINNED gap = a hand edit about to be reverted. floor_pin is
|
||||
# the only protected key; warn loudly so a direct floor edit doesn't
|
||||
# vanish silently at 08:00 (the trap behind the original Kruto bug).
|
||||
if old is not None and abs(old - new) > 5 and abs(old - new) / max(new, 1) > 0.15:
|
||||
print(f"[floors] ⚠ {w.get('name', w['wallet'][:8])} floor {old}→{new} "
|
||||
f"(p80) — was this a manual edit? add \"floor_pin\": {old} to keep it")
|
||||
w["floor"] = new
|
||||
if old is None or abs((old or 0) - new) > 0.5:
|
||||
changed.append((w.get("name", w["wallet"][:8]), old, new))
|
||||
tmp = PAPER + ".tmp"
|
||||
json.dump(cfg, open(tmp, "w"), indent=2)
|
||||
os.replace(tmp, PAPER)
|
||||
mirror_pins_to_backtest(ws)
|
||||
print(f"[floors] pinned {len(ws)} trusted-p80 floors to copybot.paper.json"
|
||||
+ (f"; changed: " + ", ".join(f"{n} {o}→{v:g}" for n, o, v in changed)
|
||||
if changed else " (no material change)"))
|
||||
|
||||
+15
-1
@@ -418,7 +418,21 @@ def main():
|
||||
f"{(str(c['held_won'])+'-'+str(c['held_lost'])):>9}"
|
||||
f"{sp:>5.0f}%{ld:>9} {(c.get('name') or c['wallet'][:10])}")
|
||||
|
||||
json.dump(sharps, open(os.path.join(HERE, "watch_sharps.json"), "w"), indent=2)
|
||||
# GENERATED-ONLY file (2026-07-13 audit): regenerated wholesale nightly —
|
||||
# any hand edit here is silently clobbered. Manual follow decisions live
|
||||
# in copybot.paper.json, not here. Log the delta vs the prior file so a
|
||||
# replacement always leaves an audit trail in daily.log. Format stays a
|
||||
# plain list (the dashboard + discord_daily read it as one).
|
||||
path = os.path.join(HERE, "watch_sharps.json")
|
||||
try:
|
||||
prev = {s.get("wallet") for s in json.load(open(path))}
|
||||
cur = {s.get("wallet") for s in sharps}
|
||||
if cur - prev or prev - cur:
|
||||
print(f" sharps Δ: +{len(cur - prev)} {[w[:10] for w in cur - prev]} "
|
||||
f"· -{len(prev - cur)} {[w[:10] for w in prev - cur]}")
|
||||
except Exception:
|
||||
pass
|
||||
json.dump(sharps, open(path, "w"), indent=2)
|
||||
print(f"\n-> watch_sharps.json ({len(sharps)} copy-positive holders)")
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user