fix: restore_positions에서 locked 잔고 포함 + 기존 매도주문 취소

- balance + locked으로 보유 수량 판단 (지정가 매도 중이면 locked에 잡힘)
- 복구 시 기존 미체결 매도 주문 취소 후 새로 제출
- 취소 후 실제 가용 수량 재조회

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
joungmin
2026-03-05 22:38:54 +09:00
parent 526003c979
commit 9944b55f94

View File

@@ -640,7 +640,7 @@ def restore_positions() -> None:
balances = upbit_client.get_balances() balances = upbit_client.get_balances()
for b in balances: for b in balances:
currency = b.get('currency', '') 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)) avg = float(b.get('avg_buy_price', 0))
if currency == 'KRW' or bal <= 0 or avg <= 0: if currency == 'KRW' or bal <= 0 or avg <= 0:
continue continue
@@ -649,23 +649,34 @@ def restore_positions() -> None:
continue continue
if ticker in positions: if ticker in positions:
continue 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] _, _, lr, stag = CASCADE_STAGES[0]
target = avg * (1 + lr) target = avg * (1 + lr)
sell_uuid = submit_limit_sell(ticker, bal, target) sell_uuid = submit_limit_sell(ticker, actual_bal, target)
positions[ticker] = { positions[ticker] = {
'entry_price': avg, 'entry_price': avg,
'entry_ts': datetime.now() - timedelta(seconds=LLM_MIN_ELAPSED), # LLM 즉시 활성 'entry_ts': datetime.now() - timedelta(seconds=LLM_MIN_ELAPSED), # LLM 즉시 활성
'running_peak': avg, 'running_peak': avg,
'qty': bal, 'qty': actual_bal,
'stage': 0, 'stage': 0,
'sell_uuid': sell_uuid, 'sell_uuid': sell_uuid,
'sell_price': target, 'sell_price': target,
'llm_last_ts': None, 'llm_last_ts': None,
'llm_active': False, 'llm_active': False,
} }
log.info(f"[복구] {ticker} 수량:{bal:.6f} 매수평균:{avg:,.0f}") log.info(f"[복구] {ticker} 수량:{actual_bal:.6f} 매수평균:{avg:,.0f}")
tg(f"♻️ <b>포지션 복구</b> {ticker}\n매수평균: {avg:,.0f}원 수량: {bal:.6f}") tg(f"♻️ <b>포지션 복구</b> {ticker}\n매수평균: {avg:,.0f}원 수량: {actual_bal:.6f}")
if positions: if positions:
log.info(f"[복구] 총 {len(positions)}개 포지션 복구됨") log.info(f"[복구] 총 {len(positions)}개 포지션 복구됨")
except Exception as e: except Exception as e: