优化:折线图初始化数据

This commit is contained in:
ivan 2026-03-31 22:12:44 +08:00
parent fef3d86fc2
commit 1a63d2d6e1
2 changed files with 55 additions and 19 deletions

View File

@ -191,9 +191,10 @@ export interface FetchCryptoChartResponse {
*/
async function fetchBinanceKlines(
binanceSymbol: string,
limit: number
limit: number,
interval: '1s' | '1m' = '1m'
): Promise<CryptoChartPoint[]> {
const url = `${BINANCE_REST}/klines?symbol=${binanceSymbol}&interval=1m&limit=${limit}`
const url = `${BINANCE_REST}/klines?symbol=${binanceSymbol}&interval=${interval}&limit=${limit}`
const res = await fetch(url)
if (!res.ok) throw new Error(`Binance API: ${res.status}`)
const raw = (await res.json()) as [number, string, string, string, string, number, ...unknown[]][]
@ -223,9 +224,41 @@ async function fetchCoinGeckoChart(
/**
*
* Binance1 退 CoinGecko
* Binance1 30S 1 退 CoinGecko
*/
const THIRTY_SEC_MS = 30 * 1000
/**
* 30S
* timeScale 30s 1s K 线
*/
export const CRYPTO_30S_RETENTION_MS = 120 * 1000
/** 30S 初始:拉取的 1s K 线条数,应略大于 {@link CRYPTO_30S_RETENTION_MS}(秒)以抵消网络延迟与 K 线闭合同步 */
const CRYPTO_30S_INITIAL_1S_LIMIT = 150
/** 加密货币分时折线图 X 轴 / 补点统一时间步长ms与图表 timeScale 一致 */
export const CRYPTO_CHART_TIME_STEP_MS = 100
/** 将毫秒时间戳对齐到 {@link CRYPTO_CHART_TIME_STEP_MS} 网格(向下取整,单调递增友好) */
export function alignCryptoChartTimeMs(tsMs: number): number {
const step = CRYPTO_CHART_TIME_STEP_MS
return Math.floor(tsMs / step) * step
}
/** 同一时间对齐戳保留最后一条,避免与 realtime 合并时重复键 */
function dedupeLatestByTime(points: CryptoChartPoint[]): CryptoChartPoint[] {
if (points.length <= 1) return points
const sorted = [...points].sort((a, b) => a[0] - b[0])
const out: CryptoChartPoint[] = []
for (const pt of sorted) {
const last = out[out.length - 1]
if (last && last[0] === pt[0]) {
out[out.length - 1] = pt
} else {
out.push(pt)
}
}
return out
}
export async function fetchCryptoChart(
params: FetchCryptoChartParams
@ -243,10 +276,21 @@ export async function fetchCryptoChart(
try {
if (useBinance) {
let points = await fetchBinanceKlines(binanceSymbol, limit)
let points: CryptoChartPoint[]
if (is30S) {
const cutoff = Date.now() - THIRTY_SEC_MS
points = points.filter(([ts]) => ts >= cutoff)
points = await fetchBinanceKlines(
binanceSymbol,
Math.min(CRYPTO_30S_INITIAL_1S_LIMIT, 1000),
'1s'
)
const cutoff = Date.now() - CRYPTO_30S_RETENTION_MS
points = dedupeLatestByTime(
points
.map(([ts, p]) => [alignCryptoChartTimeMs(ts), p] as CryptoChartPoint)
.filter(([ts]) => ts >= cutoff)
)
} else {
points = await fetchBinanceKlines(binanceSymbol, limit, '1m')
}
return { code: 0, data: points, msg: 'ok' }
}
@ -254,7 +298,7 @@ export async function fetchCryptoChart(
const days = RANGE_TO_DAYS[fetchRange] ?? 7
let points = await fetchCoinGeckoChart(coinId, days)
if (is30S) {
const cutoff = Date.now() - THIRTY_SEC_MS
const cutoff = Date.now() - CRYPTO_30S_RETENTION_MS
points = points.filter(([ts]) => ts >= cutoff)
}
return { code: 0, data: points, msg: 'ok' }
@ -278,15 +322,6 @@ interface BinanceAggTradeMsg {
const CRYPTO_UPDATE_THROTTLE_MS = 80
/** 加密货币分时折线图 X 轴 / 补点统一时间步长ms与图表 timeScale 一致 */
export const CRYPTO_CHART_TIME_STEP_MS = 100
/** 将毫秒时间戳对齐到 {@link CRYPTO_CHART_TIME_STEP_MS} 网格(向下取整,单调递增友好) */
export function alignCryptoChartTimeMs(tsMs: number): number {
const step = CRYPTO_CHART_TIME_STEP_MS
return Math.floor(tsMs / step) * step
}
/**
* Binance WebSocketaggTrade
* https://developers.binance.com/docs/zh-CN/binance-spot-api-docs/web-socket-streams#归集交易

View File

@ -414,6 +414,7 @@ import {
subscribeCryptoRealtime,
alignCryptoChartTimeMs,
CRYPTO_CHART_TIME_STEP_MS,
CRYPTO_30S_RETENTION_MS,
} from '../api/cryptoChart'
const { t } = useI18n()
@ -1225,7 +1226,7 @@ const lineColor = '#2563eb'
/** 叠加价格轴 ID曲线绑定在 overlay scale主图占满宽Y 轴数字由 InPanePriceScalePrimitive 画在图内 */
const CHART_OVERLAY_PRICE_SCALE_ID = 'overlay-price'
/** 右侧留白pxfitContent 时压缩可用宽度线头略离开物理右缘lastPrice 水波能露出一半且不依赖超大 rightOffsetbars */
const CHART_RIPPLE_RIGHT_PAD_PX = 28
const CHART_RIPPLE_RIGHT_PAD_PX = 0
function ensureChartSeries() {
if (!chartInstance || !chartContainerRef.value) return
@ -1392,7 +1393,7 @@ function applyCryptoRealtimePoint(point: [number, number]) {
let updatePoint: [number, number] | null = null
if (range === '30S') {
const cutoff = Date.now() - 30 * 1000
const cutoff = Date.now() - CRYPTO_30S_RETENTION_MS
if (prevLastT != null && ts === prevLastT) {
list[list.length - 1] = [ts, price]
} else {