feat: show ops-map summaries as detailed period tables
Replace paragraph text with day-hit stats (days/days_hit/pct) so leverage and move charts list each hour with counts. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -6,7 +6,7 @@ import math
|
||||
from collections import defaultdict
|
||||
from typing import Any, Iterable, Sequence
|
||||
|
||||
from packages.domain.buckets import shanghai_bucket
|
||||
from packages.domain.buckets import shanghai_bucket, shanghai_day
|
||||
from packages.domain.leverage import LEVERAGE_FORMULA_VERSION
|
||||
|
||||
|
||||
@@ -87,9 +87,15 @@ def aggregate_leverage(
|
||||
"""
|
||||
rows: 需含 ts_ms, leverage, side。
|
||||
返回按桶排序的聚合列表(含空桶)。
|
||||
额外字段:
|
||||
days — 该时段有样本的上海自然日数
|
||||
days_hit — 当日该时段均值 ≥ min_leverage 的天数
|
||||
pct_days_hit — days_hit / days
|
||||
"""
|
||||
want = (side or "both").upper()
|
||||
by_bucket: dict[int, list[float]] = defaultdict(list)
|
||||
# bucket -> day_ymd -> leverages
|
||||
by_bucket_day: dict[int, dict[str, list[float]]] = defaultdict(lambda: defaultdict(list))
|
||||
|
||||
for r in rows:
|
||||
lev = r.get("leverage")
|
||||
@@ -106,18 +112,32 @@ def aggregate_leverage(
|
||||
continue
|
||||
if want == "BOTH" and s not in ("C", "P"):
|
||||
continue
|
||||
b = shanghai_bucket(int(r["ts_ms"]), bucket_minutes)
|
||||
ts = int(r["ts_ms"])
|
||||
b = shanghai_bucket(ts, bucket_minutes)
|
||||
day = shanghai_day(ts)
|
||||
by_bucket[b].append(lev_f)
|
||||
by_bucket_day[b][day].append(lev_f)
|
||||
|
||||
out: list[dict[str, Any]] = []
|
||||
thr = float(min_leverage)
|
||||
for b in all_bucket_starts(bucket_minutes):
|
||||
stats = summarize_values(by_bucket.get(b, []), min_leverage=min_leverage)
|
||||
day_map = by_bucket_day.get(b, {})
|
||||
days = len(day_map)
|
||||
days_hit = 0
|
||||
for vals in day_map.values():
|
||||
if vals and (sum(vals) / len(vals)) >= thr:
|
||||
days_hit += 1
|
||||
pct_days = (days_hit / days) if days else None
|
||||
out.append(
|
||||
{
|
||||
"bucket_start_min": b,
|
||||
"bucket_hour": b // 60 if bucket_minutes >= 60 else None,
|
||||
"label": bucket_label(b, bucket_minutes),
|
||||
**stats,
|
||||
"days": days,
|
||||
"days_hit": days_hit,
|
||||
"pct_days_hit": pct_days,
|
||||
}
|
||||
)
|
||||
return out
|
||||
@@ -156,9 +176,11 @@ def aggregate_move_points(
|
||||
"""
|
||||
samples: {ts_ms, move_signed, move_abs}
|
||||
桶内同时给出 signed / abs 分布。
|
||||
额外:days — 该时段有结算样本的自然日数。
|
||||
"""
|
||||
by_signed: dict[int, list[float]] = defaultdict(list)
|
||||
by_abs: dict[int, list[float]] = defaultdict(list)
|
||||
by_bucket_days: dict[int, set[str]] = defaultdict(set)
|
||||
for s in samples:
|
||||
ts = s.get("ts_ms")
|
||||
signed = s.get("move_signed")
|
||||
@@ -174,6 +196,7 @@ def aggregate_move_points(
|
||||
b = shanghai_bucket(int(ts), bucket_minutes)
|
||||
by_signed[b].append(signed_f)
|
||||
by_abs[b].append(abs_f)
|
||||
by_bucket_days[b].add(shanghai_day(int(ts)))
|
||||
|
||||
out: list[dict[str, Any]] = []
|
||||
for b in all_bucket_starts(bucket_minutes):
|
||||
@@ -185,6 +208,7 @@ def aggregate_move_points(
|
||||
"bucket_hour": b // 60 if bucket_minutes >= 60 else None,
|
||||
"label": bucket_label(b, bucket_minutes),
|
||||
"n": signed_stats["n"],
|
||||
"days": len(by_bucket_days.get(b, set())),
|
||||
"signed": signed_stats,
|
||||
"abs": abs_stats,
|
||||
# 便捷字段(看板默认用 abs 均值)
|
||||
|
||||
Reference in New Issue
Block a user