From a3169b762dab84f8ae88ae87b74f1818381d8458 Mon Sep 17 00:00:00 2001 From: Codex Date: Sat, 6 Jun 2026 18:12:07 +0200 Subject: [PATCH] =?UTF-8?q?PINK:=20reconcile=20guard=20=E2=80=94=20refuse?= =?UTF-8?q?=20to=20silently=20drop=20orphan=20positions=20on=20restart?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit _reconcile_position_slot passed all N BingX positions (all slot_id=0) to reconcile_from_slots; with N>1 the kernel silently took one and forgot the rest. Now: sort by size desc, take only the largest, log ERROR naming every ignored orphan symbol. Caller must flatten exchange to 0 before restarting. Co-Authored-By: Claude Sonnet 4.6 --- prod/clean_arch/runtime/pink_direct.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/prod/clean_arch/runtime/pink_direct.py b/prod/clean_arch/runtime/pink_direct.py index 72257e0..8d66bd1 100644 --- a/prod/clean_arch/runtime/pink_direct.py +++ b/prod/clean_arch/runtime/pink_direct.py @@ -180,6 +180,7 @@ def _reconcile_position_slot( # Build TradeSlot[] from exchange positions from prod.clean_arch.dita_v2.contracts import TradeSlot, TradeSide + _log = logging.getLogger(__name__) reconciled = [] if positions: for row in positions if isinstance(positions, list) else ( @@ -234,6 +235,19 @@ def _reconcile_position_slot( reconciled.append(slot) if reconciled: + if len(reconciled) > 1: + # Single-slot kernel: multiple open positions = orphan contamination from + # prior retry-duplicate bug. Take the largest by size so the kernel can + # exit it; the rest must be flattened manually before restart. + reconciled.sort(key=lambda s: float(s.size or 0), reverse=True) + orphan_syms = [s.asset for s in reconciled[1:]] + _log.error( + "RECONCILE WARNING: %d BingX positions found for single slot_id=%d. " + "Taking largest (%s size=%.4f). ORPHANS IGNORED (must flatten manually): %s", + len(reconciled), slot_id, reconciled[0].asset, float(reconciled[0].size or 0), + orphan_syms, + ) + reconciled = reconciled[:1] kernel.reconcile_from_slots(reconciled) else: # No open positions — ensure slot is idle