From 407238e5d50f560b38795c905a736a5873dd9ff3 Mon Sep 17 00:00:00 2001 From: I Luk Kim Date: Mon, 13 Apr 2026 10:28:43 -0700 Subject: [PATCH] =?UTF-8?q?Fix=20'=EC=A7=80=EA=B8=88=20=EC=8B=9C=EC=9E=91'?= =?UTF-8?q?:=20immediately=20run=20breakout=20check=20after=20ORB=20detect?= =?UTF-8?q?ion?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously run_session_now() only ran orb_detect and injected future scheduled events, but scheduled breakout windows were already past so no breakout check ever fired. Now: after orb_detect, immediately run one breakout check with current snapshot prices (the core of 'μ§€κΈˆ μ‹œμž‘'). All scheduled breakout events are marked completed. Only stop_check and eod_exit events are injected into the live schedule going forward. Also improve alert message to clarify background execution (~1-2 min). Co-Authored-By: Claude Sonnet 4.6 --- apps/web/orb_trading_service.py | 19 ++++++++++++++----- .../{index-2wtBT0D_.js => index-BoVqCuLt.js} | 2 +- apps/web/static/index.html | 2 +- apps/web_frontend/src/pages/OrbTrading.tsx | 4 +++- 4 files changed, 19 insertions(+), 8 deletions(-) rename apps/web/static/assets/{index-2wtBT0D_.js => index-BoVqCuLt.js} (99%) diff --git a/apps/web/orb_trading_service.py b/apps/web/orb_trading_service.py index d68e2f0..6b3a0d0 100644 --- a/apps/web/orb_trading_service.py +++ b/apps/web/orb_trading_service.py @@ -509,10 +509,17 @@ class ORBAutoScheduler: self._log(f"πŸ”„ μ§€κΈˆ μ‹œμž‘: {session_name} β€” ORB 감지 μ‹€ν–‰ 쀑 (ν˜„μž¬ μ‹œμ„Έ κΈ°μ€€)...") - # Run detection now (blocking in thread so we don't stall the event loop) + # Phase 1: Run ORB detection (fetches historical bars for ORB window) await self._run_trading("orb_detect", [session_name], date_str) - # Inject remaining future events into the live schedule + # Phase 2: Immediately check for breakouts using current snapshot prices. + # This is the "μ§€κΈˆ μ‹œμž‘" core β€” regardless of whether scheduled breakout + # windows have already passed, we check current price right now. + self._log(f"πŸ” {session_name}: ν˜„μž¬ κ°€κ²©μœΌλ‘œ λΈŒλ ˆμ΄ν¬μ•„μ›ƒ μ¦‰μ‹œ 체크...") + 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. params = _load_session_params(self._db_path, session_name) all_events = build_schedule(today, **params) for ev in all_events: @@ -522,15 +529,17 @@ class ORBAutoScheduler: existing_names = {e["name"] for e in self._today_schedule} injected = 0 for ev in all_events: - if ev["et_dt"] <= now_et: - # Past events: mark as completed so the loop skips them + 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"]) elif ev["name"] not in existing_names: self._today_schedule.append(ev) injected += 1 self._today_schedule.sort(key=lambda e: (e["et_dt"], e.get("session", ""))) - self._log(f"βœ“ {session_name}: {injected} 이벀트 좔가됨 (였늘 남은 일정 포함)") + self._log(f"βœ“ {session_name}: {injected} 이벀트 좔가됨 (μŠ€ν†±/EOD 일정)") return {"session": session_name, "injected": injected} diff --git a/apps/web/static/assets/index-2wtBT0D_.js b/apps/web/static/assets/index-BoVqCuLt.js similarity index 99% rename from apps/web/static/assets/index-2wtBT0D_.js rename to apps/web/static/assets/index-BoVqCuLt.js index c032ffe..379705a 100644 --- a/apps/web/static/assets/index-2wtBT0D_.js +++ b/apps/web/static/assets/index-BoVqCuLt.js @@ -129,5 +129,5 @@ Please change the parent to (0,V.jsx)(wse,{session:e,selected:t===e.session_id||t===e.session_name,onSelect:()=>n(e.session_id),onDelete:()=>{window.confirm(`"${e.session_name}" μ„Έμ…˜μ„ μ‚­μ œν•˜μ‹œκ² μŠ΅λ‹ˆκΉŒ? μ˜€ν”ˆ ν¬μ§€μ…˜μ΄ μ²­μ‚°λ©λ‹ˆλ‹€.`)&&p.mutate(e.session_id)},onPause:()=>u.mutate(e.session_id),onResume:()=>d.mutate(e.session_id),onRunToday:()=>{window.confirm(`"${e.session_name}" μ„Έμ…˜μ„ ν˜„μž¬ 가격 κΈ°μ€€μœΌλ‘œ μ§€κΈˆ μ‹œμž‘ν•˜μ‹œκ² μŠ΅λ‹ˆκΉŒ?\n(μŠ€μΌ€μ€„λŸ¬κ°€ μ‹€ν–‰ 쀑이어야 ν•©λ‹ˆλ‹€)`)&&f.mutate(e.session_id)},runTodayPending:f.isPending&&f.variables===e.session_id},e.session_id))})]}),h&&(0,V.jsxs)(`div`,{style:K9,children:[(0,V.jsxs)(`div`,{style:{padding:`12px 20px`,borderBottom:`1px solid var(--border)`,display:`flex`,alignItems:`center`,gap:14},children:[(0,V.jsx)(`span`,{style:{fontWeight:600,fontSize:15,color:`var(--text1)`},children:h.session_name}),(0,V.jsxs)(`span`,{style:{fontSize:12,color:`var(--text3)`,fontFamily:`var(--font-mono)`},children:[`$`,Y9(h.initial_equity),` β†’ $`,Y9(h.current_equity??h.initial_equity)]})]}),(0,V.jsx)(`div`,{style:{display:`flex`,gap:0,borderBottom:`1px solid var(--border)`,background:`var(--bg2)`},children:_.map(e=>(0,V.jsx)(`button`,{onClick:()=>o(e.key),style:{padding:`10px 18px`,fontSize:13,fontWeight:500,cursor:`pointer`,background:`none`,border:`none`,borderBottom:`2px solid ${a===e.key?`var(--cyan)`:`transparent`}`,color:a===e.key?`var(--cyan)`:`var(--text3)`,transition:`all 0.12s`},children:e.label},e.key))}),a===`positions`&&(0,V.jsx)(Ese,{sessionId:h.session_id}),a===`candidates`&&(0,V.jsx)(Dse,{sessionId:h.session_id}),a===`trades`&&(0,V.jsx)(Ose,{sessionId:h.session_id}),a===`equity`&&(0,V.jsx)(kse,{sessionId:h.session_id,initialEquity:h.initial_equity})]}),r&&(0,V.jsx)(Tse,{onClose:()=>i(!1)})]})}var jse=new Xr({defaultOptions:{queries:{staleTime:3e4,retry:1}}});function Mse(){return(0,V.jsx)(ei,{client:jse,children:(0,V.jsx)(_n,{children:(0,V.jsxs)(`div`,{style:{display:`flex`,minHeight:`100vh`,width:`100%`},children:[(0,V.jsx)(Ea,{}),(0,V.jsx)(`main`,{style:{flex:1,minWidth:0,marginLeft:`var(--sidebar-w, 230px)`,minHeight:`100vh`,overflow:`auto`,transition:`margin-left 0.18s ease`},children:(0,V.jsxs)(Ot,{children:[(0,V.jsx)(Et,{path:`/`,element:(0,V.jsx)(Ja,{})}),(0,V.jsx)(Et,{path:`/leaderboard`,element:(0,V.jsx)(Cs,{})}),(0,V.jsx)(Et,{path:`/experiments`,element:(0,V.jsx)(Es,{})}),(0,V.jsx)(Et,{path:`/experiments/compose`,element:(0,V.jsx)(fse,{})}),(0,V.jsx)(Et,{path:`/experiments/:name`,element:(0,V.jsx)(zw,{})}),(0,V.jsx)(Et,{path:`/experiments/:name/diff`,element:(0,V.jsx)(Vw,{})}),(0,V.jsx)(Et,{path:`/experiments/:name/diff/:other`,element:(0,V.jsx)(Vw,{})}),(0,V.jsx)(Et,{path:`/lineage/:name`,element:(0,V.jsx)(Hw,{})}),(0,V.jsx)(Et,{path:`/backtest`,element:(0,V.jsx)(k4,{})}),(0,V.jsx)(Et,{path:`/backtest/compare`,element:(0,V.jsx)(J4,{})}),(0,V.jsx)(Et,{path:`/backtest/tasks/:taskId`,element:(0,V.jsx)(A4,{})}),(0,V.jsx)(Et,{path:`/backtest/results/:runId`,element:(0,V.jsx)(I4,{})}),(0,V.jsx)(Et,{path:`/backtest/direct-results/:taskId`,element:(0,V.jsx)(L4,{})}),(0,V.jsx)(Et,{path:`/paper`,element:(0,V.jsx)(y3,{})}),(0,V.jsx)(Et,{path:`/intraday`,element:(0,V.jsx)(xse,{})}),(0,V.jsx)(Et,{path:`/intraday/tasks/:taskId`,element:(0,V.jsx)(_se,{})}),(0,V.jsx)(Et,{path:`/orb-trading`,element:(0,V.jsx)(Ase,{})}),(0,V.jsx)(Et,{path:`/docs/cash-parking`,element:(0,V.jsx)(use,{})})]})})]})})})}(0,b.createRoot)(document.getElementById(`root`)).render((0,V.jsx)(y.StrictMode,{children:(0,V.jsx)(Mse,{})})); \ No newline at end of file diff --git a/apps/web/static/index.html b/apps/web/static/index.html index bc1e70e..52a60a4 100644 --- a/apps/web/static/index.html +++ b/apps/web/static/index.html @@ -5,7 +5,7 @@ Fithia2 - + diff --git a/apps/web_frontend/src/pages/OrbTrading.tsx b/apps/web_frontend/src/pages/OrbTrading.tsx index 0824c14..cf85707 100644 --- a/apps/web_frontend/src/pages/OrbTrading.tsx +++ b/apps/web_frontend/src/pages/OrbTrading.tsx @@ -794,7 +794,9 @@ export function OrbTradingPage() { onSuccess: (result) => { qc.invalidateQueries({ queryKey: ['orb-sessions'] }); qc.invalidateQueries({ queryKey: ['orb-auto-status'] }); - alert(`${result.session_name}: ${result.note}`); + alert(`${result.session_name} λ°±κ·ΈλΌμš΄λ“œ μ‹œμž‘λ¨\n\n` + + `ORB 감지 β†’ λΈŒλ ˆμ΄ν¬μ•„μ›ƒ 체크 순으둜 μ‹€ν–‰λ©λ‹ˆλ‹€ (μ•½ 1~2λΆ„ μ†Œμš”).\n` + + `μŠ€μΌ€μ€„λŸ¬ λ‘œκ·Έμ—μ„œ 진행상황을 ν™•μΈν•˜μ„Έμš”.`); }, onError: (e: Error) => alert(`μ§€κΈˆ μ‹œμž‘ μ‹€νŒ¨: ${e.message}`), });