diff --git a/daemons/tick_trader.py b/daemons/tick_trader.py index e50d0c4..e776ece 100644 --- a/daemons/tick_trader.py +++ b/daemons/tick_trader.py @@ -640,7 +640,7 @@ def restore_positions() -> None: balances = upbit_client.get_balances() for b in balances: currency = b.get('currency', '') - bal = float(b.get('balance', 0)) + bal = float(b.get('balance', 0)) + float(b.get('locked', 0)) avg = float(b.get('avg_buy_price', 0)) if currency == 'KRW' or bal <= 0 or avg <= 0: continue @@ -649,23 +649,34 @@ def restore_positions() -> None: continue if ticker in positions: continue - # cascade ① 지정가 매도 즉시 제출 + # 기존 미체결 매도 주문 전부 취소 후 새로 제출 + try: + old_orders = upbit_client.get_order(ticker, state='wait') or [] + for o in (old_orders if isinstance(old_orders, list) else []): + if o.get('side') == 'ask': + cancel_order_safe(o.get('uuid')) + log.info(f"[복구] {ticker} 기존 매도 주문 취소: {o.get('uuid')}") + except Exception: + pass + # 취소 후 실제 가용 수량 재조회 + time.sleep(0.5) + actual_bal = upbit_client.get_balance(currency) or bal _, _, lr, stag = CASCADE_STAGES[0] target = avg * (1 + lr) - sell_uuid = submit_limit_sell(ticker, bal, target) + sell_uuid = submit_limit_sell(ticker, actual_bal, target) positions[ticker] = { 'entry_price': avg, 'entry_ts': datetime.now() - timedelta(seconds=LLM_MIN_ELAPSED), # LLM 즉시 활성 'running_peak': avg, - 'qty': bal, + 'qty': actual_bal, 'stage': 0, 'sell_uuid': sell_uuid, 'sell_price': target, 'llm_last_ts': None, 'llm_active': False, } - log.info(f"[복구] {ticker} 수량:{bal:.6f} 매수평균:{avg:,.0f}원") - tg(f"♻️ 포지션 복구 {ticker}\n매수평균: {avg:,.0f}원 수량: {bal:.6f}") + log.info(f"[복구] {ticker} 수량:{actual_bal:.6f} 매수평균:{avg:,.0f}원") + tg(f"♻️ 포지션 복구 {ticker}\n매수평균: {avg:,.0f}원 수량: {actual_bal:.6f}") if positions: log.info(f"[복구] 총 {len(positions)}개 포지션 복구됨") except Exception as e: