fix: 重构交易复盘

alphaFuthures
Lxy 3 weeks ago
parent 5b938ee7f5
commit e9e291e063

@ -142,12 +142,6 @@ def review_plan_page():
return FileResponse(str(STATIC_DIR / "review_plan.html")) return FileResponse(str(STATIC_DIR / "review_plan.html"))
@app.get("/trade-review")
def trade_review_page():
"""交易复盘页面"""
return FileResponse(str(STATIC_DIR / "trade_review.html"))
@app.get("/api/v1/health") @app.get("/api/v1/health")
def health(): def health():
return {"status": "ok", "service": "market-data-buffer"} return {"status": "ok", "service": "market-data-buffer"}

@ -503,85 +503,73 @@
.tr-btn-danger:hover { background: rgba(255,59,48,0.2); } .tr-btn-danger:hover { background: rgba(255,59,48,0.2); }
.tr-date-input { background: #fff; border: 1px solid rgba(0,0,0,0.05); border-radius: 12px; padding: 0 14px; height: 40px; font-size: 13px; color: var(--text-primary); box-shadow: var(--shadow-sm); outline: none; font-family: inherit; } .tr-date-input { background: #fff; border: 1px solid rgba(0,0,0,0.05); border-radius: 12px; padding: 0 14px; height: 40px; font-size: 13px; color: var(--text-primary); box-shadow: var(--shadow-sm); outline: none; font-family: inherit; }
.tr-date-input:focus { border-color: var(--color-brand); box-shadow: 0 0 0 3px rgba(0,122,255,0.15); } .tr-date-input:focus { border-color: var(--color-brand); box-shadow: 0 0 0 3px rgba(0,122,255,0.15); }
.tr-select { background: #fff; border: 1px solid rgba(0,0,0,0.05); border-radius: 12px; padding: 0 14px; height: 40px; font-size: 13px; color: var(--text-primary); box-shadow: var(--shadow-sm); outline: none; font-family: inherit; cursor: pointer; }
.tr-kline-tab { padding: 8px 16px; border-radius: 8px; font-size: 13px; font-weight: 500; background: #fff; color: var(--text-secondary); border: 1px solid rgba(0,0,0,0.05); cursor: pointer; transition: all 0.2s; box-shadow: var(--shadow-sm); }
.tr-kline-tab:hover { background: var(--color-brand); color: #fff; border-color: var(--color-brand); }
.tr-kline-tab.active { background: var(--color-brand); color: #fff; border-color: var(--color-brand); box-shadow: 0 4px 10px rgba(0,122,255,0.2); }
.tr-sub-nav { display: flex; gap: 8px; margin-bottom: 20px; overflow-x: auto; padding-bottom: 4px; } /* 6列统计卡片 */
.tr-sub-nav-item { padding: 8px 18px; border-radius: 9999px; font-size: 13px; background: #fff; color: var(--text-secondary); cursor: pointer; white-space: nowrap; transition: all 0.2s; text-decoration: none; box-shadow: var(--shadow-sm); font-weight: 500; } .tr-stats-grid { display: grid; grid-template-columns: repeat(6, 1fr); gap: 14px; margin-bottom: 24px; }
.tr-sub-nav-item:hover { background: var(--color-brand); color: #fff; } .tr-stat-card { background: var(--bg-card); border-radius: var(--radius-card); padding: 18px; text-align: center; box-shadow: var(--shadow-sm); transition: transform 0.2s; }
.tr-sub-nav-item.active { background: var(--color-brand); color: #fff; box-shadow: 0 4px 10px rgba(0,122,255,0.2); }
.tr-stats-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; margin-bottom: 20px; }
.tr-stat-card { background: var(--bg-card); border-radius: 16px; padding: 18px; text-align: center; box-shadow: var(--shadow-sm); transition: transform 0.2s; }
.tr-stat-card:hover { transform: translateY(-2px); box-shadow: var(--shadow-md); } .tr-stat-card:hover { transform: translateY(-2px); box-shadow: var(--shadow-md); }
.tr-stat-label { font-size: 11px; color: var(--text-tertiary); margin-bottom: 6px; text-transform: uppercase; font-weight: 600; } .tr-stat-label { font-size: 11px; color: var(--text-tertiary); margin-bottom: 6px; font-weight: 600; }
.tr-stat-value { font-size: 22px; font-weight: 700; } .tr-stat-value { font-size: 24px; font-weight: 700; margin-bottom: 4px; }
.tr-stat-value.profit { color: var(--color-down); } .tr-stat-value.profit { color: var(--color-down); }
.tr-stat-value.loss { color: var(--color-up); } .tr-stat-value.loss { color: var(--color-up); }
.tr-stat-sub { font-size: 12px; color: var(--text-tertiary); margin-top: 4px; } .tr-stat-sub { font-size: 12px; color: var(--text-tertiary); margin-top: 4px; }
.tr-sub-page { display: none; } /* 图表容器 */
.tr-sub-page.active { display: block; } .tr-chart-box { background: var(--bg-card); border-radius: var(--radius-card); padding: 20px; margin-bottom: 24px; box-shadow: var(--shadow-sm); }
.tr-chart-title { font-size: 16px; font-weight: 600; color: var(--text-primary); margin-bottom: 16px; }
.tr-chart-container { width: 100%; height: 350px; }
.tr-bubble-container { width: 100%; height: 400px; }
/* 表格 */
.tr-tbl-box { background: var(--bg-card); border-radius: var(--radius-card); padding: 20px; margin-bottom: 24px; box-shadow: var(--shadow-sm); overflow-x: auto; }
.tr-tbl-title { font-size: 16px; font-weight: 600; color: var(--text-primary); margin-bottom: 16px; }
.tr-table { width: 100%; border-collapse: collapse; font-size: 13px; } .tr-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.tr-table th { background: #F5F5F7; padding: 10px 12px; text-align: left; font-weight: 600; color: var(--text-tertiary); font-size: 12px; white-space: nowrap; } .tr-table th { background: #F5F5F7; padding: 12px 8px; text-align: left; font-weight: 600; color: var(--text-tertiary); font-size: 12px; white-space: nowrap; border-bottom: 1px solid rgba(0,0,0,0.06); }
.tr-table td { padding: 9px 12px; border-bottom: 1px solid rgba(0,0,0,0.04); color: var(--text-primary); white-space: nowrap; } .tr-table td { padding: 12px 8px; border-bottom: 1px solid rgba(0,0,0,0.04); color: var(--text-primary); white-space: nowrap; }
.tr-table tbody tr { transition: background 0.15s; } .tr-table tbody tr { transition: background 0.15s; }
.tr-table tbody tr:hover { background: rgba(0,122,255,0.03); } .tr-table tbody tr:hover { background: rgba(0,122,255,0.03); }
.tr-pnl-positive { color: var(--color-down); font-weight: 600; } .tr-pnl-positive { color: var(--color-down); font-weight: 600; }
.tr-pnl-negative { color: var(--color-up); font-weight: 600; } .tr-pnl-negative { color: var(--color-up); font-weight: 600; }
.tr-dir-buy { color: var(--color-down); font-weight: 500; } .tr-link { color: var(--color-brand); cursor: pointer; text-decoration: none; font-weight: 500; }
.tr-dir-sell { color: var(--color-up); font-weight: 500; } .tr-link:hover { text-decoration: underline; }
.tr-pagination { display: flex; justify-content: center; align-items: center; gap: 8px; margin-top: 16px; }
.tr-pagination button { padding: 6px 14px; border-radius: 8px; border: 1px solid rgba(0,0,0,0.08); background: #fff; font-size: 12px; cursor: pointer; transition: all 0.2s; }
.tr-pagination button:hover { background: var(--color-brand); color: #fff; border-color: var(--color-brand); }
.tr-pagination button:disabled { opacity: 0.4; cursor: default; }
.tr-pagination span { font-size: 12px; color: var(--text-tertiary); }
.tr-empty { display: flex; flex-direction: column; align-items: center; justify-content: center; min-height: 300px; gap: 12px; }
.tr-empty-icon { font-size: 56px; opacity: 0.3; }
.tr-empty-text { font-size: 15px; color: var(--text-tertiary); }
.tr-kline-controls { display: flex; gap: 10px; margin-bottom: 16px; align-items: center; } /* K线周期按钮 */
.tr-kline-chart { width: 100%; height: 500px; background: var(--bg-card); border-radius: 16px; box-shadow: var(--shadow-sm); } .tr-kline-tabs { display: flex; gap: 8px; margin-bottom: 16px; align-items: center; }
.tr-chart-container { width: 100%; height: 300px; margin-bottom: 16px; background: var(--bg-card); border-radius: 16px; box-shadow: var(--shadow-sm); } .tr-kline-tab { padding: 6px 14px; border: 1px solid rgba(0,0,0,0.08); background: #fff; border-radius: 6px; cursor: pointer; font-size: 13px; font-weight: 500; color: var(--text-secondary); transition: all 0.2s; font-family: inherit; }
.tr-kline-tab:hover { background: var(--color-brand); color: #fff; border-color: var(--color-brand); }
/* 交易详情弹窗样式 */ .tr-kline-tab.active { background: var(--color-brand); color: #fff; border-color: var(--color-brand); box-shadow: 0 4px 10px rgba(0,122,255,0.2); }
.tr-detail-info { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 12px; margin-bottom: 20px; padding: 16px; background: #F5F5F7; border-radius: 12px; }
.tr-detail-info-item { display: flex; flex-direction: column; gap: 4px; }
.tr-detail-info-label { font-size: 12px; color: var(--text-tertiary); font-weight: 500; }
.tr-detail-info-value { font-size: 14px; font-weight: 600; color: var(--text-primary); }
.tr-detail-info-value.profit { color: var(--color-down); }
.tr-detail-info-value.loss { color: var(--color-up); }
.tr-detail-section { margin-top: 20px; }
.tr-detail-section-title { font-size: 15px; font-weight: 700; color: var(--text-primary); margin-bottom: 12px; padding-left: 8px; border-left: 3px solid var(--accent-gold); }
.tr-detail-ai-content { padding: 16px; background: #F5F5F7; border-radius: 12px; min-height: 100px; font-size: 14px; line-height: 1.8; color: var(--text-primary); white-space: pre-wrap; }
.tr-pair-symbol-link { cursor: pointer; color: var(--accent-gold); text-decoration: none; font-weight: 700; }
.tr-pair-symbol-link:hover { text-decoration: underline; }
.tr-pairs-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); gap: 16px; }
.tr-pair-card { background: var(--bg-card); border-radius: 16px; padding: 18px; box-shadow: var(--shadow-sm); transition: all 0.2s; position: relative; overflow: hidden; }
.tr-pair-card:hover { box-shadow: var(--shadow-md); transform: translateY(-2px); }
.tr-pair-card::before { content: ""; position: absolute; top: 0; left: 0; right: 0; height: 3px; }
.tr-pair-card.profit::before { background: var(--color-down); }
.tr-pair-card.loss::before { background: var(--color-up); }
.tr-pair-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; }
.tr-pair-symbol { font-size: 15px; font-weight: 700; color: var(--text-primary); }
.tr-pair-pnl { font-size: 16px; font-weight: 700; padding: 4px 12px; border-radius: 8px; }
.tr-pair-pnl.profit { color: var(--color-down); background: rgba(52,199,89,0.1); }
.tr-pair-pnl.loss { color: var(--color-up); background: rgba(255,59,48,0.1); }
.tr-pair-info { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; font-size: 12px; }
.tr-pair-info-item { display: flex; justify-content: space-between; padding: 4px 8px; background: #F5F5F7; border-radius: 6px; }
.tr-pair-info-label { color: var(--text-tertiary); }
.tr-pair-info-value { font-weight: 600; color: var(--text-primary); }
.tr-pair-actions { margin-top: 12px; display: flex; gap: 8px; }
.tr-pair-actions button { flex: 1; padding: 8px; border-radius: 8px; border: none; font-size: 12px; font-weight: 600; cursor: pointer; transition: all 0.2s; font-family: inherit; }
.tr-pair-btn-analyze { background: rgba(175,82,222,0.1); color: var(--color-ai); }
.tr-pair-btn-analyze:hover { background: rgba(175,82,222,0.2); }
/* 弹窗通用样式(复用 rv-modal 体系) */
.tr-modal-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); backdrop-filter: blur(8px); z-index: 2000; display: none; align-items: center; justify-content: center; }
.tr-modal-overlay.show { display: flex; }
.tr-modal { background: #fff; border-radius: 20px; width: 90%; max-width: 1000px; max-height: 90vh; overflow-y: auto; box-shadow: 0 25px 50px rgba(0,0,0,0.25); animation: rv-modal-in 0.25s ease; }
.tr-modal-header { display: flex; justify-content: space-between; align-items: center; padding: 20px 24px; border-bottom: 1px solid rgba(0,0,0,0.06); }
.tr-modal-title { font-size: 18px; font-weight: 700; color: var(--text-primary); }
.tr-modal-close { width: 32px; height: 32px; border-radius: 50%; border: none; background: #F5F5F7; font-size: 16px; cursor: pointer; display: flex; align-items: center; justify-content: center; color: var(--text-secondary); transition: all 0.2s; }
.tr-modal-close:hover { background: #E5E5E7; }
.tr-modal-body { padding: 24px; }
/* 弹窗内概览网格 */
.tr-overview-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; margin-bottom: 24px; }
.tr-overview-item { background: #F5F5F7; border-radius: 12px; padding: 16px; text-align: center; }
.tr-overview-label { font-size: 12px; color: var(--text-tertiary); margin-bottom: 4px; }
.tr-overview-value { font-size: 20px; font-weight: 700; color: var(--text-primary); }
.tr-overview-value.profit { color: var(--color-down); }
.tr-overview-value.loss { color: var(--color-up); }
/* AI诊断框 */
.tr-ai-box { background: linear-gradient(135deg, #f0f7ff 0%, #e6f4ff 100%); border: 1px solid #bae0ff; border-radius: 12px; padding: 20px; margin-bottom: 24px; }
.tr-ai-title { font-size: 16px; font-weight: 600; color: #0958d9; margin-bottom: 12px; display: flex; align-items: center; gap: 8px; }
.tr-ai-content { font-size: 14px; color: var(--text-primary); line-height: 1.8; white-space: pre-wrap; }
.tr-ai-tag { display: inline-block; background: #e6f4ff; color: #0958d9; padding: 2px 8px; border-radius: 4px; font-size: 12px; margin-right: 8px; margin-bottom: 8px; }
/* 品种详情弹窗内K线 */
.tr-kline-chart { width: 100%; height: 400px; }
/* 每日详情 - 品种分区 */
.tr-variety-section { margin-top: 24px; border-top: 1px solid rgba(0,0,0,0.06); padding-top: 20px; }
/* 批次管理 */
.tr-batch-item { display: flex; justify-content: space-between; align-items: center; padding: 14px 16px; background: #F5F5F7; border-radius: 12px; margin-bottom: 10px; } .tr-batch-item { display: flex; justify-content: space-between; align-items: center; padding: 14px 16px; background: #F5F5F7; border-radius: 12px; margin-bottom: 10px; }
.tr-batch-info { flex: 1; } .tr-batch-info { flex: 1; }
.tr-batch-file { font-size: 14px; font-weight: 600; color: var(--text-primary); margin-bottom: 4px; } .tr-batch-file { font-size: 14px; font-weight: 600; color: var(--text-primary); margin-bottom: 4px; }
@ -589,9 +577,17 @@
.tr-batch-delete { padding: 6px 14px; border-radius: 8px; border: none; background: rgba(255,59,48,0.1); color: var(--color-up); font-size: 12px; font-weight: 600; cursor: pointer; transition: all 0.2s; } .tr-batch-delete { padding: 6px 14px; border-radius: 8px; border: none; background: rgba(255,59,48,0.1); color: var(--color-up); font-size: 12px; font-weight: 600; cursor: pointer; transition: all 0.2s; }
.tr-batch-delete:hover { background: rgba(255,59,48,0.2); } .tr-batch-delete:hover { background: rgba(255,59,48,0.2); }
/* 空状态 */
.tr-empty { display: flex; flex-direction: column; align-items: center; justify-content: center; min-height: 200px; gap: 12px; }
.tr-empty-icon { font-size: 56px; opacity: 0.3; }
.tr-empty-text { font-size: 15px; color: var(--text-tertiary); }
@media (max-width: 1200px) {
.tr-stats-grid { grid-template-columns: repeat(3, 1fr); }
}
@media (max-width: 768px) { @media (max-width: 768px) {
.tr-stats-grid { grid-template-columns: repeat(2, 1fr); } .tr-stats-grid { grid-template-columns: repeat(2, 1fr); }
.tr-pairs-grid { grid-template-columns: 1fr; } .tr-overview-grid { grid-template-columns: repeat(2, 1fr); }
} }
@media (max-width: 768px) { @media (max-width: 768px) {
@ -1004,118 +1000,148 @@
</div> </div>
</div> </div>
<!-- 子导航 --> <!-- 6个统计卡片 -->
<nav class="tr-sub-nav">
<a href="#" class="tr-sub-nav-item active" data-sub="records">交易记录</a>
<a href="#" class="tr-sub-nav-item" data-sub="daily">每日分析</a>
<a href="#" class="tr-sub-nav-item" data-sub="variety">品种汇总</a>
<a href="#" class="tr-sub-nav-item" data-sub="pairs">逐笔分析</a>
</nav>
<!-- 统计卡片 -->
<div id="tr-stats-grid" class="tr-stats-grid"></div> <div id="tr-stats-grid" class="tr-stats-grid"></div>
<!-- 子页面: 交易记录 --> <!-- 账户权益曲线 -->
<div id="tr-sub-records" class="tr-sub-page active"> <div class="tr-chart-box">
<div id="tr-records-table-wrap"> <div class="tr-chart-title">账户权益曲线(累计净盈亏 + 每日盈亏)</div>
<table class="tr-table"> <div class="tr-chart-container" id="tr-equity-chart"></div>
<thead>
<tr>
<th>日期</th><th>时间</th><th>类型</th><th>合约</th><th>品种</th>
<th>买卖</th><th>开平</th><th>价格</th><th>手数</th>
<th>成交额</th><th>平仓盈亏</th><th>手续费</th><th>文件</th>
</tr>
</thead>
<tbody id="tr-records-body"></tbody>
</table>
</div>
<div id="tr-records-pagination" class="tr-pagination"></div>
<div id="tr-records-empty" class="tr-empty" style="display:none;">
<div class="tr-empty-icon">📥</div>
<div class="tr-empty-text">暂无交易记录,请先导入结算单文件</div>
</div>
</div> </div>
<!-- 子页面: 每日分析 --> <!-- 品种盈亏分布气泡图 -->
<div id="tr-sub-daily" class="tr-sub-page"> <div class="tr-chart-box">
<div id="tr-daily-chart" class="tr-chart-container"></div> <div class="tr-chart-title">品种盈亏分布(气泡大小=手续费占比,颜色=净盈亏)</div>
<div style="overflow-x:auto;"> <div class="tr-bubble-container" id="tr-bubble-chart"></div>
<table class="tr-table"> </div>
<thead>
<tr> <!-- 每日交易详情表 -->
<th>日期</th><th>交易笔数</th><th>净盈亏</th><th>手续费</th> <div class="tr-tbl-box">
<th>盈利笔</th><th>亏损笔</th><th>胜率</th> <div class="tr-tbl-title">每日交易详情(点击日期查看详情)</div>
<th>最大盈利</th><th>最大亏损</th><th>品种数</th> <table class="tr-table">
</tr> <thead>
</thead> <tr>
<tbody id="tr-daily-body"></tbody> <th>日期</th><th>笔数</th><th>平仓盈亏</th><th>手续费</th>
</table> <th>净盈亏</th><th>盈利笔</th><th>亏损笔</th><th>胜率</th>
</div> <th>最大盈利</th><th>最大亏损</th><th>品种数</th>
</tr>
</thead>
<tbody id="tr-daily-body"></tbody>
</table>
<div id="tr-daily-empty" class="tr-empty" style="display:none;"> <div id="tr-daily-empty" class="tr-empty" style="display:none;">
<div class="tr-empty-icon">📊</div> <div class="tr-empty-icon">📊</div>
<div class="tr-empty-text">暂无每日数据</div> <div class="tr-empty-text">暂无交易数据,请先导入结算单</div>
</div> </div>
</div> </div>
<!-- 子页面: 品种汇总 --> <!-- 品种盈亏排行表 -->
<div id="tr-sub-variety" class="tr-sub-page"> <div class="tr-tbl-box">
<div style="overflow-x:auto;"> <div class="tr-tbl-title">品种盈亏排行(点击查看品种详情)</div>
<table class="tr-table"> <table class="tr-table">
<thead> <thead>
<tr> <tr>
<th>品种</th><th>名称</th><th>交易笔数</th><th>净盈亏</th> <th>排名</th><th>代码</th><th>名称</th><th>净盈亏</th>
<th>平仓盈亏</th><th>手续费</th><th>总手数</th> <th>平仓盈亏</th><th>手续费</th><th>胜率</th>
<th>买入</th><th>卖出</th><th>胜率</th> <th>盈亏比</th><th>笔数</th><th>操作</th>
</tr> </tr>
</thead> </thead>
<tbody id="tr-variety-body"></tbody> <tbody id="tr-variety-body"></tbody>
</table> </table>
</div>
<div id="tr-variety-empty" class="tr-empty" style="display:none;"> <div id="tr-variety-empty" class="tr-empty" style="display:none;">
<div class="tr-empty-icon">📋</div> <div class="tr-empty-icon">📋</div>
<div class="tr-empty-text">暂无品种数据</div> <div class="tr-empty-text">暂无品种数据</div>
</div> </div>
</div> </div>
</div>
<!-- 子页面: 逐笔分析 --> <!-- 每日详情弹窗 -->
<div id="tr-sub-pairs" class="tr-sub-page"> <div id="tr-daily-modal" class="rv-modal-overlay" onclick="if(event.target===this)trCloseDailyModal()">
<div id="tr-pairs-grid" class="tr-pairs-grid"></div> <div class="rv-modal" style="max-width:1000px;">
<div id="tr-pairs-empty" class="tr-empty" style="display:none;"> <div class="rv-modal-header">
<div class="tr-empty-icon">🔍</div> <span class="rv-modal-title" id="tr-daily-modal-title">每日详情</span>
<div class="tr-empty-text">暂无配对交易数据</div> <button class="rv-modal-close" onclick="trCloseDailyModal()"></button>
</div>
<div class="rv-modal-body">
<div id="tr-daily-modal-stats" class="tr-overview-grid"></div>
<div class="tr-ai-box">
<div class="tr-ai-title">🤖 AI 交易诊断报告</div>
<div class="tr-ai-content" id="tr-daily-ai-content"></div>
</div>
<div class="tr-variety-section">
<div class="tr-tbl-title" style="margin-bottom:12px">当日品种盈亏统计</div>
<div class="tr-chart-container" id="tr-daily-variety-chart" style="height:280px;margin-bottom:16px;"></div>
<div class="tr-tbl-box" style="padding:0;box-shadow:none;">
<table class="tr-table">
<thead><tr><th>品种</th><th>笔数</th><th>平仓盈亏</th><th>手续费</th><th>净盈亏</th><th>胜率</th><th>操作</th></tr></thead>
<tbody id="tr-daily-variety-body"></tbody>
</table>
</div>
</div>
<div class="tr-tbl-box" style="margin-top:24px;padding:0;box-shadow:none;">
<div class="tr-tbl-title">当日交易流水</div>
<table class="tr-table">
<thead><tr><th>时间</th><th>品种</th><th>方向</th><th>开仓价</th><th>手数</th><th>盈亏</th><th>手续费</th></tr></thead>
<tbody id="tr-daily-trades-body"></tbody>
</table>
</div>
</div>
</div>
</div>
<!-- 品种详情弹窗 -->
<div id="tr-variety-modal" class="rv-modal-overlay" onclick="if(event.target===this)trCloseVarietyModal()">
<div class="rv-modal" style="max-width:1000px;">
<div class="rv-modal-header">
<span class="rv-modal-title" id="tr-variety-modal-title">品种详情</span>
<button class="rv-modal-close" onclick="trCloseVarietyModal()"></button>
</div>
<div class="rv-modal-body">
<div id="tr-variety-modal-stats" class="tr-overview-grid"></div>
<div class="tr-chart-box" style="margin-bottom:16px;">
<div class="tr-kline-tabs">
<button class="tr-kline-tab active" data-period="daily" onclick="trSwitchVarietyKlinePeriod('daily')">日线</button>
<button class="tr-kline-tab" data-period="60min" onclick="trSwitchVarietyKlinePeriod('60min')">60分钟</button>
<button class="tr-kline-tab" data-period="15min" onclick="trSwitchVarietyKlinePeriod('15min')">15分钟</button>
<button class="tr-kline-tab" data-period="5min" onclick="trSwitchVarietyKlinePeriod('5min')">5分钟</button>
<button id="tr-variety-reload-kline" class="tr-btn tr-btn-secondary" style="margin-left:auto;">刷新K线</button>
</div>
<div class="tr-kline-chart" id="tr-variety-kline-chart"></div>
</div>
<div class="tr-tbl-box" style="padding:0;box-shadow:none;">
<div class="tr-tbl-title">该品种交易记录</div>
<table class="tr-table">
<thead><tr><th>开仓时间</th><th>方向</th><th>开仓价</th><th>平仓价</th><th>盈亏</th><th>手续费</th><th>操作</th></tr></thead>
<tbody id="tr-variety-trades-body"></tbody>
</table>
</div>
</div> </div>
</div> </div>
</div> </div>
<!-- 交易详情弹窗 --> <!-- 交易详情弹窗 -->
<div id="tr-detail-modal" class="rv-modal-overlay" onclick="if(event.target===this)trCloseDetailModal()"> <div id="tr-detail-modal" class="rv-modal-overlay" onclick="if(event.target===this)trCloseDetailModal()">
<div class="rv-modal" style="max-width:1000px;"> <div class="rv-modal" style="max-width:700px;">
<div class="rv-modal-header"> <div class="rv-modal-header">
<span class="rv-modal-title" id="tr-detail-title">交易详情</span> <span class="rv-modal-title" id="tr-detail-title">交易详情</span>
<button class="rv-modal-close" onclick="trCloseDetailModal()"></button> <button class="rv-modal-close" onclick="trCloseDetailModal()"></button>
</div> </div>
<div class="rv-modal-body"> <div class="rv-modal-body">
<!-- 交易信息 --> <div id="tr-detail-info" class="tr-overview-grid"></div>
<div id="tr-detail-info" class="tr-detail-info"></div> <div class="tr-chart-box" style="margin-top:20px;">
<!-- K线图 --> <div class="tr-chart-title">K线走势与买卖点</div>
<div class="tr-detail-section"> <div class="tr-kline-tabs">
<h4 class="tr-detail-section-title">K线走势与买卖点</h4>
<div class="tr-kline-tabs" style="margin-bottom:12px;display:flex;gap:8px;flex-wrap:wrap;">
<button class="tr-kline-tab active" data-period="daily" onclick="trSwitchKlinePeriod('daily')">日线</button> <button class="tr-kline-tab active" data-period="daily" onclick="trSwitchKlinePeriod('daily')">日线</button>
<button class="tr-kline-tab" data-period="60min" onclick="trSwitchKlinePeriod('60min')">60分钟</button> <button class="tr-kline-tab" data-period="60min" onclick="trSwitchKlinePeriod('60min')">60分钟</button>
<button class="tr-kline-tab" data-period="15min" onclick="trSwitchKlinePeriod('15min')">15分钟</button> <button class="tr-kline-tab" data-period="15min" onclick="trSwitchKlinePeriod('15min')">15分钟</button>
<button class="tr-kline-tab" data-period="5min" onclick="trSwitchKlinePeriod('5min')">5分钟</button> <button class="tr-kline-tab" data-period="5min" onclick="trSwitchKlinePeriod('5min')">5分钟</button>
<button id="tr-detail-reload-kline" class="tr-btn tr-btn-secondary" style="margin-left:auto;">刷新K线</button> <button id="tr-detail-reload-kline" class="tr-btn tr-btn-secondary" style="margin-left:auto;">刷新K线</button>
</div> </div>
<div id="tr-detail-kline" style="width:100%;height:400px;"></div> <div id="tr-detail-kline" style="width:100%;height:300px;"></div>
</div> </div>
<!-- AI分析 --> <div style="margin-top:20px;text-align:center;">
<div class="tr-detail-section"> <button id="tr-detail-ai-btn" class="tr-btn tr-btn-primary">开始AI分析</button>
<h4 class="tr-detail-section-title">AI 交易分析</h4>
<div id="tr-detail-ai-content" class="tr-detail-ai-content">
<button id="tr-detail-ai-btn" class="tr-btn tr-btn-primary">开始AI分析</button>
</div>
</div> </div>
<div id="tr-detail-ai-content" class="tr-ai-box" style="display:none;margin-top:16px;"></div>
</div> </div>
</div> </div>
</div> </div>
@ -1146,19 +1172,6 @@
</div> </div>
</div> </div>
</div> </div>
<!-- AI分析结果弹窗 -->
<div id="tr-analysis-modal" class="rv-modal-overlay" onclick="if(event.target===this)trCloseAnalysisModal()">
<div class="rv-modal" style="max-width:700px;">
<div class="rv-modal-header">
<span class="rv-modal-title" id="tr-analysis-title">AI 交易分析</span>
<button class="rv-modal-close" onclick="trCloseAnalysisModal()"></button>
</div>
<div class="rv-modal-body">
<div id="tr-analysis-content" style="white-space:pre-wrap;font-size:14px;line-height:1.8;color:var(--text-primary);"></div>
</div>
</div>
</div>
</div> </div>
<!-- 复盘品种详情弹窗(移到 review-view 外部) --> <!-- 复盘品种详情弹窗(移到 review-view 外部) -->

File diff suppressed because it is too large Load Diff

@ -0,0 +1,409 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>期货智析 - 交易复盘</title>
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
<style>
*{margin:0;padding:0;box-sizing:border-box}
body{font-family:-apple-system,BlinkMacSystemFont,sans-serif;background:#f5f7fa;color:#333;line-height:1.6}
.header{background:#fff;border-bottom:1px solid #e8e8e8;padding:0 24px;height:56px;display:flex;align-items:center;justify-content:space-between;position:sticky;top:0;z-index:100}
.header-left{display:flex;align-items:center;gap:8px}
.logo{font-size:16px;font-weight:600}
.nav-tabs{display:flex;gap:24px}
.nav-tab{padding:16px 0;font-size:14px;color:#666;cursor:pointer;border-bottom:2px solid transparent}
.nav-tab.active{color:#1890ff;border-bottom-color:#1890ff;font-weight:500}
.header-right{font-size:12px;color:#999}
.main{max-width:1400px;margin:0 auto;padding:24px}
.toolbar{display:flex;align-items:center;gap:12px;margin-bottom:20px;flex-wrap:wrap}
.btn{padding:8px 16px;border:none;border-radius:6px;font-size:14px;cursor:pointer;display:flex;align-items:center;gap:6px}
.btn-primary{background:#1890ff;color:#fff}
.btn-danger{background:#fff;color:#ff4d4f;border:1px solid #ffccc7}
.btn-default{background:#fff;color:#666;border:1px solid #d9d9d9}
.date-picker{padding:8px 12px;border:1px solid #d9d9d9;border-radius:6px;font-size:14px}
.date-range{display:flex;align-items:center;gap:8px}
.toolbar-right{margin-left:auto;display:flex;gap:12px}
.cards{display:grid;grid-template-columns:repeat(6,1fr);gap:16px;margin-bottom:24px}
.card{background:#fff;border-radius:8px;padding:16px;text-align:center;box-shadow:0 1px 2px rgba(0,0,0,0.05)}
.card-label{font-size:12px;color:#999;margin-bottom:8px}
.card-value{font-size:24px;font-weight:600;margin-bottom:4px}
.card-sub{font-size:12px;color:#999}
.green{color:#52c41a}
.red{color:#ff4d4f}
.box{background:#fff;border-radius:8px;padding:20px;margin-bottom:24px;box-shadow:0 1px 2px rgba(0,0,0,0.05)}
.box-title{font-size:16px;font-weight:500;margin-bottom:16px}
.chart{width:100%;height:350px}
.tbl-box{background:#fff;border-radius:8px;padding:20px;margin-bottom:24px;box-shadow:0 1px 2px rgba(0,0,0,0.05);overflow-x:auto}
table{width:100%;border-collapse:collapse;font-size:13px}
th{background:#fafafa;padding:12px 8px;text-align:left;font-weight:500;color:#666;border-bottom:1px solid #e8e8e8;white-space:nowrap}
td{padding:12px 8px;border-bottom:1px solid #f0f0f0;white-space:nowrap}
tr:hover{background:#f5f7fa}
.link{color:#1890ff;cursor:pointer;text-decoration:none}
.modal-bg{display:none;position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.5);z-index:1000;justify-content:center;align-items:center}
.modal-bg.show{display:flex}
#vmod,#tmod{z-index:1001}
.modal{background:#fff;border-radius:12px;width:90%;max-width:1000px;max-height:90vh;overflow-y:auto}
.modal-hd{padding:20px 24px;border-bottom:1px solid #e8e8e8;display:flex;justify-content:space-between;align-items:center}
.modal-close{width:32px;height:32px;border:none;background:#f5f5f5;border-radius:50%;cursor:pointer;font-size:18px}
.modal-bd{padding:24px}
.ov{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;margin-bottom:24px}
.ov-item{background:#f5f7fa;border-radius:8px;padding:16px;text-align:center}
.ov-label{font-size:12px;color:#999}
.ov-val{font-size:20px;font-weight:600}
.ktb{display:flex;gap:8px;margin-bottom:16px}
.kbtn{padding:6px 12px;border:1px solid #d9d9d9;background:#fff;border-radius:4px;cursor:pointer;font-size:13px}
.kbtn.active{background:#1890ff;color:#fff;border-color:#1890ff}
.bubble{width:100%;height:400px}
.ai-box{background:linear-gradient(135deg, #f0f7ff 0%, #e6f4ff 100%);border:1px solid #bae0ff;border-radius:8px;padding:20px;margin-bottom:24px;position:relative}
.ai-title{font-size:16px;font-weight:600;color:#0958d9;margin-bottom:12px;display:flex;align-items:center;gap:8px}
.ai-content{font-size:14px;color:#333;line-height:1.8}
.ai-tag{display:inline-block;background:#e6f4ff;color:#0958d9;padding:2px 8px;border-radius:4px;font-size:12px;margin-right:8px;margin-bottom:8px}
.variety-sec{margin-top:24px;border-top:1px solid #e8e8e8;padding-top:20px}
@media(max-width:1200px){.cards{grid-template-columns:repeat(3,1fr)}}
@media(max-width:768px){.cards{grid-template-columns:repeat(2,1fr)}}
</style>
</head>
<body>
<div class="header">
<div class="header-left"><div class="logo">&#9670; 期货智析</div></div>
<div class="nav-tabs"><div class="nav-tab">品种分析</div><div class="nav-tab">自选</div><div class="nav-tab">复盘计划</div><div class="nav-tab active">交易复盘</div></div>
<div class="header-right"><span style="color:#52c41a">&#9679;</span> LIVE &nbsp; 2026-06-21 22:41:23</div>
</div>
<div class="main">
<div class="toolbar">
<button class="btn btn-primary">导入结算单</button><button class="btn btn-primary">批量导入</button>
<div class="date-range"><input type="date" class="date-picker" value="2026-06-01"><span>~</span><input type="date" class="date-picker" value="2026-06-12"></div>
<button class="btn btn-default">查询</button>
<div class="toolbar-right"><button class="btn btn-danger">删除当日</button><button class="btn btn-default">批次管理</button></div>
</div>
<div class="cards">
<div class="card"><div class="card-label">总盈亏</div><div class="card-value red">-656.43</div><div class="card-sub">平仓盈亏</div></div>
<div class="card"><div class="card-label">净盈亏</div><div class="card-value red">-3502.86</div><div class="card-sub">扣除手续费后</div></div>
<div class="card"><div class="card-label">胜率</div><div class="card-value">14.2%</div><div class="card-sub">盈亏比: 5.72</div></div>
<div class="card"><div class="card-label">交易笔数</div><div class="card-value">398</div><div class="card-sub">10个交易日</div></div>
<div class="card"><div class="card-label">最大回撤</div><div class="card-value red">-1768.27</div><div class="card-sub">2026-06-03</div></div>
<div class="card"><div class="card-label">日均盈亏</div><div class="card-value red">-65.64</div><div class="card-sub">最大连盈5/连亏5</div></div>
</div>
<div class="box"><div class="box-title">账户权益曲线(累计净盈亏 + 每日盈亏)</div><div class="chart" id="eq"></div></div>
<div class="box"><div class="box-title">品种盈亏分布(气泡大小=手续费占比,颜色=净盈亏)</div><div class="bubble" id="bb"></div></div>
<div class="tbl-box"><div class="box-title">每日交易详情(点击日期查看详情)</div>
<table><thead><tr><th>日期</th><th>笔数</th><th>平仓盈亏</th><th>手续费</th><th>净盈亏</th><th>盈利笔</th><th>亏损笔</th><th>胜率</th><th>最大盈利</th><th>最大亏损</th><th>品种数</th></tr></thead><tbody id="dtb"></tbody></table></div>
<div class="tbl-box"><div class="box-title">品种盈亏排行(点击查看品种详情)</div>
<table><thead><tr><th>排名</th><th>代码</th><th>名称</th><th>净盈亏</th><th>平仓盈亏</th><th>手续费</th><th>胜率</th><th>盈亏比</th><th>笔数</th><th>操作</th></tr></thead><tbody id="vtb"></tbody></table></div>
</div>
<div class="modal-bg" id="vmod">
<div class="modal">
<div class="modal-hd"><div class="box-title" id="vtitle">品种详情</div><button class="modal-close" onclick="cm('vmod')">&times;</button></div>
<div class="modal-bd">
<div class="ov">
<div class="ov-item"><div class="ov-label">净盈亏</div><div class="ov-val red" id="vn">-</div></div>
<div class="ov-item"><div class="ov-label">胜率</div><div class="ov-val" id="vw">-</div></div>
<div class="ov-item"><div class="ov-label">盈亏比</div><div class="ov-val" id="vr">-</div></div>
<div class="ov-item"><div class="ov-label">交易笔数</div><div class="ov-val" id="vc">-</div></div>
<div class="ov-item"><div class="ov-label">手续费</div><div class="ov-val" id="vf">-</div></div>
<div class="ov-item"><div class="ov-label">最大单笔盈利</div><div class="ov-val green" id="vm">-</div></div>
</div>
<div class="box" style="margin-bottom:16px">
<div class="ktb"><button class="kbtn active">日线</button><button class="kbtn">60分钟</button><button class="kbtn">15分钟</button><button class="kbtn">5分钟</button><button class="btn btn-default" style="margin-left:auto">刷新K线</button></div>
<div class="chart" id="kc" style="height:400px"></div>
</div>
<div class="tbl-box"><div class="box-title">该品种交易记录</div>
<table><thead><tr><th>开仓时间</th><th>方向</th><th>开仓价</th><th>平仓价</th><th>盈亏</th><th>手续费</th><th>操作</th></tr></thead><tbody id="vttb"></tbody></table></div>
</div></div></div>
<div class="modal-bg" id="tmod">
<div class="modal" style="max-width:700px">
<div class="modal-hd"><div class="box-title" id="ttitle">交易详情</div><button class="modal-close" onclick="cm('tmod')">&times;</button></div>
<div class="modal-bd">
<div class="ov">
<div class="ov-item"><div class="ov-label">品种</div><div class="ov-val" id="tv">-</div></div>
<div class="ov-item"><div class="ov-label">方向</div><div class="ov-val red" id="td">-</div></div>
<div class="ov-item"><div class="ov-label">开仓日期</div><div class="ov-val" id="tod">-</div></div>
<div class="ov-item"><div class="ov-label">开仓时间</div><div class="ov-val" id="tot">-</div></div>
<div class="ov-item"><div class="ov-label">开仓价</div><div class="ov-val" id="top">-</div></div>
<div class="ov-item"><div class="ov-label">平仓价</div><div class="ov-val" id="tcp">-</div></div>
<div class="ov-item"><div class="ov-label">手数</div><div class="ov-val" id="tvol">-</div></div>
<div class="ov-item"><div class="ov-label">手续费</div><div class="ov-val" id="tfee">-</div></div>
<div class="ov-item"><div class="ov-label">净盈亏</div><div class="ov-val red" id="tpnl">-</div></div>
</div>
<div class="box" style="margin-top:20px"><div class="box-title">K线走势与买卖点</div><div class="chart" id="tkc" style="height:300px"></div></div>
<div style="margin-top:20px;text-align:center"><button class="btn btn-primary">开始AI分析</button></div>
</div></div></div>
<div class="modal-bg" id="dmod">
<div class="modal" style="max-width:1000px">
<div class="modal-hd"><div class="box-title" id="dtitle">每日详情</div><button class="modal-close" onclick="cm('dmod')">&times;</button></div>
<div class="modal-bd">
<div class="ov">
<div class="ov-item"><div class="ov-label">平仓盈亏</div><div class="ov-val red" id="dpnl">-</div></div>
<div class="ov-item"><div class="ov-label">净盈亏</div><div class="ov-val red" id="dnpnl">-</div></div>
<div class="ov-item"><div class="ov-label">胜率</div><div class="ov-val" id="dwr">-</div></div>
<div class="ov-item"><div class="ov-label">手续费</div><div class="ov-val" id="dfee">-</div></div>
<div class="ov-item"><div class="ov-label">最大盈利</div><div class="ov-val green" id="dmp">-</div></div>
<div class="ov-item"><div class="ov-label">最大亏损</div><div class="ov-val red" id="dml">-</div></div>
</div>
<div class="ai-box">
<div class="ai-title">&#129302; AI 交易诊断报告</div>
<div class="ai-content" id="dai"></div>
</div>
<div class="variety-sec">
<div class="box-title" style="margin-bottom:12px">当日品种盈亏统计(点击品种名查看详情)</div>
<div class="chart" id="dv_chart" style="height:280px;margin-bottom:16px"></div>
<div class="tbl-box"><table><thead><tr><th>品种</th><th>笔数</th><th>平仓盈亏</th><th>手续费</th><th>净盈亏</th><th>胜率</th><th>操作</th></tr></thead><tbody id="dvtb"></tbody></table></div>
</div>
<div class="tbl-box" style="margin-top:24px"><div class="box-title">当日交易流水</div>
<table><thead><tr><th>时间</th><th>品种</th><th>方向</th><th>开仓价</th><th>平仓价</th><th>盈亏</th><th>手续费</th></tr></thead><tbody id="dttb"></tbody></table></div>
</div></div></div>
<script>
var D=[
{d:"06-01",fd:"2026-06-01",t:33,p:-1042.18,c:982.18,w:7,l:25,wr:"21.9%",mp:1271.67,ml:-1128.42,v:8},
{d:"06-02",fd:"2026-06-02",t:46,p:-14.49,c:239.49,w:5,l:32,wr:"13.5%",mp:487.48,ml:-465.71,v:8},
{d:"06-03",fd:"2026-06-03",t:40,p:-1768.27,c:113.27,w:5,l:30,wr:"14.3%",mp:149.99,ml:-1512.02,v:8},
{d:"06-04",fd:"2026-06-04",t:64,p:-797.55,c:367.55,w:5,l:42,wr:"10.6%",mp:387.98,ml:-1128.78,v:8},
{d:"06-05",fd:"2026-06-05",t:44,p:-749.88,c:249.88,w:1,l:31,wr:"3.1%",mp:221.09,ml:-778.98,v:7},
{d:"06-08",fd:"2026-06-08",t:37,p:512.71,c:207.29,w:1,l:27,wr:"3.6%",mp:711.09,ml:-45.6,v:5},
{d:"06-09",fd:"2026-06-09",t:46,p:544.93,c:175.07,w:9,l:29,wr:"23.7%",mp:320,ml:-270,v:11},
{d:"06-10",fd:"2026-06-10",t:33,p:1497.85,c:212.15,w:6,l:21,wr:"22.2%",mp:468.04,ml:-45.6,v:9},
{d:"06-11",fd:"2026-06-11",t:34,p:960.64,c:169.36,w:4,l:24,wr:"14.3%",mp:1315.44,ml:-370,v:9},
{d:"06-12",fd:"2026-06-12",t:21,p:199.81,c:130.19,w:3,l:18,wr:"14.3%",mp:154.11,ml:-30.4,v:7}
];
var V=[
{c:"BU",n:"沥青",np:2495.94,p:2530,f:34.06,wr:"41.7%",pr:3.1,t:24},
{c:"MA",n:"甲醇",np:997.28,p:1090,f:92.72,wr:"50%",pr:2.5,t:8},
{c:"P",n:"棕榈油",np:419.52,p:480,f:60.48,wr:"37.5%",pr:1.8,t:24},
{c:"RU",n:"橡胶",np:46.97,p:50,f:3.03,wr:"50%",pr:1.2,t:2},
{c:"PS",n:"多晶硅",np:-4.1,p:0,f:4.1,wr:"0%",pr:0,t:1},
{c:"LC",n:"碳酸锂",np:-12.2,p:0,f:12.2,wr:"0%",pr:0,t:6},
{c:"SH",n:"烧碱",np:-11.52,p:0,f:11.52,wr:"25%",pr:0,t:4},
{c:"AG",n:"沪银",np:-32.8,p:0,f:32.8,wr:"0%",pr:0,t:28},
{c:"NI",n:"沪镍",np:-29.45,p:0,f:29.45,wr:"0%",pr:0,t:13},
{c:"AO",n:"氧化铝",np:-26.79,p:20,f:46.79,wr:"37.5%",pr:0.5,t:8},
{c:"CF",n:"棉花",np:-209.63,p:-175,f:34.63,wr:"33.3%",pr:0.7,t:15},
{c:"SC",n:"原油",np:-161.6,p:0,f:161.6,wr:"0%",pr:0,t:31},
{c:"EC",n:"集运欧线",np:-127.42,p:575,f:702.42,wr:"25%",pr:1.5,t:4},
{c:"CU",n:"沪铜",np:-95.95,p:0,f:95.95,wr:"0%",pr:0,t:26},
{c:"SN",n:"沪锡",np:-71.3,p:0,f:71.3,wr:"0%",pr:0,t:78},
{c:"OI",n:"菜油",np:-752.62,p:-690,f:62.62,wr:"19.4%",pr:0.6,t:31},
{c:"MO",n:"锰硅",np:-820.8,p:0,f:820.8,wr:"0%",pr:0,t:47},
{c:"TA",n:"PTA",np:-276.02,p:-270,f:6.02,wr:"0%",pr:0,t:3},
{c:"AP",n:"苹果",np:-315.04,p:-290,f:25.04,wr:"0%",pr:0,t:2},
{c:"JM",n:"焦煤",np:-2457.42,p:-2000,f:457.42,wr:"18.8%",pr:0.82,t:16}
];
V.sort(function(a,b){return b.np-a.np});
// Seeded RNG for consistent mock data
function mulberry32(a) {
return function() {
var t = a += 0x6D2B79F5;
t = Math.imul(t ^ t >>> 15, t | 1);
t ^= t + Math.imul(t ^ t >>> 7, t | 61);
return ((t ^ t >>> 14) >>> 0) / 4294967296;
}
}
function hashDate(str) {
var h = 0;
for (var i = 0; i < str.length; i++) {
h = Math.imul(31, h) + str.charCodeAt(i) | 0;
}
return h;
}
function genDailyVariety(date) {
var seed = hashDate(date);
var rand = mulberry32(seed);
var dayData = D.filter(function(d){return d.fd===date})[0];
var total_p = dayData.p;
var total_c = dayData.c;
var result = [];
V.forEach(function(v) {
if (rand() > 0.3) {
var factor = (rand() - 0.5) * 2;
var pnl = total_p * factor * (rand() * 2);
var fee = Math.max(0, total_c * (rand() * 0.1));
var trades = Math.floor(rand() * 5) + 1;
var wins = Math.floor(rand() * trades);
result.push({ c: v.c, n: v.n, pnl: pnl, fee: fee, trades: trades, wins: wins, winRate: Math.round((wins / trades) * 100) + '%' });
}
});
result.sort(function(a,b){return b.pnl-a.pnl});
return result;
}
function renderDailyVariety(date) {
var data = genDailyVariety(date);
var tbody = document.getElementById("dvtb");
tbody.innerHTML = "";
var categories = data.map(function(x){return x.n});
var values = data.map(function(x){return x.pnl});
if(data.length===0){tbody.innerHTML="<tr><td colspan='7' style='text-align:center'>当日无品种交易数据</td></tr>";return;}
data.forEach(function(item) {
var pnlClass = item.pnl >= 0 ? 'green' : 'red';
var pnlStr = item.pnl >= 0 ? '+' + item.pnl.toFixed(2) : item.pnl.toFixed(2);
tbody.innerHTML += "<tr>" +
"<td><span class='link' onclick='ov(V.findIndex(function(v){return v.c==\""+item.c+"\"}), "+JSON.stringify(item.c)+", "+JSON.stringify(item.n)+")'>"+item.c+"</span></td>" +
"<td>"+item.trades+"</td><td class='"+pnlClass+"'>"+pnlStr+"</td><td>"+item.fee.toFixed(2)+"</td><td class='"+pnlClass+"'>"+pnlStr+"</td><td>"+item.winRate+"</td>" +
"<td><span class='link' onclick='ot("+JSON.stringify(item.n)+", \"做多\")'>详情</span></td></tr>";
});
var chartDom = document.getElementById("dv_chart");
if (dvc) dvc.dispose();
dvc = echarts.init(chartDom);
var barData = values.map(function(v){return{value:v,itemStyle:{color:v>=0?'#52c41a':'#ff4d4f'}}});
dvc.setOption({
tooltip: { trigger: 'axis', formatter: '{b}: {c}元' },
grid: {left: '3%', right: '4%', bottom: '12%', containLabel: true},
xAxis: { type: 'category', data: categories, axisLabel: {interval: 0, rotate: 30} },
yAxis: {type: 'value', name: '盈亏'},
series: [{ data: barData, type: 'bar', label: {show: true, position: 'top', fontSize: 10, formatter: function(p){return p.value>=0?'+':''+p.value.toFixed(0)}} }]
});
}
function rd(){
document.getElementById("dtb").innerHTML=D.map(function(x){
var np=x.p-x.c;
return "<tr style='cursor:pointer' onclick='od("+JSON.stringify(x.fd)+")'><td class='link'>"+x.fd+"</td><td>"+x.t+"</td><td class='"+(x.p>=0?"green":"red")+"'>"+(x.p>=0?"+":"")+x.p.toFixed(2)+"</td><td>"+x.c.toFixed(2)+"</td><td class='"+(np>=0?"green":"red")+"'>"+(np>=0?"+":"")+np.toFixed(2)+"</td><td>"+x.w+"</td><td>"+x.l+"</td><td>"+x.wr+"</td><td class='green'>+"+x.mp.toFixed(2)+"</td><td class='red'>"+x.ml.toFixed(2)+"</td><td>"+x.v+"</td></tr>";
}).join("");
}
function rv(){
document.getElementById("vtb").innerHTML=V.map(function(x,i){
var ic=i<3?["","&#129352;","&#129353;"][i]:(i+1);
var pr=x.np>=0;
return "<tr><td>"+ic+"</td><td><span class='link' onclick='ov("+i+", "+JSON.stringify(x.c)+", "+JSON.stringify(x.n)+")'>"+x.c+"</span></td><td>"+x.n+"</td><td class='"+(pr?"green":"red")+"'>"+(pr?"+":"")+x.np.toFixed(2)+"</td><td class='"+(x.p>=0?"green":"red")+"'>"+(x.p>=0?"+":"")+x.p.toFixed(2)+"</td><td>"+x.f.toFixed(2)+"</td><td>"+x.wr+"</td><td>"+x.pr.toFixed(2)+"</td><td>"+x.t+"</td><td><span class='link' onclick='ov("+i+", "+JSON.stringify(x.c)+", "+JSON.stringify(x.n)+")'>详情</span></td></tr>";
}).join("");
}
var ec,bc,kc,tkc,dvc;
function ie(){
ec=echarts.init(document.getElementById("eq"));
var cum=0;var cd=D.map(function(x){cum+=(x.p-x.c);return cum});
var dp=D.map(function(x){return x.p-x.c});
var dt=D.map(function(x){return x.fd});
ec.setOption({
tooltip:{trigger:"axis",formatter:function(p){var s=p[0].name+"<br/>";p.forEach(function(q){s+=q.marker+q.seriesName+": "+(q.value>=0?"+":"")+q.value.toFixed(2)+"<br/>"});return s}},
legend:{data:["累计净盈亏","每日净盈亏"],top:0},
grid:{left:"3%",right:"4%",bottom:"3%",containLabel:true},
xAxis:{type:"category",data:dt,axisLabel:{rotate:30}},
yAxis:[{type:"value",name:"累计净盈亏"},{type:"value",name:"每日净盈亏",splitLine:{show:false}}],
series:[
{name:"累计净盈亏",type:"line",data:cd,smooth:true,lineStyle:{width:3},areaStyle:{opacity:0.1},itemStyle:{color:"#1890ff"}},
{name:"每日净盈亏",type:"bar",yAxisIndex:1,data:dp.map(function(v){return{value:v,itemStyle:{color:v>=0?"#52c41a":"#ff4d4f"}}}),barWidth:20}
]
});
}
function ib(){
bc=echarts.init(document.getElementById("bb"));
var d=V.map(function(x,i){return{name:x.n,value:[x.t,x.np,Math.sqrt(Math.abs(x.f))*5+5,i],itemStyle:{color:x.np>=0?"#52c41a":"#ff4d4f"}}});
bc.setOption({
tooltip:{formatter:function(p){return p.name+"<br/>笔数: "+p.value[0]+"<br/>净盈亏: "+p.value[1].toFixed(2)+"<br/>手续费: "+V[p.value[3]].f.toFixed(2)}},
xAxis:{name:"交易笔数",nameLocation:"middle",nameGap:30},
yAxis:{name:"净盈亏",nameLocation:"middle",nameGap:50},
series:[{type:"scatter",data:d,symbolSize:function(a){return a[2]},label:{show:true,formatter:function(a){return a.name},position:"top",fontSize:11}}]
});
}
function gk(bp,days,vol){
var dates=[],data=[];var price=bp;var sd=new Date("2026-04-01");
for(var i=0;i<days;i++){var dd=new Date(sd);dd.setDate(dd.getDate()+i);var ds=dd.getFullYear()+"-"+(dd.getMonth()+1<10?"0":"")+(dd.getMonth()+1)+"-"+(dd.getDate()<10?"0":"")+dd.getDate();dates.push(ds);var o=price;var cc=o+(Math.random()-0.5)*vol;var h=Math.max(o,cc)+Math.random()*vol*0.5;var l=Math.min(o,cc)-Math.random()*vol*0.5;data.push([o,cc,l,h]);price=cc}
return{dates:dates,data:data}
}
function ik(cid,bp,vol,bp2,sp2){
var ch=echarts.init(document.getElementById(cid));
var kl=gk(bp,60,vol);
var op={
tooltip:{trigger:"axis",formatter:function(p){var d=kl.dates[p[0].dataIndex];return d+"<br/>开: "+p[0].value[1].toFixed(2)+"<br/>收: "+p[0].value[2].toFixed(2)+"<br/>低: "+p[0].value[3].toFixed(2)+"<br/>高: "+p[0].value[4].toFixed(2)}},
grid:{left:"3%",right:"3%",bottom:"15%",containLabel:true},
xAxis:{type:"category",data:kl.dates,axisLabel:{rotate:45,fontSize:10},splitLine:{show:false}},
yAxis:{type:"value",scale:true,splitLine:{lineStyle:{type:"dashed"}}},
dataZoom:[{type:"inside",start:60,end:100},{type:"slider",show:true,start:60,end:100,height:20,bottom:0}],
series:[{type:"candlestick",data:kl.data,itemStyle:{color:"#52c41a",color0:"#ff4d4f",borderColor:"#52c41a",borderColor0:"#ff4d4f"}}]
};
if(bp2&&bp2.length)op.series.push({type:"scatter",data:bp2.map(function(b){return[b.di,kl.data[b.di][3]]}),symbol:"triangle",symbolSize:12,itemStyle:{color:"#52c41a"},label:{show:true,formatter:"买",position:"bottom",fontSize:10,color:"#52c41a"}});
if(sp2&&sp2.length)op.series.push({type:"scatter",data:sp2.map(function(s){return[s.di,kl.data[s.di][4]]}),symbol:"pin",symbolSize:12,itemStyle:{color:"#ff4d4f"},label:{show:true,formatter:"卖",position:"top",fontSize:10,color:"#ff4d4f"}});
ch.setOption(op);return ch;
}
function cm(id){document.getElementById(id).classList.remove("show")}
function ov(idx,code,name){
var x=V[idx];if(!x)return;
document.getElementById("vtitle").textContent="品种详情:"+name+" ("+code+")";
document.getElementById("vn").textContent=(x.np>=0?"+":"")+x.np.toFixed(2);
document.getElementById("vn").className="ov-val "+(x.np>=0?"green":"red");
document.getElementById("vw").textContent=x.wr;
document.getElementById("vr").textContent=x.pr.toFixed(2);
document.getElementById("vc").textContent=x.t;
document.getElementById("vf").textContent=x.f.toFixed(2);
document.getElementById("vm").textContent="+"+Math.abs(x.p*0.5).toFixed(2);
document.getElementById("vmod").classList.add("show");
if(kc)kc.dispose();
var bp={"JM":2000,"BU":3500,"MA":2500,"P":9000,"CF":16000};
kc=ik("kc",bp[code]||5000,code==="JM"?50:code==="CF"?200:30,[{di:15},{di:45}],[{di:30}]);
var rows=[];
for(var i=0;i<Math.min(x.t,8);i++){
var dir=Math.random()>0.5?"做多":"做空";
var op2=bp[code]||5000;var cp=op2+(Math.random()-0.5)*200;
var pnl=(dir==="做多"?cp-op2:op2-cp)*1;
rows.push("<tr><td>2026-06-"+(i<9?"0":"")+(i+1)+" 10:30</td><td class='"+(dir==="做多"?"green":"red")+"'>"+dir+"</td><td>"+op2.toFixed(2)+"</td><td>"+cp.toFixed(2)+"</td><td class='"+(pnl>=0?"green":"red")+"'>"+(pnl>=0?"+":"")+pnl.toFixed(2)+"</td><td>"+x.f.toFixed(2)+"</td><td><span class='link' onclick='ot("+JSON.stringify(name)+", "+JSON.stringify(dir)+")'>查看</span></td></tr>");
}
document.getElementById("vttb").innerHTML=rows.join("");
}
function ot(name,dir){
document.getElementById("ttitle").textContent=name+" "+dir+" 交易详情";
document.getElementById("tv").textContent=name;
document.getElementById("td").textContent=dir;
document.getElementById("tod").textContent="2026-06-01";
document.getElementById("tot").textContent="11:02:11";
document.getElementById("top").textContent="16300.00";
document.getElementById("tcp").textContent="16375.00";
document.getElementById("tvol").textContent="1";
document.getElementById("tfee").textContent="4.33";
document.getElementById("tpnl").textContent="-379.33";
document.getElementById("tmod").classList.add("show");
if(tkc)tkc.dispose();
tkc=ik("tkc",16000,200,[{di:20}],[{di:35}]);
}
function genAI(x){
var tags=[], txt="";
var wr = parseFloat(x.wr);
var np = x.p - x.c;
if(np<0) tags.push(""); else tags.push("");
if(wr<10) tags.push(""); else if(wr>30) tags.push("高胜率");
if(x.t>40) tags.push("频繁交易");
if(x.c>Math.abs(x.p)) tags.push("高手续费");
tags.forEach(function(t){txt+='<span class="ai-tag">'+t+'</span>'});
txt+="<br/><strong>1. 交易质量诊断:</strong><br/>";
if(wr<15) txt+=""+x.wr+"<br/>";
else txt+="胜率尚可,但需关注盈亏比是否合理。<br/>";
txt+="<br/><strong>2. 手续费分析:</strong><br/>";
if(x.c>Math.abs(x.p)*0.5) txt+="手续费("+x.c.toFixed(2)+")占比过高,侵蚀了利润。建议优化开仓频率或申请低费率。<br/>";
else txt+="手续费控制合理。<br/>";
txt+="<br/><strong>3. 改进建议:</strong><br/>";
if(x.l>x.w) txt+="亏损笔数较多,建议设置严格止损,避免扛单。<br/>";
txt+="建议复盘前3笔亏损交易寻找共性问题。";
return txt;
}
function od(date){
var x=D.find(function(d){return d.fd===date});
if(!x) return;
document.getElementById("dtitle").textContent="每日详情:"+date;
document.getElementById("dpnl").textContent=(x.p>=0?"+":"")+x.p.toFixed(2);
document.getElementById("dpnl").className="ov-val "+(x.p>=0?"green":"red");
var np=x.p-x.c;
document.getElementById("dnpnl").textContent=(np>=0?"+":"")+np.toFixed(2);
document.getElementById("dnpnl").className="ov-val "+(np>=0?"green":"red");
document.getElementById("dwr").textContent=x.wr;
document.getElementById("dfee").textContent=x.c.toFixed(2);
document.getElementById("dmp").textContent="+"+x.mp.toFixed(2);
document.getElementById("dml").textContent=x.ml.toFixed(2);
document.getElementById("dai").innerHTML=genAI(x);
var rows=[];
for(var i=0;i<x.t;i++){
var time="09:"+(30+Math.floor(i*1.2)%30)+":"+(Math.random()<0.5?"0":"")+Math.floor(Math.random()*60);
var varName=["JM","BU","MA","P","CF"][Math.floor(Math.random()*5)];
var dir=Math.random()>0.5?"做多":"做空";
var op=1000+Math.random()*1000;
var cp=op+(Math.random()-0.5)*200;
var pnl=(dir==="做多"?cp-op:op-cp);
var fee=Math.random()*10;
rows.push("<tr><td>"+time+"</td><td>"+varName+"</td><td>"+dir+"</td><td>"+op.toFixed(2)+"</td><td>"+cp.toFixed(2)+"</td><td class='"+(pnl>=0?"green":"red")+"'>"+(pnl>=0?"+":"")+pnl.toFixed(2)+"</td><td>"+fee.toFixed(2)+"</td></tr>");
}
document.getElementById("dttb").innerHTML=rows.join("");
document.getElementById("dmod").classList.add("show");
setTimeout(function(){renderDailyVariety(date)},100);
}
window.onload=function(){rd();rv();ie();ib()};
</script>
</body>
</html>
Loading…
Cancel
Save