Analyzes market trends and provides insights on TradingView charts
Size
13.7 KB
Version
1.0.1
Created
Oct 22, 2025
Updated
1 day ago
1// ==UserScript==
2// @name Trend Analyzer for TradingView
3// @description Analyzes market trends and provides insights on TradingView charts
4// @version 1.0.1
5// @match https://*.tradingview.com/*
6// @icon https://static.tradingview.com/static/images/favicon.ico
7// ==/UserScript==
8(function() {
9 'use strict';
10
11 console.log('Trend Analyzer for TradingView initialized');
12
13 // Utility function to debounce
14 function debounce(func, wait) {
15 let timeout;
16 return function executedFunction(...args) {
17 const later = () => {
18 clearTimeout(timeout);
19 func(...args);
20 };
21 clearTimeout(timeout);
22 timeout = setTimeout(later, wait);
23 };
24 }
25
26 // Create the analyzer panel
27 function createAnalyzerPanel() {
28 const panel = document.createElement('div');
29 panel.id = 'trend-analyzer-panel';
30 panel.style.cssText = `
31 position: fixed;
32 top: 80px;
33 right: 20px;
34 width: 350px;
35 background: linear-gradient(135deg, #1e222d 0%, #2a2e39 100%);
36 border: 1px solid #3a3e49;
37 border-radius: 12px;
38 padding: 20px;
39 z-index: 10000;
40 box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
41 font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
42 color: #d1d4dc;
43 max-height: 80vh;
44 overflow-y: auto;
45 `;
46
47 panel.innerHTML = `
48 <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px;">
49 <h3 style="margin: 0; color: #fff; font-size: 18px; font-weight: 600;">📊 Trend Analyzer</h3>
50 <button id="close-analyzer" style="background: none; border: none; color: #d1d4dc; cursor: pointer; font-size: 20px; padding: 0; width: 24px; height: 24px;">×</button>
51 </div>
52
53 <div style="margin-bottom: 15px;">
54 <div style="display: flex; align-items: center; gap: 8px; margin-bottom: 10px;">
55 <span style="font-size: 14px; font-weight: 500;">Symbol:</span>
56 <span id="current-symbol" style="color: #2962ff; font-weight: 600; font-size: 14px;">Loading...</span>
57 </div>
58 </div>
59
60 <button id="analyze-trend-btn" style="
61 width: 100%;
62 padding: 12px;
63 background: linear-gradient(135deg, #2962ff 0%, #1e4db7 100%);
64 color: white;
65 border: none;
66 border-radius: 8px;
67 cursor: pointer;
68 font-size: 14px;
69 font-weight: 600;
70 margin-bottom: 15px;
71 transition: all 0.3s ease;
72 ">
73 🔍 Analyze Trend
74 </button>
75
76 <div id="analysis-result" style="
77 background: #131722;
78 border-radius: 8px;
79 padding: 15px;
80 margin-top: 15px;
81 display: none;
82 border: 1px solid #2a2e39;
83 ">
84 <div id="loading-indicator" style="text-align: center; padding: 20px; display: none;">
85 <div style="display: inline-block; width: 30px; height: 30px; border: 3px solid #2a2e39; border-top-color: #2962ff; border-radius: 50%; animation: spin 1s linear infinite;"></div>
86 <p style="margin-top: 10px; color: #787b86;">Analyzing market data...</p>
87 </div>
88 <div id="analysis-content"></div>
89 </div>
90 `;
91
92 // Add CSS animation for loading spinner
93 const style = document.createElement('style');
94 style.textContent = `
95 @keyframes spin {
96 to { transform: rotate(360deg); }
97 }
98 #trend-analyzer-panel::-webkit-scrollbar {
99 width: 8px;
100 }
101 #trend-analyzer-panel::-webkit-scrollbar-track {
102 background: #1e222d;
103 border-radius: 4px;
104 }
105 #trend-analyzer-panel::-webkit-scrollbar-thumb {
106 background: #3a3e49;
107 border-radius: 4px;
108 }
109 #trend-analyzer-panel::-webkit-scrollbar-thumb:hover {
110 background: #4a4e59;
111 }
112 #analyze-trend-btn:hover {
113 transform: translateY(-2px);
114 box-shadow: 0 4px 12px rgba(41, 98, 255, 0.4);
115 }
116 #analyze-trend-btn:active {
117 transform: translateY(0);
118 }
119 `;
120 document.head.appendChild(style);
121
122 document.body.appendChild(panel);
123
124 // Event listeners
125 document.getElementById('close-analyzer').addEventListener('click', () => {
126 panel.style.display = 'none';
127 });
128
129 document.getElementById('analyze-trend-btn').addEventListener('click', analyzeTrend);
130
131 return panel;
132 }
133
134 // Get current symbol from TradingView
135 function getCurrentSymbol() {
136 try {
137 // Try multiple selectors to find the symbol
138 const symbolButton = document.querySelector('button[aria-label="Change symbol"]');
139 if (symbolButton) {
140 return symbolButton.textContent.trim();
141 }
142
143 const symbolLink = document.querySelector('a[data-qa-id="details-element description"] .text-eFCYpbUa');
144 if (symbolLink) {
145 return symbolLink.textContent.trim();
146 }
147
148 const symbolInToolbar = document.querySelector('#header-toolbar-symbol-search .text-cq__ntSC');
149 if (symbolInToolbar) {
150 return symbolInToolbar.textContent.trim();
151 }
152
153 // Fallback to page title
154 const titleMatch = document.title.match(/^([A-Z0-9]+)/);
155 if (titleMatch) {
156 return titleMatch[1];
157 }
158
159 return 'Unknown';
160 } catch (error) {
161 console.error('Error getting symbol:', error);
162 return 'Unknown';
163 }
164 }
165
166 // Get visible chart data
167 function getChartData() {
168 try {
169 const symbol = getCurrentSymbol();
170
171 // Get price information from legend
172 const legendItems = document.querySelectorAll('.valueItem-l31H9iuA');
173 const priceData = {};
174
175 legendItems.forEach(item => {
176 const title = item.querySelector('.valueTitle-l31H9iuA');
177 const value = item.querySelector('.valueValue-l31H9iuA');
178 if (title && value) {
179 priceData[title.textContent.trim()] = value.textContent.trim();
180 }
181 });
182
183 // Get current price and change
184 const priceElement = document.querySelector('.lastContainer-JWoJqCpY');
185 const changeElement = document.querySelector('.changePercent-JWoJqCpY');
186
187 let currentPrice = 'N/A';
188 let priceChange = 'N/A';
189
190 if (priceElement) {
191 currentPrice = priceElement.textContent.trim();
192 }
193
194 if (changeElement) {
195 priceChange = changeElement.textContent.trim();
196 }
197
198 // Get interval
199 const intervalButton = document.querySelector('button[aria-label="Chart interval"]');
200 const interval = intervalButton ? intervalButton.textContent.trim() : 'Unknown';
201
202 return {
203 symbol,
204 currentPrice,
205 priceChange,
206 interval,
207 priceData,
208 timestamp: new Date().toISOString()
209 };
210 } catch (error) {
211 console.error('Error getting chart data:', error);
212 return {
213 symbol: getCurrentSymbol(),
214 error: 'Could not extract chart data'
215 };
216 }
217 }
218
219 // Analyze trend using AI
220 async function analyzeTrend() {
221 const resultDiv = document.getElementById('analysis-result');
222 const loadingDiv = document.getElementById('loading-indicator');
223 const contentDiv = document.getElementById('analysis-content');
224
225 resultDiv.style.display = 'block';
226 loadingDiv.style.display = 'block';
227 contentDiv.innerHTML = '';
228
229 try {
230 const chartData = getChartData();
231 console.log('Chart data collected:', chartData);
232
233 // Create analysis prompt
234 const prompt = `Analyze the following trading data and provide insights:
235
236Symbol: ${chartData.symbol}
237Current Price: ${chartData.currentPrice}
238Price Change: ${chartData.priceChange}
239Timeframe: ${chartData.interval}
240Additional Data: ${JSON.stringify(chartData.priceData)}
241
242Please provide:
2431. Overall trend direction (bullish, bearish, or neutral)
2442. Key support and resistance levels if identifiable
2453. Market sentiment analysis
2464. Potential trading opportunities or risks
2475. Short-term outlook
248
249Format your response in a clear, structured way.`;
250
251 const analysis = await RM.aiCall(prompt);
252
253 loadingDiv.style.display = 'none';
254
255 // Display analysis
256 contentDiv.innerHTML = `
257 <div style="color: #d1d4dc; line-height: 1.6; font-size: 13px;">
258 <div style="background: #1e222d; padding: 12px; border-radius: 6px; margin-bottom: 12px;">
259 <div style="color: #787b86; font-size: 11px; margin-bottom: 8px;">ANALYSIS FOR ${chartData.symbol}</div>
260 <div style="color: #fff; font-size: 16px; font-weight: 600;">${chartData.currentPrice}</div>
261 <div style="color: ${chartData.priceChange.includes('-') ? '#f23645' : '#089981'}; font-size: 13px; margin-top: 4px;">${chartData.priceChange}</div>
262 </div>
263 <div style="white-space: pre-wrap; word-wrap: break-word;">${formatAnalysis(analysis)}</div>
264 <div style="margin-top: 15px; padding-top: 15px; border-top: 1px solid #2a2e39; font-size: 11px; color: #787b86;">
265 Generated at ${new Date().toLocaleTimeString()}
266 </div>
267 </div>
268 `;
269
270 // Save analysis to storage
271 await GM.setValue('last_analysis_' + chartData.symbol, {
272 analysis,
273 chartData,
274 timestamp: Date.now()
275 });
276
277 } catch (error) {
278 console.error('Analysis error:', error);
279 loadingDiv.style.display = 'none';
280 contentDiv.innerHTML = `
281 <div style="color: #f23645; padding: 15px; text-align: center;">
282 <div style="font-size: 24px; margin-bottom: 8px;">⚠️</div>
283 <div style="font-size: 14px; font-weight: 600; margin-bottom: 8px;">Analysis Failed</div>
284 <div style="font-size: 12px; color: #787b86;">Unable to analyze the trend. Please try again.</div>
285 </div>
286 `;
287 }
288 }
289
290 // Format analysis text for better readability
291 function formatAnalysis(text) {
292 return text
293 .replace(/\*\*(.*?)\*\*/g, '<strong style="color: #fff;">$1</strong>')
294 .replace(/\n\n/g, '</p><p style="margin: 12px 0;">')
295 .replace(/\n/g, '<br>')
296 .replace(/^(.+)$/, '<p style="margin: 12px 0;">$1</p>')
297 .replace(/(\d+\.)\s/g, '<br><strong style="color: #2962ff;">$1</strong> ')
298 .replace(/(bullish|uptrend|buy)/gi, '<span style="color: #089981; font-weight: 600;">$1</span>')
299 .replace(/(bearish|downtrend|sell)/gi, '<span style="color: #f23645; font-weight: 600;">$1</span>')
300 .replace(/(neutral|sideways|hold)/gi, '<span style="color: #f0b90b; font-weight: 600;">$1</span>');
301 }
302
303 // Update symbol display
304 function updateSymbolDisplay() {
305 const symbolElement = document.getElementById('current-symbol');
306 if (symbolElement) {
307 const symbol = getCurrentSymbol();
308 symbolElement.textContent = symbol;
309 }
310 }
311
312 // Initialize the extension
313 function init() {
314 console.log('Initializing Trend Analyzer...');
315
316 // Wait for TradingView to load
317 const checkTradingViewLoaded = setInterval(() => {
318 const chartContainer = document.querySelector('.chart-container');
319 if (chartContainer) {
320 clearInterval(checkTradingViewLoaded);
321
322 // Create the analyzer panel
323 setTimeout(() => {
324 createAnalyzerPanel();
325 updateSymbolDisplay();
326
327 // Update symbol when it changes
328 const debouncedUpdate = debounce(updateSymbolDisplay, 1000);
329 const observer = new MutationObserver(debouncedUpdate);
330
331 const symbolButton = document.querySelector('button[aria-label="Change symbol"]');
332 if (symbolButton) {
333 observer.observe(symbolButton, {
334 childList: true,
335 subtree: true,
336 characterData: true
337 });
338 }
339
340 console.log('Trend Analyzer panel created successfully');
341 }, 2000);
342 }
343 }, 1000);
344
345 // Cleanup after 30 seconds if not loaded
346 setTimeout(() => {
347 clearInterval(checkTradingViewLoaded);
348 }, 30000);
349 }
350
351 // Start the extension
352 if (document.readyState === 'loading') {
353 document.addEventListener('DOMContentLoaded', init);
354 } else {
355 init();
356 }
357
358})();