Refine archive cumulative PnL chart proportions and axis layout.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -7452,44 +7452,70 @@ body.funds-fullscreen-open {
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: 0.72rem;
|
font-size: 0.72rem;
|
||||||
}
|
}
|
||||||
|
.archive-cum-wrap {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
.archive-cum-head {
|
.archive-cum-head {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
margin-bottom: 4px;
|
|
||||||
}
|
}
|
||||||
.archive-cum-end {
|
.archive-cum-end {
|
||||||
font-size: 0.78rem;
|
font-size: 0.78rem;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
font-variant-numeric: tabular-nums;
|
font-variant-numeric: tabular-nums;
|
||||||
}
|
}
|
||||||
|
.archive-cum-body {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 3.2em 1fr;
|
||||||
|
gap: 6px;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
.archive-cum-yaxis {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
font-size: 0.58rem;
|
||||||
|
color: rgba(255, 255, 255, 0.42);
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
padding: 2px 0 14px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
.archive-cum-plot {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 2px;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
.archive-cum-chart {
|
.archive-cum-chart {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 110px;
|
height: 96px;
|
||||||
display: block;
|
display: block;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
background: rgba(0, 0, 0, 0.15);
|
background: rgba(0, 0, 0, 0.12);
|
||||||
}
|
}
|
||||||
.archive-cum-ylabel,
|
.archive-cum-xaxis {
|
||||||
.archive-cum-xlabel {
|
display: flex;
|
||||||
fill: rgba(255, 255, 255, 0.45);
|
justify-content: space-between;
|
||||||
font-size: 9px;
|
font-size: 0.58rem;
|
||||||
font-family: inherit;
|
color: rgba(255, 255, 255, 0.42);
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
padding: 0 2px;
|
||||||
}
|
}
|
||||||
.archive-cum-zero {
|
.archive-cum-foot {
|
||||||
stroke: rgba(255, 255, 255, 0.15);
|
font-size: 0.62rem;
|
||||||
|
line-height: 1.35;
|
||||||
|
}
|
||||||
|
.archive-cum-grid {
|
||||||
|
stroke: rgba(255, 255, 255, 0.06);
|
||||||
stroke-width: 1;
|
stroke-width: 1;
|
||||||
stroke-dasharray: 3 3;
|
|
||||||
}
|
}
|
||||||
.archive-cum-area {
|
.archive-cum-grid--zero {
|
||||||
opacity: 0.18;
|
stroke: rgba(255, 255, 255, 0.16);
|
||||||
}
|
stroke-dasharray: 4 4;
|
||||||
.archive-cum-area.archive-cum-line--up {
|
|
||||||
fill: #4cd97f;
|
|
||||||
}
|
|
||||||
.archive-cum-area.archive-cum-line--down {
|
|
||||||
fill: #ff6b6b;
|
|
||||||
}
|
}
|
||||||
.archive-cum-line {
|
.archive-cum-line {
|
||||||
fill: none;
|
fill: none;
|
||||||
@@ -7507,12 +7533,16 @@ body.funds-fullscreen-open {
|
|||||||
stroke: rgba(0, 0, 0, 0.35);
|
stroke: rgba(0, 0, 0, 0.35);
|
||||||
stroke-width: 1;
|
stroke-width: 1;
|
||||||
}
|
}
|
||||||
.archive-cum-dot.archive-cum-line--up {
|
.archive-cum-dot--up {
|
||||||
fill: #4cd97f;
|
fill: #4cd97f;
|
||||||
}
|
}
|
||||||
.archive-cum-dot.archive-cum-line--down {
|
.archive-cum-dot--down {
|
||||||
fill: #ff6b6b;
|
fill: #ff6b6b;
|
||||||
}
|
}
|
||||||
|
.archive-cum-dot--last {
|
||||||
|
stroke: rgba(255, 255, 255, 0.35);
|
||||||
|
stroke-width: 1.5;
|
||||||
|
}
|
||||||
@media (max-width: 520px) {
|
@media (max-width: 520px) {
|
||||||
.archive-viz-kpis {
|
.archive-viz-kpis {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
|
|||||||
@@ -661,15 +661,21 @@
|
|||||||
if (!series.length) {
|
if (!series.length) {
|
||||||
return '<p class="archive-viz-empty muted">当前区间暂无平仓数据</p>';
|
return '<p class="archive-viz-empty muted">当前区间暂无平仓数据</p>';
|
||||||
}
|
}
|
||||||
const w = 400;
|
const w = 320;
|
||||||
const h = 110;
|
const h = 96;
|
||||||
const padL = 36;
|
const padL = 6;
|
||||||
const padR = 8;
|
const padR = 6;
|
||||||
const padT = 8;
|
const padT = 10;
|
||||||
const padB = 22;
|
const padB = 10;
|
||||||
const vals = series.map(function (s) { return s.cum; });
|
const vals = series.map(function (s) { return s.cum; });
|
||||||
const minV = Math.min(0, ...vals);
|
const rawMin = Math.min.apply(null, vals);
|
||||||
const maxV = Math.max(0, ...vals);
|
const rawMax = Math.max.apply(null, vals);
|
||||||
|
let minV = Math.min(0, rawMin);
|
||||||
|
let maxV = Math.max(0, rawMax);
|
||||||
|
const span = maxV - minV || Math.max(Math.abs(rawMax), Math.abs(rawMin), 1);
|
||||||
|
const yPad = span * 0.14;
|
||||||
|
minV -= yPad;
|
||||||
|
maxV += yPad;
|
||||||
const range = maxV - minV || 1;
|
const range = maxV - minV || 1;
|
||||||
const innerW = w - padL - padR;
|
const innerW = w - padL - padR;
|
||||||
const innerH = h - padT - padB;
|
const innerH = h - padT - padB;
|
||||||
@@ -677,78 +683,83 @@
|
|||||||
return padT + innerH - ((v - minV) / range) * innerH;
|
return padT + innerH - ((v - minV) / range) * innerH;
|
||||||
};
|
};
|
||||||
const zeroY = yOf(0);
|
const zeroY = yOf(0);
|
||||||
|
const showZero = rawMin < -0.0001 || rawMax > 0.0001;
|
||||||
const pts = series.map(function (s, i) {
|
const pts = series.map(function (s, i) {
|
||||||
const x = padL + (i / Math.max(series.length - 1, 1)) * innerW;
|
const x = padL + (i / Math.max(series.length - 1, 1)) * innerW;
|
||||||
const y = yOf(s.cum);
|
const y = yOf(s.cum);
|
||||||
return { x: x, y: y };
|
return { x: x, y: y, day: s.day, pnl: s.pnl, cum: s.cum };
|
||||||
});
|
});
|
||||||
const linePts = pts.map(function (p) { return p.x.toFixed(1) + "," + p.y.toFixed(1); }).join(" ");
|
const linePts = pts.map(function (p) { return p.x.toFixed(1) + "," + p.y.toFixed(1); }).join(" ");
|
||||||
const areaPts =
|
|
||||||
linePts +
|
|
||||||
" " +
|
|
||||||
pts[pts.length - 1].x.toFixed(1) +
|
|
||||||
"," +
|
|
||||||
zeroY.toFixed(1) +
|
|
||||||
" " +
|
|
||||||
pts[0].x.toFixed(1) +
|
|
||||||
"," +
|
|
||||||
zeroY.toFixed(1);
|
|
||||||
const last = series[series.length - 1];
|
const last = series[series.length - 1];
|
||||||
const lastCls = last.cum >= 0 ? "pnl-pos" : "pnl-neg";
|
const lastCls = last.cum >= 0 ? "pnl-pos" : "pnl-neg";
|
||||||
const lineCls = last.cum >= 0 ? "archive-cum-line--up" : "archive-cum-line--down";
|
const lineCls = last.cum >= 0 ? "archive-cum-line--up" : "archive-cum-line--down";
|
||||||
const sign = last.cum > 0 ? "+" : "";
|
const sign = last.cum > 0 ? "+" : "";
|
||||||
const fmtAxis = function (v) {
|
const fmtAxis = function (v) {
|
||||||
const a = Math.abs(v);
|
const a = Math.abs(v);
|
||||||
if (a >= 100) return (v > 0 ? "+" : "") + v.toFixed(0);
|
if (a >= 100) return (v > 0 ? "+" : "") + v.toFixed(0) + "U";
|
||||||
if (a >= 10) return (v > 0 ? "+" : "") + v.toFixed(1);
|
if (a >= 10) return (v > 0 ? "+" : "") + v.toFixed(1) + "U";
|
||||||
return (v > 0 ? "+" : "") + v.toFixed(2);
|
return (v > 0 ? "+" : "") + v.toFixed(2) + "U";
|
||||||
};
|
};
|
||||||
const yTicks = [maxV, 0, minV].filter(function (v, i, arr) {
|
const yTop = fmtAxis(maxV);
|
||||||
return arr.indexOf(v) === i;
|
const yMid = showZero ? "0" : fmtAxis((maxV + minV) / 2);
|
||||||
});
|
const yBot = fmtAxis(minV);
|
||||||
const yLabels = yTicks
|
const gridLines = [maxV, showZero ? 0 : null, minV]
|
||||||
|
.filter(function (v, i, arr) {
|
||||||
|
return v != null && arr.indexOf(v) === i;
|
||||||
|
})
|
||||||
.map(function (v) {
|
.map(function (v) {
|
||||||
|
const y = yOf(v).toFixed(1);
|
||||||
|
const cls = v === 0 ? "archive-cum-grid archive-cum-grid--zero" : "archive-cum-grid";
|
||||||
|
return '<line x1="' + padL + '" y1="' + y + '" x2="' + (w - padR) + '" y2="' + y + '" class="' + cls + '" />';
|
||||||
|
})
|
||||||
|
.join("");
|
||||||
|
const dots = pts
|
||||||
|
.map(function (p, i) {
|
||||||
|
const isLast = i === pts.length - 1;
|
||||||
|
const dotCls = p.cum >= 0 ? "archive-cum-dot--up" : "archive-cum-dot--down";
|
||||||
|
const r = isLast ? 4.2 : 2.6;
|
||||||
|
const dayPnl = p.pnl > 0 ? "+" + p.pnl.toFixed(2) : p.pnl.toFixed(2);
|
||||||
|
const cumPnl = p.cum > 0 ? "+" + p.cum.toFixed(2) : p.cum.toFixed(2);
|
||||||
return (
|
return (
|
||||||
'<text x="' +
|
'<circle cx="' + p.x.toFixed(1) + '" cy="' + p.y.toFixed(1) + '" r="' + r + '" class="archive-cum-dot ' + dotCls + (isLast ? " archive-cum-dot--last" : "") + '">' +
|
||||||
(padL - 4) +
|
'<title>' + esc(p.day.slice(5)) + " 当日" + dayPnl + "U · 累计" + cumPnl + "U</title>" +
|
||||||
'" y="' +
|
"</circle>"
|
||||||
(yOf(v) + 3.5).toFixed(1) +
|
|
||||||
'" class="archive-cum-ylabel" text-anchor="end">' +
|
|
||||||
esc(fmtAxis(v)) +
|
|
||||||
"</text>"
|
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
.join("");
|
.join("");
|
||||||
const xLabels =
|
const dayCount = series.length;
|
||||||
'<text x="' +
|
const peak = fmtAxis(rawMax);
|
||||||
padL +
|
const trough = fmtAxis(rawMin);
|
||||||
'" y="' +
|
const xStart = esc(series[0].day.slice(5));
|
||||||
(h - 4) +
|
const xEnd = esc(series[series.length - 1].day.slice(5));
|
||||||
'" class="archive-cum-xlabel" text-anchor="start">' +
|
|
||||||
esc(series[0].day.slice(5)) +
|
|
||||||
"</text>" +
|
|
||||||
(series.length > 1
|
|
||||||
? '<text x="' +
|
|
||||||
(padL + innerW) +
|
|
||||||
'" y="' +
|
|
||||||
(h - 4) +
|
|
||||||
'" class="archive-cum-xlabel" text-anchor="end">' +
|
|
||||||
esc(series[series.length - 1].day.slice(5)) +
|
|
||||||
"</text>"
|
|
||||||
: "");
|
|
||||||
return (
|
return (
|
||||||
|
'<div class="archive-cum-wrap">' +
|
||||||
'<div class="archive-cum-head">' +
|
'<div class="archive-cum-head">' +
|
||||||
'<span class="archive-viz-block-title">累计盈亏</span>' +
|
'<span class="archive-viz-block-title">累计盈亏</span>' +
|
||||||
'<span class="archive-cum-end ' + lastCls + '">' + sign + last.cum.toFixed(2) + "U</span>" +
|
'<span class="archive-cum-end ' + lastCls + '">' + sign + last.cum.toFixed(2) + "U</span>" +
|
||||||
"</div>" +
|
"</div>" +
|
||||||
'<svg class="archive-cum-chart" viewBox="0 0 ' + w + " " + h + '" preserveAspectRatio="none" aria-hidden="true">' +
|
'<div class="archive-cum-body">' +
|
||||||
yLabels +
|
'<div class="archive-cum-yaxis" aria-hidden="true">' +
|
||||||
xLabels +
|
'<span>' + esc(yTop) + "</span>" +
|
||||||
'<line x1="' + padL + '" y1="' + zeroY.toFixed(1) + '" x2="' + (w - padR) + '" y2="' + zeroY.toFixed(1) + '" class="archive-cum-zero" />' +
|
'<span>' + esc(yMid) + "</span>" +
|
||||||
'<polygon points="' + areaPts + '" class="archive-cum-area ' + lineCls + '" />' +
|
'<span>' + esc(yBot) + "</span>" +
|
||||||
'<polyline points="' + linePts + '" class="archive-cum-line ' + lineCls + '" />' +
|
"</div>" +
|
||||||
'<circle cx="' + pts[pts.length - 1].x.toFixed(1) + '" cy="' + pts[pts.length - 1].y.toFixed(1) + '" r="3" class="archive-cum-dot ' + lineCls + '" />' +
|
'<div class="archive-cum-plot">' +
|
||||||
"</svg>"
|
'<svg class="archive-cum-chart" viewBox="0 0 ' + w + " " + h + '" preserveAspectRatio="xMidYMid meet" aria-hidden="true">' +
|
||||||
|
gridLines +
|
||||||
|
'<polyline points="' + linePts + '" class="archive-cum-line ' + lineCls + '" vector-effect="non-scaling-stroke" />' +
|
||||||
|
dots +
|
||||||
|
"</svg>" +
|
||||||
|
'<div class="archive-cum-xaxis" aria-hidden="true">' +
|
||||||
|
"<span>" + xStart + "</span>" +
|
||||||
|
(series.length > 1 ? "<span>" + xEnd + "</span>" : "") +
|
||||||
|
"</div>" +
|
||||||
|
"</div>" +
|
||||||
|
"</div>" +
|
||||||
|
'<div class="archive-cum-foot muted">' +
|
||||||
|
dayCount + " 个交易日 · 区间高 " + esc(peak) + " · 低 " + esc(trough) +
|
||||||
|
"</div>" +
|
||||||
|
"</div>"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1229,7 +1229,7 @@
|
|||||||
<script src="/assets/plan.js?v=20260614-plan-refresh"></script>
|
<script src="/assets/plan.js?v=20260614-plan-refresh"></script>
|
||||||
<script src="/assets/calculator.js?v=3"></script>
|
<script src="/assets/calculator.js?v=3"></script>
|
||||||
<script src="/assets/trade_stats_calendar.js?v=3"></script>
|
<script src="/assets/trade_stats_calendar.js?v=3"></script>
|
||||||
<script src="/assets/archive.js?v=20260710-archive-viz3"></script>
|
<script src="/assets/archive.js?v=20260710-archive-cum"></script>
|
||||||
<script src="/assets/funds.js?v=20260707-hub-options-funds"></script>
|
<script src="/assets/funds.js?v=20260707-hub-options-funds"></script>
|
||||||
<script src="/assets/dashboard.js?v=20260708-options-expiry-cd"></script>
|
<script src="/assets/dashboard.js?v=20260708-options-expiry-cd"></script>
|
||||||
<script src="/assets/strategy.js?v=3"></script>
|
<script src="/assets/strategy.js?v=3"></script>
|
||||||
|
|||||||
Reference in New Issue
Block a user