diff --git a/app/api/analyze/route.ts b/app/api/analyze/route.ts index 38d9009..b8f1dd9 100644 --- a/app/api/analyze/route.ts +++ b/app/api/analyze/route.ts @@ -40,6 +40,7 @@ export async function POST(req: NextRequest) { return NextResponse.json({ ...result, + layoutsAnalyzed: finalLayouts, settings: { symbol: finalSymbol, timeframe: finalTimeframe, diff --git a/components/AIAnalysisPanel.tsx b/components/AIAnalysisPanel.tsx index f287e11..5ef7929 100644 --- a/components/AIAnalysisPanel.tsx +++ b/components/AIAnalysisPanel.tsx @@ -15,21 +15,36 @@ const timeframes = [ export default function AIAnalysisPanel() { const [symbol, setSymbol] = useState('BTCUSD') - const [layout, setLayout] = useState(layouts[0]) + const [selectedLayouts, setSelectedLayouts] = useState([layouts[0]]) const [timeframe, setTimeframe] = useState('60') const [loading, setLoading] = useState(false) const [result, setResult] = useState(null) const [error, setError] = useState(null) + const toggleLayout = (layout: string) => { + setSelectedLayouts(prev => + prev.includes(layout) + ? prev.filter(l => l !== layout) + : [...prev, layout] + ) + } + async function handleAnalyze() { setLoading(true) setError(null) setResult(null) + + if (selectedLayouts.length === 0) { + setError('Please select at least one layout') + setLoading(false) + return + } + try { const res = await fetch('/api/analyze', { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ symbol, layout, timeframe }) + body: JSON.stringify({ symbol, layouts: selectedLayouts, timeframe }) }) const data = await res.json() if (!res.ok) throw new Error(data.error || 'Unknown error') @@ -50,13 +65,6 @@ export default function AIAnalysisPanel() { onChange={e => setSymbol(e.target.value)} placeholder="Symbol (e.g. BTCUSD)" /> - toggleLayout(layout)} + className="form-checkbox h-4 w-4 text-blue-600 rounded" + /> + {layout} + + ))} + + {selectedLayouts.length > 0 && ( +
+ Selected: {selectedLayouts.join(', ')} +
+ )} + {error && (
{error.includes('frame was detached') ? ( @@ -93,12 +126,19 @@ export default function AIAnalysisPanel() { )} {result && (
-
Summary: {result.summary}
-
Sentiment: {result.marketSentiment}
-
Recommendation: {result.recommendation} ({result.confidence}%)
-
Support: {result.keyLevels?.support?.join(', ')}
-
Resistance: {result.keyLevels?.resistance?.join(', ')}
-
Reasoning: {result.reasoning}
+ {result.layoutsAnalyzed && ( +
+ Layouts analyzed: {result.layoutsAnalyzed.join(', ')} +
+ )} +
+
Summary: {result.summary}
+
Sentiment: {result.marketSentiment}
+
Recommendation: {result.recommendation} ({result.confidence}%)
+
Support: {result.keyLevels?.support?.join(', ')}
+
Resistance: {result.keyLevels?.resistance?.join(', ')}
+
Reasoning: {result.reasoning}
+
)}
diff --git a/lib/tradingview.ts b/lib/tradingview.ts index 75a8b3d..a680b62 100644 --- a/lib/tradingview.ts +++ b/lib/tradingview.ts @@ -306,50 +306,72 @@ export class TradingViewCapture { if (layoutButton) { await layoutButton.click() console.log('Clicked layout button') - await new Promise(res => setTimeout(res, 1000)) - // Look for search input or layout items - const searchSelectors = [ - 'input[name="search"]', - 'input[placeholder*="search" i]', - 'input[type="search"]', - 'input[data-name="search"]' + // Wait longer for the layout menu to appear + await new Promise(res => setTimeout(res, 2000)) + + // Take a debug screenshot of the layout menu + const debugMenuPath = path.resolve(`debug_layout_menu_${layout.replace(/\s+/g, '_')}.png`) as `${string}.png` + await page.screenshot({ path: debugMenuPath }) + console.log('Layout menu screenshot saved:', debugMenuPath) + + // Look for layout menu items with more specific selectors + const layoutItemSelectors = [ + `[data-name="chart-layout-list-item"]`, + `[data-testid*="layout"]`, + `.layout-item`, + `[role="option"]`, + `[role="menuitem"]`, + `.tv-dropdown-behavior__item`, + `.tv-menu__item`, + `li[data-value*="${layout}"]`, + `div[data-layout-name="${layout}"]` ] - let searchInput = null - for (const selector of searchSelectors) { + let layoutItem = null + let foundMethod = '' + let foundElement = false + + // Try to find layout item by exact text match first + for (const selector of layoutItemSelectors) { try { - searchInput = await page.waitForSelector(selector, { timeout: 3000 }) - if (searchInput) break + console.log(`Trying selector: ${selector}`) + const items = await page.$$(selector) + console.log(`Found ${items.length} items with selector: ${selector}`) + + for (const item of items) { + const text = await page.evaluate(el => { + const element = el as HTMLElement + return (element.innerText || element.textContent || '').trim() + }, item) + console.log(`Item text: "${text}"`) + + if (text && text.toLowerCase() === layout.toLowerCase()) { + layoutItem = item + foundMethod = `exact match with selector: ${selector}` + break + } + } + if (layoutItem) break } catch (e) { - // Continue to next selector + console.log(`Error with selector ${selector}:`, e) } } - if (searchInput) { - console.log('Found search input, typing layout name...') - await searchInput.type(layout, { delay: 50 }) - await new Promise(res => setTimeout(res, 2000)) - - // Try to find and click the layout item - const layoutItemSelectors = [ - '[data-name="chart-layout-list-item"]', - '[data-testid*="layout-item"]', - '.layout-item', - '[role="option"]' - ] - - let layoutItem = null + // If no exact match, try partial match + if (!layoutItem) { for (const selector of layoutItemSelectors) { try { const items = await page.$$(selector) for (const item of items) { const text = await page.evaluate(el => { const element = el as HTMLElement - return element.innerText || element.textContent + return (element.innerText || element.textContent || '').trim() }, item) + if (text && text.toLowerCase().includes(layout.toLowerCase())) { layoutItem = item + foundMethod = `partial match with selector: ${selector}` break } } @@ -358,33 +380,104 @@ export class TradingViewCapture { // Continue to next selector } } - - if (layoutItem) { - await layoutItem.click() - console.log('Clicked layout item:', layout) - } else { - console.log('Layout item not found, trying generic approach...') - await page.evaluate((layout) => { - const items = Array.from(document.querySelectorAll('*')) - const item = items.find(el => el.textContent?.toLowerCase().includes(layout.toLowerCase())) - if (item) (item as HTMLElement).click() - }, layout) - } - } else { - console.log('Search input not found, trying to find layout directly...') - await page.evaluate((layout) => { - const items = Array.from(document.querySelectorAll('*')) - const item = items.find(el => el.textContent?.toLowerCase().includes(layout.toLowerCase())) - if (item) (item as HTMLElement).click() + } + + // If still no match, try a more comprehensive search + if (!layoutItem) { + console.log('No layout item found with standard selectors, trying comprehensive search...') + const foundElement = await page.evaluate((layout) => { + const allElements = Array.from(document.querySelectorAll('*')) + + // Look for elements that contain the layout name + const candidates = allElements.filter(el => { + const text = (el.textContent || '').trim() + return text && text.toLowerCase().includes(layout.toLowerCase()) + }) + + console.log('Found candidates:', candidates.map(el => ({ + tag: el.tagName, + text: el.textContent?.trim(), + classes: el.className + }))) + + // Prioritize clickable elements + const clickable = candidates.find(el => + el.tagName === 'BUTTON' || + el.tagName === 'A' || + el.hasAttribute('role') || + el.classList.contains('item') || + el.classList.contains('option') || + el.classList.contains('menu') + ) + + if (clickable) { + (clickable as HTMLElement).click() + return true + } + + // Fall back to exact text match + const exactMatch = candidates.find(el => + (el.textContent || '').trim().toLowerCase() === layout.toLowerCase() + ) + + if (exactMatch) { + (exactMatch as HTMLElement).click() + return true + } + + return false }, layout) - } - await new Promise(res => setTimeout(res, 4000)) - console.log('Layout loaded:', layout) + + if (foundElement) { + foundMethod = 'comprehensive search with click' + console.log(`Found and clicked layout item "${layout}" using: ${foundMethod}`) + } + } + + if (layoutItem) { + console.log(`Found layout item "${layout}" using: ${foundMethod}`) + await layoutItem.click() + console.log('Clicked layout item:', layout) + + // Wait for layout to actually load + await new Promise(res => setTimeout(res, 5000)) + + // Take a screenshot after layout change + const debugAfterPath = path.resolve(`debug_after_layout_${layout.replace(/\s+/g, '_')}.png`) as `${string}.png` + await page.screenshot({ path: debugAfterPath }) + console.log('After layout change screenshot saved:', debugAfterPath) + + } else if (foundElement && foundMethod === 'comprehensive search with click') { + console.log('Layout item was clicked via comprehensive search') + + // Wait for layout to actually load + await new Promise(res => setTimeout(res, 5000)) + + // Take a screenshot after layout change + const debugAfterPath = path.resolve(`debug_after_layout_${layout.replace(/\s+/g, '_')}.png`) as `${string}.png` + await page.screenshot({ path: debugAfterPath }) + console.log('After layout change screenshot saved:', debugAfterPath) + + } else { + console.log('Layout item not found with any method') + + // List all text content on the page for debugging + const allTexts = await page.evaluate(() => { + const elements = Array.from(document.querySelectorAll('*')) + return elements + .map(el => (el.textContent || '').trim()) + .filter(text => text && text.length > 0 && text.length < 100) + .slice(0, 50) // Limit to first 50 for debugging + }) + console.log('Available texts on page:', allTexts) + } } else { console.log('Layout button not found, skipping layout loading') } + + console.log('Layout loading completed for:', layout) } catch (e: any) { - const debugLayoutErrorPath = path.resolve('debug_layout_error.png') as `${string}.png` + const debugLayoutErrorPath = path.resolve(`debug_layout_error_${layout.replace(/\s+/g, '_')}.png`) as `${string}.png` await page.screenshot({ path: debugLayoutErrorPath }) console.error('TradingView layout not found or could not be loaded:', e) console.log('Continuing without layout...') diff --git a/screenshots/SOLUSD_5_1752064560978_Diy module.png b/screenshots/SOLUSD_5_1752064560978_Diy module.png new file mode 100644 index 0000000..0a01dcd Binary files /dev/null and b/screenshots/SOLUSD_5_1752064560978_Diy module.png differ diff --git a/screenshots/SOLUSD_5_1752064560978_ai.png b/screenshots/SOLUSD_5_1752064560978_ai.png new file mode 100644 index 0000000..f2b0a4a Binary files /dev/null and b/screenshots/SOLUSD_5_1752064560978_ai.png differ diff --git a/trading-settings.json b/trading-settings.json new file mode 100644 index 0000000..365a54b --- /dev/null +++ b/trading-settings.json @@ -0,0 +1,9 @@ +{ + "symbol": "SOLUSD", + "timeframe": "5", + "layouts": [ + "ai", + "Diy module" + ], + "lastUpdated": 1752064560981 +} \ No newline at end of file