data fixes, partial custom indicator support
This commit is contained in:
@@ -4,9 +4,11 @@ import Card from 'primevue/card'
|
||||
import { createTradingViewDatafeed } from '../composables/useTradingViewDatafeed'
|
||||
import { useTradingViewShapes } from '../composables/useTradingViewShapes'
|
||||
import { useTradingViewIndicators } from '../composables/useTradingViewIndicators'
|
||||
import { useCustomIndicators, getCustomIndicatorsGetter } from '../composables/useCustomIndicators'
|
||||
import { useChartStore } from '../stores/chart'
|
||||
import type { IChartingLibraryWidget } from '../types/tradingview'
|
||||
import { intervalToSeconds } from '../utils'
|
||||
import { wsManager } from '../composables/useWebSocket'
|
||||
|
||||
// Convert seconds to TradingView interval string
|
||||
function secondsToInterval(seconds: number): string {
|
||||
@@ -22,12 +24,25 @@ let datafeed: any = null
|
||||
let isUpdatingFromChart = false // Flag to prevent circular updates
|
||||
let shapeCleanup: (() => void) | null = null // Cleanup function for shape sync
|
||||
let indicatorCleanup: (() => void) | null = null // Cleanup function for indicator sync
|
||||
let customIndicatorCleanup: (() => void) | null = null // Cleanup for custom TV studies
|
||||
let chartInitialized = false // Guard against double-init on reconnect
|
||||
|
||||
const maybeInitChart = () => {
|
||||
if (chartInitialized || !chartContainer.value) return
|
||||
chartInitialized = true
|
||||
initChart()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (!chartContainer.value) return
|
||||
// Wait for workspace to be ready (persistent stores loaded from container)
|
||||
// before initializing TradingView, so stores are populated when onChartReady fires.
|
||||
watch(wsManager.sessionStatus, (status) => {
|
||||
if (status === 'ready') maybeInitChart()
|
||||
}, { immediate: true })
|
||||
})
|
||||
|
||||
// Wait for TradingView library to load
|
||||
const initChart = () => {
|
||||
// Wait for TradingView library to load
|
||||
function initChart() {
|
||||
if (!window.TradingView) {
|
||||
setTimeout(initChart, 100)
|
||||
return
|
||||
@@ -43,16 +58,23 @@ onMounted(() => {
|
||||
container: chartContainer.value!,
|
||||
library_path: '/charting_library/',
|
||||
locale: 'en',
|
||||
// Register the two generic custom study dispatch types.
|
||||
// Must be provided here — TV has no dynamic study registration API.
|
||||
custom_indicators_getter: getCustomIndicatorsGetter(),
|
||||
disabled_features: [
|
||||
'use_localstorage_for_settings',
|
||||
'header_symbol_search',
|
||||
'symbol_search_hot_key'
|
||||
],
|
||||
enabled_features: [],
|
||||
// Restrict indicators to only those supported by both TA-Lib and TradingView
|
||||
// Restrict indicators to only those supported by both TA-Lib and TradingView.
|
||||
// Custom AI-generated indicators (from custom_indicators_getter) must also be listed here.
|
||||
studies_access: {
|
||||
type: 'white',
|
||||
tools: [
|
||||
// AI custom indicator dispatch studies
|
||||
{ name: 'dxo_customstudy_overlay' },
|
||||
{ name: 'dxo_customstudy_pane' },
|
||||
// Overlap Studies (14)
|
||||
{ name: 'Moving Average' },
|
||||
{ name: 'Moving Average Exponential' },
|
||||
@@ -150,15 +172,13 @@ onMounted(() => {
|
||||
if (tvWidget) {
|
||||
shapeCleanup = useTradingViewShapes(tvWidget)
|
||||
indicatorCleanup = useTradingViewIndicators(tvWidget)
|
||||
customIndicatorCleanup = useCustomIndicators(tvWidget)
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Failed to initialize TradingView widget:', error)
|
||||
}
|
||||
}
|
||||
|
||||
initChart()
|
||||
})
|
||||
}
|
||||
|
||||
function initializeVisibleRange() {
|
||||
if (!tvWidget) return
|
||||
@@ -281,6 +301,12 @@ onBeforeUnmount(() => {
|
||||
indicatorCleanup = null
|
||||
}
|
||||
|
||||
// Cleanup custom TV studies
|
||||
if (customIndicatorCleanup) {
|
||||
customIndicatorCleanup()
|
||||
customIndicatorCleanup = null
|
||||
}
|
||||
|
||||
if (tvWidget) {
|
||||
tvWidget.remove()
|
||||
tvWidget = null
|
||||
|
||||
@@ -238,14 +238,7 @@ const handleMessage = (data: WebSocketMessage) => {
|
||||
|
||||
// Stop agent processing
|
||||
const stopAgent = () => {
|
||||
// Send empty message to trigger interrupt without new agent round
|
||||
const wsMessage = {
|
||||
type: 'agent_user_message',
|
||||
session_id: SESSION_ID,
|
||||
content: '',
|
||||
attachments: []
|
||||
}
|
||||
wsManager.send(wsMessage)
|
||||
wsManager.send({ type: 'agent_stop', session_id: SESSION_ID })
|
||||
isAgentProcessing.value = false
|
||||
removeToolCallBubble()
|
||||
lastSentMessageId = null
|
||||
@@ -586,7 +579,9 @@ onUnmounted(() => {
|
||||
}
|
||||
|
||||
.workspace-loading {
|
||||
flex: 1;
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 9999;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
@@ -639,7 +634,7 @@ onUnmounted(() => {
|
||||
.stop-button-container {
|
||||
position: absolute;
|
||||
bottom: 80px;
|
||||
left: 20px;
|
||||
right: 20px;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user