fix期权交易记录列名改为期权盈亏,并补充曲线审计报告。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,82 @@
|
|||||||
|
# 审计报告:已实现盈亏曲线(实例 + 中控)(2026-10-13)
|
||||||
|
|
||||||
|
## 1. 范围
|
||||||
|
|
||||||
|
- 实例数据统计曲线:`query_realized_pnl_series_with_db` / `/api/instance/cash_curve`
|
||||||
|
- 中控桥接:`GET /api/hub/options/pnl_curve`(`crypto_okx`)
|
||||||
|
- 中控汇总:`pull_all_pnl_curves` → `build_stats_curve`(`crypto_okx_hub`)
|
||||||
|
- 真盈亏口径一致性:`realized_pnl_usdt` = 期权币盈亏 × 平仓价(卖回 VWAP 优先)
|
||||||
|
- 安全面:桥接 Token、会话鉴权、注入与信息泄露
|
||||||
|
|
||||||
|
相关提交(摘要):
|
||||||
|
|
||||||
|
| 仓库 | 提交 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| crypto_okx | `b31f861` | 实例曲线改累计已实现盈亏 |
|
||||||
|
| crypto_okx | `117f595` | 暴露 `/api/hub/options/pnl_curve` |
|
||||||
|
| crypto_okx_hub | `f2605b1` | 中控曲线改各机叠加,不再用余额采样 |
|
||||||
|
|
||||||
|
## 2. 审计方法
|
||||||
|
|
||||||
|
1. 静态审阅实例曲线、桥接 API、中控聚合与前端绘制路径
|
||||||
|
2. Security Review 子代理(实例仓 + 中控仓;中控侧因 CRLF 噪声 diff 不完整,人工补审 `stats_agg_lib` / `bridge_client_lib`)
|
||||||
|
3. 本地单测:
|
||||||
|
|
||||||
|
```text
|
||||||
|
# crypto_okx
|
||||||
|
python -m unittest tests.test_instance_realized_pnl_curve \
|
||||||
|
tests.test_options_account_pnl tests.test_options_review_pnl_freeze -q
|
||||||
|
→ OK (12)
|
||||||
|
|
||||||
|
# crypto_okx_hub
|
||||||
|
python -m unittest tests.test_stats_realized_pnl_curve -q
|
||||||
|
→ OK (1)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. 安全审计结论
|
||||||
|
|
||||||
|
| 级别 | 结论 |
|
||||||
|
| --- | --- |
|
||||||
|
| Critical / High / Medium | **未发现**由本批曲线改动引入的安全缺陷 |
|
||||||
|
|
||||||
|
要点:
|
||||||
|
|
||||||
|
- `/api/hub/options/pnl_curve`:`hub_bridge_token_ok`(`hmac.compare_digest`),空 Token 拒绝
|
||||||
|
- `/api/instance/cash_curve`:`@login_required`(会话 / embed_sso)
|
||||||
|
- SQL 固定语句,`range` 仅作窗口解析,不进 SQL 拼接
|
||||||
|
- 响应为聚合点列,无凭证 / 逐笔敏感字段外泄
|
||||||
|
|
||||||
|
既有基线风险(Bridge Token 出现在设置页、长寿命 embed_sso 等)**非本批引入**,不计入本次闭环。
|
||||||
|
|
||||||
|
## 4. 正确性 / 产品审计发现
|
||||||
|
|
||||||
|
| 级别 | 位置 | 问题 | 建议 |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| Medium | `crypto_okx_hub` `build_stats_curve` | 某机拉曲线失败时 `points=[]`,合计直接不含该机 → **离线瞬间合计台阶可能陡降**(上次成功值未保留) | 失败机沿用上次成功曲线,或合计仅对 `online` 且有点的机求和并在 UI 标明「部分机离线」 |
|
||||||
|
| Medium | `instance_cash_sample_lib._trade_realized_usdt` | 无 `realized_pnl_usdt` 时回退 `币盈亏×close_index_px`,**未走卖回 VWAP**;与冻结真盈亏可能不一致 | 回退改调 `resolve_true_pnl_usdt_from_trade`,或曲线只计 `realized_pnl_usdt IS NOT NULL` |
|
||||||
|
| Low | `query_realized_pnl_series_with_db` | `trade_count` 为全历史可计价平仓笔数,非当前窗口笔数 | 改名或改为窗口计数,避免与 KPI「笔数」混淆 |
|
||||||
|
| Low | 中控 KPI vs 曲线 | 表「已实现」来自窗口 stats;曲线 `latest` 为带基线的累计水平 | 文案已区分;保持即可,勿拿曲线终点直接当窗口 KPI |
|
||||||
|
| Low | 部署依赖 | 旧实例无 `pnl_curve` → 404 → 中控该机空曲线 | 全实例更新到含 `117f595` 后再看中控合计 |
|
||||||
|
| Info | 中控 summary vs curve | summary 用 overview snaps;curve 另拉 `pnl_curve` | 刷新时刻可能略不一致,属缓存架构特性 |
|
||||||
|
|
||||||
|
## 5. 口径核对(通过项)
|
||||||
|
|
||||||
|
| 项 | 状态 |
|
||||||
|
| --- | --- |
|
||||||
|
| 曲线不受划转/提现影响(不读余额采样) | 通过 |
|
||||||
|
| 累计台阶:平仓加一笔、窗口前累计作基线 | 通过(单测覆盖) |
|
||||||
|
| 中控合计:1 分钟桶 + 各机前向填充后求和 | 通过(单测覆盖) |
|
||||||
|
| 桥接与实例 UI 共用同一查询函数 | 通过 |
|
||||||
|
| 中控文案「已实现盈亏曲线」与空态提示 | 通过 |
|
||||||
|
|
||||||
|
## 6. 结论
|
||||||
|
|
||||||
|
- **安全**:本批改动在鉴权与注入面上合格,无 Critical/High/Medium 安全问题。
|
||||||
|
- **正确性**:主路径(已冻结 `realized_pnl_usdt` 的平仓累加、划转无关)成立;需关注 **中控离线掉台阶** 与 **未冻结行用指数价回退** 两条 Medium。
|
||||||
|
- **上线条件**:实例先更新(暴露 `pnl_curve`),再依赖中控合计曲线;否则部分机空白属预期。
|
||||||
|
|
||||||
|
## 7. 建议后续(非阻断)
|
||||||
|
|
||||||
|
1. 中控:离线/失败机保留上一成功曲线参与合计,或合计旁显示离线名单
|
||||||
|
2. 实例:曲线点只认已冻结 U,或回退统一走 `resolve_true_pnl_usdt_from_trade`
|
||||||
|
3. 实盘冒烟:两机各有平仓 → 中控合计 ≈ 两机曲线终点之和;一机停服刷新 → 确认合计是否异常跳变(验证第 4 节 Medium)
|
||||||
@@ -456,11 +456,11 @@
|
|||||||
var pendingSell = stRaw === "待卖回";
|
var pendingSell = stRaw === "待卖回";
|
||||||
if (needsMark) {
|
if (needsMark) {
|
||||||
badgeExtra +=
|
badgeExtra +=
|
||||||
' <span class="or-badge" style="background:rgba(255,170,60,.28)" title="缺开仓价或卖回价,未能按新真盈亏公式更新">缺价未核</span>';
|
' <span class="or-badge" style="background:rgba(255,170,60,.28)" title="缺开仓价或卖回价,未能按新公式更新期权盈亏">缺价未核</span>';
|
||||||
}
|
}
|
||||||
if (pendingSell) {
|
if (pendingSell) {
|
||||||
badgeExtra +=
|
badgeExtra +=
|
||||||
' <span class="or-badge" style="background:rgba(120,180,255,.28)" title="平仓卖回未完成(可能多笔),完成后按卖回VWAP结算真盈亏">待卖回</span>';
|
' <span class="or-badge" style="background:rgba(120,180,255,.28)" title="平仓卖回未完成(可能多笔),完成后按卖回VWAP结算期权盈亏">待卖回</span>';
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
'<tr class="or-trade-row' +
|
'<tr class="or-trade-row' +
|
||||||
@@ -589,7 +589,7 @@
|
|||||||
escapeHtml(t.source_label || t.source_type) +
|
escapeHtml(t.source_label || t.source_type) +
|
||||||
"</span>" +
|
"</span>" +
|
||||||
(needsMarkR
|
(needsMarkR
|
||||||
? ' <span class="or-badge" style="background:rgba(255,170,60,.28)" title="缺开仓价或卖回价,未能按新真盈亏公式更新">缺价未核</span>'
|
? ' <span class="or-badge" style="background:rgba(255,170,60,.28)" title="缺开仓价或卖回价,未能按新公式更新期权盈亏">缺价未核</span>'
|
||||||
: "") +
|
: "") +
|
||||||
"</td>" +
|
"</td>" +
|
||||||
"<td>" +
|
"<td>" +
|
||||||
@@ -909,11 +909,12 @@
|
|||||||
|
|
||||||
var meta = $("or-detail-meta");
|
var meta = $("or-detail-meta");
|
||||||
if (meta) {
|
if (meta) {
|
||||||
|
var pnlLabel = t.is_hedge ? "盈亏" : "期权盈亏";
|
||||||
var cells = [
|
var cells = [
|
||||||
["标的", t.underlying || "—"],
|
["标的", t.underlying || "—"],
|
||||||
["合约/计划", tradeTitle(t)],
|
["合约/计划", tradeTitle(t)],
|
||||||
["初始权利金", fmtPremiumU(t.initial_premium_usdt)],
|
["初始权利金", fmtPremiumU(t.initial_premium_usdt)],
|
||||||
["盈亏", fmtPnl(t.realized_pnl_total)],
|
[pnlLabel, fmtPnl(t.realized_pnl_total)],
|
||||||
["盈利率", fmtProfitRate(resolveProfitRate(t))],
|
["盈利率", fmtProfitRate(resolveProfitRate(t))],
|
||||||
["期权手续费", fmtOptionFee(t.option_fee_usdt)],
|
["期权手续费", fmtOptionFee(t.option_fee_usdt)],
|
||||||
["持有", fmtHold(t.hold_seconds)],
|
["持有", fmtHold(t.hold_seconds)],
|
||||||
@@ -937,7 +938,7 @@
|
|||||||
if (pair[0] === "盈亏" || pair[0] === "永续盈亏" || pair[0] === "期权盈亏") {
|
if (pair[0] === "盈亏" || pair[0] === "永续盈亏" || pair[0] === "期权盈亏") {
|
||||||
cls = pnlClass(t.realized_pnl_total);
|
cls = pnlClass(t.realized_pnl_total);
|
||||||
if (pair[0] === "永续盈亏") cls = pnlClass(t.realized_pnl_perp);
|
if (pair[0] === "永续盈亏") cls = pnlClass(t.realized_pnl_perp);
|
||||||
if (pair[0] === "期权盈亏") cls = pnlClass(t.realized_pnl_options);
|
if (pair[0] === "期权盈亏" && t.is_hedge) cls = pnlClass(t.realized_pnl_options);
|
||||||
} else if (pair[0] === "结果") {
|
} else if (pair[0] === "结果") {
|
||||||
cls = resultClass(e.result_tag);
|
cls = resultClass(e.result_tag);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -229,7 +229,7 @@
|
|||||||
<span class="or-step" aria-hidden="true">1</span>
|
<span class="or-step" aria-hidden="true">1</span>
|
||||||
<div>
|
<div>
|
||||||
<div class="or-section-title" id="or-list-title">期权交易记录</div>
|
<div class="or-section-title" id="or-list-title">期权交易记录</div>
|
||||||
<p class="or-section-desc">真盈亏=期权历史币盈亏×平仓价(有卖回 VWAP 优先,否则用冻结平仓价)。缺价标「缺价未核」。期权历史仅记币收益。</p>
|
<p class="or-section-desc">期权盈亏=期权历史币盈亏×平仓价(有卖回 VWAP 优先,否则用冻结平仓价)。缺价标「缺价未核」。期权历史仅记币收益。</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="options-strike-table-wrap or-trades-table-wrap" id="or-trades-wrap">
|
<div class="options-strike-table-wrap or-trades-table-wrap" id="or-trades-wrap">
|
||||||
@@ -239,7 +239,7 @@
|
|||||||
<th>类型</th>
|
<th>类型</th>
|
||||||
<th>标的/合约</th>
|
<th>标的/合约</th>
|
||||||
<th>初始权利金</th>
|
<th>初始权利金</th>
|
||||||
<th title="期权历史币盈亏×平仓价">真盈亏</th>
|
<th title="期权历史币盈亏×平仓价">期权盈亏</th>
|
||||||
<th>盈利率</th>
|
<th>盈利率</th>
|
||||||
<th title="仅期权开/平手续费">手续费</th>
|
<th title="仅期权开/平手续费">手续费</th>
|
||||||
<th>开仓时间</th>
|
<th>开仓时间</th>
|
||||||
@@ -465,4 +465,4 @@
|
|||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="/static/options_review.js?v=27"></script>
|
<script src="/static/options_review.js?v=28"></script>
|
||||||
|
|||||||
Reference in New Issue
Block a user