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