feat(hub): add period date range and trade stats to inner-light-mind
Support today/week/month/custom range selection with sick count, PnL, and per-exchange breakdown; update docs. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -5570,6 +5570,41 @@ body.funds-fullscreen-open {
|
||||
padding: 12px;
|
||||
min-height: 100%;
|
||||
}
|
||||
.archive-period-bar {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.archive-period-tabs {
|
||||
display: inline-flex;
|
||||
gap: 4px;
|
||||
}
|
||||
.archive-period-btn {
|
||||
padding: 5px 10px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--border-soft);
|
||||
background: var(--inset-surface);
|
||||
color: var(--muted);
|
||||
cursor: pointer;
|
||||
font-family: var(--font);
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
.archive-period-btn.is-active {
|
||||
color: var(--text);
|
||||
border-color: var(--accent);
|
||||
background: color-mix(in srgb, var(--accent) 12%, transparent);
|
||||
}
|
||||
.archive-period-range {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
.archive-period-range.hidden,
|
||||
.archive-period-day-input.hidden {
|
||||
display: none;
|
||||
}
|
||||
.archive-period-sep {
|
||||
color: var(--muted);
|
||||
font-size: 0.82rem;
|
||||
}
|
||||
.archive-stats-bar {
|
||||
padding: 10px 12px;
|
||||
border-radius: 8px;
|
||||
@@ -5578,6 +5613,13 @@ body.funds-fullscreen-open {
|
||||
font-size: 0.82rem;
|
||||
color: var(--text);
|
||||
line-height: 1.45;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
.archive-stats-sub {
|
||||
font-size: 0.78rem;
|
||||
color: var(--muted);
|
||||
}
|
||||
.archive-acc-section {
|
||||
border: 1px solid var(--border-soft);
|
||||
|
||||
@@ -9,7 +9,11 @@
|
||||
const elFilterProfit = document.getElementById("archive-filter-profit");
|
||||
const elFilterLoss = document.getElementById("archive-filter-loss");
|
||||
const elFilterSick = document.getElementById("archive-filter-sick");
|
||||
const elPeriodTabs = document.getElementById("archive-period-tabs");
|
||||
const elTradingDay = document.getElementById("archive-trading-day");
|
||||
const elPeriodRangeWrap = document.getElementById("archive-period-range-wrap");
|
||||
const elDateFrom = document.getElementById("archive-date-from");
|
||||
const elDateTo = document.getElementById("archive-date-to");
|
||||
const elSearch = document.getElementById("archive-search");
|
||||
const elBtnChartToggle = document.getElementById("archive-btn-chart-toggle");
|
||||
const elBtnRefresh = document.getElementById("archive-btn-refresh");
|
||||
@@ -45,6 +49,10 @@
|
||||
let quotes = [];
|
||||
let dailyTrades = [];
|
||||
let dailyStats = { open_count: 0, by_exchange: {} };
|
||||
let periodMode = "today";
|
||||
let periodLabel = "";
|
||||
let dateFrom = "";
|
||||
let dateTo = "";
|
||||
let tradingDay = "";
|
||||
let selected = null;
|
||||
let trades = [];
|
||||
@@ -292,9 +300,35 @@
|
||||
return r;
|
||||
}
|
||||
|
||||
function syncPeriodUI() {
|
||||
if (elPeriodTabs) {
|
||||
elPeriodTabs.querySelectorAll(".archive-period-btn").forEach(function (btn) {
|
||||
btn.classList.toggle("is-active", btn.getAttribute("data-period") === periodMode);
|
||||
});
|
||||
}
|
||||
if (elTradingDay) {
|
||||
elTradingDay.classList.toggle("hidden", periodMode !== "today");
|
||||
}
|
||||
if (elPeriodRangeWrap) {
|
||||
elPeriodRangeWrap.classList.toggle("hidden", periodMode !== "range");
|
||||
}
|
||||
}
|
||||
|
||||
function setPeriodMode(mode) {
|
||||
periodMode = mode || "today";
|
||||
syncPeriodUI();
|
||||
}
|
||||
|
||||
function queryDailyParams() {
|
||||
const q = new URLSearchParams();
|
||||
if (elTradingDay && elTradingDay.value) q.set("trading_day", elTradingDay.value);
|
||||
q.set("period", periodMode);
|
||||
if (periodMode === "today" && elTradingDay && elTradingDay.value) {
|
||||
q.set("trading_day", elTradingDay.value);
|
||||
}
|
||||
if (periodMode === "range") {
|
||||
if (elDateFrom && elDateFrom.value) q.set("date_from", elDateFrom.value);
|
||||
if (elDateTo && elDateTo.value) q.set("date_to", elDateTo.value);
|
||||
}
|
||||
const ex = (elExchange && elExchange.value) || "";
|
||||
if (ex) q.set("exchange_key", ex);
|
||||
if (elFilterProfit && elFilterProfit.checked) q.set("filter_profit", "1");
|
||||
@@ -304,6 +338,14 @@
|
||||
return q.toString();
|
||||
}
|
||||
|
||||
function fmtPnlStat(v) {
|
||||
const n = Number(v);
|
||||
if (!Number.isFinite(n)) return "—";
|
||||
const cls = n >= 0 ? "pnl-pos" : "pnl-neg";
|
||||
const text = (n >= 0 ? "+" : "") + n.toFixed(2) + "U";
|
||||
return '<span class="' + cls + '">' + text + "</span>";
|
||||
}
|
||||
|
||||
function renderExchangeOptions() {
|
||||
if (!elExchange || !meta) return;
|
||||
const cur = elExchange.value;
|
||||
@@ -320,14 +362,47 @@
|
||||
function renderStats() {
|
||||
if (!elStats) return;
|
||||
const st = dailyStats || { open_count: 0, by_exchange: {} };
|
||||
const parts = ["今日开仓 " + (st.open_count || 0) + " 次"];
|
||||
const label = periodLabel || "本日";
|
||||
const sickPct = st.sick_pct != null ? st.sick_pct : 0;
|
||||
let html =
|
||||
'<div class="archive-stats-line"><strong>' +
|
||||
esc(label) +
|
||||
"</strong> · 开仓 " +
|
||||
(st.open_count || 0) +
|
||||
" 次 · 犯病 " +
|
||||
(st.sick_count || 0) +
|
||||
" 次(" +
|
||||
sickPct +
|
||||
"%) · 盈亏 " +
|
||||
fmtPnlStat(st.pnl_total) +
|
||||
" · 剔除犯病盈亏 " +
|
||||
fmtPnlStat(st.pnl_ex_sick) +
|
||||
"</div>";
|
||||
const byEx = st.by_exchange || {};
|
||||
Object.keys(byEx)
|
||||
.sort()
|
||||
.forEach(function (ex) {
|
||||
parts.push(exchangeLabel(ex) + " " + byEx[ex]);
|
||||
const exKeys = Object.keys(byEx).sort();
|
||||
if (exKeys.length) {
|
||||
const exParts = exKeys.map(function (ex) {
|
||||
const e = byEx[ex] || {};
|
||||
const sickN = e.sick_count || 0;
|
||||
const openN = e.open_count || 0;
|
||||
const sickShare = openN ? Math.round((sickN / openN) * 1000) / 10 : 0;
|
||||
return (
|
||||
esc(exchangeLabel(ex)) +
|
||||
" " +
|
||||
openN +
|
||||
"次/犯病" +
|
||||
sickN +
|
||||
"(" +
|
||||
sickShare +
|
||||
"%)/盈亏" +
|
||||
fmtPnlStat(e.pnl_total) +
|
||||
"/剔犯" +
|
||||
fmtPnlStat(e.pnl_ex_sick)
|
||||
);
|
||||
});
|
||||
elStats.textContent = parts.join(" · ");
|
||||
html += '<div class="archive-stats-sub">' + exParts.join(" · ") + "</div>";
|
||||
}
|
||||
elStats.innerHTML = html;
|
||||
}
|
||||
|
||||
function quotePreview(text) {
|
||||
@@ -990,15 +1065,26 @@
|
||||
setStatus(j.detail || "加载失败");
|
||||
return;
|
||||
}
|
||||
periodMode = j.period || periodMode || "today";
|
||||
periodLabel = j.period_label || periodLabel || "";
|
||||
dateFrom = j.date_from || dateFrom || "";
|
||||
dateTo = j.date_to || dateTo || "";
|
||||
tradingDay = j.trading_day || tradingDay;
|
||||
if (elTradingDay && tradingDay && !elTradingDay.value) elTradingDay.value = tradingDay;
|
||||
if (elTradingDay && tradingDay) elTradingDay.value = tradingDay;
|
||||
if (elDateFrom && dateFrom) elDateFrom.value = dateFrom;
|
||||
if (elDateTo && dateTo) elDateTo.value = dateTo;
|
||||
if (elQuoteDate && tradingDay && !elQuoteDate.value) elQuoteDate.value = tradingDay;
|
||||
syncPeriodUI();
|
||||
dailyTrades = j.trades || [];
|
||||
dailyStats = j.stats || { open_count: 0, by_exchange: {} };
|
||||
renderStats();
|
||||
renderTrades();
|
||||
setStatus(
|
||||
(tradingDay || "当日") + " · " + dailyTrades.length + " 笔 · " + new Date().toLocaleTimeString()
|
||||
(periodLabel || tradingDay || "当日") +
|
||||
" · 列表 " +
|
||||
dailyTrades.length +
|
||||
" 笔 · " +
|
||||
new Date().toLocaleTimeString()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1056,7 +1142,19 @@
|
||||
if (elBtnRefresh) elBtnRefresh.addEventListener("click", loadDailyTrades);
|
||||
if (elBtnSync) elBtnSync.addEventListener("click", syncAll);
|
||||
if (elExchange) elExchange.addEventListener("change", loadDailyTrades);
|
||||
if (elPeriodTabs) {
|
||||
elPeriodTabs.addEventListener("click", function (ev) {
|
||||
const btn = ev.target.closest(".archive-period-btn");
|
||||
if (!btn) return;
|
||||
const next = btn.getAttribute("data-period") || "today";
|
||||
if (next === periodMode) return;
|
||||
setPeriodMode(next);
|
||||
loadDailyTrades();
|
||||
});
|
||||
}
|
||||
if (elTradingDay) elTradingDay.addEventListener("change", loadDailyTrades);
|
||||
if (elDateFrom) elDateFrom.addEventListener("change", loadDailyTrades);
|
||||
if (elDateTo) elDateTo.addEventListener("change", loadDailyTrades);
|
||||
[elFilterProfit, elFilterLoss, elFilterSick].forEach(function (el) {
|
||||
if (el) el.addEventListener("change", loadDailyTrades);
|
||||
});
|
||||
@@ -1117,6 +1215,7 @@
|
||||
if (!inited) {
|
||||
loadMarkAutoPref();
|
||||
setChartOpen(false);
|
||||
syncPeriodUI();
|
||||
bindEvents();
|
||||
inited = true;
|
||||
}
|
||||
|
||||
@@ -266,10 +266,21 @@
|
||||
<label class="chk-label"><input type="checkbox" id="archive-filter-profit" /> 盈利单</label>
|
||||
<label class="chk-label"><input type="checkbox" id="archive-filter-loss" /> 亏损单</label>
|
||||
<label class="chk-label"><input type="checkbox" id="archive-filter-sick" /> 犯病</label>
|
||||
<label class="archive-field">
|
||||
<div class="archive-period-bar archive-field">
|
||||
<span>日期</span>
|
||||
<input id="archive-trading-day" type="date" />
|
||||
</label>
|
||||
<div class="archive-period-tabs" id="archive-period-tabs" role="tablist">
|
||||
<button type="button" class="archive-period-btn is-active" data-period="today">本日</button>
|
||||
<button type="button" class="archive-period-btn" data-period="week">本周</button>
|
||||
<button type="button" class="archive-period-btn" data-period="month">本月</button>
|
||||
<button type="button" class="archive-period-btn" data-period="range">区间</button>
|
||||
</div>
|
||||
<input id="archive-trading-day" type="date" class="archive-period-day-input" title="本日交易日" />
|
||||
<span id="archive-period-range-wrap" class="archive-period-range hidden">
|
||||
<input id="archive-date-from" type="date" title="起始日" />
|
||||
<span class="archive-period-sep">~</span>
|
||||
<input id="archive-date-to" type="date" title="结束日" />
|
||||
</span>
|
||||
</div>
|
||||
<button type="button" id="archive-btn-chart-toggle" class="ghost">图表</button>
|
||||
<label class="archive-field archive-search-field">
|
||||
<span>搜索</span>
|
||||
@@ -573,7 +584,7 @@
|
||||
<script src="https://unpkg.com/lightweight-charts@4.2.0/dist/lightweight-charts.standalone.production.js"></script>
|
||||
<script src="/assets/chart_draw.js?v=20260609-market-day-split"></script>
|
||||
<script src="/assets/chart.js?v=20260609-market-day-split"></script>
|
||||
<script src="/assets/archive.js?v=20260612-inner-light-fix"></script>
|
||||
<script src="/assets/archive.js?v=20260612-period-stats"></script>
|
||||
<script src="/assets/funds.js?v=20260609-hub-funds-fold"></script>
|
||||
<script src="/assets/dashboard.js?v=20260612-dash-monitor-count"></script>
|
||||
<script src="/assets/ai_review_render.js?v=3"></script>
|
||||
|
||||
Reference in New Issue
Block a user