Enhance dashboard with exchange labels, split SL/TP columns, and daily risk limits.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -66,7 +66,41 @@
|
||||
}
|
||||
|
||||
.dashboard-risk-grid {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: stretch;
|
||||
gap: 0;
|
||||
margin-bottom: 0;
|
||||
overflow-x: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
.dashboard-risk-item {
|
||||
flex: 1 1 0;
|
||||
min-width: 5.8rem;
|
||||
padding: 0.45rem 0.55rem;
|
||||
text-align: center;
|
||||
border-right: 1px solid var(--table-border);
|
||||
}
|
||||
|
||||
.dashboard-risk-item:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.dashboard-risk-label {
|
||||
font-size: 0.62rem;
|
||||
line-height: 1.3;
|
||||
color: var(--text-muted);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.dashboard-risk-value {
|
||||
font-size: 0.8rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-title);
|
||||
margin-top: 0.18rem;
|
||||
font-variant-numeric: tabular-nums;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.dashboard-risk-grid .stat-item {
|
||||
@@ -84,13 +118,12 @@
|
||||
|
||||
.dash-symbol-title {
|
||||
font-weight: 600;
|
||||
line-height: 1.35;
|
||||
line-height: 1.45;
|
||||
}
|
||||
|
||||
.dash-symbol-sub {
|
||||
font-size: 0.72rem;
|
||||
line-height: 1.35;
|
||||
margin-top: 0.12rem;
|
||||
.dash-symbol-ex {
|
||||
font-weight: 400;
|
||||
font-size: 0.78rem;
|
||||
}
|
||||
|
||||
.dash-main-badge {
|
||||
|
||||
+54
-20
@@ -59,30 +59,42 @@
|
||||
.replace(/"/g, '"');
|
||||
}
|
||||
|
||||
function slTpText(row) {
|
||||
var parts = [];
|
||||
if (row.stop_loss != null) parts.push('SL ' + fmtNum(row.stop_loss));
|
||||
if (row.take_profit != null) parts.push('TP ' + fmtNum(row.take_profit));
|
||||
if (row.trailing_be) parts.push('移动保本');
|
||||
return parts.length ? parts.join(' · ') : '—';
|
||||
function slText(row) {
|
||||
if (row.trailing_be && row.stop_loss == null) return '移动保本';
|
||||
if (row.stop_loss != null) return fmtNum(row.stop_loss);
|
||||
return '—';
|
||||
}
|
||||
|
||||
function tpText(row) {
|
||||
if (row.trailing_be) return '移动保本';
|
||||
if (row.take_profit != null) return fmtNum(row.take_profit);
|
||||
return '—';
|
||||
}
|
||||
|
||||
function symbolCellHtml(row) {
|
||||
var name = row.symbol_name || row.symbol || '';
|
||||
var code = row.symbol_code || '';
|
||||
if (!code && row.symbol && String(row.symbol).toLowerCase() !== String(name).toLowerCase()) {
|
||||
code = row.symbol;
|
||||
}
|
||||
var exchange = row.symbol_exchange || '';
|
||||
var mainBadge = row.symbol_is_main
|
||||
? ' <span class="badge planned dash-main-badge">主力</span>' : '';
|
||||
var titleInner = escHtml(name) + mainBadge;
|
||||
var titleInner = escHtml(name);
|
||||
if (exchange) {
|
||||
titleInner += ' <span class="dash-symbol-ex text-muted">' + escHtml(exchange) + '</span>';
|
||||
}
|
||||
titleInner += mainBadge;
|
||||
if (code && String(name).toLowerCase() !== String(code).toLowerCase()) {
|
||||
titleInner += ' <span class="text-accent">' + escHtml(code) + '</span>';
|
||||
} else if (!name && code) {
|
||||
titleInner = '<span class="text-accent">' + escHtml(code) + '</span>';
|
||||
titleInner = (exchange
|
||||
? '<span class="dash-symbol-ex text-muted">' + escHtml(exchange) + '</span> '
|
||||
: '') + '<span class="text-accent">' + escHtml(code) + '</span>';
|
||||
}
|
||||
var sub = row.symbol_exchange || code || '';
|
||||
return (
|
||||
'<div class="dash-symbol-cell">' +
|
||||
'<div class="dash-symbol-title">' + titleInner + '</div>' +
|
||||
(sub ? '<div class="dash-symbol-sub text-muted">' + escHtml(sub) + '</div>' : '') +
|
||||
'</div>'
|
||||
);
|
||||
}
|
||||
@@ -132,6 +144,24 @@
|
||||
var maxPos = lim.max_active_positions != null ? lim.max_active_positions : st.max_active_positions;
|
||||
var manualCnt = risk.manual_close_count_today != null ? risk.manual_close_count_today : 0;
|
||||
var manualLim = lim.manual_close_daily_limit;
|
||||
var dailyOpens = risk.daily_open_count != null
|
||||
? risk.daily_open_count
|
||||
: (st.daily_open_count != null ? st.daily_open_count : '—');
|
||||
var dailyPosLim = lim.daily_position_limit != null
|
||||
? lim.daily_position_limit
|
||||
: st.daily_position_limit;
|
||||
var dailyRiskUsed = risk.daily_risk_used_pct != null
|
||||
? risk.daily_risk_used_pct
|
||||
: st.daily_risk_used_pct;
|
||||
var dailyRiskLim = lim.daily_trading_risk_pct_limit != null
|
||||
? lim.daily_trading_risk_pct_limit
|
||||
: st.daily_trading_risk_pct_limit;
|
||||
var dailyRiskText = dailyRiskUsed != null ? fmtNum(dailyRiskUsed) + '%' : '—';
|
||||
if (dailyRiskLim != null && dailyRiskUsed != null) {
|
||||
dailyRiskText += ' / ' + fmtNum(dailyRiskLim) + '%';
|
||||
} else if (dailyRiskLim != null) {
|
||||
dailyRiskText += ' / ' + fmtNum(dailyRiskLim) + '%';
|
||||
}
|
||||
var marginPct = risk.margin_pct_used;
|
||||
var maxMarginPct = lim.max_margin_pct;
|
||||
var marginPctText = marginPct != null ? fmtNum(marginPct) + '%' : '—';
|
||||
@@ -157,23 +187,24 @@
|
||||
var items = [
|
||||
{ label: '风控开关', value: enabled ? '开启' : '关闭' },
|
||||
{ label: '持仓限制', value: active + ' / ' + (maxPos != null ? maxPos : '—') },
|
||||
{ label: '日手动平仓', value: manualCnt + ' / ' + (manualLim != null ? manualLim : '—') },
|
||||
{ label: '日持仓限制', value: dailyOpens + ' / ' + (dailyPosLim != null ? dailyPosLim : '—') },
|
||||
{ label: '日交易风险', value: dailyRiskText },
|
||||
{ label: '手动平仓(冷静期触发)', value: manualCnt + ' / ' + (manualLim != null ? manualLim : '—') },
|
||||
{ label: '冷静期(默认)', value: fmtHours(lim.cooling_hours_manual) },
|
||||
{ label: '复盘后冷静', value: fmtHours(lim.cooling_hours_manual_journal) },
|
||||
{ label: '冷静剩余', value: fmtRemainSec(st.freeze_remaining_sec) },
|
||||
{ label: '保证金占比', value: marginPctText },
|
||||
{ label: '保证金上限', value: maxMarginPct != null ? fmtNum(maxMarginPct) + '%' : '—' },
|
||||
{ label: '滚仓保证金上限', value: lim.roll_max_margin_pct != null ? fmtNum(lim.roll_max_margin_pct) + '%' : '—' },
|
||||
{ label: '综合保证金上限', value: lim.roll_max_margin_pct != null ? fmtNum(lim.roll_max_margin_pct) + '%' : '—' },
|
||||
{ label: '计仓模式', value: sizingDetail },
|
||||
{ label: '单笔风险', value: lim.risk_percent != null ? fmtNum(lim.risk_percent) + '%' : '—' },
|
||||
{ label: '交易日切', value: lim.trading_day_reset_hour != null ? lim.trading_day_reset_hour + ':00' : '—' }
|
||||
];
|
||||
|
||||
riskGridEl.innerHTML = items.map(function (it) {
|
||||
return (
|
||||
'<div class="stat-item">' +
|
||||
'<div class="label">' + escHtml(it.label) + '</div>' +
|
||||
'<div class="value">' + escHtml(it.value) + '</div>' +
|
||||
'<div class="dashboard-risk-item">' +
|
||||
'<div class="dashboard-risk-label">' + escHtml(it.label) + '</div>' +
|
||||
'<div class="dashboard-risk-value">' + escHtml(it.value) + '</div>' +
|
||||
'</div>'
|
||||
);
|
||||
}).join('');
|
||||
@@ -320,7 +351,7 @@
|
||||
function renderPositions(rows) {
|
||||
if (!posBody) return;
|
||||
if (!rows.length) {
|
||||
posBody.innerHTML = '<tr><td colspan="8" class="text-muted">暂无持仓</td></tr>';
|
||||
posBody.innerHTML = '<tr><td colspan="9" class="text-muted">暂无持仓</td></tr>';
|
||||
posRowCache = {};
|
||||
return;
|
||||
}
|
||||
@@ -337,7 +368,8 @@
|
||||
'<td class="dash-p-mark">' + (r.current_price != null ? fmtNum(r.current_price) : '—') + '</td>' +
|
||||
'<td class="dash-p-pnl ' + pnlClass(r.float_pnl) + '">' + fmtPnl(r.float_pnl) + '</td>' +
|
||||
'<td class="dash-p-margin">' + (r.margin != null ? fmtMoney(r.margin) : '—') + '</td>' +
|
||||
'<td class="dash-p-sltp">' + escHtml(slTpText(r)) + '</td>' +
|
||||
'<td class="dash-p-sl">' + escHtml(slText(r)) + '</td>' +
|
||||
'<td class="dash-p-tp">' + escHtml(tpText(r)) + '</td>' +
|
||||
'</tr>'
|
||||
);
|
||||
}).join('');
|
||||
@@ -396,7 +428,8 @@
|
||||
var markEl = row.querySelector('.dash-p-mark');
|
||||
var pnlEl = row.querySelector('.dash-p-pnl');
|
||||
var marginEl = row.querySelector('.dash-p-margin');
|
||||
var sltpEl = row.querySelector('.dash-p-sltp');
|
||||
var slEl = row.querySelector('.dash-p-sl');
|
||||
var tpEl = row.querySelector('.dash-p-tp');
|
||||
if (lotsEl) lotsEl.textContent = String(r.lots);
|
||||
if (entryEl) entryEl.textContent = fmtNum(r.entry_price);
|
||||
if (markEl) markEl.textContent = r.current_price != null ? fmtNum(r.current_price) : '—';
|
||||
@@ -405,7 +438,8 @@
|
||||
pnlEl.className = 'dash-p-pnl ' + pnlClass(r.float_pnl);
|
||||
}
|
||||
if (marginEl) marginEl.textContent = r.margin != null ? fmtMoney(r.margin) : '—';
|
||||
if (sltpEl) sltpEl.textContent = slTpText(r);
|
||||
if (slEl) slEl.textContent = slText(r);
|
||||
if (tpEl) tpEl.textContent = tpText(r);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user