fix: remove broken auto-absorb (caused exponential drift feedback loop)

The auto-absorb added -drift to adjustments, but adjustments are part of
the ledger_drift calculation, so each absorb DOUBLED the drift next
heartbeat:  ->  ->  ->  -> ... -> ,139,590 in 20 minutes.

Reverted to only anchoring cash to chain (CASH≠CHAIN fix), which works
correctly. LEDGER DRIFT is now just a warning (cosmetic accounting issue
that doesn't affect trading). If it bothers you, the manual fix (stop,
query chain, clear state, restart) still works.

Added NOTE comment explaining why auto-absorbing ledger_drift via
adjustments creates a feedback loop.
This commit is contained in:
2026-07-20 16:12:46 +08:00
parent ac2ae13a79
commit f4d1c2a71f
+7 -20
View File
@@ -1713,16 +1713,15 @@ class Copybot:
bankstr = f" · banked ${reserve:,.0f}" if reserve else ""
drift = self.ledger_drift()
gap = self.chain_cash_gap()
# ponytail: auto-anchor to chain - proxy instability causes untracked
# fills leading to LEDGER DRIFT and CASH≠CHAIN. Two corrections:
# 1. If chain cash differs from book cash -> set book cash = chain
# 2. If ledger still drifts after cash correction -> absorb into adjustments
# Ceiling: this masks real bugs if a position is truly untracked, but
# the chain balance already reflects all real positions' cost.
if self.engine.ex.live and gap is not None:
# ponytail: auto-anchor cash to chain when CASH≠CHAIN > $1. Proxy
# instability causes untracked fills; the chain is the source of truth.
# NOTE: do NOT try to auto-fix LEDGER DRIFT via adjustments - it creates
# a feedback loop (adjustment changes the drift calc, which doubles the
# drift next heartbeat, exponentially). LEDGER DRIFT is a cosmetic
# accounting issue that doesn't affect trading; just warn on it.
if self.engine.ex.live and gap is not None and abs(gap) > 1.0:
chain_bal = self._chain_bal[1]
book_cash = self.engine.state.get("cash", bank)
# 1. anchor cash to chain
if chain_bal is not None and abs(chain_bal - book_cash) > 1.0:
self.engine.state["cash"] = chain_bal
self.engine.state.setdefault("adjustments", []).append({
@@ -1734,18 +1733,6 @@ class Copybot:
log(f"chain anchor: book cash ${book_cash:.2f} -> chain ${chain_bal:.2f} "
f"(gap ${gap:+.2f}, auto-corrected)")
gap = 0
drift = self.ledger_drift()
# 2. absorb residual ledger drift into adjustments
if abs(drift) > 0.50:
self.engine.state.setdefault("adjustments", []).append({
"ts": time.time(),
"amount": -drift,
"note": f"auto-absorb ledger drift ${drift:+.2f}"
})
self.engine.persist()
log(f"auto-absorb ledger drift ${drift:+.2f} (untracked fill, "
f"chain cash OK at ${chain_bal:.2f})")
drift = 0
driftstr = f" · ⚠ LEDGER DRIFT ${drift:+.2f}" if abs(drift) > 0.05 else ""
if gap is not None and abs(gap) > 0.5:
driftstr += f" · ⚠ CASH≠CHAIN ${gap:+.2f}"