mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-07-11 14:56:57 +08:00
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:
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user