持仓监控平仓自动记入交易记录,新增交易记录页与实盘资金设置

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-06-15 14:56:21 +08:00
parent 38ff40111a
commit 5aa9f11733
12 changed files with 733 additions and 23 deletions
+74 -9
View File
@@ -1,12 +1,18 @@
(function () {
var timer = null;
var keyTimer = null;
var posTimer = null;
function fmtDist(v) {
if (v === null || v === undefined) return '--';
return v.toFixed(2);
return Number(v).toFixed(2);
}
function pollPrices() {
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;
@@ -19,9 +25,7 @@
var priceEl = el.querySelector('.live-price');
var upEl = el.querySelector('.dist-up');
var downEl = el.querySelector('.dist-down');
if (priceEl) {
priceEl.textContent = row.price != null ? row.price : '--';
}
if (priceEl) priceEl.textContent = row.price != null ? row.price : '--';
if (upEl) upEl.textContent = fmtDist(row.dist_upper);
if (downEl) downEl.textContent = fmtDist(row.dist_lower);
});
@@ -29,10 +33,71 @@
.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 (timer) clearInterval(timer);
pollPrices();
timer = setInterval(pollPrices, 1000);
if (keyTimer) clearInterval(keyTimer);
if (posTimer) clearInterval(posTimer);
pollKeyPrices();
pollPositions();
keyTimer = setInterval(pollKeyPrices, 1000);
posTimer = setInterval(pollPositions, 1000);
}
document.addEventListener('DOMContentLoaded', startPolling);
+3 -3
View File
@@ -25,10 +25,10 @@
}
if (row.in_zone && distEl) {
distEl.innerHTML = '<span class="text-profit" style="font-weight:600">在区间内</span>';
} else if (distEl && upEl && downEl) {
} else if (distEl) {
distEl.innerHTML =
'距上 <span class="dist-up">' + fmtDist(row.dist_upper) + '</span>' +
' · 距下 <span class="dist-down">' + fmtDist(row.dist_lower) + '</span>';
'距上<span class="dist-up">' + fmtDist(row.dist_upper) + '</span> ' +
'距下<span class="dist-down">' + fmtDist(row.dist_lower) + '</span>';
}
});
})
+2
View File
@@ -162,4 +162,6 @@
bindModal();
recalc();
});
window.recalc = recalc;
})();
+42
View File
@@ -0,0 +1,42 @@
(function () {
var switchEl = document.getElementById('trade-edit-switch');
if (!switchEl) return;
function setEditMode(on) {
document.querySelectorAll('.cell-edit-hide').forEach(function (el) {
el.style.display = on ? 'none' : '';
});
document.querySelectorAll('.cell-edit-show').forEach(function (el) {
if (el.type === 'hidden') return;
el.style.display = on ? '' : 'none';
});
document.querySelectorAll('.trade-save-btn').forEach(function (btn) {
btn.disabled = !on;
});
}
switchEl.addEventListener('change', function () {
setEditMode(switchEl.checked);
});
document.querySelectorAll('.trade-save-btn').forEach(function (btn) {
btn.addEventListener('click', function () {
var row = btn.closest('tr[data-trade-id]');
if (!row) return;
var id = row.getAttribute('data-trade-id');
var form = document.createElement('form');
form.method = 'POST';
form.action = '/update_trade/' + id;
row.querySelectorAll('.cell-edit-show').forEach(function (el) {
if (!el.name) return;
var input = document.createElement('input');
input.type = 'hidden';
input.name = el.name;
input.value = el.value;
form.appendChild(input);
});
document.body.appendChild(form);
form.submit();
});
});
})();