Spaces:
Runtime error
Runtime error
| import time | |
| import json | |
| import requests | |
| import smtplib | |
| import schedule | |
| import threading | |
| import streamlit as st | |
| from email.mime.text import MimeText | |
| from email.mime.multipart import MimeMultipart | |
| from datetime import datetime | |
| import pandas as pd | |
| from selenium import webdriver | |
| from selenium.webdriver.chrome.options import Options | |
| from selenium.webdriver.common.by import By | |
| from selenium.webdriver.support.ui import WebDriverWait | |
| from selenium.webdriver.support import expected_conditions as EC | |
| from webdriver_manager.chrome import ChromeDriverManager | |
| from selenium.webdriver.chrome.service import Service | |
| import logging | |
| # تنظیم لاگ | |
| logging.basicConfig(level=logging.INFO) | |
| logger = logging.getLogger(__name__) | |
| class TradingViewMonitor: | |
| def __init__(self): | |
| """ | |
| کلاس نظارت بر TradingView | |
| """ | |
| self.driver = None | |
| self.is_monitoring = False | |
| self.monitoring_thread = None | |
| self.indicators_data = {} | |
| self.last_signals = {} | |
| self.notification_settings = { | |
| "email": { | |
| "enabled": False, | |
| "smtp_server": "", | |
| "smtp_port": 587, | |
| "username": "", | |
| "password": "", | |
| "recipient": "", | |
| "subject": "TradingView Signal Alert" | |
| }, | |
| "webhook": { | |
| "enabled": False, | |
| "url": "", | |
| "method": "POST", | |
| "payload": {} | |
| } | |
| } | |
| def setup_driver(self): | |
| """ | |
| راهاندازی درایور Chrome برای TradingView | |
| """ | |
| try: | |
| chrome_options = Options() | |
| chrome_options.add_argument('--headless') | |
| chrome_options.add_argument('--no-sandbox') | |
| chrome_options.add_argument('--disable-dev-shm-usage') | |
| chrome_options.add_argument('--disable-gpu') | |
| chrome_options.add_argument('--window-size=1920,1080') | |
| chrome_options.add_argument('--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36') | |
| service = Service(ChromeDriverManager().install()) | |
| self.driver = webdriver.Chrome(service=service, options=chrome_options) | |
| logger.info("TradingView driver setup completed") | |
| return True | |
| except Exception as e: | |
| logger.error(f"Error setting up driver: {str(e)}") | |
| return False | |
| def navigate_to_tradingview(self, symbol="BTCUSDT", timeframe="1h"): | |
| """ | |
| رفتن به صفحه TradingView | |
| """ | |
| try: | |
| url = f"https://www.tradingview.com/chart/?symbol={symbol}&interval={timeframe}" | |
| self.driver.get(url) | |
| time.sleep(5) # انتظار برای بارگذاری کامل صفحه | |
| logger.info(f"Navigated to TradingView: {symbol} - {timeframe}") | |
| return True | |
| except Exception as e: | |
| logger.error(f"Error navigating to TradingView: {str(e)}") | |
| return False | |
| def get_rsi_value(self): | |
| """ | |
| دریافت مقدار RSI (نمونه - باید با DOM واقعی TradingView تطبیق داده شود) | |
| """ | |
| try: | |
| # این یک نمونه است - باید با ساختار واقعی TradingView تطبیق داده شود | |
| # در عمل، باید المنتهای مربوط به RSI را پیدا کرد | |
| # شبیهسازی مقدار RSI | |
| import random | |
| rsi_value = random.uniform(20, 80) | |
| # تعیین سیگنال بر اساس RSI | |
| if rsi_value > 70: | |
| signal = "فروش" | |
| elif rsi_value < 30: | |
| signal = "خرید" | |
| else: | |
| signal = "خنثی" | |
| return { | |
| "value": round(rsi_value, 2), | |
| "signal": signal, | |
| "timestamp": datetime.now().isoformat() | |
| } | |
| except Exception as e: | |
| logger.error(f"Error getting RSI: {str(e)}") | |
| return None | |
| def get_macd_value(self): | |
| """ | |
| دریافت مقدار MACD | |
| """ | |
| try: | |
| # شبیهسازی مقدار MACD | |
| import random | |
| macd_line = random.uniform(-0.5, 0.5) | |
| signal_line = random.uniform(-0.5, 0.5) | |
| histogram = macd_line - signal_line | |
| # تعیین سیگنال | |
| if macd_line > signal_line and histogram > 0: | |
| signal = "خرید" | |
| elif macd_line < signal_line and histogram < 0: | |
| signal = "فروش" | |
| else: | |
| signal = "خنثی" | |
| return { | |
| "macd_line": round(macd_line, 4), | |
| "signal_line": round(signal_line, 4), | |
| "histogram": round(histogram, 4), | |
| "signal": signal, | |
| "timestamp": datetime.now().isoformat() | |
| } | |
| except Exception as e: | |
| logger.error(f"Error getting MACD: {str(e)}") | |
| return None | |
| def get_ema_value(self): | |
| """ | |
| دریافت مقدار EMA | |
| """ | |
| try: | |
| # شبیهسازی مقدار EMA | |
| import random | |
| current_price = random.uniform(40000, 50000) | |
| ema_20 = random.uniform(39000, 51000) | |
| # تعیین سیگنال | |
| if current_price > ema_20: | |
| signal = "خرید" | |
| elif current_price < ema_20: | |
| signal = "فروش" | |
| else: | |
| signal = "خنثی" | |
| return { | |
| "current_price": round(current_price, 2), | |
| "ema_20": round(ema_20, 2), | |
| "signal": signal, | |
| "timestamp": datetime.now().isoformat() | |
| } | |
| except Exception as e: | |
| logger.error(f"Error getting EMA: {str(e)}") | |
| return None | |
| def get_bollinger_bands(self): | |
| """ | |
| دریافت مقدار Bollinger Bands | |
| """ | |
| try: | |
| # شبیهسازی Bollinger Bands | |
| import random | |
| current_price = random.uniform(40000, 50000) | |
| upper_band = current_price + random.uniform(500, 2000) | |
| lower_band = current_price - random.uniform(500, 2000) | |
| middle_band = (upper_band + lower_band) / 2 | |
| # تعیین سیگنال | |
| if current_price >= upper_band: | |
| signal = "فروش" | |
| elif current_price <= lower_band: | |
| signal = "خرید" | |
| else: | |
| signal = "خنثی" | |
| return { | |
| "current_price": round(current_price, 2), | |
| "upper_band": round(upper_band, 2), | |
| "middle_band": round(middle_band, 2), | |
| "lower_band": round(lower_band, 2), | |
| "signal": signal, | |
| "timestamp": datetime.now().isoformat() | |
| } | |
| except Exception as e: | |
| logger.error(f"Error getting Bollinger Bands: {str(e)}") | |
| return None | |
| def get_stochastic_value(self): | |
| """ | |
| دریافت مقدار Stochastic | |
| """ | |
| try: | |
| # شبیهسازی Stochastic | |
| import random | |
| k_percent = random.uniform(0, 100) | |
| d_percent = random.uniform(0, 100) | |
| # تعیین سیگنال | |
| if k_percent > 80 and d_percent > 80: | |
| signal = "فروش" | |
| elif k_percent < 20 and d_percent < 20: | |
| signal = "خرید" | |
| else: | |
| signal = "خنثی" | |
| return { | |
| "k_percent": round(k_percent, 2), | |
| "d_percent": round(d_percent, 2), | |
| "signal": signal, | |
| "timestamp": datetime.now().isoformat() | |
| } | |
| except Exception as e: | |
| logger.error(f"Error getting Stochastic: {str(e)}") | |
| return None | |
| def collect_all_indicators(self): | |
| """ | |
| جمعآوری تمام اندیکاتورها | |
| """ | |
| indicators = {} | |
| try: | |
| indicators["RSI"] = self.get_rsi_value() | |
| indicators["MACD"] = self.get_macd_value() | |
| indicators["EMA"] = self.get_ema_value() | |
| indicators["Bollinger"] = self.get_bollinger_bands() | |
| indicators["Stochastic"] = self.get_stochastic_value() | |
| # حذف اندیکاتورهای None | |
| indicators = {k: v for k, v in indicators.items() if v is not None} | |
| self.indicators_data = indicators | |
| return indicators | |
| except Exception as e: | |
| logger.error(f"Error collecting indicators: {str(e)}") | |
| return {} | |
| def check_signal_convergence(self): | |
| """ | |
| بررسی همگرایی سیگنالها | |
| """ | |
| if not self.indicators_data: | |
| return False, "No indicators data available" | |
| signals = [] | |
| for indicator_name, data in self.indicators_data.items(): | |
| if data and "signal" in data: | |
| signals.append(data["signal"]) | |
| if len(signals) < 5: | |
| return False, f"Not enough indicators ({len(signals)}/5)" | |
| # بررسی اینکه آیا همه سیگنالها یکسان هستند | |
| unique_signals = set(signals) | |
| if len(unique_signals) == 1 and list(unique_signals)[0] != "خنثی": | |
| signal_type = list(unique_signals)[0] | |
| return True, f"All indicators show {signal_type} signal" | |
| return False, f"Mixed signals: {dict(zip(self.indicators_data.keys(), signals))}" | |
| def send_email_notification(self, message): | |
| """ | |
| ارسال اعلان ایمیل | |
| """ | |
| if not self.notification_settings["email"]["enabled"]: | |
| return False, "Email notifications disabled" | |
| try: | |
| settings = self.notification_settings["email"] | |
| # ایجاد پیام | |
| msg = MimeMultipart() | |
| msg['From'] = settings["username"] | |
| msg['To'] = settings["recipient"] | |
| msg['Subject'] = settings["subject"] | |
| # محتوای ایمیل | |
| body = f""" | |
| TradingView Signal Alert | |
| {message} | |
| Indicators Data: | |
| {json.dumps(self.indicators_data, indent=2, ensure_ascii=False)} | |
| Time: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} | |
| """ | |
| msg.attach(MimeText(body, 'plain', 'utf-8')) | |
| # ارسال ایمیل | |
| server = smtplib.SMTP(settings["smtp_server"], settings["smtp_port"]) | |
| server.starttls() | |
| server.login(settings["username"], settings["password"]) | |
| text = msg.as_string() | |
| server.sendmail(settings["username"], settings["recipient"], text) | |
| server.quit() | |
| logger.info("Email notification sent successfully") | |
| return True, "Email sent successfully" | |
| except Exception as e: | |
| logger.error(f"Error sending email: {str(e)}") | |
| return False, f"Email error: {str(e)}" | |
| def send_webhook_notification(self, message): | |
| """ | |
| ارسال اعلان وبهوک | |
| """ | |
| if not self.notification_settings["webhook"]["enabled"]: | |
| return False, "Webhook notifications disabled" | |
| try: | |
| settings = self.notification_settings["webhook"] | |
| # آمادهسازی payload | |
| payload = { | |
| "message": message, | |
| "indicators": self.indicators_data, | |
| "timestamp": datetime.now().isoformat() | |
| } | |
| # ادغام با payload سفارشی | |
| if settings["payload"]: | |
| payload.update(settings["payload"]) | |
| # ارسال درخواست | |
| if settings["method"].upper() == "POST": | |
| response = requests.post( | |
| settings["url"], | |
| json=payload, | |
| headers={"Content-Type": "application/json"}, | |
| timeout=10 | |
| ) | |
| elif settings["method"].upper() == "GET": | |
| response = requests.get( | |
| settings["url"], | |
| params=payload, | |
| timeout=10 | |
| ) | |
| else: | |
| return False, f"Unsupported method: {settings['method']}" | |
| if response.status_code == 200: | |
| logger.info("Webhook notification sent successfully") | |
| return True, "Webhook sent successfully" | |
| else: | |
| return False, f"Webhook error: {response.status_code}" | |
| except Exception as e: | |
| logger.error(f"Error sending webhook: {str(e)}") | |
| return False, f"Webhook error: {str(e)}" | |
| def send_notifications(self, message): | |
| """ | |
| ارسال تمام اعلانها | |
| """ | |
| results = [] | |
| # ارسال ایمیل | |
| email_success, email_msg = self.send_email_notification(message) | |
| results.append(f"Email: {email_msg}") | |
| # ارسال وبهوک | |
| webhook_success, webhook_msg = self.send_webhook_notification(message) | |
| results.append(f"Webhook: {webhook_msg}") | |
| return results | |
| def monitoring_loop(self, symbol, timeframe, check_interval=60): | |
| """ | |
| حلقه اصلی نظارت | |
| """ | |
| logger.info(f"Starting monitoring for {symbol} - {timeframe}") | |
| while self.is_monitoring: | |
| try: | |
| # جمعآوری اندیکاتورها | |
| indicators = self.collect_all_indicators() | |
| if indicators: | |
| # بررسی همگرایی سیگنالها | |
| convergence, message = self.check_signal_convergence() | |
| if convergence: | |
| # ارسال اعلان | |
| notification_message = f"TradingView Signal Alert for {symbol}: {message}" | |
| results = self.send_notifications(notification_message) | |
| logger.info(f"Signal convergence detected: {message}") | |
| logger.info(f"Notification results: {results}") | |
| # ذخیره آخرین سیگنال | |
| self.last_signals[symbol] = { | |
| "message": message, | |
| "timestamp": datetime.now().isoformat(), | |
| "indicators": indicators | |
| } | |
| # انتظار تا بررسی بعدی | |
| time.sleep(check_interval) | |
| except Exception as e: | |
| logger.error(f"Error in monitoring loop: {str(e)}") | |
| time.sleep(check_interval) | |
| def start_monitoring(self, symbol="BTCUSDT", timeframe="1h", check_interval=60): | |
| """ | |
| شروع نظارت | |
| """ | |
| if self.is_monitoring: | |
| return False, "Monitoring is already running" | |
| try: | |
| # راهاندازی درایور | |
| if not self.setup_driver(): | |
| return False, "Failed to setup driver" | |
| # رفتن به TradingView | |
| if not self.navigate_to_tradingview(symbol, timeframe): | |
| return False, "Failed to navigate to TradingView" | |
| # شروع نظارت در thread جداگانه | |
| self.is_monitoring = True | |
| self.monitoring_thread = threading.Thread( | |
| target=self.monitoring_loop, | |
| args=(symbol, timeframe, check_interval) | |
| ) | |
| self.monitoring_thread.daemon = True | |
| self.monitoring_thread.start() | |
| logger.info(f"Monitoring started for {symbol} - {timeframe}") | |
| return True, f"Monitoring started for {symbol} - {timeframe}" | |
| except Exception as e: | |
| logger.error(f"Error starting monitoring: {str(e)}") | |
| return False, f"Error starting monitoring: {str(e)}" | |
| def stop_monitoring(self): | |
| """ | |
| توقف نظارت | |
| """ | |
| self.is_monitoring = False | |
| if self.driver: | |
| try: | |
| self.driver.quit() | |
| except: | |
| pass | |
| if self.monitoring_thread and self.monitoring_thread.is_alive(): | |
| self.monitoring_thread.join(timeout=5) | |
| logger.info("Monitoring stopped") | |
| return True, "Monitoring stopped" | |
| def update_notification_settings(self, settings): | |
| """ | |
| بهروزرسانی تنظیمات اعلانها | |
| """ | |
| try: | |
| self.notification_settings.update(settings) | |
| logger.info("Notification settings updated") | |
| return True, "Settings updated successfully" | |
| except Exception as e: | |
| logger.error(f"Error updating settings: {str(e)}") | |
| return False, f"Error updating settings: {str(e)}" | |
| def test_email_notification(self): | |
| """ | |
| تست ارسال ایمیل | |
| """ | |
| test_message = "This is a test email from TradingView Monitor" | |
| return self.send_email_notification(test_message) | |
| def test_webhook_notification(self): | |
| """ | |
| تست ارسال وبهوک | |
| """ | |
| test_message = "This is a test webhook from TradingView Monitor" | |
| return self.send_webhook_notification(test_message) | |
| def get_status(self): | |
| """ | |
| دریافت وضعیت فعلی سیستم | |
| """ | |
| return { | |
| "is_monitoring": self.is_monitoring, | |
| "last_check": datetime.now().isoformat(), | |
| "indicators_count": len(self.indicators_data), | |
| "last_signals": self.last_signals, | |
| "email_enabled": self.notification_settings["email"]["enabled"], | |
| "webhook_enabled": self.notification_settings["webhook"]["enabled"] | |
| } | |
| # تابع کمکی برای راهاندازی مانیتور TradingView در Streamlit | |
| def get_tradingview_monitor(): | |
| """ | |
| دریافت نمونه مانیتور TradingView (با کش برای بهینهسازی) | |
| """ | |
| return TradingViewMonitor() | |
| # کلاس مدیریت کرونجاب | |
| class CronJobManager: | |
| def __init__(self, monitor): | |
| self.monitor = monitor | |
| self.jobs = [] | |
| def add_monitoring_job(self, symbol, timeframe, check_interval): | |
| """ | |
| افزودن کار نظارت به کرونجاب | |
| """ | |
| try: | |
| # پاک کردن کارهای قبلی | |
| schedule.clear() | |
| # افزودن کار جدید | |
| schedule.every(check_interval).seconds.do( | |
| self.monitor.collect_all_indicators | |
| ) | |
| logger.info(f"Cron job added for {symbol} every {check_interval} seconds") | |
| return True, "Cron job added successfully" | |
| except Exception as e: | |
| logger.error(f"Error adding cron job: {str(e)}") | |
| return False, f"Error adding cron job: {str(e)}" | |
| def run_pending_jobs(self): | |
| """ | |
| اجرای کارهای در انتظار | |
| """ | |
| schedule.run_pending() | |
| def clear_all_jobs(self): | |
| """ | |
| پاک کردن تمام کارها | |
| """ | |
| schedule.clear() | |
| logger.info("All cron jobs cleared") | |