feat(hub): add period date range and trade stats to inner-light-mind
Support today/week/month/custom range selection with sick count, PnL, and per-exchange breakdown; update docs. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+106
-20
@@ -1195,6 +1195,93 @@ def trading_day_bounds_ms(
|
||||
return int(start.timestamp() * 1000), int(end.timestamp() * 1000)
|
||||
|
||||
|
||||
def resolve_period_bounds(
|
||||
*,
|
||||
period: str = "",
|
||||
trading_day: str = "",
|
||||
date_from: str = "",
|
||||
date_to: str = "",
|
||||
reset_hour: int = TRADING_DAY_RESET_HOUR,
|
||||
) -> tuple[int, int, str, str, str]:
|
||||
"""返回 (start_ms, end_ms, date_from, date_to, period_label)。"""
|
||||
td = today_trading_day(reset_hour=reset_hour)
|
||||
p = (period or "today").strip().lower()
|
||||
if p in ("day", "today", ""):
|
||||
d = (trading_day or "").strip()[:10] or td
|
||||
start_ms, end_ms = trading_day_bounds_ms(d, reset_hour=reset_hour)
|
||||
return start_ms, end_ms, d, d, f"本日 {d}"
|
||||
if p == "week":
|
||||
day_dt = datetime.strptime(td, "%Y-%m-%d")
|
||||
monday = day_dt - timedelta(days=day_dt.weekday())
|
||||
df = monday.strftime("%Y-%m-%d")
|
||||
start_ms, _ = trading_day_bounds_ms(df, reset_hour=reset_hour)
|
||||
_, end_ms = trading_day_bounds_ms(td, reset_hour=reset_hour)
|
||||
return start_ms, end_ms, df, td, f"本周 {df}~{td}"
|
||||
if p == "month":
|
||||
day_dt = datetime.strptime(td, "%Y-%m-%d")
|
||||
first = day_dt.replace(day=1)
|
||||
df = first.strftime("%Y-%m-%d")
|
||||
start_ms, _ = trading_day_bounds_ms(df, reset_hour=reset_hour)
|
||||
_, end_ms = trading_day_bounds_ms(td, reset_hour=reset_hour)
|
||||
return start_ms, end_ms, df, td, f"本月 {df}~{td}"
|
||||
if p == "range":
|
||||
df = (date_from or "").strip()[:10] or td
|
||||
dt = (date_to or "").strip()[:10] or df
|
||||
if df > dt:
|
||||
df, dt = dt, df
|
||||
start_ms, _ = trading_day_bounds_ms(df, reset_hour=reset_hour)
|
||||
_, end_ms = trading_day_bounds_ms(dt, reset_hour=reset_hour)
|
||||
label = f"区间 {df}~{dt}" if df != dt else f"区间 {df}"
|
||||
return start_ms, end_ms, df, dt, label
|
||||
d = (trading_day or "").strip()[:10] or td
|
||||
start_ms, end_ms = trading_day_bounds_ms(d, reset_hour=reset_hour)
|
||||
return start_ms, end_ms, d, d, f"本日 {d}"
|
||||
|
||||
|
||||
def _compute_period_stats(trade_rows: list[dict[str, Any]]) -> dict[str, Any]:
|
||||
total = len(trade_rows)
|
||||
sick = 0
|
||||
pnl_all = 0.0
|
||||
pnl_ex = 0.0
|
||||
by_ex: dict[str, dict[str, Any]] = {}
|
||||
for td_row in trade_rows:
|
||||
ex = str(td_row.get("exchange_key") or "?")
|
||||
pnl = float(td_row.get("pnl_amount") or 0)
|
||||
tag = str(td_row.get("behavior_tag") or "")
|
||||
is_sick = tag == "sick"
|
||||
if is_sick:
|
||||
sick += 1
|
||||
pnl_all += pnl
|
||||
if not is_sick:
|
||||
pnl_ex += pnl
|
||||
if ex not in by_ex:
|
||||
by_ex[ex] = {
|
||||
"open_count": 0,
|
||||
"sick_count": 0,
|
||||
"pnl_total": 0.0,
|
||||
"pnl_ex_sick": 0.0,
|
||||
}
|
||||
bucket = by_ex[ex]
|
||||
bucket["open_count"] += 1
|
||||
bucket["pnl_total"] += pnl
|
||||
if is_sick:
|
||||
bucket["sick_count"] += 1
|
||||
else:
|
||||
bucket["pnl_ex_sick"] += pnl
|
||||
for ex in by_ex:
|
||||
by_ex[ex]["pnl_total"] = round(by_ex[ex]["pnl_total"], 4)
|
||||
by_ex[ex]["pnl_ex_sick"] = round(by_ex[ex]["pnl_ex_sick"], 4)
|
||||
sick_pct = round(sick / total * 100, 1) if total else 0.0
|
||||
return {
|
||||
"open_count": total,
|
||||
"sick_count": sick,
|
||||
"sick_pct": sick_pct,
|
||||
"pnl_total": round(pnl_all, 4),
|
||||
"pnl_ex_sick": round(pnl_ex, 4),
|
||||
"by_exchange": by_ex,
|
||||
}
|
||||
|
||||
|
||||
def list_review_quotes(*, db_path: Path | None = None) -> list[dict[str, Any]]:
|
||||
init_db(db_path)
|
||||
conn = _connect(db_path)
|
||||
@@ -1310,6 +1397,9 @@ def delete_review_quote(quote_id: int, *, db_path: Path | None = None) -> bool:
|
||||
def list_daily_trades(
|
||||
trading_day: str = "",
|
||||
*,
|
||||
period: str = "",
|
||||
date_from: str = "",
|
||||
date_to: str = "",
|
||||
exchange_key: str = "",
|
||||
filter_profit: bool = False,
|
||||
filter_loss: bool = False,
|
||||
@@ -1317,10 +1407,15 @@ def list_daily_trades(
|
||||
search: str = "",
|
||||
db_path: Path | None = None,
|
||||
) -> dict[str, Any]:
|
||||
"""按交易日列出开仓记录(默认当日),含各所开仓统计。"""
|
||||
"""按日期区间列出开仓记录(本日/本周/本月/自选),含犯病与盈亏统计。"""
|
||||
init_db(db_path)
|
||||
td = (trading_day or "").strip()[:10] or today_trading_day()
|
||||
start_ms, end_ms = trading_day_bounds_ms(td)
|
||||
p = (period or "today").strip().lower() or "today"
|
||||
start_ms, end_ms, df, dt, period_label = resolve_period_bounds(
|
||||
period=p,
|
||||
trading_day=trading_day,
|
||||
date_from=date_from,
|
||||
date_to=date_to,
|
||||
)
|
||||
ex_filter = (exchange_key or "").strip().lower()
|
||||
conn = _connect(db_path)
|
||||
try:
|
||||
@@ -1329,15 +1424,6 @@ def list_daily_trades(
|
||||
if ex_filter:
|
||||
where += " AND exchange_key=?"
|
||||
params.append(ex_filter)
|
||||
stat_rows = conn.execute(
|
||||
f"""
|
||||
SELECT exchange_key, COUNT(*) AS open_count
|
||||
FROM archive_trade_cache
|
||||
WHERE {where}
|
||||
GROUP BY exchange_key
|
||||
""",
|
||||
params,
|
||||
).fetchall()
|
||||
rows = conn.execute(
|
||||
f"""
|
||||
SELECT * FROM archive_trade_cache
|
||||
@@ -1347,6 +1433,7 @@ def list_daily_trades(
|
||||
params,
|
||||
).fetchall()
|
||||
overlays_by_ex: dict[str, dict[int, dict]] = {}
|
||||
all_rows: list[dict[str, Any]] = []
|
||||
trades: list[dict[str, Any]] = []
|
||||
q = (search or "").strip().lower()
|
||||
for r in rows:
|
||||
@@ -1354,6 +1441,7 @@ def list_daily_trades(
|
||||
if ex_k not in overlays_by_ex:
|
||||
overlays_by_ex[ex_k] = load_overlays(ex_k, db_path=db_path)
|
||||
td_row = _trade_row_to_dict(r, overlays_by_ex[ex_k].get(int(r["trade_id"])))
|
||||
all_rows.append(td_row)
|
||||
pnl = float(td_row.get("pnl_amount") or 0)
|
||||
tag = td_row.get("behavior_tag") or ""
|
||||
if filter_profit and pnl <= 0.0001:
|
||||
@@ -1378,16 +1466,14 @@ def list_daily_trades(
|
||||
if q not in blob:
|
||||
continue
|
||||
trades.append(td_row)
|
||||
by_exchange = {
|
||||
str(sr["exchange_key"]): int(sr["open_count"] or 0) for sr in stat_rows
|
||||
}
|
||||
return {
|
||||
"trading_day": td,
|
||||
"period": p,
|
||||
"period_label": period_label,
|
||||
"trading_day": dt,
|
||||
"date_from": df,
|
||||
"date_to": dt,
|
||||
"trades": trades,
|
||||
"stats": {
|
||||
"open_count": sum(by_exchange.values()),
|
||||
"by_exchange": by_exchange,
|
||||
},
|
||||
"stats": _compute_period_stats(all_rows),
|
||||
}
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
Reference in New Issue
Block a user