fix: align order markers to kline candles using binary search

- Use binary search to find exact kline candle for each order
- Sort markers by time as required by lightweight-charts
- Filter orders outside kline data range
- Based on lightweight-charts issue #1182 recommendation
This commit is contained in:
tinkle-community
2025-12-27 19:38:01 +08:00
parent 8fb0d2e7e9
commit e204707845
2 changed files with 105 additions and 43 deletions

View File

@@ -311,6 +311,29 @@ export function ChartWithOrders({
console.log('[ChartWithOrders] Kline data received:', klineData.length, 'candles')
candlestickSeriesRef.current.setData(klineData)
// 构建 K 线时间集合,用于快速查找
const klineTimeSet = new Set(klineData.map(k => k.time as number))
const klineMinTime = klineData.length > 0 ? klineData[0].time : 0
const klineMaxTime = klineData.length > 0 ? klineData[klineData.length - 1].time : 0
console.log('[ChartWithOrders] Kline time range:', klineMinTime, '-', klineMaxTime, 'candles:', klineData.length)
// 计算时间周期的秒数
const getIntervalSeconds = (interval: string): number => {
const match = interval.match(/(\d+)([smhd])/)
if (!match) return 60 // 默认1分钟
const [, num, unit] = match
const n = parseInt(num)
switch (unit) {
case 's': return n
case 'm': return n * 60
case 'h': return n * 3600
case 'd': return n * 86400
default: return 60
}
}
const intervalSeconds = getIntervalSeconds(interval)
console.log('[ChartWithOrders] Interval:', interval, '=', intervalSeconds, 'seconds')
// 2. 获取订单数据并添加标记
if (traderID) {
console.log('[ChartWithOrders] Fetching orders for trader:', traderID, 'symbol:', symbol)
@@ -321,36 +344,42 @@ export function ChartWithOrders({
console.log('[ChartWithOrders] No orders to display')
}
// 过滤掉无效时间戳的订单小于2024年的时间戳
const minValidTimestamp = new Date('2024-01-01').getTime() / 1000
const validOrders = orders.filter(order => {
if (order.time < minValidTimestamp) {
console.warn('[ChartWithOrders] ⚠️ Skipping order with invalid timestamp:', order.time, '(', new Date(order.time * 1000).toISOString(), ')')
return false
// 转换订单为图表标记,并对齐到 K 线时间
const markers: Array<{
time: Time
position: 'belowBar'
color: string
shape: 'circle'
text: string
price: number
size: number
}> = []
orders.forEach((order) => {
// 将订单时间对齐到 K 线周期(向下取整)
const alignedTime = Math.floor(order.time / intervalSeconds) * intervalSeconds
// 检查对齐后的时间是否在 K 线数据中存在
if (!klineTimeSet.has(alignedTime)) {
console.warn('[ChartWithOrders] ⚠️ Skipping order - no matching kline:',
order.time, '→', alignedTime, '(', new Date(order.time * 1000).toISOString(), ')')
return
}
return true
})
console.log('[ChartWithOrders] Valid orders:', validOrders.length, 'out of', orders.length)
// 转换订单为图表标记 - 简洁版:只用 B/S
const markers = validOrders.map((order) => {
// 使用 side 字段判断买卖(更准确)
// side = BUY → 绿色 B
// side = SELL → 红色 S
const isBuy = order.side === 'BUY'
return {
time: order.time as Time,
markers.push({
time: alignedTime as Time,
position: 'belowBar' as const,
color: isBuy ? '#0ECB81' : '#F6465D',
shape: 'circle' as const,
text: isBuy ? 'B' : 'S',
price: order.price,
size: 1,
}
})
})
console.log('[ChartWithOrders] Valid markers (with matching klines):', markers.length, 'out of', orders.length)
console.log('[ChartWithOrders] Setting', markers.length, 'markers on chart')
try {