diff --git a/crypto_monitor_binance/app.py b/crypto_monitor_binance/app.py index a043fc4..4669a02 100644 --- a/crypto_monitor_binance/app.py +++ b/crypto_monitor_binance/app.py @@ -9617,7 +9617,7 @@ try: _repo_root = Path(__file__).resolve().parent.parent if str(_repo_root) not in sys.path: sys.path.insert(0, str(_repo_root)) - from hub_bridge import install_on_app + from hub_bridge import install_on_app, register_trade_stats_calendar_route install_on_app( app, @@ -9637,6 +9637,13 @@ try: render_main_page_fn=render_main_page, login_required_fn=login_required, ) + register_trade_stats_calendar_route( + app, + login_required_fn=login_required, + load_pnls_fn=_load_completed_trade_pnls, + row_matches_segment_fn=_pnl_row_matches_segment, + reset_hour=TRADING_DAY_RESET_HOUR, + ) except Exception as _hub_err: print(f"[hub_bridge] binance: {_hub_err}") diff --git a/crypto_monitor_gate/app.py b/crypto_monitor_gate/app.py index 4bfe51d..a8de8d4 100644 --- a/crypto_monitor_gate/app.py +++ b/crypto_monitor_gate/app.py @@ -9537,7 +9537,7 @@ try: _repo_root = Path(__file__).resolve().parent.parent if str(_repo_root) not in sys.path: sys.path.insert(0, str(_repo_root)) - from hub_bridge import install_on_app + from hub_bridge import install_on_app, register_trade_stats_calendar_route install_on_app( app, @@ -9558,6 +9558,13 @@ try: render_main_page_fn=render_main_page, login_required_fn=login_required, ) + register_trade_stats_calendar_route( + app, + login_required_fn=login_required, + load_pnls_fn=_load_completed_trade_pnls, + row_matches_segment_fn=_pnl_row_matches_segment, + reset_hour=TRADING_DAY_RESET_HOUR, + ) except Exception as _hub_err: print(f"[hub_bridge] gate: {_hub_err}") diff --git a/crypto_monitor_gate_bot/app.py b/crypto_monitor_gate_bot/app.py index 0a698c0..9ab569a 100644 --- a/crypto_monitor_gate_bot/app.py +++ b/crypto_monitor_gate_bot/app.py @@ -9533,7 +9533,7 @@ try: _repo_root = Path(__file__).resolve().parent.parent if str(_repo_root) not in sys.path: sys.path.insert(0, str(_repo_root)) - from hub_bridge import install_on_app + from hub_bridge import install_on_app, register_trade_stats_calendar_route install_on_app( app, @@ -9554,6 +9554,13 @@ try: render_main_page_fn=render_main_page, login_required_fn=login_required, ) + register_trade_stats_calendar_route( + app, + login_required_fn=login_required, + load_pnls_fn=_load_completed_trade_pnls, + row_matches_segment_fn=_pnl_row_matches_segment, + reset_hour=TRADING_DAY_RESET_HOUR, + ) except Exception as _hub_err: print(f"[hub_bridge] gate_bot: {_hub_err}") diff --git a/crypto_monitor_okx/app.py b/crypto_monitor_okx/app.py index 82e50ff..c68beb5 100644 --- a/crypto_monitor_okx/app.py +++ b/crypto_monitor_okx/app.py @@ -8998,7 +8998,7 @@ try: _repo_root = Path(__file__).resolve().parent.parent if str(_repo_root) not in sys.path: sys.path.insert(0, str(_repo_root)) - from hub_bridge import install_on_app + from hub_bridge import install_on_app, register_trade_stats_calendar_route install_on_app( app, @@ -9018,6 +9018,13 @@ try: render_main_page_fn=render_main_page, login_required_fn=login_required, ) + register_trade_stats_calendar_route( + app, + login_required_fn=login_required, + load_pnls_fn=_load_completed_trade_pnls, + row_matches_segment_fn=_pnl_row_matches_segment, + reset_hour=TRADING_DAY_RESET_HOUR, + ) except Exception as _hub_err: print(f"[hub_bridge] okx: {_hub_err}") diff --git a/embed_templates/embed_boot_scripts.html b/embed_templates/embed_boot_scripts.html index fb51acf..dd86113 100644 --- a/embed_templates/embed_boot_scripts.html +++ b/embed_templates/embed_boot_scripts.html @@ -664,6 +664,36 @@ function switchStatsSegment(){ q.set("stats_segment", key); const qs = q.toString(); history.replaceState(null, "", qs ? (window.location.pathname + "?" + qs) : window.location.pathname); + if(statsCalendarWidget) statsCalendarWidget.load(); +} + +let statsCalendarWidget = null; + +function initStatsCalendarWidget(){ + const grid = document.getElementById("stats-calendar"); + if(!grid || !window.TradeStatsCalendar) return; + statsCalendarWidget = new TradeStatsCalendar({ + gridEl: grid, + titleEl: document.getElementById("stats-cal-title"), + prevBtn: document.getElementById("stats-cal-prev"), + nextBtn: document.getElementById("stats-cal-next"), + apiUrl: "/api/stats/calendar", + showSick: false, + buildQuery: function(year, month){ + const q = new URLSearchParams(); + q.set("year", String(year)); + q.set("month", String(month)); + const sel = document.getElementById("stats-segment-select"); + if(sel) q.set("segment", sel.value || "all"); + return q; + }, + parseResponse: function(data){ + if(data && data.ok === false) return {}; + return (data && data.days) || {}; + } + }); + statsCalendarWidget.ensureMonth(new Date()); + statsCalendarWidget.load(); } function initStatsSegmentFromUrl(){ @@ -700,6 +730,7 @@ attachListWindowToExports(); toggleListWindowCustom(); bindListWindowDateAutoCustom(); initStatsSegmentFromUrl(); +initStatsCalendarWidget(); if(document.getElementById("journal-list")) loadJournals(); if(document.getElementById("review-list")) loadReviews(); const reviewToggle = document.getElementById("review-mode-toggle"); diff --git a/embed_templates/embed_page_fragment.html b/embed_templates/embed_page_fragment.html index 6f030dd..86f33f2 100644 --- a/embed_templates/embed_page_fragment.html +++ b/embed_templates/embed_page_fragment.html @@ -452,6 +452,14 @@ +