Add all-time stats tab with monthly breakdown on instance analytics.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1844,8 +1844,38 @@ def _compute_period_metrics(trades):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _bounds_for_month_key(ym):
|
||||||
|
"""ym: YYYY-MM → 该自然月首末日(北京日历)."""
|
||||||
|
y, m = [int(x) for x in str(ym).split("-", 1)]
|
||||||
|
start = f"{y:04d}-{m:02d}-01"
|
||||||
|
if m == 12:
|
||||||
|
end = f"{y:04d}-12-31"
|
||||||
|
else:
|
||||||
|
end = (datetime(y, m + 1, 1) - timedelta(days=1)).date().strftime("%Y-%m-%d")
|
||||||
|
return start, end
|
||||||
|
|
||||||
|
|
||||||
|
def _build_monthly_stats_rows(conn, all_tr, seg_key):
|
||||||
|
"""按北京交易日所在自然月聚合;新月在前."""
|
||||||
|
by_month = {}
|
||||||
|
for p, t, td in all_tr:
|
||||||
|
if not td or len(str(td)) < 7:
|
||||||
|
continue
|
||||||
|
mk = str(td)[:7]
|
||||||
|
by_month.setdefault(mk, []).append((p, t, td))
|
||||||
|
rows = []
|
||||||
|
for mk in sorted(by_month.keys(), reverse=True):
|
||||||
|
metrics = _compute_period_metrics(by_month[mk])
|
||||||
|
ms, me = _bounds_for_month_key(mk)
|
||||||
|
metrics["opens_count"] = _count_opens_for_segment(conn, ms, me, seg_key)
|
||||||
|
metrics["range_label"] = f"{ms} ~ {me}"
|
||||||
|
metrics["month_key"] = mk
|
||||||
|
rows.append(metrics)
|
||||||
|
return rows
|
||||||
|
|
||||||
|
|
||||||
def compute_stats_bundle(conn, trading_day, now_dt=None):
|
def compute_stats_bundle(conn, trading_day, now_dt=None):
|
||||||
"""日 / 周 / 月 统计:平仓按北京时间交易日(默认 8:00 切日)计入."""
|
"""日 / 周 / 月 / 全部 统计:平仓按北京时间交易日(默认 8:00 切日)计入."""
|
||||||
now_dt = now_dt or app_now()
|
now_dt = now_dt or app_now()
|
||||||
pnls = _load_completed_trade_pnls(conn)
|
pnls = _load_completed_trade_pnls(conn)
|
||||||
total_opens_all = conn.execute("SELECT COUNT(*) FROM order_monitors").fetchone()[0]
|
total_opens_all = conn.execute("SELECT COUNT(*) FROM order_monitors").fetchone()[0]
|
||||||
@@ -1857,26 +1887,37 @@ def compute_stats_bundle(conn, trading_day, now_dt=None):
|
|||||||
day_tr = [(p, t, td) for p, t, td, _r in seg_rows if td == trading_day]
|
day_tr = [(p, t, td) for p, t, td, _r in seg_rows if td == trading_day]
|
||||||
week_tr = [(p, t, td) for p, t, td, _r in seg_rows if t and w_start <= td <= w_end]
|
week_tr = [(p, t, td) for p, t, td, _r in seg_rows if t and w_start <= td <= w_end]
|
||||||
month_tr = [(p, t, td) for p, t, td, _r in seg_rows if t and m_start <= td <= m_end]
|
month_tr = [(p, t, td) for p, t, td, _r in seg_rows if t and m_start <= td <= m_end]
|
||||||
|
all_tr = [(p, t, td) for p, t, td, _r in seg_rows if t]
|
||||||
dm = _compute_period_metrics(day_tr)
|
dm = _compute_period_metrics(day_tr)
|
||||||
wm = _compute_period_metrics(week_tr)
|
wm = _compute_period_metrics(week_tr)
|
||||||
mm = _compute_period_metrics(month_tr)
|
mm = _compute_period_metrics(month_tr)
|
||||||
|
am = _compute_period_metrics(all_tr)
|
||||||
dm["opens_count"] = _count_opens_for_segment(conn, trading_day, trading_day, seg_key)
|
dm["opens_count"] = _count_opens_for_segment(conn, trading_day, trading_day, seg_key)
|
||||||
wm["opens_count"] = _count_opens_for_segment(conn, w_start, w_end, seg_key)
|
wm["opens_count"] = _count_opens_for_segment(conn, w_start, w_end, seg_key)
|
||||||
mm["opens_count"] = _count_opens_for_segment(conn, m_start, m_end, seg_key)
|
mm["opens_count"] = _count_opens_for_segment(conn, m_start, m_end, seg_key)
|
||||||
|
am["opens_count"] = _count_opens_for_segment(conn, "1970-01-01", "9999-12-31", seg_key)
|
||||||
dm["range_label"] = f"北京时间交易日 {trading_day}({TRADING_DAY_RESET_HOUR}:00 切日)"
|
dm["range_label"] = f"北京时间交易日 {trading_day}({TRADING_DAY_RESET_HOUR}:00 切日)"
|
||||||
wm["range_label"] = f"{w_start} ~ {w_end}(北京日期,近7天)"
|
wm["range_label"] = f"{w_start} ~ {w_end}(北京日期,近7天)"
|
||||||
mm["range_label"] = f"{m_start} ~ {m_end}(北京自然月)"
|
mm["range_label"] = f"{m_start} ~ {m_end}(北京自然月)"
|
||||||
return dm, wm, mm
|
tds = [td for _, _, td in all_tr if td]
|
||||||
|
if tds:
|
||||||
|
am["range_label"] = f"全部历史 {min(tds)} ~ {max(tds)}(北京交易日)"
|
||||||
|
else:
|
||||||
|
am["range_label"] = "全部历史(暂无平仓)"
|
||||||
|
am["monthly_rows"] = _build_monthly_stats_rows(conn, all_tr, seg_key)
|
||||||
|
return dm, wm, mm, am
|
||||||
|
|
||||||
segments = []
|
segments = []
|
||||||
seg_defs = effective_stats_segment_defs(
|
seg_defs = effective_stats_segment_defs(
|
||||||
STATS_SEGMENT_DEFS, POSITION_SIZING_MODE, KEY_AUTO_ORDER_ENABLED
|
STATS_SEGMENT_DEFS, POSITION_SIZING_MODE, KEY_AUTO_ORDER_ENABLED
|
||||||
)
|
)
|
||||||
for seg_key, seg_title, _meta in seg_defs:
|
for seg_key, seg_title, _meta in seg_defs:
|
||||||
dm, wm, mm = slice_metrics(seg_key)
|
dm, wm, mm, am = slice_metrics(seg_key)
|
||||||
segments.append({"key": seg_key, "title": seg_title, "day": dm, "week": wm, "month": mm})
|
segments.append(
|
||||||
|
{"key": seg_key, "title": seg_title, "day": dm, "week": wm, "month": mm, "all": am}
|
||||||
|
)
|
||||||
|
|
||||||
dm, wm, mm = slice_metrics("all")
|
dm, wm, mm, am = slice_metrics("all")
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"trading_day": trading_day,
|
"trading_day": trading_day,
|
||||||
@@ -1884,6 +1925,7 @@ def compute_stats_bundle(conn, trading_day, now_dt=None):
|
|||||||
"day": dm,
|
"day": dm,
|
||||||
"week": wm,
|
"week": wm,
|
||||||
"month": mm,
|
"month": mm,
|
||||||
|
"all": am,
|
||||||
"segments": segments,
|
"segments": segments,
|
||||||
"stats_reset_hour": TRADING_DAY_RESET_HOUR,
|
"stats_reset_hour": TRADING_DAY_RESET_HOUR,
|
||||||
}
|
}
|
||||||
|
|||||||
+47
-11
@@ -1842,45 +1842,80 @@ def _compute_period_metrics(trades):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _bounds_for_month_key(ym):
|
||||||
|
"""ym: YYYY-MM → 该自然月首末日(北京日历)."""
|
||||||
|
y, m = [int(x) for x in str(ym).split("-", 1)]
|
||||||
|
start = f"{y:04d}-{m:02d}-01"
|
||||||
|
if m == 12:
|
||||||
|
end = f"{y:04d}-12-31"
|
||||||
|
else:
|
||||||
|
end = (datetime(y, m + 1, 1) - timedelta(days=1)).date().strftime("%Y-%m-%d")
|
||||||
|
return start, end
|
||||||
|
|
||||||
|
|
||||||
|
def _build_monthly_stats_rows(conn, all_tr, seg_key):
|
||||||
|
"""按北京交易日所在自然月聚合;新月在前."""
|
||||||
|
by_month = {}
|
||||||
|
for p, t, td in all_tr:
|
||||||
|
if not td or len(str(td)) < 7:
|
||||||
|
continue
|
||||||
|
mk = str(td)[:7]
|
||||||
|
by_month.setdefault(mk, []).append((p, t, td))
|
||||||
|
rows = []
|
||||||
|
for mk in sorted(by_month.keys(), reverse=True):
|
||||||
|
metrics = _compute_period_metrics(by_month[mk])
|
||||||
|
ms, me = _bounds_for_month_key(mk)
|
||||||
|
metrics["opens_count"] = _count_opens_for_segment(conn, ms, me, seg_key)
|
||||||
|
metrics["range_label"] = f"{ms} ~ {me}"
|
||||||
|
metrics["month_key"] = mk
|
||||||
|
rows.append(metrics)
|
||||||
|
return rows
|
||||||
|
|
||||||
|
|
||||||
def compute_stats_bundle(conn, trading_day, now_dt=None):
|
def compute_stats_bundle(conn, trading_day, now_dt=None):
|
||||||
"""日 / 周 / 月 统计:平仓按北京时间交易日(默认 8:00 切日)计入."""
|
"""日 / 周 / 月 / 全部 统计:平仓按北京时间交易日(默认 8:00 切日)计入."""
|
||||||
now_dt = now_dt or app_now()
|
now_dt = now_dt or app_now()
|
||||||
pnls = _load_completed_trade_pnls(conn)
|
pnls = _load_completed_trade_pnls(conn)
|
||||||
total_opens_all = conn.execute("SELECT COUNT(*) FROM order_monitors").fetchone()[0]
|
total_opens_all = conn.execute("SELECT COUNT(*) FROM order_monitors").fetchone()[0]
|
||||||
w_start, w_end = _session_week_bounds(trading_day)
|
w_start, w_end = _session_week_bounds(trading_day)
|
||||||
m_start, m_end = _calendar_month_bounds(now_dt)
|
m_start, m_end = _calendar_month_bounds(now_dt)
|
||||||
|
|
||||||
def in_week(tr):
|
|
||||||
return tr[2] and w_start <= tr[2] <= w_end
|
|
||||||
|
|
||||||
def in_month(tr):
|
|
||||||
return tr[2] and m_start <= tr[2] <= m_end
|
|
||||||
|
|
||||||
def slice_metrics(seg_key):
|
def slice_metrics(seg_key):
|
||||||
seg_rows = [tr for tr in pnls if _pnl_row_matches_segment(tr[3], seg_key)]
|
seg_rows = [tr for tr in pnls if _pnl_row_matches_segment(tr[3], seg_key)]
|
||||||
day_tr = [(p, t, td) for p, t, td, _r in seg_rows if td == trading_day]
|
day_tr = [(p, t, td) for p, t, td, _r in seg_rows if td == trading_day]
|
||||||
week_tr = [(p, t, td) for p, t, td, _r in seg_rows if t and w_start <= td <= w_end]
|
week_tr = [(p, t, td) for p, t, td, _r in seg_rows if t and w_start <= td <= w_end]
|
||||||
month_tr = [(p, t, td) for p, t, td, _r in seg_rows if t and m_start <= td <= m_end]
|
month_tr = [(p, t, td) for p, t, td, _r in seg_rows if t and m_start <= td <= m_end]
|
||||||
|
all_tr = [(p, t, td) for p, t, td, _r in seg_rows if t]
|
||||||
dm = _compute_period_metrics(day_tr)
|
dm = _compute_period_metrics(day_tr)
|
||||||
wm = _compute_period_metrics(week_tr)
|
wm = _compute_period_metrics(week_tr)
|
||||||
mm = _compute_period_metrics(month_tr)
|
mm = _compute_period_metrics(month_tr)
|
||||||
|
am = _compute_period_metrics(all_tr)
|
||||||
dm["opens_count"] = _count_opens_for_segment(conn, trading_day, trading_day, seg_key)
|
dm["opens_count"] = _count_opens_for_segment(conn, trading_day, trading_day, seg_key)
|
||||||
wm["opens_count"] = _count_opens_for_segment(conn, w_start, w_end, seg_key)
|
wm["opens_count"] = _count_opens_for_segment(conn, w_start, w_end, seg_key)
|
||||||
mm["opens_count"] = _count_opens_for_segment(conn, m_start, m_end, seg_key)
|
mm["opens_count"] = _count_opens_for_segment(conn, m_start, m_end, seg_key)
|
||||||
|
am["opens_count"] = _count_opens_for_segment(conn, "1970-01-01", "9999-12-31", seg_key)
|
||||||
dm["range_label"] = f"北京时间交易日 {trading_day}({TRADING_DAY_RESET_HOUR}:00 切日)"
|
dm["range_label"] = f"北京时间交易日 {trading_day}({TRADING_DAY_RESET_HOUR}:00 切日)"
|
||||||
wm["range_label"] = f"{w_start} ~ {w_end}(北京日期,近7天)"
|
wm["range_label"] = f"{w_start} ~ {w_end}(北京日期,近7天)"
|
||||||
mm["range_label"] = f"{m_start} ~ {m_end}(北京自然月)"
|
mm["range_label"] = f"{m_start} ~ {m_end}(北京自然月)"
|
||||||
return dm, wm, mm
|
tds = [td for _, _, td in all_tr if td]
|
||||||
|
if tds:
|
||||||
|
am["range_label"] = f"全部历史 {min(tds)} ~ {max(tds)}(北京交易日)"
|
||||||
|
else:
|
||||||
|
am["range_label"] = "全部历史(暂无平仓)"
|
||||||
|
am["monthly_rows"] = _build_monthly_stats_rows(conn, all_tr, seg_key)
|
||||||
|
return dm, wm, mm, am
|
||||||
|
|
||||||
segments = []
|
segments = []
|
||||||
seg_defs = effective_stats_segment_defs(
|
seg_defs = effective_stats_segment_defs(
|
||||||
STATS_SEGMENT_DEFS, POSITION_SIZING_MODE, KEY_AUTO_ORDER_ENABLED
|
STATS_SEGMENT_DEFS, POSITION_SIZING_MODE, KEY_AUTO_ORDER_ENABLED
|
||||||
)
|
)
|
||||||
for seg_key, seg_title, _meta in seg_defs:
|
for seg_key, seg_title, _meta in seg_defs:
|
||||||
dm, wm, mm = slice_metrics(seg_key)
|
dm, wm, mm, am = slice_metrics(seg_key)
|
||||||
segments.append({"key": seg_key, "title": seg_title, "day": dm, "week": wm, "month": mm})
|
segments.append(
|
||||||
|
{"key": seg_key, "title": seg_title, "day": dm, "week": wm, "month": mm, "all": am}
|
||||||
|
)
|
||||||
|
|
||||||
dm, wm, mm = slice_metrics("all")
|
dm, wm, mm, am = slice_metrics("all")
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"trading_day": trading_day,
|
"trading_day": trading_day,
|
||||||
@@ -1888,6 +1923,7 @@ def compute_stats_bundle(conn, trading_day, now_dt=None):
|
|||||||
"day": dm,
|
"day": dm,
|
||||||
"week": wm,
|
"week": wm,
|
||||||
"month": mm,
|
"month": mm,
|
||||||
|
"all": am,
|
||||||
"segments": segments,
|
"segments": segments,
|
||||||
"stats_reset_hour": TRADING_DAY_RESET_HOUR,
|
"stats_reset_hour": TRADING_DAY_RESET_HOUR,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1843,8 +1843,38 @@ def _compute_period_metrics(trades):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _bounds_for_month_key(ym):
|
||||||
|
"""ym: YYYY-MM → 该自然月首末日(北京日历)."""
|
||||||
|
y, m = [int(x) for x in str(ym).split("-", 1)]
|
||||||
|
start = f"{y:04d}-{m:02d}-01"
|
||||||
|
if m == 12:
|
||||||
|
end = f"{y:04d}-12-31"
|
||||||
|
else:
|
||||||
|
end = (datetime(y, m + 1, 1) - timedelta(days=1)).date().strftime("%Y-%m-%d")
|
||||||
|
return start, end
|
||||||
|
|
||||||
|
|
||||||
|
def _build_monthly_stats_rows(conn, all_tr, seg_key):
|
||||||
|
"""按北京交易日所在自然月聚合;新月在前."""
|
||||||
|
by_month = {}
|
||||||
|
for p, t, td in all_tr:
|
||||||
|
if not td or len(str(td)) < 7:
|
||||||
|
continue
|
||||||
|
mk = str(td)[:7]
|
||||||
|
by_month.setdefault(mk, []).append((p, t, td))
|
||||||
|
rows = []
|
||||||
|
for mk in sorted(by_month.keys(), reverse=True):
|
||||||
|
metrics = _compute_period_metrics(by_month[mk])
|
||||||
|
ms, me = _bounds_for_month_key(mk)
|
||||||
|
metrics["opens_count"] = _count_opens_for_segment(conn, ms, me, seg_key)
|
||||||
|
metrics["range_label"] = f"{ms} ~ {me}"
|
||||||
|
metrics["month_key"] = mk
|
||||||
|
rows.append(metrics)
|
||||||
|
return rows
|
||||||
|
|
||||||
|
|
||||||
def compute_stats_bundle(conn, trading_day, now_dt=None):
|
def compute_stats_bundle(conn, trading_day, now_dt=None):
|
||||||
"""日 / 周 / 月 统计:平仓按北京时间交易日(默认 8:00 切日)计入."""
|
"""日 / 周 / 月 / 全部 统计:平仓按北京时间交易日(默认 8:00 切日)计入."""
|
||||||
now_dt = now_dt or app_now()
|
now_dt = now_dt or app_now()
|
||||||
pnls = _load_completed_trade_pnls(conn)
|
pnls = _load_completed_trade_pnls(conn)
|
||||||
total_opens_all = conn.execute("SELECT COUNT(*) FROM order_monitors").fetchone()[0]
|
total_opens_all = conn.execute("SELECT COUNT(*) FROM order_monitors").fetchone()[0]
|
||||||
@@ -1856,26 +1886,37 @@ def compute_stats_bundle(conn, trading_day, now_dt=None):
|
|||||||
day_tr = [(p, t, td) for p, t, td, _r in seg_rows if td == trading_day]
|
day_tr = [(p, t, td) for p, t, td, _r in seg_rows if td == trading_day]
|
||||||
week_tr = [(p, t, td) for p, t, td, _r in seg_rows if t and w_start <= td <= w_end]
|
week_tr = [(p, t, td) for p, t, td, _r in seg_rows if t and w_start <= td <= w_end]
|
||||||
month_tr = [(p, t, td) for p, t, td, _r in seg_rows if t and m_start <= td <= m_end]
|
month_tr = [(p, t, td) for p, t, td, _r in seg_rows if t and m_start <= td <= m_end]
|
||||||
|
all_tr = [(p, t, td) for p, t, td, _r in seg_rows if t]
|
||||||
dm = _compute_period_metrics(day_tr)
|
dm = _compute_period_metrics(day_tr)
|
||||||
wm = _compute_period_metrics(week_tr)
|
wm = _compute_period_metrics(week_tr)
|
||||||
mm = _compute_period_metrics(month_tr)
|
mm = _compute_period_metrics(month_tr)
|
||||||
|
am = _compute_period_metrics(all_tr)
|
||||||
dm["opens_count"] = _count_opens_for_segment(conn, trading_day, trading_day, seg_key)
|
dm["opens_count"] = _count_opens_for_segment(conn, trading_day, trading_day, seg_key)
|
||||||
wm["opens_count"] = _count_opens_for_segment(conn, w_start, w_end, seg_key)
|
wm["opens_count"] = _count_opens_for_segment(conn, w_start, w_end, seg_key)
|
||||||
mm["opens_count"] = _count_opens_for_segment(conn, m_start, m_end, seg_key)
|
mm["opens_count"] = _count_opens_for_segment(conn, m_start, m_end, seg_key)
|
||||||
|
am["opens_count"] = _count_opens_for_segment(conn, "1970-01-01", "9999-12-31", seg_key)
|
||||||
dm["range_label"] = f"北京时间交易日 {trading_day}({TRADING_DAY_RESET_HOUR}:00 切日)"
|
dm["range_label"] = f"北京时间交易日 {trading_day}({TRADING_DAY_RESET_HOUR}:00 切日)"
|
||||||
wm["range_label"] = f"{w_start} ~ {w_end}(北京日期,近7天)"
|
wm["range_label"] = f"{w_start} ~ {w_end}(北京日期,近7天)"
|
||||||
mm["range_label"] = f"{m_start} ~ {m_end}(北京自然月)"
|
mm["range_label"] = f"{m_start} ~ {m_end}(北京自然月)"
|
||||||
return dm, wm, mm
|
tds = [td for _, _, td in all_tr if td]
|
||||||
|
if tds:
|
||||||
|
am["range_label"] = f"全部历史 {min(tds)} ~ {max(tds)}(北京交易日)"
|
||||||
|
else:
|
||||||
|
am["range_label"] = "全部历史(暂无平仓)"
|
||||||
|
am["monthly_rows"] = _build_monthly_stats_rows(conn, all_tr, seg_key)
|
||||||
|
return dm, wm, mm, am
|
||||||
|
|
||||||
segments = []
|
segments = []
|
||||||
seg_defs = effective_stats_segment_defs(
|
seg_defs = effective_stats_segment_defs(
|
||||||
STATS_SEGMENT_DEFS, POSITION_SIZING_MODE, KEY_AUTO_ORDER_ENABLED
|
STATS_SEGMENT_DEFS, POSITION_SIZING_MODE, KEY_AUTO_ORDER_ENABLED
|
||||||
)
|
)
|
||||||
for seg_key, seg_title, _meta in seg_defs:
|
for seg_key, seg_title, _meta in seg_defs:
|
||||||
dm, wm, mm = slice_metrics(seg_key)
|
dm, wm, mm, am = slice_metrics(seg_key)
|
||||||
segments.append({"key": seg_key, "title": seg_title, "day": dm, "week": wm, "month": mm})
|
segments.append(
|
||||||
|
{"key": seg_key, "title": seg_title, "day": dm, "week": wm, "month": mm, "all": am}
|
||||||
|
)
|
||||||
|
|
||||||
dm, wm, mm = slice_metrics("all")
|
dm, wm, mm, am = slice_metrics("all")
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"trading_day": trading_day,
|
"trading_day": trading_day,
|
||||||
@@ -1883,6 +1924,7 @@ def compute_stats_bundle(conn, trading_day, now_dt=None):
|
|||||||
"day": dm,
|
"day": dm,
|
||||||
"week": wm,
|
"week": wm,
|
||||||
"month": mm,
|
"month": mm,
|
||||||
|
"all": am,
|
||||||
"segments": segments,
|
"segments": segments,
|
||||||
"stats_reset_hour": TRADING_DAY_RESET_HOUR,
|
"stats_reset_hour": TRADING_DAY_RESET_HOUR,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -212,6 +212,13 @@
|
|||||||
.inst-stats-details>summary{cursor:pointer;font-size:.84rem;color:#9aa3bf;padding:8px 0;user-select:none;list-style-position:inside}
|
.inst-stats-details>summary{cursor:pointer;font-size:.84rem;color:#9aa3bf;padding:8px 0;user-select:none;list-style-position:inside}
|
||||||
.inst-stats-details>summary::-webkit-details-marker{color:#6d7689}
|
.inst-stats-details>summary::-webkit-details-marker{color:#6d7689}
|
||||||
.inst-stats-details[open]>summary{margin-bottom:6px;color:#cfd3ef}
|
.inst-stats-details[open]>summary{margin-bottom:6px;color:#cfd3ef}
|
||||||
|
.inst-stats-month-table-wrap{overflow:auto;-webkit-overflow-scrolling:touch}
|
||||||
|
.inst-stats-month-table{width:100%;border-collapse:collapse;font-size:.8rem;font-variant-numeric:tabular-nums}
|
||||||
|
.inst-stats-month-table th,.inst-stats-month-table td{padding:8px 10px;text-align:right;border-bottom:1px solid #2a3348;white-space:nowrap}
|
||||||
|
.inst-stats-month-table th:first-child,.inst-stats-month-table td:first-child{text-align:left}
|
||||||
|
.inst-stats-month-table th{color:#8892b0;font-weight:600;font-size:.72rem}
|
||||||
|
.inst-stats-month-table td{color:#e8ecf4}
|
||||||
|
.inst-stats-month-table tbody tr:last-child td{border-bottom:none}
|
||||||
@media (max-width:640px){.inst-stats-kpis{grid-template-columns:1fr}.inst-stats-risk-grid{grid-template-columns:1fr}}
|
@media (max-width:640px){.inst-stats-kpis{grid-template-columns:1fr}.inst-stats-risk-grid{grid-template-columns:1fr}}
|
||||||
.key-history{margin-top:12px;padding-top:10px;border-top:1px solid #2a3150}
|
.key-history{margin-top:12px;padding-top:10px;border-top:1px solid #2a3150}
|
||||||
.key-history h3{font-size:.88rem;color:#b8c4ff;margin-bottom:6px}
|
.key-history h3{font-size:.88rem;color:#b8c4ff;margin-bottom:6px}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
(function (global) {
|
(function (global) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var PERIODS = ["day", "week", "month"];
|
var PERIODS = ["day", "week", "month", "all"];
|
||||||
|
|
||||||
function statsSegmentSelect() {
|
function statsSegmentSelect() {
|
||||||
return document.getElementById("stats-segment-select");
|
return document.getElementById("stats-segment-select");
|
||||||
|
|||||||
@@ -859,6 +859,13 @@ html[data-theme="light"] .inst-stats-details > summary {
|
|||||||
html[data-theme="light"] .inst-stats-details[open] > summary {
|
html[data-theme="light"] .inst-stats-details[open] > summary {
|
||||||
color: #0d4a7a !important;
|
color: #0d4a7a !important;
|
||||||
}
|
}
|
||||||
|
html[data-theme="light"] .inst-stats-month-table th {
|
||||||
|
color: #4a6078 !important;
|
||||||
|
}
|
||||||
|
html[data-theme="light"] .inst-stats-month-table td {
|
||||||
|
color: #142232 !important;
|
||||||
|
border-bottom-color: #d0dae4 !important;
|
||||||
|
}
|
||||||
|
|
||||||
html[data-theme="light"] .key-history {
|
html[data-theme="light"] .key-history {
|
||||||
border-top-color: #d0dae4 !important;
|
border-top-color: #d0dae4 !important;
|
||||||
|
|||||||
@@ -78,6 +78,42 @@
|
|||||||
<div class="stat-item"><div class="label">期内最大亏损日</div><div class="value">{% if s.worst_day %}{{ s.worst_day }}({{ funds_fmt(s.worst_day_pnl) }}U){% else %}-{% endif %}</div></div>
|
<div class="stat-item"><div class="label">期内最大亏损日</div><div class="value">{% if s.worst_day %}{{ s.worst_day }}({{ funds_fmt(s.worst_day_pnl) }}U){% else %}-{% endif %}</div></div>
|
||||||
</div>
|
</div>
|
||||||
</details>
|
</details>
|
||||||
|
{% if period_key == 'all' %}
|
||||||
|
<div class="inst-stats-block inst-stats-monthly" style="margin-top:14px">
|
||||||
|
<div class="inst-stats-block-title">按月统计</div>
|
||||||
|
{% if s.monthly_rows %}
|
||||||
|
<div class="inst-stats-month-table-wrap">
|
||||||
|
<table class="inst-stats-month-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>月份</th>
|
||||||
|
<th>开单</th>
|
||||||
|
<th>平仓</th>
|
||||||
|
<th>胜率</th>
|
||||||
|
<th>净盈亏</th>
|
||||||
|
<th>最大回撤</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for m in s.monthly_rows %}
|
||||||
|
{% set m_net_cls = 'pos-pnl-profit' if m.net_pnl_u > 0 else ('pos-pnl-loss' if m.net_pnl_u < 0 else '') %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ m.month_key }}</td>
|
||||||
|
<td>{{ m.opens_count }}</td>
|
||||||
|
<td>{{ m.closed_count }}</td>
|
||||||
|
<td>{% if m.win_rate_pct is not none %}{{ m.win_rate_pct }}%{% else %}—{% endif %}</td>
|
||||||
|
<td class="{{ m_net_cls }}">{% if m.net_pnl_u > 0 %}+{% endif %}{{ funds_fmt(m.net_pnl_u) }}</td>
|
||||||
|
<td class="pos-pnl-loss">{{ funds_fmt(m.max_drawdown_u) }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<p class="inst-stats-empty" style="margin-top:0">暂无按月平仓数据</p>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
<div class="grid">
|
<div class="grid">
|
||||||
@@ -263,6 +299,7 @@
|
|||||||
{% if page == 'records' %}
|
{% if page == 'records' %}
|
||||||
{% include 'records_panel.html' %}
|
{% include 'records_panel.html' %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
</div>
|
||||||
{% if page == 'env_config' %}
|
{% if page == 'env_config' %}
|
||||||
{% include 'env_config_panel.html' %}
|
{% include 'env_config_panel.html' %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@@ -306,13 +343,14 @@
|
|||||||
<button type="button" class="stats-period-tab active" data-stats-period="day" role="tab" aria-selected="true" onclick="switchStatsPeriod('day')">日统计</button>
|
<button type="button" class="stats-period-tab active" data-stats-period="day" role="tab" aria-selected="true" onclick="switchStatsPeriod('day')">日统计</button>
|
||||||
<button type="button" class="stats-period-tab" data-stats-period="week" role="tab" aria-selected="false" onclick="switchStatsPeriod('week')">周统计</button>
|
<button type="button" class="stats-period-tab" data-stats-period="week" role="tab" aria-selected="false" onclick="switchStatsPeriod('week')">周统计</button>
|
||||||
<button type="button" class="stats-period-tab" data-stats-period="month" role="tab" aria-selected="false" onclick="switchStatsPeriod('month')">月统计</button>
|
<button type="button" class="stats-period-tab" data-stats-period="month" role="tab" aria-selected="false" onclick="switchStatsPeriod('month')">月统计</button>
|
||||||
|
<button type="button" class="stats-period-tab" data-stats-period="all" role="tab" aria-selected="false" onclick="switchStatsPeriod('all')">全部统计</button>
|
||||||
</div>
|
</div>
|
||||||
{{ period_stats_pane("day", seg.day) }}
|
{{ period_stats_pane("day", seg.day) }}
|
||||||
{{ period_stats_pane("week", seg.week) }}
|
{{ period_stats_pane("week", seg.week) }}
|
||||||
{{ period_stats_pane("month", seg.month) }}
|
{{ period_stats_pane("month", seg.month) }}
|
||||||
|
{{ period_stats_pane("all", seg.all) }}
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
|
||||||
|
|||||||
@@ -7,8 +7,8 @@
|
|||||||
<script src="/static/autofill_guard.js?v=1"></script>
|
<script src="/static/autofill_guard.js?v=1"></script>
|
||||||
<link rel="stylesheet" href="/static/instance_theme_early.css?v=4">
|
<link rel="stylesheet" href="/static/instance_theme_early.css?v=4">
|
||||||
<link rel="stylesheet" href="/static/account_risk_badge.css?v=4">
|
<link rel="stylesheet" href="/static/account_risk_badge.css?v=4">
|
||||||
<link rel="stylesheet" href="/static/instance_page.css?v=12">
|
<link rel="stylesheet" href="/static/instance_page.css?v=13">
|
||||||
<link rel="stylesheet" href="/static/instance_theme.css?v=109">
|
<link rel="stylesheet" href="/static/instance_theme.css?v=110">
|
||||||
<script src="/static/account_risk_badge.js?v=4"></script>
|
<script src="/static/account_risk_badge.js?v=4"></script>
|
||||||
<meta name="theme-color" content="#0b0d14">
|
<meta name="theme-color" content="#0b0d14">
|
||||||
<title>{{ pwa_app_name }}</title>
|
<title>{{ pwa_app_name }}</title>
|
||||||
@@ -158,7 +158,7 @@ const ORDER_ENTRY_MODEL_CODE_TO_CATEGORY = {{ entry_model_code_to_category | toj
|
|||||||
<script src="/static/symbol_live_price.js?v=2"></script>
|
<script src="/static/symbol_live_price.js?v=2"></script>
|
||||||
<script src="/static/strategy_roll.js?v=6"></script>
|
<script src="/static/strategy_roll.js?v=6"></script>
|
||||||
<script src="/static/key_monitor_form.js?v=2"></script>
|
<script src="/static/key_monitor_form.js?v=2"></script>
|
||||||
<script src="/static/instance_stats.js?v=4"></script>
|
<script src="/static/instance_stats.js?v=5"></script>
|
||||||
{% include 'embed_boot_scripts.html' %}
|
{% include 'embed_boot_scripts.html' %}
|
||||||
<script src="/static/records_review_page.js?v=4"></script>
|
<script src="/static/records_review_page.js?v=4"></script>
|
||||||
<script src="/static/options_expiry_countdown.js?v=1"></script>
|
<script src="/static/options_expiry_countdown.js?v=1"></script>
|
||||||
|
|||||||
@@ -17,8 +17,8 @@
|
|||||||
<link rel="apple-touch-icon" href="/static/icons/apple-touch-icon.png">
|
<link rel="apple-touch-icon" href="/static/icons/apple-touch-icon.png">
|
||||||
<link rel="manifest" href="/static/icons/manifest.webmanifest">
|
<link rel="manifest" href="/static/icons/manifest.webmanifest">
|
||||||
<title>{{ pwa_app_name }}</title>
|
<title>{{ pwa_app_name }}</title>
|
||||||
<link rel="stylesheet" href="/static/instance_page.css?v=12">
|
<link rel="stylesheet" href="/static/instance_page.css?v=13">
|
||||||
<link rel="stylesheet" href="/static/instance_theme.css?v=109">
|
<link rel="stylesheet" href="/static/instance_theme.css?v=110">
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body
|
<body
|
||||||
@@ -110,6 +110,42 @@
|
|||||||
<div class="stat-item"><div class="label">期内最大亏损日</div><div class="value">{% if s.worst_day %}{{ s.worst_day }}({{ funds_fmt(s.worst_day_pnl) }}U){% else %}-{% endif %}</div></div>
|
<div class="stat-item"><div class="label">期内最大亏损日</div><div class="value">{% if s.worst_day %}{{ s.worst_day }}({{ funds_fmt(s.worst_day_pnl) }}U){% else %}-{% endif %}</div></div>
|
||||||
</div>
|
</div>
|
||||||
</details>
|
</details>
|
||||||
|
{% if period_key == 'all' %}
|
||||||
|
<div class="inst-stats-block inst-stats-monthly" style="margin-top:14px">
|
||||||
|
<div class="inst-stats-block-title">按月统计</div>
|
||||||
|
{% if s.monthly_rows %}
|
||||||
|
<div class="inst-stats-month-table-wrap">
|
||||||
|
<table class="inst-stats-month-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>月份</th>
|
||||||
|
<th>开单</th>
|
||||||
|
<th>平仓</th>
|
||||||
|
<th>胜率</th>
|
||||||
|
<th>净盈亏</th>
|
||||||
|
<th>最大回撤</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for m in s.monthly_rows %}
|
||||||
|
{% set m_net_cls = 'pos-pnl-profit' if m.net_pnl_u > 0 else ('pos-pnl-loss' if m.net_pnl_u < 0 else '') %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ m.month_key }}</td>
|
||||||
|
<td>{{ m.opens_count }}</td>
|
||||||
|
<td>{{ m.closed_count }}</td>
|
||||||
|
<td>{% if m.win_rate_pct is not none %}{{ m.win_rate_pct }}%{% else %}—{% endif %}</td>
|
||||||
|
<td class="{{ m_net_cls }}">{% if m.net_pnl_u > 0 %}+{% endif %}{{ funds_fmt(m.net_pnl_u) }}</td>
|
||||||
|
<td class="pos-pnl-loss">{{ funds_fmt(m.max_drawdown_u) }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<p class="inst-stats-empty" style="margin-top:0">暂无按月平仓数据</p>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
<div class="container">
|
<div class="container">
|
||||||
@@ -384,10 +420,12 @@
|
|||||||
<button type="button" class="stats-period-tab active" data-stats-period="day" role="tab" aria-selected="true" onclick="switchStatsPeriod('day')">日统计</button>
|
<button type="button" class="stats-period-tab active" data-stats-period="day" role="tab" aria-selected="true" onclick="switchStatsPeriod('day')">日统计</button>
|
||||||
<button type="button" class="stats-period-tab" data-stats-period="week" role="tab" aria-selected="false" onclick="switchStatsPeriod('week')">周统计</button>
|
<button type="button" class="stats-period-tab" data-stats-period="week" role="tab" aria-selected="false" onclick="switchStatsPeriod('week')">周统计</button>
|
||||||
<button type="button" class="stats-period-tab" data-stats-period="month" role="tab" aria-selected="false" onclick="switchStatsPeriod('month')">月统计</button>
|
<button type="button" class="stats-period-tab" data-stats-period="month" role="tab" aria-selected="false" onclick="switchStatsPeriod('month')">月统计</button>
|
||||||
|
<button type="button" class="stats-period-tab" data-stats-period="all" role="tab" aria-selected="false" onclick="switchStatsPeriod('all')">全部统计</button>
|
||||||
</div>
|
</div>
|
||||||
{{ period_stats_pane("day", seg.day) }}
|
{{ period_stats_pane("day", seg.day) }}
|
||||||
{{ period_stats_pane("week", seg.week) }}
|
{{ period_stats_pane("week", seg.week) }}
|
||||||
{{ period_stats_pane("month", seg.month) }}
|
{{ period_stats_pane("month", seg.month) }}
|
||||||
|
{{ period_stats_pane("all", seg.all) }}
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
@@ -429,7 +467,7 @@ const ORDER_ENTRY_MODEL_CODE_TO_CATEGORY = {{ entry_model_code_to_category | toj
|
|||||||
<script src="/static/manual_order_rr_preview.js?v=5"></script>
|
<script src="/static/manual_order_rr_preview.js?v=5"></script>
|
||||||
<script src="/static/symbol_live_price.js?v=2"></script>
|
<script src="/static/symbol_live_price.js?v=2"></script>
|
||||||
<script src="/static/strategy_roll.js?v=6"></script>
|
<script src="/static/strategy_roll.js?v=6"></script>
|
||||||
<script src="/static/instance_stats.js?v=4"></script>
|
<script src="/static/instance_stats.js?v=5"></script>
|
||||||
<script>
|
<script>
|
||||||
const JOURNAL_ENTRY_REASON_OPTIONS = {{ entry_reason_options | tojson }};
|
const JOURNAL_ENTRY_REASON_OPTIONS = {{ entry_reason_options | tojson }};
|
||||||
const JOURNAL_ORDER_TYPE_OPTIONS = {{ order_type_options | tojson }};
|
const JOURNAL_ORDER_TYPE_OPTIONS = {{ order_type_options | tojson }};
|
||||||
|
|||||||
Reference in New Issue
Block a user