fix(hub): load full pre-entry history for archive chart pan/zoom
range=history serves archive seed through close (not now). Default view focuses hold period; user can scroll/zoom left to see global morphology before entry. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1734,6 +1734,7 @@ def api_archive_ohlcv(
|
||||
anchor_ms: str = "",
|
||||
opened_ms: str = "",
|
||||
closed_ms: str = "",
|
||||
range: str = "",
|
||||
at: str = "",
|
||||
bars: str = "",
|
||||
):
|
||||
@@ -1758,6 +1759,7 @@ def api_archive_ohlcv(
|
||||
closed_ms=close_ms,
|
||||
mode=mode,
|
||||
bars=bar_n,
|
||||
range_mode=(range or "").strip().lower() or "window",
|
||||
)
|
||||
if not result.get("ok"):
|
||||
raise HTTPException(status_code=404, detail=result.get("msg") or "无 K 线")
|
||||
|
||||
@@ -252,16 +252,18 @@
|
||||
});
|
||||
}
|
||||
|
||||
function focusHoldRange(candles, tr, tf) {
|
||||
/** 初始只聚焦持仓段;完整历史已加载,可向左拖动/滚轮缩小查看建仓前全局。 */
|
||||
function focusInitialTradeView(candles, tr, tf) {
|
||||
if (!chart || !candles.length || !tr) return;
|
||||
const mode = (elViewMode && elViewMode.value) || "hold";
|
||||
const openSec = tradeOpenMs(tr) ? msToBarTime(tradeOpenMs(tr), tf) : null;
|
||||
const closeSec = tradeCloseMs(tr) ? msToBarTime(tradeCloseMs(tr), tf) : null;
|
||||
let fromIdx = 0;
|
||||
let toIdx = candles.length - 1;
|
||||
let openIdx = 0;
|
||||
let closeIdx = candles.length - 1;
|
||||
if (openSec != null) {
|
||||
for (let i = 0; i < candles.length; i++) {
|
||||
if (candles[i].time >= openSec) {
|
||||
fromIdx = Math.max(0, i - 12);
|
||||
openIdx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -269,11 +271,21 @@
|
||||
if (closeSec != null) {
|
||||
for (let i = candles.length - 1; i >= 0; i--) {
|
||||
if (candles[i].time <= closeSec) {
|
||||
toIdx = Math.min(candles.length - 1, i + 12);
|
||||
closeIdx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
const span = Math.max(24, closeIdx - openIdx + 20);
|
||||
let fromIdx;
|
||||
let toIdx;
|
||||
if (mode === "entry") {
|
||||
fromIdx = Math.max(0, openIdx - Math.floor(span * 0.35));
|
||||
toIdx = Math.min(candles.length - 1, openIdx + Math.floor(span * 0.65));
|
||||
} else {
|
||||
fromIdx = Math.max(0, openIdx - 10);
|
||||
toIdx = Math.min(candles.length - 1, closeIdx + 14);
|
||||
}
|
||||
if (toIdx <= fromIdx) {
|
||||
toIdx = Math.min(candles.length - 1, fromIdx + 80);
|
||||
}
|
||||
@@ -303,9 +315,24 @@
|
||||
vertLines: { color: isDark ? "#1a2030" : "#e8ecf2" },
|
||||
horzLines: { color: isDark ? "#1a2030" : "#e8ecf2" },
|
||||
},
|
||||
rightPriceScale: { borderColor: isDark ? "#2a3348" : "#d0d7e2" },
|
||||
timeScale: { borderColor: isDark ? "#2a3348" : "#d0d7e2", timeVisible: true },
|
||||
rightPriceScale: { borderColor: isDark ? "#2a3348" : "#d0d7e2", autoScale: true },
|
||||
timeScale: {
|
||||
borderColor: isDark ? "#2a3348" : "#d0d7e2",
|
||||
timeVisible: true,
|
||||
secondsVisible: false,
|
||||
},
|
||||
crosshair: { mode: LightweightCharts.CrosshairMode.Normal },
|
||||
handleScroll: {
|
||||
mouseWheel: true,
|
||||
pressedMouseMove: true,
|
||||
horzTouchDrag: true,
|
||||
vertTouchDrag: false,
|
||||
},
|
||||
handleScale: {
|
||||
axisPressedMouseMove: true,
|
||||
mouseWheel: true,
|
||||
pinch: true,
|
||||
},
|
||||
});
|
||||
candleSeries = chart.addCandlestickSeries({
|
||||
upColor: "#22c55e",
|
||||
@@ -335,19 +362,23 @@
|
||||
const tr = pickAnchorTrade();
|
||||
const anchor = anchorMsForTrade(tr);
|
||||
const jump = (elJumpAt && elJumpAt.value || "").trim();
|
||||
const openMs = tradeOpenMs(tr);
|
||||
const closeMs = tradeCloseMs(tr);
|
||||
const params = new URLSearchParams({
|
||||
exchange_key: selected.exchange_key,
|
||||
symbol: selected.symbol,
|
||||
timeframe: timeframe,
|
||||
mode: (elViewMode && elViewMode.value) || "hold",
|
||||
bars: "200",
|
||||
});
|
||||
if (jump) params.set("at", jump);
|
||||
else if (anchor) params.set("anchor_ms", String(anchor));
|
||||
const openMs = tradeOpenMs(tr);
|
||||
const closeMs = tradeCloseMs(tr);
|
||||
if (openMs) params.set("opened_ms", String(openMs));
|
||||
if (closeMs) params.set("closed_ms", String(closeMs));
|
||||
if (openMs && closeMs) {
|
||||
params.set("range", "history");
|
||||
params.set("opened_ms", String(openMs));
|
||||
params.set("closed_ms", String(closeMs));
|
||||
} else {
|
||||
params.set("bars", "200");
|
||||
if (jump) params.set("at", jump);
|
||||
else if (anchor) params.set("anchor_ms", String(anchor));
|
||||
}
|
||||
setStatus("加载 K 线…");
|
||||
const r = await apiFetch("/api/archive/ohlcv?" + params.toString());
|
||||
const j = await r.json();
|
||||
@@ -375,11 +406,12 @@
|
||||
candleSeries.setMarkers(buildTradeMarkers(tr, candles, timeframe));
|
||||
}
|
||||
if (tr && tradeOpenMs(tr) && tradeCloseMs(tr)) {
|
||||
focusHoldRange(candles, tr, timeframe);
|
||||
focusInitialTradeView(candles, tr, timeframe);
|
||||
} else if (candles.length > 10) {
|
||||
chart.timeScale().setVisibleLogicalRange({ from: candles.length - 120, to: candles.length + 5 });
|
||||
}
|
||||
setStatus("K 线 " + candles.length + " 根 · " + timeframe + (tr ? " · 已标注开/平" : ""));
|
||||
const histHint = openMs && closeMs ? " · 可拖动/滚轮缩放查看建仓前走势" : "";
|
||||
setStatus("K 线 " + candles.length + " 根 · " + timeframe + (tr ? " · 已标注开/平" : "") + histHint);
|
||||
}
|
||||
|
||||
function renderTrades() {
|
||||
|
||||
@@ -189,7 +189,7 @@
|
||||
<div id="page-archive" class="page hidden">
|
||||
<div class="page-head">
|
||||
<h1><span class="head-tag">ARC</span> 币种档案</h1>
|
||||
<p class="page-desc">一所一币一行 · 交易时间线 · 永久 5m K 线(15m/1h/4h 聚合)</p>
|
||||
<p class="page-desc">一所一币一行 · 交易时间线 · 建档 30 天 K 线可拖动缩放(默认聚焦持仓段,不含拉到「现在」)</p>
|
||||
</div>
|
||||
<div class="archive-toolbar toolbar">
|
||||
<label class="archive-field">
|
||||
@@ -349,7 +349,7 @@
|
||||
<div id="toast"></div>
|
||||
<script src="https://unpkg.com/lightweight-charts@4.2.0/dist/lightweight-charts.standalone.production.js"></script>
|
||||
<script src="/assets/chart.js?v=20260604-upnl-contracts"></script>
|
||||
<script src="/assets/archive.js?v=20260607-hub-archive-v3"></script>
|
||||
<script src="/assets/archive.js?v=20260607-hub-archive-v4"></script>
|
||||
<script src="/assets/ai_review_render.js?v=2"></script>
|
||||
<script src="/assets/app.js?v=20260607-hub-archive-v1"></script>
|
||||
</body>
|
||||
|
||||
Reference in New Issue
Block a user