@ -489,8 +489,14 @@ class ORBAutoScheduler:
After detection , injects the session ' s remaining today-events into the
active schedule so breakout checks , stop checks , and EOD exit still fire .
Returns a summary dict or { " error " : " <reason> " } on failure .
All exceptions are caught and logged so the fire - and - forget task never
silently vanishes .
"""
# Log immediately so the user knows the task started — even before any
# DB / engine work that might fail.
self . _log ( f " 🔄 지금 시작 요청: { session_name } " )
try :
from apps . orb_trader . state import ORBStateManager
now_et = self . _now_et ( )
@ -500,26 +506,28 @@ class ORBAutoScheduler:
state_mgr = ORBStateManager ( self . _db_path )
session = state_mgr . get_session ( session_name )
if session is None :
self . _log ( f " ❌ 세션 ' { session_name } ' 을 DB에서 찾을 수 없음 " )
return { " error " : f " Session ' { session_name } ' not found " }
# Already ran if daily_state.phase is set (engine wrote it during orb_detect)
# Already ran if daily_state.phase is beyond idle
daily = state_mgr . get_daily_state ( session . session_id , date_str )
if daily . phase :
return { " error " : f " Session ' { session_name } ' already ran today (phase= { daily . phase } ) " }
if daily . phase not in ( " idle " , " " , None ) :
self . _log ( f " ⚠ { session_name } : 오늘 이미 실행됨 (phase= { daily . phase } ) " )
return { " error " : f " already ran today (phase= { daily . phase } ) " }
self . _log ( f " 🔄 지금 시작: { session_name } — ORB 감지 실행 중 (현재 시세 기준)..." )
self . _log ( f " ORB 감지 실행 중 (현재 시세 기준)..." )
# Phase 1: Run ORB detection (fetches historical bars for ORB window)
# Phase 1: ORB detection — fetches historical bars for the ORB window
await self . _run_trading ( " orb_detect " , [ session_name ] , date_str )
# Phase 2: Immediately check for breakouts using current snapshot prices.
# This is the "지금 시작" core — regardless of whe ther scheduled breakout
# windows have already passed, we check current price right now.
self . _log ( f " 🔍 { session_name } : 현재 가격으로 브레이크아웃 즉시 체크..." )
# Phase 2: Immediate breakout check using current snapshot prices.
# This is the core of "지금 시작" — the scheduled breakout windows have
# already passed, so we check once manually right now.
self . _log ( f " 현재 가격으로 브레이크아웃 즉시 체크..." )
await self . _run_trading ( " breakout " , [ session_name ] , date_str )
# Phase 3: Inject remaining future events (stop checks + EOD exit only).
# Skip all breakout events — we just ran the manual check above.
# Mark all scheduled breakout events as done — we already ran one above.
params = _load_session_params ( self . _db_path , session_name )
all_events = build_schedule ( today , * * params )
for ev in all_events :
@ -530,7 +538,6 @@ class ORBAutoScheduler:
injected = 0
for ev in all_events :
if ev [ " kind " ] == " breakout " :
# Already handled by the manual check above
self . _completed . add ( ev [ " name " ] )
elif ev [ " et_dt " ] < = now_et :
self . _completed . add ( ev [ " name " ] )
@ -539,10 +546,15 @@ class ORBAutoScheduler:
injected + = 1
self . _today_schedule . sort ( key = lambda e : ( e [ " et_dt " ] , e . get ( " session " , " " ) ) )
self . _log ( f " ✓ { session_name } : { injected } 이벤트 추가됨 (스톱/EOD 일정) " )
self . _log ( f " ✓ 완료 — 스톱/EOD 이벤트 { injected } 개 추가됨 " )
return { " session " : session_name , " injected " : injected }
except Exception as exc :
tb = traceback . format_exc ( )
self . _log ( f " ❌ 지금 시작 오류: { exc } " )
log . error ( " run_session_now error: %s \n %s " , exc , tb )
return { " error " : str ( exc ) }
# ── Main scheduler loop ────────────────────────────────────────────────────
async def _run_loop ( self ) - > None :