feat: 档案统计独立卡片、共用交易日历与四所统计页日历
内照明心统计表移至顶部卡片,右侧为日历/图表/交易记录;日历样式适配浅深主题,四所统计分析页同步展示按月盈亏日历。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
"""按交易日聚合实例 trade_records 盈亏,供统计分析页日历 API 使用。"""
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Any, Callable
|
||||
|
||||
|
||||
def build_trade_stats_calendar(
|
||||
pnls: list[tuple],
|
||||
year: int,
|
||||
month: int,
|
||||
segment_key: str,
|
||||
row_matches_fn: Callable[[Any, str], bool],
|
||||
*,
|
||||
reset_hour: int = 8,
|
||||
) -> dict[str, Any]:
|
||||
"""pnls: _load_completed_trade_pnls 返回值 (pnl, close_dt, trading_day, row)。"""
|
||||
y = int(year)
|
||||
m = int(month)
|
||||
if m < 1 or m > 12:
|
||||
raise ValueError("month 无效")
|
||||
first = f"{y:04d}-{m:02d}-01"
|
||||
if m == 12:
|
||||
next_first = datetime(y + 1, 1, 1)
|
||||
else:
|
||||
next_first = datetime(y, m + 1, 1)
|
||||
last = (next_first - timedelta(days=1)).strftime("%Y-%m-%d")
|
||||
seg = (segment_key or "all").strip() or "all"
|
||||
days: dict[str, dict[str, Any]] = {}
|
||||
for pnl, _close_dt, td, row in pnls:
|
||||
if not td or td < first or td > last:
|
||||
continue
|
||||
if not row_matches_fn(row, seg):
|
||||
continue
|
||||
bucket = days.setdefault(
|
||||
td,
|
||||
{
|
||||
"trading_day": td,
|
||||
"open_count": 0,
|
||||
"pnl_total": 0.0,
|
||||
"turnover_total": 0.0,
|
||||
"commission_total": 0.0,
|
||||
"has_sick": False,
|
||||
"sick_count": 0,
|
||||
},
|
||||
)
|
||||
bucket["open_count"] += 1
|
||||
bucket["pnl_total"] += float(pnl or 0)
|
||||
try:
|
||||
bucket["turnover_total"] += float(row["exchange_turnover_usdt"] or 0)
|
||||
except (TypeError, ValueError, KeyError):
|
||||
pass
|
||||
try:
|
||||
bucket["commission_total"] += float(row["exchange_commission_usdt"] or 0)
|
||||
except (TypeError, ValueError, KeyError):
|
||||
pass
|
||||
for d in days.values():
|
||||
d["pnl_total"] = round(float(d["pnl_total"]), 4)
|
||||
d["turnover_total"] = round(float(d["turnover_total"]), 4)
|
||||
d["commission_total"] = round(float(d["commission_total"]), 4)
|
||||
month_pnl = sum(float(d["pnl_total"]) for d in days.values())
|
||||
month_count = sum(int(d["open_count"]) for d in days.values())
|
||||
return {
|
||||
"year": y,
|
||||
"month": m,
|
||||
"date_from": first,
|
||||
"date_to": last,
|
||||
"segment": seg,
|
||||
"reset_hour": int(reset_hour),
|
||||
"days": days,
|
||||
"month_pnl_total": round(month_pnl, 4),
|
||||
"month_open_count": month_count,
|
||||
}
|
||||
Reference in New Issue
Block a user