chart data loading

This commit is contained in:
2026-03-24 21:37:49 -04:00
parent f6bd22a8ef
commit c76887ab92
65 changed files with 6350 additions and 713 deletions

View File

@@ -31,12 +31,18 @@ interface Subscription {
subscriptionId: string
}
interface SymbolDenominators {
tick: number
base: number
}
export class WebSocketDatafeed implements IBasicDataFeed {
private pendingRequests: Map<string, PendingRequest> = new Map()
private subscriptions: Map<string, Subscription> = new Map()
private requestTimeout = 10000 // 10 seconds
private configuration: DatafeedConfiguration | null = null
private messageHandler: MessageHandler
private symbolDenominators: Map<string, SymbolDenominators> = new Map() // Track denominators per symbol
constructor() {
// Use the shared WebSocket connection (managed by App.vue authentication)
@@ -53,19 +59,27 @@ export class WebSocketDatafeed implements IBasicDataFeed {
const requestId = message.request_id || this.generateRequestId()
message.request_id = requestId
console.log('[TradingView Datafeed] Sending request:', requestId, message.type, message)
return new Promise((resolve, reject) => {
const timeout = window.setTimeout(() => {
console.error('[TradingView Datafeed] Request timeout:', requestId, message.type)
this.pendingRequests.delete(requestId)
reject(new Error('Request timeout'))
}, this.requestTimeout)
this.pendingRequests.set(requestId, { resolve, reject, timeout })
const wsState = wsManager.getWebSocket()?.readyState
console.log('[TradingView Datafeed] WebSocket state before send:', wsState, 'OPEN=' + WebSocket.OPEN)
wsManager.send(message)
})
}
private handleMessage(message: any): void {
console.log('[TradingView Datafeed] Received message:', message)
console.log('[TradingView Datafeed] Received message:', message.type, message)
console.log('[TradingView Datafeed] Pending requests count:', this.pendingRequests.size)
// Handle responses to pending requests
if (message.request_id && this.pendingRequests.has(message.request_id)) {
@@ -75,27 +89,30 @@ export class WebSocketDatafeed implements IBasicDataFeed {
clearTimeout(pending.timeout)
if (message.type === 'error') {
console.log('[TradingView Datafeed] Resolving with error:', message.error_message)
console.error('[TradingView Datafeed] Resolving with error:', message.error_message)
pending.reject(new Error(message.error_message || 'Unknown error'))
} else {
console.log('[TradingView Datafeed] Resolving with response')
console.log('[TradingView Datafeed] Resolving with response:', message.type)
pending.resolve(message)
}
} else if (message.request_id) {
console.log('[TradingView Datafeed] No pending request found for:', message.request_id)
console.warn('[TradingView Datafeed] No pending request found for:', message.request_id, 'Available:', Array.from(this.pendingRequests.keys()))
}
// Handle real-time bar updates
if (message.type === 'bar_update') {
const subscription = this.subscriptions.get(message.subscription_id)
if (subscription && message.bar) {
const symbolKey = subscription.symbolInfo.ticker || subscription.symbolInfo.name
const denoms = this.symbolDenominators.get(symbolKey) || { tick: 1, base: 1 }
const bar: Bar = {
time: message.bar.time * 1000, // Convert to milliseconds
open: parseFloat(message.bar.data.open),
high: parseFloat(message.bar.data.high),
low: parseFloat(message.bar.data.low),
close: parseFloat(message.bar.data.close),
volume: parseFloat(message.bar.data.volume)
open: parseFloat(message.bar.open) / denoms.tick,
high: parseFloat(message.bar.high) / denoms.tick,
low: parseFloat(message.bar.low) / denoms.tick,
close: parseFloat(message.bar.close) / denoms.tick,
volume: parseFloat(message.bar.volume) / denoms.base
}
subscription.onTick(bar)
}
@@ -159,20 +176,42 @@ export class WebSocketDatafeed implements IBasicDataFeed {
onResolve: (symbolInfo: LibrarySymbolInfo) => void,
onError: (reason: string) => void
): void {
console.log('[TradingView Datafeed] Resolving symbol:', symbolName)
console.log('[TradingView Datafeed] WebSocket state:', wsManager.getWebSocket()?.readyState)
this.sendRequest<any>({
type: 'resolve_symbol',
symbol: symbolName
})
.then((response) => {
console.log('[TradingView Datafeed] Received response:', response)
if (response.symbol_info) {
console.log('[TradingView Datafeed] Resolved symbol info:', response.symbol_info)
// Store the denominators for this symbol
const symbolKey = response.symbol_info.ticker || response.symbol_info.name
const tickDenom = response.symbol_info.tick_denominator || 1
const baseDenom = response.symbol_info.base_denominator || 1
this.symbolDenominators.set(symbolKey, {
tick: tickDenom,
base: baseDenom
})
console.log('[TradingView Datafeed] Stored denominators:', symbolKey, { tick: tickDenom, base: baseDenom })
onResolve(response.symbol_info)
} else {
console.error('[TradingView Datafeed] No symbol_info in response')
onError('Symbol not found')
}
})
.catch((error) => {
console.error('Failed to resolve symbol:', error)
console.error('[TradingView Datafeed] Failed to resolve symbol:', symbolName, error)
console.error('[TradingView Datafeed] Error details:', {
message: error.message,
stack: error.stack,
pendingRequests: this.pendingRequests.size
})
onError(error instanceof Error ? error.message : 'Unknown error')
})
}
@@ -189,9 +228,12 @@ export class WebSocketDatafeed implements IBasicDataFeed {
onResult: (bars: Bar[], meta: HistoryMetadata) => void,
onError: (reason: string) => void
): void {
const symbolKey = symbolInfo.ticker || symbolInfo.name
const denoms = this.symbolDenominators.get(symbolKey) || { tick: 1, base: 1 }
this.sendRequest<any>({
type: 'get_bars',
symbol: symbolInfo.ticker || symbolInfo.name,
symbol: symbolKey,
resolution: resolution,
from_time: periodParams.from,
to_time: periodParams.to,
@@ -199,15 +241,20 @@ export class WebSocketDatafeed implements IBasicDataFeed {
})
.then((response) => {
if (response.history) {
console.log('[TradingView Datafeed] Raw bar sample:', response.history.bars?.[0])
console.log('[TradingView Datafeed] Denominators:', denoms)
const bars: Bar[] = (response.history.bars || []).map((bar: any) => ({
time: bar.time * 1000, // Convert to milliseconds
open: parseFloat(bar.data.open),
high: parseFloat(bar.data.high),
low: parseFloat(bar.data.low),
close: parseFloat(bar.data.close),
volume: parseFloat(bar.data.volume)
open: parseFloat(bar.open) / denoms.tick,
high: parseFloat(bar.high) / denoms.tick,
low: parseFloat(bar.low) / denoms.tick,
close: parseFloat(bar.close) / denoms.tick,
volume: parseFloat(bar.volume) / denoms.base
}))
console.log('[TradingView Datafeed] Scaled bar sample:', bars[0])
const meta: HistoryMetadata = {
noData: bars.length === 0,
nextTime: response.history.next_time