alianilikhaniA1 commited on
Commit
5723cba
·
verified ·
1 Parent(s): db12e45

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +732 -134
app.py CHANGED
@@ -1,164 +1,762 @@
 
 
 
1
  import ccxt
2
  import pandas as pd
 
 
3
  import asyncio
4
  import logging
5
- import httpx
6
- from tenacity import retry, wait_exponential, stop_after_attempt
7
- from telegram import Bot
8
- from telegram.error import TelegramError
9
- from ta.momentum import RSIIndicator
10
- from ta.trend import EMAIndicator
11
- import config
 
 
 
 
 
 
 
 
 
 
 
 
12
 
 
13
  logging.basicConfig(
 
14
  format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
15
- level=logging.INFO
 
 
 
16
  )
17
  logger = logging.getLogger(__name__)
18
 
19
- class LBankMonitor:
20
- def __init__(self):
21
- self.http_client = httpx.AsyncClient(
22
- limits=httpx.Limits(
23
- max_connections=config.HTTP_CONFIG['max_connections'],
24
- max_keepalive_connections=config.HTTP_CONFIG['max_keepalive']
25
- ),
26
- timeout=config.HTTP_CONFIG['timeout']
27
- )
28
-
29
- self.exchange = ccxt.lbank({
30
- 'apiKey': config.API_CONFIG['apiKey'],
31
- 'secret': config.API_CONFIG['secret'],
32
- 'enableRateLimit': True,
33
- 'session': self.http_client
34
- })
35
-
36
- self.bot = Bot(token=config.TELEGRAM_CONFIG['bot_token'])
37
- self.semaphore = asyncio.Semaphore(config.THROTTLING['max_parallel'])
38
- self.connected = False
39
 
