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: