账户绩效汇总
按一组固定的时间窗口分桶返回用户的交易绩效。供账户仪表盘的综合绩效明细表格渲染 Today / Yesterday / Last 7d / Last 30d / This Year / All Time 各行。
GET /account/performance-summary
Authorization: Bearer <token>
请求参数
| 名称 | 类型 | 必需 | 说明 |
|---|---|---|---|
symbol | string | 否 | 过滤到单个市场(如 BTC-USDT)。缺省时聚合全部市场。 |
没有日期参数:桶集合固定,由服务端按 UTC 解析。
分桶
所有桶边界均为 UTC。设 today_start = 当日 UTC 零点。
bucket | 窗口(起点含,终点不含) |
|---|---|
today | [today_start, now) |
yesterday | [today_start − 1d, today_start) |
week | [today_start − 7d, now) |
month | [today_start − 30d, now) |
year | [当年 1 月 1 日 UTC, now) |
all | (−∞, now) |
桶之间的重叠是有意为之:today 包含在 week 内,week 包含在 month 内,依此类推。每一行都独立地从底层表计算。
响应
{
"symbol": null,
"buckets": [
{
"bucket": "today",
"start_ts": 1778716800,
"end_ts": 1778740012,
"volume": "12500.00",
"trade_count": 18,
"realized_pnl": "42.10",
"unrealized_pnl": "-3.25",
"start_unrealized_pnl": "-3.25",
"pnl": "42.10",
"wins": 7,
"losses": 4,
"win_rate": "63.64",
"used_capital": "850.00",
"pnl_bps": 495
},
{ "bucket": "yesterday", "...": "..." },
{ "bucket": "week", "...": "..." },
{ "bucket": "month", "...": "..." },
{ "bucket": "year", "...": "..." },
{ "bucket": "all", "...": "..." }
]
}
buckets 始终是按上述顺序排列的 6 元素数组。没有任何活动的桶,其所有数值字段为 0、win_rate 为 0、pnl_bps 为 0。
响应字段
顶层
| 字段 | 类型 | 说明 |
|---|---|---|
symbol | string | null | 回显 symbol 请求参数。 |
buckets | PerformanceBucket[] | 始终 6 个条目,顺序为:today、yesterday、week、month、year、all。 |
PerformanceBucket
| 字段 | 类型 | 说明 |
|---|---|---|
bucket | string | today、yesterday、week、month、year、all 之一。 |
start_ts | int64 | 桶起点,UTC Unix 秒级时间戳。对 all,下界为 unix 纪元(0)。 |
end_ts | int64 | 桶终点,UTC Unix 秒级时间戳,不含。对开放端点的桶(today、week、month、year、all)等于 "now"。 |
volume | Decimal | [start_ts, end_ts) 内用户为 maker 或 taker 的 trades 上的 SUM(price × amount)。 |
trade_count | int64 | 上述 trades 行数。 |
realized_pnl | Decimal | [start_ts, end_ts) 内的 SUM(realized_pnl_events.realized_pnl)。 |
unrealized_pnl | Decimal | 用户当前未平仓位按最新标记价格估值的浮动盈亏。与桶窗口无关;逐行提供仅为方便。 |
start_unrealized_pnl | Decimal | 预期语义:用户带入 start_ts 时刻的浮动盈亏。v1 实际行为:除 all 外的每个桶,该值就是用户当前未平仓位按当前标记价格计算的未实现盈亏 —— 与 unrealized_pnl 字段完全相同,与 start_ts 时刻的持仓无关。对 all 恒为 0。见 v1 简化。 |
pnl | Decimal | realized_pnl + unrealized_pnl − start_unrealized_pnl。即表格中展示的"区间 PnL"。由于 v1 的 start_unrealized_pnl 行为,除 all 外的每个桶该值退化为 realized_pnl(all 桶为 realized_pnl + unrealized_pnl)。 |
wins | int64 | 窗口内 realized_pnl > 0 的 realized_pnl_events 行数。 |
losses | int64 | 窗口内 realized_pnl ≤ 0 的 realized_pnl_events 行数。 |
win_rate | Decimal | wins / (wins + losses) × 100。窗口内没有平仓时为 0。 |
used_capital | Decimal | 窗口内使用的资金(USD)。见 used_capital 语义。 |
pnl_bps | int64 | pnl / used_capital × 10000,四舍五入为整数基点。used_capital 为 0 时为 0。 |
used_capital 语义
预期定义是*"桶内观测到的 [未平仓抵押 + 未结算 PnL] 峰值"*,与目前 Subsquid 支撑的仪表盘渲染该列的口径一致。
v1 实现返回一个简化值:
used_capital = max(
sum_open_position_collateral_at_end_of_bucket
- realized_pnl_in_bucket
+ start_unrealized_pnl,
0
)
它由请求时刻已有的数据算得(当前未平仓位、该桶的已实现 PnL、该桶的期初未实现 PnL)。它不是桶内峰值;当仓位在桶中段大于桶末时,可能低估真实的资金占用。
用户没有未平仓位且桶内已实现 PnL 持平时,used_capital 可能为 0,此时 pnl_bps 也为 0。
v1 简化
以下是明确的 v1 取舍,后续会在不改变响应结构的前提下收紧:
start_unrealized_pnl完全不考察start_ts时刻的持仓。v1 实现计算的是当前所有未平仓位按当前标记价格的未实 现盈亏,并把这同一个值用作除all外每个桶的start_unrealized_pnl(all为0)。后果:对today/yesterday/week/month/year各行,pnl = realized_pnl + unrealized_pnl − start_unrealized_pnl抵消为纯realized_pnl—— 未实现盈亏实际上只影响all行。这是已知的 v1 局限;忠实的"期初带入 PnL"需要一张历史open_positions_snapshot表,目前尚不存在。used_capital是时点值(桶末)而非桶内峰值;见上文。- 窗口内的
max_capital不属于本端点。若峰值抵押数据可用,将以新增字段的方式加入,不改动现有字段。
这些简化对 month/year/all 各行的影响大于 today/yesterday/week。客户端应把简化后的行当作方向性指标,而非精确审计。
错误
| HTTP | code | 场景 |
|---|---|---|
| 500 | PERFORMANCE_SUMMARY_FAILED | 聚合六个桶中任一时数据库出错。 |
错误响应体:
{ "error": "...", "code": "..." }
注意事项
- 所有金额均以十进制字符串返回。
- v1 中响应在服务端不做缓存。调用方应自行限频(仪表盘每 60 秒轮询一次)。
- 端点先通过
tokio::try_join!计算两个与窗口无关的聚合值(当前未实现 PnL、未平仓抵押),再以派生任务并发执行各桶的聚合查询(tokio::spawn,每桶两个)。市场或用户增多不会成倍增加往返次数。