40
- @retry(wait=wait_exponential(multiplier=1, min=2, max=10), stop=stop_after_attempt(3))
41
- async def fetch_ohlcv(self, symbol, timeframe):
42
- async with self.semaphore:
43
- try:
44
- ohlcv = await self.exchange.fetch_ohlcv(symbol, timeframe)
45
- df = pd.DataFrame(ohlcv, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
46
- df['timestamp'] = pd.to_datetime(df['timestamp'], unit='ms')
47
- return df
48
- except Exception as e:
49
- logger.error(f"Error fetching {symbol} ({timeframe}): {str(e)}")
50
- raise
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
 
52
  def calculate_indicators(self, df):
53
- df['rsi'] = RSIIndicator(df['close']).rsi()
54
- df['ema20'] = EMAIndicator(df['close'], window=20).ema_indicator()
55
- df['ema50'] = EMAIndicator(df['close'], window=50).ema_indicator()
56
- return df
57
-
58
- async def analyze_symbol(self, symbol):
59
- try:
60
- signals = []
61
- for tf in config.TIMEFRAMES:
62
- df = await self.fetch_ohlcv(symbol, tf)
63
- if df is not None and not df.empty:
64
- df = self.calculate_indicators(df)
65
- current_close = df['close'].iloc[-1]
66
- rsi = df['rsi'].iloc[-1]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
 
68
- # منطق پیشرفته تحلیل (نمونه)
69
- if rsi < config.THRESHOLDS['rsi_oversold']:
70
- signals.append({
71
- 'symbol': symbol,
72
- 'timeframe': tf,
73
- 'signal': 'خرید',
74
- 'confidence': 95,
75
- 'price': current_close
76
- })
77
- return signals
78
- except Exception as e:
79
- logger.error(f"Analysis failed for {symbol}: {str(e)}")
80
- return []
81
-
82
- async def send_telegram(self, message):
83
- try:
84
- emojis = {
85
- 'خرید': '🟢💰', 'فروش': '🔴📉',
86
- 'اتصال': '🌐', 'خطا': '❌'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87
  }
88
- for k, v in emojis.items():
89
- if k in message:
90
- message = f"{v} {message}"
91
- break
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92
 
93
- await self.bot.send_message(
94
- chat_id=config.TELEGRAM_CONFIG['chat_id'],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
95
  text=message,
96
- parse_mode='HTML'
97
  )
98
- except TelegramError as e:
99
- logger.error(f"Telegram error: {str(e)}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
 
101
- async def check_connection(self):
 
102
  try:
103
- await self.exchange.fetch_balance()
104
- if not self.connected:
105
- await self.send_telegram("✅ اتصال به صرافی با موفقیت برقرار شد")
106
- self.connected = True
107
  return True
108
  except Exception as e:
109
- if self.connected:
110
- await self.send_telegram(f"⚠️ اتصال قطع شد! خطا: {str(e)}")
111
- self.connected = False
112
  return False
113
 
114
- async def run_analysis(self):
115
- if await self.check_connection():
116
- symbols = config.SYMBOLS
117
- batch_size = config.THROTTLING['batch_size']
118
-
119
- for i in range(0, len(symbols), batch_size):
120
- batch = symbols[i:i+batch_size]
121
- tasks = [self.analyze_symbol(symbol) for symbol in batch]
122
- results = await asyncio.gather(*tasks)
123
-
124
- for signals in results:
125
- for signal in signals:
126
- if signal['confidence'] >= config.THRESHOLDS['min_confidence']:
127
- await self.generate_signal_message(signal)
128
-
129
- await asyncio.sleep(config.THROTTLING['delay_between_batches'])
130
-
131
- async def generate_signal_message(self, signal):
132
- message = (
133
- f"🚨 <b>سیگنال معاملاتی!</b> 🚨\n\n"
134
- f"🏷 ارز: {signal['symbol']}\n"
135
- f"📊 تایم فریم: {signal['timeframe']}\n"
136
- f"🔢 نوع سیگنال: {signal['signal']}\n"
137
- f"📈 قیمت فعلی: {signal['price']:.4f}\n"
138
- f"✅ اعتبار: {signal['confidence']}%\n"
139
- f"🎯 حد سود: {signal['price'] * 1.03:.4f}\n"
140
- f"🛑 حد ضرر: {signal['price'] * 0.97:.4f}\n"
141
- f"⏳ مدت اعتبار: ۶ ساعت"
142
- )
143
- await self.send_telegram(message)
144
-
145
- async def main(self):
146
- await self.send_telegram("🤖 ربات مانیتورینگ LBank فعال شد!")
147
  while True:
148
  try:
149
- await self.run_analysis()
150
- await asyncio.sleep(1800)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151
  except Exception as e:
152
- logger.error(f"Critical error: {str(e)}")
153
- await self.send_telegram(f"🔥 خطای سیستمی: {str(e)}")
154
- await asyncio.sleep(60)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
155
 
156
- async def close(self):
157
- await self.http_client.aclose()
 
 
 
 
 
 
 
 
158
 
159
  if __name__ == "__main__":
160
- monitor = LBankMonitor()
161
- try:
162
- asyncio.run(monitor.main())
163
- except KeyboardInterrupt:
164
- asyncio.run(monitor.close())
 
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
  import ccxt
5
  import pandas as pd
6
+ import numpy as np
7
+ import time
8
  import asyncio
9
  import logging
10
+ import json
11
+ import os
12
+ import math
13
+ import talib
14
+ import telegram
15
+ from telegram.ext import Updater, CommandHandler, MessageHandler, Filters
16
+ from datetime import datetime, timedelta
17
+ import threading
18
+ import schedule
19
+ import warnings
20
+ import requests
21
+ from collections import defaultdict
22
+ import matplotlib.pyplot as plt
23
+ import matplotlib
24
+ import io
25
+ from config import Config
26
+
27
+ # تنظیم matplotlib برای پشتیبانی از زبان فارسی
28
+ matplotlib.rc('font', family='DejaVu Sans')
29
 
30
+ # تنظیمات لاگینگ
31
  logging.basicConfig(
32
+ level=logging.INFO,
33
  format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
34
+ handlers=[
35
+ logging.FileHandler("crypto_analyzer.log", encoding='utf-8'),
36
+ logging.StreamHandler()
37
+ ]
38
  )
39
  logger = logging.getLogger(__name__)
40
 
41
+ # اخطارهای غیرضروری را نادیده بگیر
42
+ warnings.filterwarnings("ignore")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
 
44
+ class CryptoAnalyzer:
45
+ def __init__(self, config_path="config.json"):
46
+ """مقداردهی اولیه کلاس تحلیلگر ارزهای دیجیتال"""
47
+ try:
48
+ # بارگیری تنظیمات
49
+ self.config = Config()
50
+
51
+ # تنظیمات صرافی
52
+ self.exchange = ccxt.lbank({
53
+ 'apiKey': self.config.API_KEY,
54
+ 'secret': self.config.SECRET_KEY,
55
+ 'enableRateLimit': True,
56
+ 'options': {'defaultType': 'spot'}
57
+ })
58
+
59
+ # دیکشنری برای ذخیره داده‌های بازار
60
+ self.market_data = {}
61
+
62
+ # لیست ارزهای تحت نظارت
63
+ self.symbols = self.config.SYMBOLS
64
+
65
+ # تایم فریم‌ها
66
+ self.timeframes = self.config.TIMEFRAMES
67
+
68
+ # تنظیمات تلگرام
69
+ self.bot = telegram.Bot(token=self.config.TELEGRAM_TOKEN)
70
+ self.chat_id = self.config.TELEGRAM_CHAT_ID
71
+
72
+ # وضعیت اتصال
73
+ self.is_connected = False
74
+
75
+ # دیکشنری برای ذخیره سیگنال‌های فعال
76
+ self.active_signals = {}
77
+
78
+ # بررسی اتصال به صرافی
79
+ self.check_exchange_connection()
80
+
81
+ logger.info("🔄 سیستم تحلیلگر ارزهای دیجیتال با موفقیت راه‌اندازی شد!")
82
+ self.send_telegram_message("🔄 سیستم تحلیلگر ارزهای دیجیتال با موفقیت راه‌اندازی شد!")
83
+
84
+ except Exception as e:
85
+ logger.error(f"خطا در راه‌اندازی سیستم: {str(e)}")
86
+ self.send_telegram_message(f"❌ خطا در راه‌اندازی سیستم: {str(e)}")
87
+
88
+ def check_exchange_connection(self):
89
+ """بررسی اتصال به صرافی"""
90
+ try:
91
+ self.exchange.load_markets()
92
+ self.is_connected = True
93
+ logger.info("✅ اتصال به صرافی با موفقیت برقرار شد!")
94
+ self.send_telegram_message("✅ اتصال به صرافی با موفقیت برقرار شد!")
95
+ return True
96
+ except Exception as e:
97
+ self.is_connected = False
98
+ logger.error(f"❌ خطا در اتصال به صرافی: {str(e)}")
99
+ self.send_telegram_message(f"❌ خطا در اتصال به صرافی: {str(e)}")
100
+ return False
101
+
102
+ async def fetch_ohlcv_data(self, symbol, timeframe):
103
+ """دریافت داده‌های OHLCV برای یک ارز در یک تایم فریم خاص"""
104
+ try:
105
+ limit = 100 # تعداد کندل‌ها
106
+ ohlcv = self.exchange.fetch_ohlcv(symbol, timeframe, limit=limit)
107
+ df = pd.DataFrame(ohlcv, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
108
+ df['timestamp'] = pd.to_datetime(df['timestamp'], unit='ms')
109
+ df.set_index('timestamp', inplace=True)
110
+
111
+ if symbol not in self.market_data:
112
+ self.market_data[symbol] = {}
113
+
114
+ self.market_data[symbol][timeframe] = df
115
+ logger.debug(f"داده‌های {symbol} در تایم فریم {timeframe} با موفقیت دریافت شد.")
116
+
117
+ return df
118
+ except Exception as e:
119
+ logger.error(f"خطا در دریافت داده‌های {symbol} در تایم فریم {timeframe}: {str(e)}")
120
+ return None
121
+
122
+ async def fetch_order_book(self, symbol):
123
+ """دریافت اردربوک برای یک ارز"""
124
+ try:
125
+ order_book = self.exchange.fetch_order_book(symbol)
126
+ return order_book
127
+ except Exception as e:
128
+ logger.error(f"خطا در دریافت اردربوک {symbol}: {str(e)}")
129
+ return None
130
 
131
  def calculate_indicators(self, df):
132
+ """محاسبه شاخص‌های تکنیکال مختلف"""
133
+ try:
134
+ # محاسبه RSI
135
+ df['rsi'] = talib.RSI(df['close'], timeperiod=14)
136
+
137
+ # محاسبه MACD
138
+ macd, macd_signal, macd_hist = talib.MACD(df['close'])
139
+ df['macd'] = macd
140
+ df['macd_signal'] = macd_signal
141
+ df['macd_hist'] = macd_hist
142
+
143
+ # محاسبه میانگین متحرک
144
+ df['sma_20'] = talib.SMA(df['close'], timeperiod=20)
145
+ df['sma_50'] = talib.SMA(df['close'], timeperiod=50)
146
+ df['sma_200'] = talib.SMA(df['close'], timeperiod=200)
147
+
148
+ # محاسبه Bollinger Bands
149
+ df['upper_band'], df['middle_band'], df['lower_band'] = talib.BBANDS(df['close'], timeperiod=20)
150
+
151
+ # محاسبه MFI (Money Flow Index)
152
+ df['mfi'] = talib.MFI(df['high'], df['low'], df['close'], df['volume'], timeperiod=14)
153
+
154
+ # محاسبه ATR (Average True Range)
155
+ df['atr'] = talib.ATR(df['high'], df['low'], df['close'], timeperiod=14)
156
+
157
+ return df
158
+ except Exception as e:
159
+ logger.error(f"خطا در محاسبه شاخص‌ها: {str(e)}")
160
+ return df
161
+
162
+ def calculate_volume_profile(self, df):
163
+ """تحلیل پروفایل حجم معاملات"""
164
+ try:
165
+ # تقسیم محدوده قیمت به ۱۰ دسته
166
+ price_range = df['high'].max() - df['low'].min()
167
+ bin_size = price_range / 10
168
+
169
+ price_bins = []
170
+ volume_bins = []
171
+
172
+ for i in range(10):
173
+ lower = df['low'].min() + i * bin_size
174
+ upper = lower + bin_size
175
+ mask = (df['close'] >= lower) & (df['close'] < upper)
176
+ volume_in_bin = df.loc[mask, 'volume'].sum()
177
+
178
+ price_bins.append((lower + upper) / 2)
179
+ volume_bins.append(volume_in_bin)
180
+
181
+ return {
182
+ 'price_bins': price_bins,
183
+ 'volume_bins': volume_bins
184
+ }
185
+ except Exception as e:
186
+ logger.error(f"خطا در محاسبه پروفایل حجم: {str(e)}")
187
+ return None
188
+
189
+ def calculate_delta(self, symbol, timeframe):
190
+ """محاسبه دلتا (تفاوت بین حجم خرید و فروش)"""
191
+ try:
192
+ df = self.market_data[symbol][timeframe]
193
+
194
+ # محاسبه دلتا ساده با استفاده از تغییرات قیمت و حجم
195
+ df['delta'] = np.where(df['close'] > df['open'], df['volume'], -df['volume'])
196
+ df['cumulative_delta'] = df['delta'].cumsum()
197
+
198
+ # محاسبه دلتا ریورس
199
+ df['reverse_delta'] = -df['delta']
200
+ df['cumulative_reverse_delta'] = df['reverse_delta'].cumsum()
201
+
202
+ return df['cumulative_delta'].iloc[-1], df['cumulative_reverse_delta'].iloc[-1]
203
+ except Exception as e:
204
+ logger.error(f"خطا در محاسبه دلتا برای {symbol} در تایم فریم {timeframe}: {str(e)}")
205
+ return 0, 0
206
+
207
+ def analyze_bid_ask(self, order_book):
208
+ """تحلیل بید اسک (خرید و فروش) از اطلاعات اردربوک"""
209
+ try:
210
+ # محاسبات بید اسک
211
+ bids = order_book['bids']
212
+ asks = order_book['asks']
213
+
214
+ # مجموع حجم در سمت خرید و فروش
215
+ total_bid_volume = sum([bid[1] for bid in bids[:10]])
216
+ total_ask_volume = sum([ask[1] for ask in asks[:10]])
217
+
218
+ # نسبت حجم خرید به فروش
219
+ bid_ask_ratio = total_bid_volume / total_ask_volume if total_ask_volume > 0 else float('inf')
220
+
221
+ # فشار خرید یا فروش
222
+ if bid_ask_ratio > 1.5:
223
+ pressure = "خرید"
224
+ pressure_strength = min(bid_ask_ratio / 5, 1.0) # نرمال‌سازی قدرت
225
+ elif bid_ask_ratio < 0.67:
226
+ pressure = "فروش"
227
+ pressure_strength = min((1 / bid_ask_ratio) / 5, 1.0) # نرمال سازی قدرت
228
+ else:
229
+ pressure = "خنثی"
230
+ pressure_strength = 0.0
231
+
232
+ return {
233
+ 'bid_ask_ratio': bid_ask_ratio,
234
+ 'pressure': pressure,
235
+ 'pressure_strength': pressure_strength
236
+ }
237
+ except Exception as e:
238
+ logger.error(f"خطا در تحلیل بید اسک: {str(e)}")
239
+ return {
240
+ 'bid_ask_ratio': 1.0,
241
+ 'pressure': "خنثی",
242
+ 'pressure_strength': 0.0
243
+ }
244
+
245
+ def analyze_order_flow(self, symbol, timeframes):
246
+ """تحلیل جریان سفارشات (Order Flow) برای یک ارز در تایم فریم‌های مختلف"""
247
+ order_flow_score = 0
248
+ try:
249
+ order_book = asyncio.run(self.fetch_order_book(symbol))
250
+ if not order_book:
251
+ return 0
252
+
253
+ bid_ask_analysis = self.analyze_bid_ask(order_book)
254
+
255
+ # جمع آوری دلتا از تمام تایم فریم‌ها
256
+ total_delta = 0
257
+ for tf in timeframes:
258
+ if symbol in self.market_data and tf in self.market_data[symbol]:
259
+ delta, _ = self.calculate_delta(symbol, tf)
260
+ # وزن دهی به دلتا بر اساس تایم فریم
261
+ weight = {'1m': 0.1, '5m': 0.15, '15m': 0.2, '30m': 0.25, '1h': 0.3}.get(tf, 0.2)
262
+ total_delta += delta * weight
263
+
264
+ # ترکیب دلتا با تحلیل بید اسک
265
+ if bid_ask_analysis['pressure'] == "خرید" and total_delta > 0:
266
+ order_flow_score = 0.5 + bid_ask_analysis['pressure_strength'] * 0.5
267
+ elif bid_ask_analysis['pressure'] == "فروش" and total_delta < 0:
268
+ order_flow_score = -0.5 - bid_ask_analysis['pressure_strength'] * 0.5
269
+ else:
270
+ order_flow_score = total_delta / (abs(total_delta) + 1) * 0.3 # نرمال سازی بین -0.3 و 0.3
271
+
272
+ return order_flow_score
273
+ except Exception as e:
274
+ logger.error(f"خطا در تحلیل جریان سفارشات برای {symbol}: {str(e)}")
275
+ return order_flow_score
276
+
277
+ def analyze_elliott_waves(self, df):
278
+ """تحلیل امواج الیوت"""
279
+ try:
280
+ # این یک تقریب ساده از تحلیل امواج الیوت است
281
+ # برای تحلیل دقیق، الگوریتم‌های پیچیده‌تری نیاز است
282
+
283
+ # پیدا کردن نقاط اوج و فرود اخیر
284
+ prices = df['close'].values
285
+ window_size = 5
286
+ peaks = []
287
+ troughs = []
288
+
289
+ for i in range(window_size, len(prices) - window_size):
290
+ if all(prices[i] > prices[i-j] for j in range(1, window_size+1)) and all(prices[i] > prices[i+j] for j in range(1, window_size+1)):
291
+ peaks.append((i, prices[i]))
292
+ if all(prices[i] < prices[i-j] for j in range(1, window_size+1)) and all(prices[i] < prices[i+j] for j in range(1, window_size+1)):
293
+ troughs.append((i, prices[i]))
294
+
295
+ # حداقل ۵ نقطه ��رای تحلیل موج الیوت نیاز است
296
+ if len(peaks) < 3 or len(troughs) < 2:
297
+ return {
298
+ 'wave_pattern': 'نامشخص',
299
+ 'confidence': 0.0,
300
+ 'potential_direction': 'خنثی'
301
+ }
302
+
303
+ # مرتب‌سازی براساس زمان
304
+ all_points = sorted(peaks + troughs, key=lambda x: x[0])
305
+
306
+ # تلاش برای تشخیص الگوهای امواج الیوت ۵-۳
307
+ if len(all_points) >= 5:
308
+ # بررسی اینکه آیا ۵ موج رو به بالا داریم
309
+ if all_points[0][1] < all_points[1][1] > all_points[2][1] < all_points[3][1] > all_points[4][1]:
310
+ # احتمالا در موج ۵ صعودی هستیم
311
+ return {
312
+ 'wave_pattern': 'موج 5 صعودی',
313
+ 'confidence': 0.7,
314
+ 'potential_direction': 'نزولی' # پس از موج ۵ صعودی، احتمال نزول وجود دارد
315
+ }
316
+ elif all_points[0][1] > all_points[1][1] < all_points[2][1] > all_points[3][1] < all_points[4][1]:
317
+ # احتمالا در موج ۵ نزولی هستیم
318
+ return {
319
+ 'wave_pattern': 'موج 5 نزولی',
320
+ 'confidence': 0.7,
321
+ 'potential_direction': 'صعودی' # پس از موج ۵ نزولی، احتمال صعود وجود دارد
322
+ }
323
+
324
+ # اگر الگوی مشخصی شناسایی نشد
325
+ return {
326
+ 'wave_pattern': 'در حال تشکیل',
327
+ 'confidence': 0.3,
328
+ 'potential_direction': 'نامشخص'
329
+ }
330
+ except Exception as e:
331
+ logger.error(f"خطا در تحلیل امواج الیوت: {str(e)}")
332
+ return {
333
+ 'wave_pattern': 'خطا',
334
+ 'confidence': 0.0,
335
+ 'potential_direction': 'خنثی'
336
+ }
337
+
338
+ def analyze_footprint(self, symbol, timeframe):
339
+ """تحلیل فوت‌پرینت (نقشه حرارتی معاملات)"""
340
+ try:
341
+ df = self.market_data[symbol][timeframe]
342
+
343
+ # تجزیه و تحلیل بر اساس تغییرات قیمت و حجم
344
+ df['price_change'] = df['close'] - df['open']
345
+ df['normalized_volume'] = df['volume'] / df['volume'].mean()
346
+
347
+ # شناسایی کندل‌های مهم با حجم بالا
348
+ high_volume_candles = df[df['normalized_volume'] > 1.5]
349
+
350
+ # محاسبه نسبت کندل‌های صعودی پرحجم به کل کندل‌های پرحجم
351
+ if len(high_volume_candles) > 0:
352
+ bullish_ratio = len(high_volume_candles[high_volume_candles['price_change'] > 0]) / len(high_volume_candles)
353
+ else:
354
+ bullish_ratio = 0.5
355
+
356
+ # نمره دهی به فوت‌پرینت
357
+ if bullish_ratio > 0.7:
358
+ footprint_score = (bullish_ratio - 0.5) * 2 # نرمال سازی بین 0 و 1
359
+ direction = "صعودی"
360
+ elif bullish_ratio < 0.3:
361
+ footprint_score = ((0.5 - bullish_ratio) * 2) * -1 # نرمال سازی بین -1 و 0
362
+ direction = "نزولی"
363
+ else:
364
+ footprint_score = (bullish_ratio - 0.5) * 2 # بین -0.4 و 0.4
365
+ direction = "خنثی"
366
+
367
+ return {
368
+ 'footprint_score': footprint_score,
369
+ 'direction': direction,
370
+ 'high_volume_candles_count': len(high_volume_candles)
371
+ }
372
+ except Exception as e:
373
+ logger.error(f"خطا در تحلیل فوت‌پرینت برای {symbol} در تایم فریم {timeframe}: {str(e)}")
374
+ return {
375
+ 'footprint_score': 0,
376
+ 'direction': "خنثی",
377
+ 'high_volume_candles_count': 0
378
+ }
379
+
380
+ def analyze_poc(self, symbol, timeframe):
381
+ """تحلیل نقطه کنترل قیمت (Point of Control)"""
382
+ try:
383
+ df = self.market_data[symbol][timeframe]
384
+
385
+ # محاسبه پروفایل حجم
386
+ volume_profile = self.calculate_volume_profile(df)
387
+ if not volume_profile:
388
+ return {
389
+ 'poc_price': df['close'].iloc[-1],
390
+ 'distance_from_poc': 0,
391
+ 'poc_significance': 0
392
+ }
393
+
394
+ # پیدا کردن قیمت با بیشترین حجم (POC)
395
+ max_volume_index = np.argmax(volume_profile['volume_bins'])
396
+ poc_price = volume_profile['price_bins'][max_volume_index]
397
+
398
+ # محاسبه فاصله از POC
399
+ current_price = df['close'].iloc[-1]
400
+ distance_from_poc = (current_price - poc_price) / poc_price * 100
401
+
402
+ # محاسبه اهمیت POC (بر اساس نسبت حجم در POC به میانگین حجم)
403
+ avg_volume = np.mean(volume_profile['volume_bins'])
404
+ max_volume = volume_profile['volume_bins'][max_volume_index]
405
+ poc_significance = max_volume / avg_volume if avg_volume > 0 else 1
406
+
407
+ return {
408
+ 'poc_price': poc_price,
409
+ 'distance_from_poc': distance_from_poc,
410
+ 'poc_significance': min(poc_significance / 3, 1.0) # نرمال‌سازی بین 0 و 1
411
+ }
412
+ except Exception as e:
413
+ logger.error(f"خطا در تحلیل POC برای {symbol} در تایم فریم {timeframe}: {str(e)}")
414
+ return {
415
+ 'poc_price': 0,
416
+ 'distance_from_poc': 0,
417
+ 'poc_significance': 0
418
+ }
419
+
420
+ def generate_trading_signal(self, symbol):
421
+ """تولید سیگنال معاملاتی برای یک ارز بر اساس تحلیل‌های انجام شده"""
422
+ try:
423
+ # جمع‌آوری نتایج تحلیل‌ها از تمام تایم فریم‌ها
424
+ timeframe_analysis = {}
425
+ signal_strength = 0
426
+ signal_direction = "خنثی"
427
+
428
+ for tf in self.timeframes:
429
+ # بررسی وجود داده برای این تایم فریم
430
+ if symbol not in self.market_data or tf not in self.market_data[symbol]:
431
+ continue
432
 
433
+ df = self.market_data[symbol][tf]
434
+
435
+ # وزن تایم فریم در تحلیل نهایی
436
+ tf_weight = {'1m': 0.05, '5m': 0.1, '15m': 0.2, '30m': 0.25, '1h': 0.4}.get(tf, 0.2)
437
+
438
+ # تحلیل‌های مختلف
439
+ order_flow_score = self.analyze_order_flow(symbol, [tf])
440
+ footprint_analysis = self.analyze_footprint(symbol, tf)
441
+ poc_analysis = self.analyze_poc(symbol, tf)
442
+ elliott_analysis = self.analyze_elliott_waves(df)
443
+
444
+ # تحلیل شاخص‌های تکنیکال
445
+ rsi = df['rsi'].iloc[-1] if 'rsi' in df.columns else 50
446
+ macd_hist = df['macd_hist'].iloc[-1] if 'macd_hist' in df.columns else 0
447
+
448
+ # محاسبه امتیاز نهایی تایم فریم
449
+ tf_score = 0
450
+
451
+ # امتیاز بر اساس RSI
452
+ if rsi > 70:
453
+ tf_score -= 0.3 # اشباع خرید
454
+ elif rsi < 30:
455
+ tf_score += 0.3 # اشباع فروش
456
+ elif rsi > 60:
457
+ tf_score += 0.1 # روند صعودی
458
+ elif rsi < 40:
459
+ tf_score -= 0.1 # روند نزولی
460
+
461
+ # امتیاز بر اساس MACD
462
+ if macd_hist > 0 and macd_hist > df['macd_hist'].iloc[-2]:
463
+ tf_score += 0.2 # سیگنال صعودی قوی
464
+ elif macd_hist > 0:
465
+ tf_score += 0.1 # سیگنال صعودی
466
+ elif macd_hist < 0 and macd_hist < df['macd_hist'].iloc[-2]:
467
+ tf_score -= 0.2 # سیگنال نزولی قوی
468
+ elif macd_hist < 0:
469
+ tf_score -= 0.1 # سیگنال نزولی
470
+
471
+ # امتیاز بر اساس میانگین متحرک
472
+ if 'sma_20' in df.columns and 'sma_50' in df.columns:
473
+ if df['close'].iloc[-1] > df['sma_20'].iloc[-1] > df['sma_50'].iloc[-1]:
474
+ tf_score += 0.2 # روند صعودی قوی
475
+ elif df['close'].iloc[-1] < df['sma_20'].iloc[-1] < df['sma_50'].iloc[-1]:
476
+ tf_score -= 0.2 # روند نزولی قوی
477
+
478
+ # امتیاز بر اساس Order Flow
479
+ tf_score += order_flow_score
480
+
481
+ # امتیاز بر اساس Footprint
482
+ tf_score += footprint_analysis['footprint_score']
483
+
484
+ # امتیاز بر اساس POC
485
+ if poc_analysis['distance_from_poc'] < 0 and poc_analysis['poc_significance'] > 0.5:
486
+ tf_score += 0.2 # قیمت زیر POC با اهمیت بالا
487
+ elif poc_analysis['distance_from_poc'] > 0 and poc_analysis['poc_significance'] > 0.5:
488
+ tf_score -= 0.2 # قیمت بالای POC با اهمیت بالا
489
+
490
+ # امتیاز بر اساس امواج الیوت
491
+ if elliott_analysis['potential_direction'] == "صعودی":
492
+ tf_score += 0.3 * elliott_analysis['confidence']
493
+ elif elliott_analysis['potential_direction'] == "نزولی":
494
+ tf_score -= 0.3 * elliott_analysis['confidence']
495
+
496
+ # ذخیره نتیجه تحلیل تایم فریم
497
+ timeframe_analysis[tf] = {
498
+ 'score': tf_score,
499
+ 'order_flow': order_flow_score,
500
+ 'footprint': footprint_analysis,
501
+ 'poc': poc_analysis,
502
+ 'elliott': elliott_analysis
503
+ }
504
+
505
+ # افزودن امتیاز تایم فریم به امتیاز کلی با اعمال وزن
506
+ signal_strength += tf_score * tf_weight
507
+
508
+ # تعیین جهت سیگنال
509
+ if signal_strength > 0.3:
510
+ signal_direction = "خرید"
511
+ elif signal_strength < -0.3:
512
+ signal_direction = "فروش"
513
+
514
+ # تعیین قدرت سیگنال (بین 90% تا 100%)
515
+ normalized_strength = min(abs(signal_strength) * 10 / 8, 1.0) # نرمال‌سازی
516
+ signal_strength_percent = 90 + (normalized_strength * 10)
517
+
518
+ # محاسبه حد سود و حد ضرر
519
+ current_price = self.market_data[symbol]['1h']['close'].iloc[-1]
520
+ atr = self.market_data[symbol]['1h']['atr'].iloc[-1] if 'atr' in self.market_data[symbol]['1h'].columns else current_price * 0.01
521
+
522
+ if signal_direction == "خرید":
523
+ stop_loss = current_price - (atr * 2)
524
+ take_profit = current_price + (atr * 4)
525
+ elif signal_direction == "فروش":
526
+ stop_loss = current_price + (atr * 2)
527
+ take_profit = current_price - (atr * 4)
528
+ else:
529
+ stop_loss = 0
530
+ take_profit = 0
531
+
532
+ # تعیین مدت زمان ماندن در معامله (حداکثر ۶ ساعت)
533
+ if signal_strength_percent > 97:
534
+ duration_hours = 6
535
+ elif signal_strength_percent > 95:
536
+ duration_hours = 4
537
+ else:
538
+ duration_hours = 2
539
+
540
+ return {
541
+ 'symbol': symbol,
542
+ 'direction': signal_direction,
543
+ 'strength': signal_strength_percent,
544
+ 'entry_price': current_price,
545
+ 'stop_loss': stop_loss,
546
+ 'take_profit': take_profit,
547
+ 'duration_hours': duration_hours,
548
+ 'analysis': timeframe_analysis,
549
+ 'timestamp': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
550
  }
551
+ except Exception as e:
552
+ logger.error(f"خطا در تولید سیگنال معاملاتی برای {symbol}: {str(e)}")
553
+ return None
554
+
555
+ def should_send_signal(self, signal):
556
+ """تصمیم‌گیری برای ارسال سیگنال بر اساس قدرت آن و وضعیت سیگنال‌های قبلی"""
557
+ try:
558
+ # اگر سیگنال خنثی است، ارسال نکن
559
+ if signal['direction'] == "خنثی":
560
+ return False
561
+
562
+ # بررسی قدرت سیگنال
563
+ if signal['strength'] < 90:
564
+ return False
565
+
566
+ # بررسی عدم تکرار سیگنال‌ها در زمان کوتاه
567
+ symbol = signal['symbol']
568
+ if symbol in self.active_signals:
569
+ last_signal = self.active_signals[symbol]
570
+ time_diff = datetime.now() - datetime.strptime(last_signal['timestamp'], '%Y-%m-%d %H:%M:%S')
571
+
572
+ # اگر کمتر از ۲ ساعت از سیگنال قبلی گذشته باشد و جهت یکسان باشد
573
+ if time_diff.total_seconds() < 7200 and last_signal['direction'] == signal['direction']:
574
+ return False
575
+
576
+ # اگر قدرت سیگنال جدید کمتر از سیگن��ل قبلی باشد
577
+ if signal['strength'] <= last_signal['strength'] and time_diff.total_seconds() < 14400:
578
+ return False
579
 
580
+ return True
581
+ except Exception as e:
582
+ logger.error(f"خطا در تصمیم‌گیری برای ارسال سیگنال: {str(e)}")
583
+ return False
584
+
585
+ def create_signal_chart(self, symbol):
586
+ """ایجاد نمودار برای سیگنال معاملاتی"""
587
+ try:
588
+ if symbol not in self.market_data or '1h' not in self.market_data[symbol]:
589
+ return None
590
+
591
+ df = self.market_data[symbol]['1h'].copy()
592
+
593
+ # رسم نمودار
594
+ plt.figure(figsize=(10, 6))
595
+
596
+ # نمودار قیمت بسته شدن
597
+ plt.plot(df.index[-30:], df['close'].values[-30:], label='قیمت', color='blue')
598
+
599
+ # اضافه کردن میانگین متحرک
600
+ if 'sma_20' in df.columns and 'sma_50' in df.columns:
601
+ plt.plot(df.index[-30:], df['sma_20'].values[-30:], label='SMA 20', color='orange', alpha=0.7)
602
+ plt.plot(df.index[-30:], df['sma_50'].values[-30:], label='SMA 50', color='red', alpha=0.7)
603
+
604
+ # تنظیمات نمودار
605
+ plt.title(f"تحلیل ارز {symbol}", fontsize=14)
606
+ plt.grid(True, alpha=0.3)
607
+ plt.legend()
608
+
609
+ # ذخیره نمودار در حافظه
610
+ buf = io.BytesIO()
611
+ plt.savefig(buf, format='png', dpi=100)
612
+ plt.close()
613
+ buf.seek(0)
614
+
615
+ return buf
616
+ except Exception as e:
617
+ logger.error(f"خطا در ایجاد نمودار برای {symbol}: {str(e)}")
618
+ return None
619
+
620
+ def send_signal_to_telegram(self, signal):
621
+ """ارسال سیگنال معاملاتی به تلگرام"""
622
+ try:
623
+ if not signal:
624
+ return False
625
+
626
+ direction_emoji = "🟢 خرید" if signal['direction'] == "خرید" else "🔴 فروش"
627
+
628
+ # ساخت متن پیام
629
+ message = f"""
630
+ ⚡️ *سیگنال معاملاتی جدید* ⚡️
631
+
632
+ {direction_emoji} *{signal['symbol']}*
633
+
634
+ 💰 قیمت ورود: `{signal['entry_price']}`
635
+ 🛑 حد ضرر: `{signal['stop_loss']}`
636
+ ✅ حد سود: `{signal['take_profit']}`
637
+
638
+ ⏱ مدت زمان: *{signal['duration_hours']} ساعت*
639
+ 💪 قدرت سیگنال: *{signal['strength']:.1f}%*
640
+
641
+ ⏰ تاریخ سیگنال: {signal['timestamp']}
642
+
643
+ 🤖 سیگنال‌دهی خودکار هوشمند
644
+ """
645
+
646
+ # ارسال پیام متنی
647
+ self.bot.send_message(
648
+ chat_id=self.chat_id,
649
  text=message,
650
+ parse_mode=telegram.ParseMode.MARKDOWN
651
  )
652
+
653
+ # ارسال نمودار
654
+ chart = self.create_signal_chart(signal['symbol'])
655
+ if chart:
656
+ self.bot.send_photo(
657
+ chat_id=self.chat_id,
658
+ photo=chart,
659
+ caption=f"نمودار تحلیلی {signal['symbol']}"
660
+ )
661
+
662
+ # ذخیره سیگنال در لیست سیگنال‌های فعال
663
+ self.active_signals[signal['symbol']] = signal
664
+
665
+ logger.info(f"سیگنال معاملاتی برای {signal['symbol']} با موفقیت ارسال شد.")
666
+ return True
667
+
668
+ except Exception as e:
669
+ logger.error(f"خطا در ارسال سیگنال به تلگرام: {str(e)}")
670
+ return False
671
 
672
+ def send_telegram_message(self, message):
673
+ """ارسال پیام ساده به تلگرام"""
674
  try:
675
+ self.bot.send_message(
676
+ chat_id=self.chat_id,
677
+ text=message
678
+ )
679
  return True
680
  except Exception as e:
681
+ logger.error(f"خطا در ارسال پیام به تلگرام: {str(e)}")
 
 
682
  return False
683
 
684
+ async def periodic_analysis(self):
685
+ """انجام تحلیل دوره‌ای بر روی تمام ارزها"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
686
  while True:
687
  try:
688
+ logger.info("شروع تحلیل دوره‌ای بازار...")
689
+ tasks = []
690
+ for symbol in self.symbols:
691
+ for timeframe in self.timeframes:
692
+ tasks.append(self.fetch_ohlcv_data(symbol, timeframe))
693
+
694
+ # اجرای همزمان تمام درخواست‌ها
695
+ await asyncio.gather(*tasks)
696
+
697
+ # تحلیل و تولید سیگنال برای هر ارز
698
+ for symbol in self.symbols:
699
+ signal = self.generate_trading_signal(symbol)
700
+ if signal and self.should_send_signal(signal):
701
+ self.send_signal_to_telegram(signal)
702
+
703
+ logger.info("تحلیل دوره‌ای با موفقیت انجام شد. خواب برای 5 دقیقه...")
704
+ await asyncio.sleep(300) # خواب برای 5 دقیقه
705
+
706
  except Exception as e:
707
+ logger.error(f"خطا در تحلیل دوره‌ای: {str(e)}")
708
+ await asyncio.sleep(60) # در صورت خطا 1 دقیقه صبر کنید
709
+
710
+ def start_telegram_handlers(self):
711
+ """شروع کردن هندلرهای تلگرام برای دریافت دستورات"""
712
+ updater = Updater(self.config.TELEGRAM_TOKEN, use_context=True)
713
+ dp = updater.dispatcher
714
+
715
+ # دستور وضعیت
716
+ def status(update, context):
717
+ status_msg = "وضعیت سیستم:\n"
718
+ status_msg += f"• اتصال به صرافی: {'✅' if self.is_connected else '❌'}\n"
719
+ status_msg += f"• تعداد ارزهای تحت نظارت: {len(self.symbols)}\n"
720
+ status_msg += f"• سیگنال‌های فعال: {len(self.active_signals)}"
721
+ update.message.reply_text(status_msg)
722
+
723
+ # دستور راهنما
724
+ def help(update, context):
725
+ help_text = """
726
+ دستورات موجود:
727
+ /status - نمایش وضعیت سیستم
728
+ /analyze [symbol] - تحلیل فوری یک ارز
729
+ /list_signals - نمایش سیگنال‌های فعال
730
+ """
731
+ update.message.reply_text(help_text)
732
+
733
+ # ثبت هندلرها
734
+ dp.add_handler(CommandHandler("status", status))
735
+ dp.add_handler(CommandHandler("help", help))
736
+
737
+ # شروع پولینگ
738
+ updater.start_polling()
739
+ logger.info("هندلرهای تلگرام با موفقیت راه‌اندازی شدند.")
740
+
741
+ def run(self):
742
+ """شروع اجرای اصلی برنامه"""
743
+ try:
744
+ # شروع هندلرهای تلگرام در یک ریسمان جداگانه
745
+ telegram_thread = threading.Thread(target=self.start_telegram_handlers)
746
+ telegram_thread.daemon = True
747
+ telegram_thread.start()
748
 
749
+ # شروع تحلیل دوره‌ای در لوپ asyncio
750
+ loop = asyncio.get_event_loop()
751
+ loop.run_until_complete(self.periodic_analysis())
752
+
753
+ except KeyboardInterrupt:
754
+ logger.info("دریافت سیگنال توقف. خاموش کردن سیستم...")
755
+ self.send_telegram_message("🛑 سیستم در حال خاموش شدن...")
756
+ except Exception as e:
757
+ logger.error(f"خطای غیرمنتظره: {str(e)}")
758
+ self.send_telegram_message(f"☠️ خطای بحرانی: {str(e)}")
759
 
760
  if __name__ == "__main__":
761
+ analyzer = CryptoAnalyzer()
762
+ analyzer.run()