This commit is contained in:
aaron 2026-05-20 22:08:43 +08:00
parent fe5c8a7c3f
commit e43760e006
2 changed files with 55 additions and 0 deletions

View File

@ -1138,6 +1138,39 @@ def _report_order_line(item: dict) -> str:
return f"- **{symbol}** · {side} · 目标 {_fmt_price(item.get('target_price'))} · 距目标 {_fmt_pct(distance)}"
def _paper_running_days() -> float:
conn = get_conn()
try:
row = conn.execute(
"""
SELECT MIN(first_at) AS first_at
FROM (
SELECT MIN(opened_at) AS first_at FROM paper_trades
UNION ALL
SELECT MIN(created_at) AS first_at FROM paper_orders
) x
WHERE first_at IS NOT NULL AND first_at <> ''
"""
).fetchone()
finally:
conn.close()
first_at = _parse_time(row["first_at"] if row else "")
if not first_at:
return 0.0
now = datetime.now(first_at.tzinfo) if first_at.tzinfo else datetime.now()
seconds = max(0.0, (now - first_at).total_seconds())
return max(1.0, seconds / 86400)
def _periodized_return_pct(total_return_pct: float, running_days: float, period_days: float) -> float:
if running_days <= 0:
return 0.0
growth = 1 + total_return_pct / 100
if growth <= 0:
return -100.0
return round((growth ** (period_days / running_days) - 1) * 100, 4)
def send_paper_trading_report(days: int = 30) -> dict:
days = max(1, min(_safe_int(days, 30), 365))
summary = get_paper_trading_summary(days=days)
@ -1152,6 +1185,9 @@ def send_paper_trading_report(days: int = 30) -> dict:
current_balance = _safe_float(summary.get("current_balance_usdt"))
return_pct = _safe_float(summary.get("account_total_return_pct"))
win_rate = _safe_float(summary.get("win_rate"))
running_days = _paper_running_days()
monthly_return_pct = _periodized_return_pct(return_pct, running_days, 30)
annualized_return_pct = _periodized_return_pct(return_pct, running_days, 365)
template = "green" if total_pnl >= 0 else "red"
elements = [
{
@ -1161,7 +1197,17 @@ def send_paper_trading_report(days: int = 30) -> dict:
"columns": [
{"tag": "column", "width": "weighted", "weight": 1, "elements": [_card_field("初始资金", f"{initial_equity:.2f} USDT")]},
{"tag": "column", "width": "weighted", "weight": 1, "elements": [_card_field("当前资金", f"{current_balance:.2f} USDT")]},
{"tag": "column", "width": "weighted", "weight": 1, "elements": [_card_field("运行天数", f"{running_days:.1f}")]},
],
},
{
"tag": "column_set",
"flex_mode": "none",
"background_style": "default",
"columns": [
{"tag": "column", "width": "weighted", "weight": 1, "elements": [_card_field("账户收益率", _fmt_pct(return_pct))]},
{"tag": "column", "width": "weighted", "weight": 1, "elements": [_card_field("月化收益率", _fmt_pct(monthly_return_pct))]},
{"tag": "column", "width": "weighted", "weight": 1, "elements": [_card_field("年化收益率", _fmt_pct(annualized_return_pct))]},
],
},
{
@ -1201,6 +1247,9 @@ def send_paper_trading_report(days: int = 30) -> dict:
return {
"ok": bool(ok),
"days": days,
"running_days": running_days,
"monthly_return_pct": monthly_return_pct,
"annualized_return_pct": annualized_return_pct,
"summary": summary,
"sent_at": _now(),
"push_result": push_result,

View File

@ -427,12 +427,18 @@ def test_send_paper_trading_report_pushes_performance_summary(monkeypatch, buy_n
result = send_paper_trading_report(days=30)
assert result["ok"] is True
assert result["running_days"] >= 1
assert "monthly_return_pct" in result
assert "annualized_return_pct" in result
assert pushed[-1]["metadata"]["event_type"] == "trade_report"
text = _visible_card_text(pushed[-1])
assert "交易报告" in text
assert "初始资金" in text
assert "当前资金" in text
assert "运行天数" in text
assert "账户收益率" in text
assert "月化收益率" in text
assert "年化收益率" in text
assert "成功率" in text
assert "战绩概览" in text
_assert_no_paper_trading_copy(pushed[-1])