This commit is contained in:
2026-03-02 01:36:14 -04:00
parent d907c5765e
commit 311df9aeda
21 changed files with 2772 additions and 33 deletions

View File

@@ -0,0 +1,217 @@
<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount, watch } from 'vue'
import Card from 'primevue/card'
import { createTradingViewDatafeed } from '../composables/useTradingViewDatafeed'
import { useChartStore } from '../stores/chart'
import type { IChartingLibraryWidget } from '../types/tradingview'
const chartContainer = ref<HTMLDivElement | null>(null)
const chartStore = useChartStore()
let tvWidget: IChartingLibraryWidget | null = null
let datafeed: any = null
let isUpdatingFromChart = false // Flag to prevent circular updates
onMounted(() => {
if (!chartContainer.value) return
// Wait for TradingView library to load
const initChart = () => {
if (!window.TradingView) {
setTimeout(initChart, 100)
return
}
try {
datafeed = createTradingViewDatafeed()
tvWidget = new window.TradingView.widget({
symbol: chartStore.chart_state.symbol, // Use symbol from store
datafeed: datafeed,
interval: chartStore.chart_state.interval as any,
container: chartContainer.value!,
library_path: '/charting_library/',
locale: 'en',
disabled_features: [
'use_localstorage_for_settings',
'header_symbol_search',
'symbol_search_hot_key'
],
enabled_features: ['study_templates'],
fullscreen: false,
autosize: true,
theme: 'Dark',
timezone: 'Etc/UTC'
})
tvWidget.onChartReady(() => {
console.log('TradingView chart ready')
setupChartListeners()
setupStoreWatchers()
// Initialize visible range on chart load
initializeVisibleRange()
})
} catch (error) {
console.error('Failed to initialize TradingView widget:', error)
}
}
initChart()
})
function initializeVisibleRange() {
if (!tvWidget) return
const chart = tvWidget.activeChart()
const timeScale = chart.getVisibleRange()
if (timeScale) {
const startTime = Math.floor(timeScale.from)
const endTime = Math.floor(timeScale.to)
console.log('[ChartView] Initial visible range:', {
from: new Date(startTime * 1000).toISOString(),
to: new Date(endTime * 1000).toISOString()
})
chartStore.chart_state.start_time = startTime
chartStore.chart_state.end_time = endTime
}
}
function setupChartListeners() {
if (!tvWidget) return
const chart = tvWidget.activeChart()
// Listen for symbol changes
chart.onSymbolChanged().subscribe(null, () => {
const symbolInfo = chart.symbolExt()
if (symbolInfo && symbolInfo.ticker) {
console.log('[ChartView] Symbol changed to:', symbolInfo.ticker)
isUpdatingFromChart = true
chartStore.chart_state.symbol = symbolInfo.ticker
isUpdatingFromChart = false
}
})
// Listen for interval changes
chart.onIntervalChanged().subscribe(null, (interval: string) => {
console.log('[ChartView] Interval changed to:', interval)
isUpdatingFromChart = true
chartStore.chart_state.interval = interval
isUpdatingFromChart = false
})
// Listen for visible range changes (when user pans/zooms)
chart.onVisibleRangeChanged().subscribe(null, () => {
const timeScale = chart.getVisibleRange()
if (timeScale) {
// Convert from seconds to seconds (TradingView uses seconds for visible range)
const startTime = Math.floor(timeScale.from)
const endTime = Math.floor(timeScale.to)
console.log('[ChartView] Visible range changed:', {
from: new Date(startTime * 1000).toISOString(),
to: new Date(endTime * 1000).toISOString()
})
isUpdatingFromChart = true
chartStore.chart_state.start_time = startTime
chartStore.chart_state.end_time = endTime
isUpdatingFromChart = false
}
})
}
function setupStoreWatchers() {
if (!tvWidget) return
const chart = tvWidget.activeChart()
// Watch for external changes to symbol (e.g., from backend/agent)
watch(
() => chartStore.chart_state.symbol,
(newSymbol) => {
if (isUpdatingFromChart) return // Ignore updates that came from the chart itself
console.log('[ChartView] Store symbol changed externally to:', newSymbol)
const currentSymbol = chart.symbolExt()
if (currentSymbol && currentSymbol.ticker !== newSymbol) {
chart.setSymbol(newSymbol, () => {
console.log('[ChartView] Chart symbol updated to:', newSymbol)
})
}
}
)
// Watch for external changes to interval
watch(
() => chartStore.chart_state.interval,
(newInterval) => {
if (isUpdatingFromChart) return
console.log('[ChartView] Store interval changed externally to:', newInterval)
if (chart.resolution() !== newInterval) {
chart.setResolution(newInterval, () => {
console.log('[ChartView] Chart interval updated to:', newInterval)
})
}
}
)
// Note: Visible range is typically only set by user interaction,
// but we could add a watcher here if we want the backend to be able
// to change the visible range programmatically
}
onBeforeUnmount(() => {
if (tvWidget) {
tvWidget.remove()
tvWidget = null
}
if (datafeed && typeof datafeed.destroy === 'function') {
datafeed.destroy()
datafeed = null
}
})
</script>
<template>
<Card class="chart-card">
<template #content>
<div ref="chartContainer" class="chart-container"></div>
</template>
</Card>
</template>
<style scoped>
.chart-card {
height: 100% !important;
display: flex;
flex-direction: column;
overflow: hidden;
border: none;
background: var(--p-surface-0);
}
.chart-card :deep(.p-card-body) {
flex: 1;
display: flex;
flex-direction: column;
padding: 0;
overflow: hidden;
}
.chart-card :deep(.p-card-content) {
flex: 1;
display: flex;
overflow: hidden;
padding: 0;
}
.chart-container {
flex: 1;
width: 100%;
height: 100%;
}
</style>