From c29dc2c8ac00a93f108ebbf3d15e2076d82dd795 Mon Sep 17 00:00:00 2001 From: DiTus Date: Fri, 2 Jan 2026 11:52:41 +0100 Subject: [PATCH] feat: robust fee collection in clp_manager.py - positions in 'CLOSING' state are now monitored and fee collection retries until successful before marking as 'CLOSED' --- clp_manager.py | 48 +++++++++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/clp_manager.py b/clp_manager.py index 2d43cd7..d48f2e8 100644 --- a/clp_manager.py +++ b/clp_manager.py @@ -739,12 +739,14 @@ def main(): while True: try: status_data = load_status_data() - open_positions = [p for p in status_data if p.get('status') == 'OPEN'] + # Include CLOSING status to ensure we finish what we started (fee collection retries) + open_positions = [p for p in status_data if p.get('status') in ['OPEN', 'CLOSING']] active_auto_pos = next((p for p in open_positions if p.get('type') == 'AUTOMATIC'), None) if active_auto_pos: token_id = active_auto_pos['token_id'] + current_status = active_auto_pos.get('status') pos_details, pool_c = get_position_details(w3, npm, factory, token_id) if pos_details: @@ -851,29 +853,33 @@ def main(): 'hedge_pnl_realized_usd': active_auto_pos.get('hedge_pnl_realized', 0.0), 'hedge_fees_paid_usd': active_auto_pos.get('hedge_fees_paid', 0.0) } - # We use 'target_value' as a proxy for 'Initial Hedge Equity' + 'Initial Uni Val' if strictly tracking strategy? - # For now, let's pass what we have. - # To get 'hedge_equity', we ideally need clp_hedger to write it to JSON. - # Current implementation of kpi_tracker uses 'hedge_equity' in NAV. - # If we leave it 0, NAV will be underreported. - # WORKAROUND: Assume Hedge PnL Realized IS the equity change if we ignore margin. - log_kpi_snapshot(snapshot) - if not in_range and CLOSE_POSITION_ENABLED: - logger.warning(f"🛑 Closing Position {token_id} (Out of Range)") - update_position_status(token_id, "CLOSING") + # --- CLOSING LOGIC --- + if current_status == "CLOSING" or (not in_range and CLOSE_POSITION_ENABLED): + if current_status != "CLOSING": + logger.warning(f"🛑 Closing Position {token_id} (Out of Range)") + update_position_status(token_id, "CLOSING") - # 1. Remove Liquidity - if decrease_liquidity(w3, npm, account, token_id, pos_details['liquidity'], pos_details['token0_decimals'], pos_details['token1_decimals']): - # 2. Collect Fees - collect_fees(w3, npm, account, token_id) - update_position_status(token_id, "CLOSED") - - # 3. Optional Rebalance (Sell 50% WETH if fell below) - if REBALANCE_ON_CLOSE_BELOW_RANGE and current_tick < tick_lower: - # Simple rebalance logic here (similar to original check_and_swap surplus logic) - pass + # 1. Remove Liquidity (if any left) + liq_to_remove = pos_details['liquidity'] + success_liq = True + if liq_to_remove > 0: + success_liq = decrease_liquidity(w3, npm, account, token_id, liq_to_remove, pos_details['token0_decimals'], pos_details['token1_decimals']) + + # 2. Collect Fees (Retry if previous attempt failed or if liquidity was just removed) + if success_liq: + if collect_fees(w3, npm, account, token_id): + update_position_status(token_id, "CLOSED") + + # 3. Optional Rebalance (Sell 50% WETH if fell below) + if REBALANCE_ON_CLOSE_BELOW_RANGE and current_tick < tick_lower: + # Simple rebalance logic here (similar to original check_and_swap surplus logic) + pass + else: + logger.error(f"❌ Fee collection failed for {token_id}. Will retry in next loop.") + else: + logger.error(f"❌ Liquidity removal failed for {token_id}. Will retry in next loop.") elif OPEN_POSITION_ENABLED: logger.info("🔍 No active position. Analyzing market (Fast scan: 37s)...")