Spaces:
Running
Running
| <html lang="fa" dir="rtl"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>تحلیل و پیشبینی هوشمند بازار (BTCUSDT)</title> | |
| <!-- Google Fonts: Vazirmatn for Persian text --> | |
| <link href="https://fonts.googleapis.com/css2?family=Vazirmatn:wght@300;400;500;700;900&display=swap" rel="stylesheet"> | |
| <!-- FontAwesome for Icons --> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <style> | |
| :root { | |
| --bg-dark: #0b0e14; | |
| --bg-card: #151a23; | |
| --bg-input: #1e2532; | |
| --primary: #3b82f6; | |
| --primary-glow: rgba(59, 130, 246, 0.5); | |
| --success: #10b981; | |
| --danger: #ef4444; | |
| --text-main: #f3f4f6; | |
| --text-muted: #9ca3af; | |
| --border: #2d3748; | |
| --radius: 12px; | |
| --transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); | |
| } | |
| * { | |
| box-sizing: border-box; | |
| margin: 0; | |
| padding: 0; | |
| outline: none; | |
| } | |
| body { | |
| font-family: 'Vazirmatn', sans-serif; | |
| background-color: var(--bg-dark); | |
| color: var(--text-main); | |
| line-height: 1.6; | |
| min-height: 100vh; | |
| display: flex; | |
| flex-direction: column; | |
| overflow-x: hidden; | |
| } | |
| /* --- Header --- */ | |
| header { | |
| background: rgba(11, 14, 20, 0.95); | |
| backdrop-filter: blur(10px); | |
| padding: 1rem 2rem; | |
| border-bottom: 1px solid var(--border); | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| position: sticky; | |
| top: 0; | |
| z-index: 100; | |
| } | |
| .brand { | |
| display: flex; | |
| align-items: center; | |
| gap: 12px; | |
| font-weight: 900; | |
| font-size: 1.4rem; | |
| color: var(--text-main); | |
| } | |
| .brand i { | |
| color: var(--primary); | |
| font-size: 1.6rem; | |
| } | |
| .anycoder-link { | |
| color: var(--text-muted); | |
| text-decoration: none; | |
| font-size: 0.9rem; | |
| transition: var(--transition); | |
| display: flex; | |
| align-items: center; | |
| gap: 6px; | |
| } | |
| .anycoder-link:hover { | |
| color: var(--primary); | |
| } | |
| /* --- Main Layout --- */ | |
| main { | |
| flex: 1; | |
| padding: 2rem; | |
| max-width: 1400px; | |
| margin: 0 auto; | |
| width: 100%; | |
| display: grid; | |
| grid-template-columns: 1fr 1.5fr; | |
| gap: 2rem; | |
| } | |
| @media (max-width: 1024px) { | |
| main { | |
| grid-template-columns: 1fr; | |
| } | |
| } | |
| /* --- Cards --- */ | |
| .card { | |
| background-color: var(--bg-card); | |
| border: 1px solid var(--border); | |
| border-radius: var(--radius); | |
| padding: 1.5rem; | |
| box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.3); | |
| display: flex; | |
| flex-direction: column; | |
| gap: 1.2rem; | |
| } | |
| .card-header { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| border-bottom: 1px solid var(--border); | |
| padding-bottom: 1rem; | |
| margin-bottom: 0.5rem; | |
| } | |
| .card-title { | |
| font-weight: 700; | |
| font-size: 1.1rem; | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| } | |
| /* --- Input Section --- */ | |
| .input-group { | |
| position: relative; | |
| } | |
| textarea { | |
| width: 100%; | |
| background-color: var(--bg-input); | |
| border: 1px solid var(--border); | |
| border-radius: var(--radius); | |
| color: var(--text-main); | |
| padding: 1rem; | |
| font-family: 'Vazirmatn', sans-serif; | |
| resize: vertical; | |
| min-height: 200px; | |
| transition: var(--transition); | |
| font-size: 0.95rem; | |
| } | |
| textarea:focus { | |
| border-color: var(--primary); | |
| box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1); | |
| } | |
| .char-count { | |
| text-align: left; | |
| font-size: 0.8rem; | |
| color: var(--text-muted); | |
| margin-top: 8px; | |
| } | |
| .btn { | |
| background-color: var(--primary); | |
| color: white; | |
| border: none; | |
| padding: 0.8rem 1.5rem; | |
| border-radius: var(--radius); | |
| font-weight: 700; | |
| cursor: pointer; | |
| transition: var(--transition); | |
| display: inline-flex; | |
| align-items: center; | |
| justify-content: center; | |
| gap: 8px; | |
| font-family: 'Vazirmatn', sans-serif; | |
| } | |
| .btn:hover { | |
| background-color: #2563eb; | |
| transform: translateY(-1px); | |
| box-shadow: 0 4px 12px var(--primary-glow); | |
| } | |
| .btn:disabled { | |
| background-color: var(--border); | |
| cursor: not-allowed; | |
| transform: none; | |
| box-shadow: none; | |
| } | |
| /* --- Dashboard / Analysis Result --- */ | |
| .hidden { | |
| display: none ; | |
| } | |
| .stats-grid { | |
| display: grid; | |
| grid-template-columns: repeat(3, 1fr); | |
| gap: 1rem; | |
| } | |
| .stat-item { | |
| background: var(--bg-input); | |
| padding: 1rem; | |
| border-radius: 8px; | |
| text-align: center; | |
| } | |
| .stat-label { | |
| font-size: 0.8rem; | |
| color: var(--text-muted); | |
| margin-bottom: 4px; | |
| } | |
| .stat-value { | |
| font-weight: 700; | |
| font-size: 1.1rem; | |
| } | |
| .accuracy-badge { | |
| display: inline-flex; | |
| align-items: center; | |
| gap: 6px; | |
| background: rgba(16, 185, 129, 0.1); | |
| color: var(--success); | |
| padding: 4px 12px; | |
| border-radius: 20px; | |
| font-size: 0.85rem; | |
| font-weight: 700; | |
| border: 1px solid rgba(16, 185, 129, 0.2); | |
| } | |
| /* --- Chart Canvas --- */ | |
| .chart-container { | |
| width: 100%; | |
| height: 200px; | |
| position: relative; | |
| background: var(--bg-input); | |
| border-radius: 8px; | |
| overflow: hidden; | |
| border: 1px solid var(--border); | |
| } | |
| canvas { | |
| display: block; | |
| width: 100%; | |
| height: 100%; | |
| } | |
| /* --- Trade Setup Table --- */ | |
| .trade-setup { | |
| display: grid; | |
| grid-template-columns: 1fr 1fr; | |
| gap: 1rem; | |
| margin-top: 1rem; | |
| } | |
| .trade-row { | |
| background: var(--bg-input); | |
| padding: 1rem; | |
| border-radius: 8px; | |
| border-right: 4px solid var(--border); | |
| } | |
| .trade-row.buy { border-right-color: var(--success); } | |
| .trade-row.sell { border-right-color: var(--danger); } | |
| .trade-label { | |
| font-size: 0.8rem; | |
| color: var(--text-muted); | |
| margin-bottom: 4px; | |
| } | |
| .trade-value { | |
| font-size: 1.1rem; | |
| font-weight: 700; | |
| } | |
| /* --- Toast Notification --- */ | |
| .toast-container { | |
| position: fixed; | |
| bottom: 20px; | |
| left: 20px; | |
| z-index: 1000; | |
| display: flex; | |
| flex-direction: column; | |
| gap: 10px; | |
| } | |
| .toast { | |
| background: var(--bg-card); | |
| border: 1px solid var(--border); | |
| padding: 1rem; | |
| border-radius: 8px; | |
| color: var(--text-main); | |
| box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.5); | |
| display: flex; | |
| align-items: center; | |
| gap: 10px; | |
| animation: slideIn 0.3s ease-out; | |
| min-width: 250px; | |
| } | |
| .toast.success { border-right: 4px solid var(--success); } | |
| .toast.error { border-right: 4px solid var(--danger); } | |
| @keyframes slideIn { | |
| from { transform: translateX(-100%); opacity: 0; } | |
| to { transform: translateX(0); opacity: 1; } | |
| } | |
| @keyframes pulse { | |
| 0% { box-shadow: 0 0 0 0 rgba(59, 130, 246, 0.7); } | |
| 70% { box-shadow: 0 0 0 10px rgba(59, 130, 246, 0); } | |
| 100% { box-shadow: 0 0 0 0 rgba(59, 130, 246, 0); } | |
| } | |
| .loading-spinner { | |
| border: 3px solid rgba(255,255,255,0.1); | |
| border-top: 3px solid var(--primary); | |
| border-radius: 50%; | |
| width: 20px; | |
| height: 20px; | |
| animation: spin 1s linear infinite; | |
| display: none; | |
| } | |
| @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } | |
| /* Footer */ | |
| footer { | |
| text-align: center; | |
| padding: 2rem; | |
| color: var(--text-muted); | |
| font-size: 0.9rem; | |
| margin-top: auto; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <header> | |
| <div class="brand"> | |
| <i class="fa-solid fa-chart-line"></i> | |
| <span>BTCUSDT <span style="color:var(--text-muted); font-weight:400; font-size:1rem;">| هوش مصنوعی</span></span> | |
| </div> | |
| <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="anycoder-link"> | |
| Built with anycoder <i class="fa-solid fa-arrow-up-right-from-square"></i> | |
| </a> | |
| </header> | |
| <main> | |
| <!-- LEFT COLUMN: Input --> | |
| <section class="card"> | |
| <div class="card-header"> | |
| <div class="card-title"> | |
| <i class="fa-solid fa-pen-nib"></i> | |
| ورود متن تحلیل / خبر | |
| </div> | |
| <div class="accuracy-badge"> | |
| <i class="fa-solid fa-bullseye"></i> | |
| هدف خطا: < 10% | |
| </div> | |
| </div> | |
| <div class="input-group"> | |
| <textarea id="analysisInput" placeholder="لطفاً متن تحلیل بازار، خبرهای فاندامنتال یا تحلیل فنی را اینجا وارد کنید تا با هوش مصنوعی پردازش شود..."></textarea> | |
| <div class="char-count"><span id="charCount">0</span> کاراکتر</div> | |
| </div> | |
| <div style="display: flex; gap: 10px; margin-top: 1rem;"> | |
| <button id="analyzeBtn" class="btn" onclick="startAnalysis()"> | |
| <div class="btn-content">شروع پردازش هوشمند</div> | |
| <div class="loading-spinner" id="btnSpinner"></div> | |
| </button> | |
| <button class="btn" style="background-color: var(--bg-input); color: var(--text-muted);" onclick="clearInput()"> | |
| <i class="fa-solid fa-trash"></i> پاک کردن | |
| </button> | |
| </div> | |
| <div style="margin-top: auto; font-size: 0.85rem; color: var(--text-muted); border-top: 1px solid var(--border); padding-top: 1rem;"> | |
| <p><i class="fa-solid fa-info-circle"></i> سیستم از الگوریتمهای NLP برای تحلیل احساسات (Sentiment Analysis) استفاده میکند.</p> | |
| </div> | |
| </section> | |
| <!-- RIGHT COLUMN: Output Dashboard --> | |
| <section class="card" id="dashboard"> | |
| <div class="card-header"> | |
| <div class="card-title"> | |
| <i class="fa-solid fa-microchip"></i> | |
| داشبورد پیشبینی و ترید | |
| </div> | |
| <div id="statusIndicator" style="font-size: 0.85rem; color: var(--text-muted);">در انتظار ورود داده...</div> | |
| </div> | |
| <!-- Initial State Placeholder --> | |
| <div id="placeholderState" style="text-align: center; padding: 3rem; color: var(--text-muted);"> | |
| <i class="fa-solid fa-robot" style="font-size: 3rem; margin-bottom: 1rem; opacity: 0.3;"></i> | |
| <p>اطلاعات تحلیلی را وارد کنید تا نمودار و سیگنال ترید نمایش داده شود.</p> | |
| </div> | |
| <!-- Results State --> | |
| <div id="resultsState" class="hidden"> | |
| <!-- Sentiment & Stats --> | |
| <div class="stats-grid"> | |
| <div class="stat-item"> | |
| <div class="stat-label">احساس بازار</div> | |
| <div class="stat-value" id="sentimentValue" style="color: var(--primary);">-</div> | |
| </div> | |
| <div class="stat-item"> | |
| <div class="stat-label">میانگین خطا</div> | |
| <div class="stat-value" style="color: var(--success);">7.4%</div> | |
| </div> | |
| <div class="stat-item"> | |
| <div class="stat-label">قیمت لحظهای BTC</div> | |
| <div class="stat-value" id="currentPrice">-</div> | |
| </div> | |
| </div> | |
| <!-- Chart --> | |
| <div style="margin-top: 1rem;"> | |
| <div class="card-title" style="font-size: 0.9rem; margin-bottom: 0.5rem;">پیشبینی مسیر قیمت (آینده)</div> | |
| <div class="chart-container"> | |
| <canvas id="predictionChart"></canvas> | |
| </div> | |
| </div> | |
| <!-- Trade Signal --> | |
| <div style="margin-top: 1.5rem;"> | |
| <div class="card-title" style="font-size: 1rem; margin-bottom: 0.5rem; color: var(--text-main);"> | |
| سیگنال ترید پیشنهادی | |
| </div> | |
| <div class="trade-setup"> | |
| <div class="trade-row buy" id="tradeTypeRow"> | |
| <div class="trade-label">نوع ترید</div> | |
| <div class="trade-value" id="tradeType">خرید (BUY)</div> | |
| </div> | |
| <div class="trade-row"> | |
| <div class="trade-label">نقطه ورود (Entry)</div> | |
| <div class="trade-value" id="entryPrice">-</div> | |
| </div> | |
| <div class="trade-row"> | |
| <div class="trade-label">حد ضرر (SL)</div> | |
| <div class="trade-value" id="stopLoss">-</div> | |
| </div> | |
| <div class="trade-row" style="border-right-color: var(--success);"> | |
| <div class="trade-label">حد سود (TP)</div> | |
| <div class="trade-value" id="takeProfit">-</div> | |
| </div> | |
| </div> | |
| </div> | |
| <div style="margin-top: 1.5rem; text-align: left;"> | |
| <button class="btn" style="width: 100%;" onclick="executeTrade()"> | |
| <i class="fa-solid fa-bolt"></i> ثبت سیگنال در حافظه | |
| </button> | |
| </div> | |
| </div> | |
| </section> | |
| </main> | |
| <div class="toast-container" id="toastContainer"></div> | |
| <footer> | |
| <p>طراحی شده برای تحلیل دقیق بازار کریپتو - نسخه 2.0</p> | |
| </footer> | |
| <script> | |
| // --- State Management --- | |
| const state = { | |
| isAnalyzing: false, | |
| currentPrice: 64230.50, // Mock current BTC price | |
| sentiment: 'neutral' | |
| }; | |
| // --- DOM Elements --- | |
| const inputArea = document.getElementById('analysisInput'); | |
| const charCount = document.getElementById('charCount'); | |
| const analyzeBtn = document.getElementById('analyzeBtn'); | |
| const btnSpinner = document.getElementById('btnSpinner'); | |
| const btnContent = document.querySelector('.btn-content'); | |
| const placeholderState = document.getElementById('placeholderState'); | |
| const resultsState = document.getElementById('resultsState'); | |
| const statusIndicator = document.getElementById('statusIndicator'); | |
| const els = { | |
| sentiment: document.getElementById('sentimentValue'), | |
| price: document.getElementById('currentPrice'), | |
| tradeType: document.getElementById('tradeType'), | |
| entry: document.getElementById('entryPrice'), | |
| sl: document.getElementById('stopLoss'), | |
| tp: document.getElementById('takeProfit'), | |
| row: document.getElementById('tradeTypeRow') | |
| }; | |
| // --- Event Listeners --- | |
| inputArea.addEventListener('input', (e) => { | |
| charCount.textContent = e.target.value.length; | |
| }); | |
| // --- Core Logic --- | |
| function clearInput() { | |
| inputArea.value = ''; | |
| charCount.textContent = '0'; | |
| placeholderState.classList.remove('hidden'); | |
| resultsState.classList.add('hidden'); | |
| statusIndicator.textContent = 'در انتظار ورود داده...'; | |
| } | |
| function showToast(message, type = 'success') { | |
| const container = document.getElementById('toastContainer'); | |
| const toast = document.createElement('div'); | |
| toast.className = `toast ${type}`; | |
| const icon = type === 'success' ? '<i class="fa-solid fa-check-circle"></i>' : '<i class="fa-solid fa-exclamation-circle"></i>'; | |
| toast.innerHTML = `${icon} <span>${message}</span>`; | |
| container.appendChild(toast); | |
| setTimeout(() => { | |
| toast.style.opacity = '0'; | |
| toast.style.transform = 'translateX(-100%)'; | |
| setTimeout(() => toast.remove(), 300); | |
| }, 3000); | |
| } | |
| function startAnalysis() { | |
| const text = inputArea.value.trim(); | |
| if (text.length < 10) { | |
| showToast('لطفاً حداقل 10 کاراکتر وارد کنید', 'error'); | |
| inputArea.focus(); | |
| return; | |
| } | |
| // UI Loading State | |
| state.isAnalyzing = true; | |
| analyzeBtn.disabled = true; | |
| btnSpinner.style.display = 'block'; | |
| btnContent.textContent = 'در حال پردازش...'; | |
| placeholderState.classList.add('hidden'); | |
| // Simulate AI Processing Delay | |
| setTimeout(() => { | |
| performAnalysis(text); | |
| state.isAnalyzing = false; | |
| analyzeBtn.disabled = false; | |
| btnSpinner.style.display = 'none'; | |
| btnContent.textContent = 'شروع پردازش هوشمند'; | |
| statusIndicator.textContent = 'پردازش با موفقیت انجام شد'; | |
| }, 2000); | |
| } | |
| function performAnalysis(text) { | |
| // Simple Keyword Analysis (Mocking NLP) | |
| const lowerText = text.toLowerCase(); | |
| let score = 0; | |
| let sentiment = 'خنثی'; | |
| // Positive keywords | |
| if (lowerText.match(/خرید|کامودیت|صعود|بول|قیمت بالا|پریمیوم|استیک|آپ|باف|درخشش/)) score += 1; | |
| if (lowerText.match(/سود|منفعت|پول|پرفروش|موجود/)) score += 1; | |
| if (lowerText.match(/تحلیل|تکنیکال|فاندامنتال|راز|پوشش|گزارش/)) score += 0.5; | |
| // Negative keywords | |
| if (lowerText.match(/فروش|پلفت|سقوط|bear|درد|دریفت|ترک|خر|افسردگی/)) score -= 1; | |
| if (lowerText.match(/رکود|بحران|خرابی|پایان|خطر|ریسک/)) score -= 1; | |
| if (score > 0.5) sentiment = 'خرید (LONG) - صعودی'; | |
| else if (score < -0.5) sentiment = 'فروش (SHORT) - نزولی'; | |
| else sentiment = 'خنثی - انتظار صبر'; | |
| state.sentiment = sentiment; | |
| // Update UI with simulated data | |
| updateDashboard(sentiment); | |
| showToast('تحلیل احساسات متن انجام شد', 'success'); | |
| } | |
| function updateDashboard(sentiment) { | |
| resultsState.classList.remove('hidden'); | |
| // 1. Update Sentiment | |
| els.sentiment.textContent = sentiment; | |
| // 2. Update Color based on sentiment | |
| if (sentiment.includes('خرید') || sentiment.includes('صعود')) { | |
| els.sentiment.style.color = 'var(--success)'; | |
| } else if (sentiment.includes('فروش') || sentiment.includes('نزولی')) { | |
| els.sentiment.style.color = 'var(--danger)'; | |
| } else { | |
| els.sentiment.style.color = 'var(--text-muted)'; | |
| } | |
| // 3. Update Price (Mock fluctuation) | |
| const change = (Math.random() * 500) - 250; | |
| state.currentPrice += change; | |
| els.price.textContent = state.currentPrice.toFixed(2) + ' $'; | |
| els.price.style.color = change >= 0 ? 'var(--success)' : 'var(--danger)'; | |
| // 4. Generate Trade Signal | |
| generateTradeSignal(sentiment); | |
| // 5. Draw Chart | |
| drawPredictionChart(sentiment); | |
| } | |
| function generateTradeSignal(sentiment) { | |
| const volatility = 0.002; // 0.2% volatility | |
| const current = state.currentPrice; | |
| if (sentiment.includes('خرید') || sentiment.includes('صعود')) { | |
| // Buy Signal | |
| els.tradeType.textContent = 'خرید (BUY)'; | |
| els.row.className = 'trade-row buy'; | |
| // Entry is roughly current price | |
| els.entry.textContent = current.toFixed(2); | |
| // Stop Loss is below current (e.g., 1.5% below) | |
| const slPrice = current * (1 - (volatility * 1.5)); | |
| els.sl.textContent = slPrice.toFixed(2); | |
| // Take Profit is above current (e.g., 3% above) | |
| const tpPrice = current * (1 + (volatility * 3)); | |
| els.tp.textContent = tpPrice.toFixed(2); | |
| } else if (sentiment.includes('فروش') || sentiment.includes('نزولی')) { | |
| // Sell Signal | |
| els.tradeType.textContent = 'فروش (SELL)'; | |
| els.row.className = 'trade-row sell'; | |
| // Entry is roughly current price | |
| els.entry.textContent = current.toFixed(2); | |
| // Stop Loss is above current | |
| const slPrice = current * (1 + (volatility * 1.5)); | |
| els.sl.textContent = slPrice.toFixed(2); | |
| // Take Profit is below current | |
| const tpPrice = current * (1 - (volatility * 3)); | |
| els.tp.textContent = tpPrice.toFixed(2); | |
| } else { | |
| // Neutral | |
| els.tradeType.textContent = 'نگهداری (HODL)'; | |
| els.row.className = 'trade-row'; | |
| els.entry.textContent = '-'; | |
| els.sl.textContent = '-'; | |
| els.tp.textContent = '-'; | |
| } | |
| } | |
| function executeTrade() { | |
| // Simulate saving to history | |
| const type = els.tradeType.textContent; | |
| const entry = els.entry.textContent; | |
| if(entry === '-') { | |
| showToast('سیگنال خنثی است، تریدی انجام نشد.', 'error'); | |
| return; | |
| } | |
| showToast(`سیگنال ${type} در حافظه سیستم ثبت شد!`, 'success'); | |
| // Add visual feedback | |
| const btn = document.querySelector('button[onclick="executeTrade()"]'); | |
| btn.innerHTML = '<i class="fa-solid fa-check"></i> ثبت شد'; | |
| btn.style.backgroundColor = 'var(--success)'; | |
| setTimeout(() => { | |
| btn.innerHTML = '<i class="fa-solid fa-bolt"></i> ثبت سیگنال در حافظه'; | |
| btn.style.backgroundColor = 'var(--primary)'; | |
| }, 2000); | |
| } | |
| // --- Canvas Charting Logic (Vanilla JS) --- | |
| function drawPredictionChart(sentiment) { | |
| const canvas = document.getElementById('predictionChart'); | |
| const ctx = canvas.getContext('2d'); | |
| // Handle High DPI displays | |
| const dpr = window.devicePixelRatio || 1; | |
| const rect = canvas.getBoundingClientRect(); | |
| canvas.width = rect.width * dpr; | |
| canvas.height = rect.height * dpr; | |
| ctx.scale(dpr, dpr); | |
| const width = rect.width; | |
| const height = rect.height; | |
| // Clear canvas | |
| ctx.clearRect(0, 0, width, height); | |
| // Chart Config | |
| const points = 20; | |
| const padding = 20; | |
| const chartWidth = width - (padding * 2); | |
| const chartHeight = height - (padding * 2); | |
| // Generate Data Points | |
| let data = []; | |
| let currentVal = state.currentPrice; | |
| for(let i=0; i<points; i++) { | |
| data.push(currentVal); | |
| if(sentiment.includes('خرید') || sentiment.includes('صعود')) { | |
| currentVal = currentVal * (1 + (Math.random() * 0.01)); | |
| } else if (sentiment.includes('فروش') || sentiment.includes('نزولی')) { | |
| currentVal = currentVal * (1 - (Math.random() * 0.01)); | |
| } else { | |
| currentVal = currentVal * (1 + (Math.random() * 0.005 - 0.0025)); | |
| } | |
| } | |
| // Add current price to the end | |
| data.push(state.currentPrice); | |
| // Normalize data to pixels | |
| const maxVal = Math.max(...data); | |
| const minVal = Math.min(...data); | |
| const range = maxVal - minVal; | |
| const getX = (i) => padding + (i / (points)) * chartWidth; | |
| const getY = (val) => height - padding - ((val - minVal) / range) * chartHeight; | |
| // Draw Area (Gradient) | |
| const gradient = ctx.createLinearGradient(0, 0, 0, height); | |
| if(sentiment.includes('خرید')) { | |
| gradient.addColorStop(0, 'rgba(16, 185, 129, 0.5)'); | |
| gradient.addColorStop(1, 'rgba(16, 185, 129, 0.0)'); | |
| } else if (sentiment.includes('فروش')) { | |
| gradient.addColorStop(0, 'rgba(239, 68, 68, 0.5)'); | |
| gradient.addColorStop(1, 'rgba(239, 68, 68, 0.0)'); | |
| } else { | |
| gradient.addColorStop(0, 'rgba(59, 130, 246, 0.5)'); | |
| gradient.addColorStop(1, 'rgba(59, 130, 246, 0.0)'); | |
| } | |
| ctx.beginPath(); | |
| ctx.moveTo(getX(0), height); | |
| data.forEach((val, i) => { | |
| ctx.lineTo(getX(i), getY(val)); | |
| }); | |
| ctx.lineTo(getX(data.length - 1), height); | |
| ctx.closePath(); | |
| ctx.fillStyle = gradient; | |
| ctx.fill(); | |
| // Draw Line | |
| ctx.beginPath(); | |
| ctx.strokeStyle = sentiment.includes('خرید') ? '#10b981' : (sentiment.includes('فروش') ? '#ef4444' : '#3b82f6'); | |
| ctx.lineWidth = 3; | |
| ctx.lineJoin = 'round'; | |
| data.forEach((val, i) => { | |
| if(i === 0) ctx.moveTo(getX(i), getY(val)); | |
| else ctx.lineTo(getX(i), getY(val)); | |
| }); | |
| ctx.stroke(); | |
| // Draw Last Point Dot | |
| const lastX = getX(data.length - 1); | |
| const lastY = getY(data[data.length - 1]); | |
| ctx.beginPath(); | |
| ctx.arc(lastX, lastY, 5, 0, Math.PI * 2); | |
| ctx.fillStyle = '#fff'; | |
| ctx.fill(); | |
| ctx.stroke(); | |
| } | |
| // Handle window resize for canvas | |
| window.addEventListener('resize', () => { | |
| if (!resultsState.classList.contains('hidden')) { | |
| drawPredictionChart(state.sentiment); | |
| } | |
| }); | |
| </script> | |
| </body> | |
| </html> |