fix(hub): archive chart perpetual label and trade row highlight
Label archive K-lines as USDT swap in title and API; highlight the full trade row when opening its chart. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -67,6 +67,20 @@ def normalize_chart_timeframe(raw: str | None, default: str = "5m") -> str:
|
|||||||
return tf if tf in CHART_TIMEFRAMES else default
|
return tf if tf in CHART_TIMEFRAMES else default
|
||||||
|
|
||||||
|
|
||||||
|
def normalize_perpetual_symbol(symbol: str) -> str:
|
||||||
|
"""BTC/USDT → BTC/USDT:USDT(与四所 ccxt swap 行情一致)。"""
|
||||||
|
sym = (symbol or "").strip().upper()
|
||||||
|
if not sym:
|
||||||
|
return ""
|
||||||
|
if ":" in sym:
|
||||||
|
return sym
|
||||||
|
if "/" in sym:
|
||||||
|
base, quote = sym.split("/", 1)
|
||||||
|
quote_clean = quote.split(":")[0]
|
||||||
|
return f"{base}/{quote_clean}:{quote_clean}"
|
||||||
|
return sym
|
||||||
|
|
||||||
|
|
||||||
def sync_timeframe_for_display(timeframe: str) -> str:
|
def sync_timeframe_for_display(timeframe: str) -> str:
|
||||||
"""展示周期对应的入库 / 同步周期。"""
|
"""展示周期对应的入库 / 同步周期。"""
|
||||||
tf = normalize_chart_timeframe(timeframe)
|
tf = normalize_chart_timeframe(timeframe)
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ from hub_ohlcv_lib import (
|
|||||||
TIMEFRAME_MS,
|
TIMEFRAME_MS,
|
||||||
aggregate_ohlcv_bars,
|
aggregate_ohlcv_bars,
|
||||||
normalize_chart_timeframe,
|
normalize_chart_timeframe,
|
||||||
|
normalize_perpetual_symbol,
|
||||||
)
|
)
|
||||||
from hub_trades_lib import (
|
from hub_trades_lib import (
|
||||||
display_entry_type_label,
|
display_entry_type_label,
|
||||||
@@ -927,10 +928,13 @@ def resolve_archive_chart(
|
|||||||
if not candles:
|
if not candles:
|
||||||
return {"ok": False, "msg": "视窗内无 K 线"}
|
return {"ok": False, "msg": "视窗内无 K 线"}
|
||||||
|
|
||||||
|
ex_sym = normalize_perpetual_symbol(sym)
|
||||||
return {
|
return {
|
||||||
"ok": True,
|
"ok": True,
|
||||||
"exchange_key": ex_k,
|
"exchange_key": ex_k,
|
||||||
"symbol": sym,
|
"symbol": sym,
|
||||||
|
"exchange_symbol": ex_sym,
|
||||||
|
"market_type": "swap",
|
||||||
"timeframe": tf,
|
"timeframe": tf,
|
||||||
"mode": (mode or "hold").strip().lower(),
|
"mode": (mode or "hold").strip().lower(),
|
||||||
"range_mode": rm,
|
"range_mode": rm,
|
||||||
|
|||||||
@@ -5800,13 +5800,15 @@ body.funds-fullscreen-open {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
.archive-trade-row.is-active {
|
.archive-trade-row.is-active {
|
||||||
background: var(--inset-surface);
|
background: color-mix(in srgb, var(--accent) 16%, var(--inset-surface));
|
||||||
|
box-shadow: inset 3px 0 0 var(--accent);
|
||||||
}
|
}
|
||||||
.archive-trade-row.archive-trade-sick td {
|
.archive-trade-row.archive-trade-sick td {
|
||||||
color: var(--red);
|
color: var(--red);
|
||||||
}
|
}
|
||||||
.archive-trade-row.archive-trade-sick.is-active {
|
.archive-trade-row.archive-trade-sick.is-active {
|
||||||
background: var(--inset-surface);
|
background: color-mix(in srgb, var(--accent) 12%, color-mix(in srgb, var(--red) 8%, var(--panel)));
|
||||||
|
box-shadow: inset 3px 0 0 var(--accent);
|
||||||
}
|
}
|
||||||
.archive-trade-row.archive-trade-sick .archive-tag-select,
|
.archive-trade-row.archive-trade-sick .archive-tag-select,
|
||||||
.archive-trade-row.archive-trade-sick .archive-note-input {
|
.archive-trade-row.archive-trade-sick .archive-note-input {
|
||||||
|
|||||||
@@ -66,6 +66,8 @@
|
|||||||
let inited = false;
|
let inited = false;
|
||||||
let markAuto = true;
|
let markAuto = true;
|
||||||
let lastCandles = [];
|
let lastCandles = [];
|
||||||
|
let chartExchangeSymbol = "";
|
||||||
|
let chartMarketType = "swap";
|
||||||
let searchTimer = null;
|
let searchTimer = null;
|
||||||
|
|
||||||
function esc(s) {
|
function esc(s) {
|
||||||
@@ -309,13 +311,27 @@
|
|||||||
scheduleChartResize();
|
scheduleChartResize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function formatChartContractLabel(sym, exchangeSymbol, marketType) {
|
||||||
|
const base = String(sym || "—");
|
||||||
|
const mt = String(marketType || "").toLowerCase();
|
||||||
|
if (mt === "swap" || (exchangeSymbol && String(exchangeSymbol).indexOf(":") >= 0)) {
|
||||||
|
return base + " 永续";
|
||||||
|
}
|
||||||
|
return base;
|
||||||
|
}
|
||||||
|
|
||||||
function updateChartTitle() {
|
function updateChartTitle() {
|
||||||
if (!elChartTitle) return;
|
if (!elChartTitle) return;
|
||||||
if (!selected) {
|
if (!selected) {
|
||||||
elChartTitle.textContent = "—";
|
elChartTitle.textContent = "—";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
elChartTitle.textContent = selected.symbol + " · " + exchangeLabel(selected.exchange_key);
|
const label = formatChartContractLabel(
|
||||||
|
selected.symbol,
|
||||||
|
chartExchangeSymbol,
|
||||||
|
chartMarketType
|
||||||
|
);
|
||||||
|
elChartTitle.textContent = label + " · " + exchangeLabel(selected.exchange_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function apiFetch(url, opts) {
|
async function apiFetch(url, opts) {
|
||||||
@@ -871,6 +887,8 @@
|
|||||||
setStatus(j.detail || "K 线加载失败");
|
setStatus(j.detail || "K 线加载失败");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
chartExchangeSymbol = j.exchange_symbol || "";
|
||||||
|
chartMarketType = j.market_type || "swap";
|
||||||
if (chart) {
|
if (chart) {
|
||||||
destroyChart();
|
destroyChart();
|
||||||
}
|
}
|
||||||
@@ -900,7 +918,14 @@
|
|||||||
}
|
}
|
||||||
updateChartTitle();
|
updateChartTitle();
|
||||||
scheduleChartResize();
|
scheduleChartResize();
|
||||||
setStatus("K 线 " + candles.length + " 根 · " + timeframe);
|
setStatus(
|
||||||
|
"K 线 " +
|
||||||
|
candles.length +
|
||||||
|
" 根 · " +
|
||||||
|
timeframe +
|
||||||
|
" · " +
|
||||||
|
formatChartContractLabel(selected.symbol, chartExchangeSymbol, chartMarketType)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function openTradeChart(tr) {
|
async function openTradeChart(tr) {
|
||||||
@@ -913,6 +938,7 @@
|
|||||||
}
|
}
|
||||||
selected = { exchange_key: exKey, symbol: sym };
|
selected = { exchange_key: exKey, symbol: sym };
|
||||||
selectedTradeId = String(tr.trade_id || tr.id);
|
selectedTradeId = String(tr.trade_id || tr.id);
|
||||||
|
renderTrades();
|
||||||
setChartOpen(true);
|
setChartOpen(true);
|
||||||
await loadSymbolTradesForChart(exKey, sym);
|
await loadSymbolTradesForChart(exKey, sym);
|
||||||
await loadChart();
|
await loadChart();
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||||
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&family=Orbitron:wght@500;600;700&display=swap" rel="stylesheet" media="print" onload="this.media='all'" />
|
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&family=Orbitron:wght@500;600;700&display=swap" rel="stylesheet" media="print" onload="this.media='all'" />
|
||||||
<noscript><link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&family=Orbitron:wght@500;600;700&display=swap" rel="stylesheet" /></noscript>
|
<noscript><link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&family=Orbitron:wght@500;600;700&display=swap" rel="stylesheet" /></noscript>
|
||||||
<link rel="stylesheet" href="/assets/app.css?v=20260612-trade-symbol-col" />
|
<link rel="stylesheet" href="/assets/app.css?v=20260612-archive-chart-row" />
|
||||||
<link rel="stylesheet" href="/assets/dashboard.css?v=20260612-dash-monitor-count" />
|
<link rel="stylesheet" href="/assets/dashboard.css?v=20260612-dash-monitor-count" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@@ -584,7 +584,7 @@
|
|||||||
<script src="https://unpkg.com/lightweight-charts@4.2.0/dist/lightweight-charts.standalone.production.js"></script>
|
<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_draw.js?v=20260609-market-day-split"></script>
|
||||||
<script src="/assets/chart.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-trade-symbol-col"></script>
|
<script src="/assets/archive.js?v=20260612-archive-chart-row"></script>
|
||||||
<script src="/assets/funds.js?v=20260609-hub-funds-fold"></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/dashboard.js?v=20260612-dash-monitor-count"></script>
|
||||||
<script src="/assets/ai_review_render.js?v=3"></script>
|
<script src="/assets/ai_review_render.js?v=3"></script>
|
||||||
|
|||||||
Reference in New Issue
Block a user