chain-balance gates in reconcile_exits + retry queue — ghosts dropped, sells clamped to chain (closes #7); tape ingest pings the daily channel at start + finish
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+29
@@ -1623,6 +1623,18 @@ class Copybot:
|
||||
keep.append(r) # resolver owns this token right now
|
||||
continue
|
||||
shares = min(r["shares"], mine["shares"])
|
||||
if getattr(self.engine.ex, "live", False):
|
||||
try: # closes #7: never retry a ghost
|
||||
chain_sh = self.engine.ex._shares_held(tok)
|
||||
except Exception:
|
||||
keep.append(r)
|
||||
continue
|
||||
if chain_sh < 0.01:
|
||||
log(f"retry: ghost holding (chain 0) — dropping "
|
||||
f"{r.get('label','?')[:40]}")
|
||||
st["my_pos"].pop(tok, None)
|
||||
continue
|
||||
shares = min(shares, chain_sh)
|
||||
price = self.engine._live_price(tok, "sell")
|
||||
if price is None:
|
||||
res = {"ok": False, "resp": "no bid side"}
|
||||
@@ -2024,6 +2036,23 @@ class Copybot:
|
||||
pos = mp.get(token)
|
||||
if not pos:
|
||||
continue
|
||||
# closes #7: the BOOK's share count can drift above the CHAIN's
|
||||
# (venue rounding on partial exits — the BetBoom 0.12sh ghost
|
||||
# looped "not enough balance" forever). Chain is the arbiter:
|
||||
# clamp the exit to what actually exists; a ghost (<0.01sh on
|
||||
# chain) is dropped outright, loudly, instead of retried.
|
||||
if getattr(self.engine.ex, "live", False):
|
||||
try:
|
||||
chain_sh = self.engine.ex._shares_held(token)
|
||||
except Exception:
|
||||
continue # no chain truth -> no action this pass
|
||||
if chain_sh < 0.01:
|
||||
log(f"reconcile: ghost position (book "
|
||||
f"{pos['shares']:.2f}sh, chain 0) — dropping "
|
||||
f"{pos.get('title','?')[:40]}")
|
||||
del mp[token]
|
||||
continue
|
||||
pos["shares"] = min(pos["shares"], chain_sh)
|
||||
name = self.names.get(wallet.lower(), wallet[:10])
|
||||
log(f"reconcile: {name} exited {pos.get('title','?')[:42]} while we "
|
||||
f"weren't listening — mirror-exiting {pos['shares']:.1f}sh now")
|
||||
|
||||
@@ -9,14 +9,32 @@ import gzip
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
import ssl
|
||||
import subprocess
|
||||
import time
|
||||
import urllib.request
|
||||
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
DB = os.path.join(HERE, "..", "live", "rtds.duckdb")
|
||||
APP, SEG = "wwf-recorder", "/data/segments"
|
||||
|
||||
|
||||
def ping(msg):
|
||||
"""Best-effort Discord ping to the DAILY channel (user ask 2026-07-19:
|
||||
the nightly pull announces start + finish). Never fatal."""
|
||||
try:
|
||||
hook = json.load(open(os.path.join(HERE, "..", "config.json"))).get("daily_webhook")
|
||||
if not hook:
|
||||
return
|
||||
req = urllib.request.Request(hook, data=json.dumps({"content": msg}).encode(),
|
||||
headers={"Content-Type": "application/json",
|
||||
"User-Agent": "Mozilla/5.0"})
|
||||
urllib.request.urlopen(req, timeout=10,
|
||||
context=ssl._create_unverified_context()).read()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
# launchd runs daily.sh with a minimal PATH (no /opt/homebrew/bin) — the
|
||||
# 2026-07-18 run died on FileNotFoundError: 'flyctl'. Resolve it explicitly.
|
||||
FLYCTL = shutil.which("flyctl") or "/opt/homebrew/bin/flyctl"
|
||||
@@ -42,6 +60,7 @@ def main():
|
||||
have = {r[0] for r in con.execute("SELECT segment FROM ingested").fetchall()}
|
||||
segs = [s for s in box(f"ls {SEG}").split()
|
||||
if s.endswith(".gz") and s not in have]
|
||||
ping(f"📼 tape ingest started — {len(segs)} segment(s) queued")
|
||||
total = 0
|
||||
for s in sorted(segs):
|
||||
raw = box(f"base64 {SEG}/{s}")
|
||||
@@ -76,6 +95,8 @@ def main():
|
||||
print(f"[ingest] {s}: {len(rows)} trades + {len(aux)} aux")
|
||||
n = con.execute("SELECT count(*) FROM trades").fetchone()[0]
|
||||
print(f"[ingest] +{total} rows · rtds.duckdb now {n:,} trades")
|
||||
ping(f"📼 tape ingest done: +{total:,} rows across {len(segs)} segment(s) "
|
||||
f"· rtds.duckdb now {n:,} trades")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Reference in New Issue
Block a user