持仓监控独立导航页,交易记录与复盘合并为同一页

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-06-15 15:06:18 +08:00
parent 5aa9f11733
commit 9ba9733523
8 changed files with 273 additions and 244 deletions
+2 -72
View File
@@ -1,17 +1,11 @@
(function () {
var keyTimer = null;
var posTimer = null;
function fmtDist(v) {
if (v === null || v === undefined) return '--';
return Number(v).toFixed(2);
}
function fmtNum(v, digits) {
if (v === null || v === undefined) return '--';
return Number(v).toFixed(digits === undefined ? 2 : digits);
}
function pollKeyPrices() {
var list = document.getElementById('key-monitor-list');
if (!list || !list.querySelector('.key-item')) return;
@@ -33,72 +27,8 @@
.catch(function () { /* ignore */ });
}
function buildPosCard(row) {
var pnlClass = '';
if (row.float_pnl > 0) pnlClass = 'pnl-pos';
if (row.float_pnl < 0) pnlClass = 'pnl-neg';
var pnlText = '--';
if (row.float_pnl != null) {
var sign = row.float_pnl >= 0 ? '+' : '';
pnlText = sign + fmtNum(row.float_pnl) + '元';
if (row.float_pct != null) {
pnlText += ' (' + sign + fmtNum(row.float_pct) + '%)';
}
}
var rr = row.rr_ratio != null ? row.rr_ratio + ':1' : '--';
var openT = (row.open_time || '').replace('T', ' ').slice(0, 16);
return (
'<div class="pos-card" data-pos-id="' + row.id + '">' +
'<div class="pos-card-head">' +
'<div><div class="title">' + row.symbol + ' <span class="badge dir">' + row.direction + '</span></div></div>' +
'<form method="post" action="/close_position/' + row.id + '" style="display:inline" onsubmit="return confirm(\'确认平仓?\')">' +
'<button type="submit" class="btn-del pos-del">平仓</button></form>' +
'</div>' +
'<div class="pos-card-meta">来源 <strong>手动输入</strong> · 风险 <strong>' +
fmtNum(row.risk_pct) + '%≈' + fmtNum(row.risk_amount) + '元</strong></div>' +
'<div class="pos-metrics">' +
'<div class="cell"><label>成交价</label><div>' + fmtNum(row.entry_price) + '</div></div>' +
'<div class="cell"><label>止损</label><div>' + fmtNum(row.stop_loss) + '</div></div>' +
'<div class="cell"><label>止盈</label><div>' + fmtNum(row.take_profit) + '</div></div>' +
'<div class="cell"><label>盈亏比</label><div>' + rr + '</div></div>' +
'<div class="cell"><label>标记价</label><div>' + (row.mark_price != null ? fmtNum(row.mark_price) : '--') + '</div></div>' +
'<div class="cell ' + pnlClass + '"><label>浮盈亏</label><div>' + pnlText + '</div></div>' +
'</div>' +
'<div class="pos-footer">' +
'<span>保证金 ' + fmtNum(row.margin) + '元</span>' +
'<span>仓位占比 ' + fmtNum(row.position_pct) + '%</span>' +
'<span>开仓 ' + (openT || '--') + '</span>' +
'<span>持仓 ' + (row.holding_duration || '--') + '</span>' +
'<span>张数 ' + row.lots + '</span>' +
'</div></div>'
);
}
function pollPositions() {
var list = document.getElementById('position-live-list');
if (!list) return;
fetch('/api/position_live')
.then(function (r) { return r.json(); })
.then(function (rows) {
if (!rows.length) {
list.innerHTML = '<div class="empty-hint">暂无持仓,左侧录入后显示</div>';
return;
}
list.innerHTML = rows.map(buildPosCard).join('');
})
.catch(function () { /* ignore */ });
}
function startPolling() {
if (keyTimer) clearInterval(keyTimer);
if (posTimer) clearInterval(posTimer);
document.addEventListener('DOMContentLoaded', function () {
pollKeyPrices();
pollPositions();
keyTimer = setInterval(pollKeyPrices, 1000);
posTimer = setInterval(pollPositions, 1000);
}
document.addEventListener('DOMContentLoaded', startPolling);
});
})();
+71
View File
@@ -0,0 +1,71 @@
(function () {
var posTimer = null;
function fmtNum(v, digits) {
if (v === null || v === undefined) return '--';
return Number(v).toFixed(digits === undefined ? 2 : digits);
}
function buildPosCard(row) {
var pnlClass = '';
if (row.float_pnl > 0) pnlClass = 'pnl-pos';
if (row.float_pnl < 0) pnlClass = 'pnl-neg';
var pnlText = '--';
if (row.float_pnl != null) {
var sign = row.float_pnl >= 0 ? '+' : '';
pnlText = sign + fmtNum(row.float_pnl) + '元';
if (row.float_pct != null) {
pnlText += ' (' + sign + fmtNum(row.float_pct) + '%)';
}
}
var rr = row.rr_ratio != null ? row.rr_ratio + ':1' : '--';
var openT = (row.open_time || '').replace('T', ' ').slice(0, 16);
return (
'<div class="pos-card" data-pos-id="' + row.id + '">' +
'<div class="pos-card-head">' +
'<div><div class="title">' + row.symbol + ' <span class="badge dir">' + row.direction + '</span></div></div>' +
'<form method="post" action="/close_position/' + row.id + '" style="display:inline" onsubmit="return confirm(\'确认平仓?\')">' +
'<button type="submit" class="btn-del pos-del">平仓</button></form>' +
'</div>' +
'<div class="pos-card-meta">来源 <strong>手动输入</strong> · 风险 <strong>' +
fmtNum(row.risk_pct) + '%≈' + fmtNum(row.risk_amount) + '元</strong></div>' +
'<div class="pos-metrics">' +
'<div class="cell"><label>成交价</label><div>' + fmtNum(row.entry_price) + '</div></div>' +
'<div class="cell"><label>止损</label><div>' + fmtNum(row.stop_loss) + '</div></div>' +
'<div class="cell"><label>止盈</label><div>' + fmtNum(row.take_profit) + '</div></div>' +
'<div class="cell"><label>盈亏比</label><div>' + rr + '</div></div>' +
'<div class="cell"><label>标记价</label><div>' + (row.mark_price != null ? fmtNum(row.mark_price) : '--') + '</div></div>' +
'<div class="cell ' + pnlClass + '"><label>浮盈亏</label><div>' + pnlText + '</div></div>' +
'</div>' +
'<div class="pos-footer">' +
'<span>保证金 ' + fmtNum(row.margin) + '元</span>' +
'<span>仓位占比 ' + fmtNum(row.position_pct) + '%</span>' +
'<span>开仓 ' + (openT || '--') + '</span>' +
'<span>持仓 ' + (row.holding_duration || '--') + '</span>' +
'<span>张数 ' + row.lots + '</span>' +
'</div></div>'
);
}
function pollPositions() {
var list = document.getElementById('position-live-list');
if (!list) return;
fetch('/api/position_live')
.then(function (r) { return r.json(); })
.then(function (rows) {
if (!rows.length) {
list.innerHTML = '<div class="empty-hint">暂无持仓,左侧录入后显示</div>';
return;
}
list.innerHTML = rows.map(buildPosCard).join('');
})
.catch(function () { /* ignore */ });
}
document.addEventListener('DOMContentLoaded', function () {
pollPositions();
posTimer = setInterval(pollPositions, 1000);
});
})();