fix: persist WF shadow state to DB and tighten ATR max stop
- core/price_db.py: add wf_state table CRUD (ensure/upsert/load/delete) to persist shadow_cons_wins across restarts - core/trader.py: save WF blocked state on shadow enter/close, restore shadow_cons_wins on startup from DB - core/monitor.py: lower ATR_MAX_STOP 4.0% → 2.0% based on sweep results - atr_sweep.py: new ATR_MAX_STOP sweep tool using real ATR calc from DB Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -18,6 +18,7 @@ from .price_db import (
|
||||
ensure_trade_results_table, record_trade, load_recent_wins,
|
||||
ensure_sell_prices_table, upsert_sell_price, load_sell_prices,
|
||||
get_cumulative_krw_profit,
|
||||
ensure_wf_state_table, upsert_wf_state, load_wf_states, delete_wf_state,
|
||||
)
|
||||
|
||||
load_dotenv()
|
||||
@@ -148,6 +149,10 @@ def _shadow_enter(ticker: str) -> None:
|
||||
}
|
||||
|
||||
cons = _shadow_cons_wins.get(ticker, 0)
|
||||
try:
|
||||
upsert_wf_state(ticker, is_blocked=True, shadow_cons_wins=cons)
|
||||
except Exception as e:
|
||||
logger.error(f"WF 상태 DB 저장 실패 {ticker}: {e}")
|
||||
logger.info(
|
||||
f"[Shadow진입] {ticker} @ {price:,.0f}원 "
|
||||
f"(가상 — WF 재활 {cons}/{WF_SHADOW_WINS}연승 필요)"
|
||||
@@ -185,6 +190,15 @@ def close_shadow(ticker: str, sell_price: float, pnl_pct: float, reason: str) ->
|
||||
if do_wf_reset:
|
||||
_shadow_cons_wins.pop(ticker, None)
|
||||
|
||||
# shadow 상태 DB 갱신 (_shadow_lock 해제 후)
|
||||
try:
|
||||
if do_wf_reset:
|
||||
delete_wf_state(ticker)
|
||||
else:
|
||||
upsert_wf_state(ticker, is_blocked=True, shadow_cons_wins=cons)
|
||||
except Exception as e:
|
||||
logger.error(f"WF 상태 DB 갱신 실패 {ticker}: {e}")
|
||||
|
||||
mark = "✅" if is_win else "❌"
|
||||
logger.info(
|
||||
f"[Shadow청산] {ticker} {spos['buy_price']:,.0f}→{sell_price:,.0f}원 "
|
||||
@@ -239,7 +253,7 @@ def restore_positions() -> None:
|
||||
|
||||
DB에 저장된 실제 매수가를 복원하고, Upbit 잔고에 없으면 DB에서도 삭제한다.
|
||||
"""
|
||||
# trade_results / sell_prices 테이블 초기화
|
||||
# trade_results / sell_prices / wf_state 테이블 초기화
|
||||
try:
|
||||
ensure_trade_results_table()
|
||||
except Exception as e:
|
||||
@@ -248,6 +262,21 @@ def restore_positions() -> None:
|
||||
# 시작 시 복리 예산 복원 (이전 세션 수익 반영)
|
||||
_recalc_compound_budget()
|
||||
|
||||
# WF 상태 복원 (shadow 연속승 횟수 유지)
|
||||
try:
|
||||
ensure_wf_state_table()
|
||||
wf_states = load_wf_states()
|
||||
for ticker, state in wf_states.items():
|
||||
if state["is_blocked"]:
|
||||
_shadow_cons_wins[ticker] = state["shadow_cons_wins"]
|
||||
if wf_states:
|
||||
logger.info(
|
||||
f"[복원] WF 차단 상태 {len(wf_states)}건 복원: "
|
||||
+ ", ".join(f"{t}(shadow={s['shadow_cons_wins']})" for t, s in wf_states.items())
|
||||
)
|
||||
except Exception as e:
|
||||
logger.warning(f"WF 상태 복원 실패 (무시): {e}")
|
||||
|
||||
try:
|
||||
ensure_sell_prices_table()
|
||||
except Exception as e:
|
||||
|
||||
Reference in New Issue
Block a user