Kaynağa Gözat

Initial Commit V1.0

Sasan Salamzadeh 1 yıl önce
işleme
e6544faa44
3 değiştirilmiş dosya ile 516 ekleme ve 0 silme
  1. 320 0
      Arbitrage.py
  2. 98 0
      README-fa.md
  3. 98 0
      README.md

+ 320 - 0
Arbitrage.py

@@ -0,0 +1,320 @@
+import asyncio
+import requests
+from datetime import datetime
+from telegram import Bot
+import platform
+
+# Telegram bot configuration (replace with your own bot token and chat ID)
+TELEGRAM_BOT_TOKEN = "YourBotToken"  # Replace with your Telegram bot token
+TELEGRAM_CHAT_ID = "YourID"     # Replace with your Telegram chat ID
+
+# API endpoints
+NOBITEX_API = "https://apiv2.nobitex.ir/v3/orderbook/USDTIRT"
+BITPIN_API = "https://api.bitpin.ir/v4/mth/matches/USDT_IRT/"
+
+# Minimum volume for Nobitex sell orders (in USDT)
+MIN_SELL_VOLUME = 1000.0
+
+def log_error(message):
+    """Log errors with timestamp."""
+    print(f"Error at {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}: {message}")
+
+async def send_telegram_message(message):
+    """Send Telegram message with error handling."""
+    try:
+        bot = Bot(token=TELEGRAM_BOT_TOKEN)
+        await bot.send_message(chat_id=TELEGRAM_CHAT_ID, text=message)
+        print("Sent message:", message)
+    except Exception as e:
+        log_error(f"Failed to send Telegram message: {e}")
+
+def fetch_nobitex_data():
+    """Fetch Nobitex orderbook data with error handling."""
+    try:
+        response = requests.get(NOBITEX_API, timeout=10)
+        response.raise_for_status()
+        data = response.json()
+        if data.get("status") == "ok":
+            print("Nobitex API read successfully at", response.headers.get('Date', datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
+            return data.get("asks", []), data.get("bids", [])
+        else:
+            log_error(f"Nobitex API error: {data.get('status')}")
+            return [], []
+    except (requests.RequestException, ValueError) as e:
+        log_error(f"Error fetching Nobitex data: {e}")
+        return [], []
+
+def fetch_bitpin_data():
+    """Fetch Bitpin trade data with error handling."""
+    try:
+        response = requests.get(BITPIN_API, timeout=10)
+        response.raise_for_status()
+        data = response.json()
+        print("Bitpin API read successfully at", response.headers.get('Date', datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
+        return data
+    except (requests.RequestException, ValueError) as e:
+        log_error(f"Error fetching Bitpin data: {e}")
+        return []
+
+def calculate_weighted_average(orders, target_volume, start_index=0):
+    """Calculate weighted average price for orders starting from start_index until target_volume is reached."""
+    try:
+        total_volume = 0.0
+        weighted_price_sum = 0.0
+        used_orders = []
+        for i in range(start_index, len(orders)):
+            price, volume = orders[i]
+            volume_to_use = min(volume, target_volume - total_volume)
+            weighted_price_sum += price * volume_to_use
+            total_volume += volume_to_use
+            used_orders.append((price, volume_to_use))
+            if total_volume >= target_volume:
+                break
+        if total_volume < target_volume:
+            print(f"Debug: Insufficient volume, got {total_volume:.2f}, needed {target_volume:.2f}")
+            return None, 0.0
+        avg_price = weighted_price_sum / total_volume
+        print(f"Debug: Aggregated {len(used_orders)} orders: {[(p, v) for p, v in used_orders]}, Total Volume: {total_volume:.2f}, Avg Price: {avg_price:.1f}")
+        return avg_price, total_volume
+    except (ZeroDivisionError, TypeError) as e:
+        log_error(f"Error in calculate_weighted_average: {e}")
+        return None, 0.0
+
+async def check_arbitrage():
+    """Check for arbitrage opportunities with error handling."""
+    try:
+        # Fetch data from both APIs
+        nobitex_asks, nobitex_bids = fetch_nobitex_data()
+        bitpin_trades = fetch_bitpin_data()
+
+        if not nobitex_asks or not nobitex_bids or not bitpin_trades:
+            print("No data to compare")
+            return
+
+        # Convert Bitpin prices to IRR (toman to rial) and sort by price
+        bitpin_buys = []
+        bitpin_sells = []
+        try:
+            for trade in bitpin_trades:
+                price = float(trade["price"]) * 10
+                volume = float(trade["base_amount"])
+                if trade["side"] == "buy":
+                    bitpin_buys.append((price, volume))
+                elif trade["side"] == "sell":
+                    bitpin_sells.append((price, volume))
+            bitpin_buys = sorted(bitpin_buys, key=lambda x: x[0], reverse=True)
+            bitpin_sells = sorted(bitpin_sells, key=lambda x: x[0])
+        except (KeyError, ValueError, TypeError) as e:
+            log_error(f"Error parsing Bitpin trades: {e}")
+            return
+
+        # Sort Nobitex asks by price ascending and bids by price descending
+        try:
+            nobitex_asks = sorted(
+                [(float(ask[0]), float(ask[1])) for ask in nobitex_asks],
+                key=lambda x: x[0]
+            )
+            nobitex_bids = sorted(
+                [(float(bid[0]), float(bid[1])) for bid in nobitex_bids],
+                key=lambda x: x[0], reverse=True
+            )
+        except (ValueError, TypeError) as e:
+            log_error(f"Error parsing Nobitex asks/bids: {e}")
+            return
+
+        # Track best arbitrage opportunity
+        best_ask_sell_diff = float('-inf')
+        best_bid_buy_diff = float('-inf')
+        best_ask_sell_details = None
+        best_bid_buy_details = None
+
+        # Process first valid Nobitex ask
+        if nobitex_asks:
+            try:
+                ask_price, ask_volume = nobitex_asks[0]
+                orig_ask_price, orig_ask_volume = ask_price, ask_volume
+                print(f"Debug: First Nobitex ask - Price: {ask_price:.1f}, Volume: {ask_volume:.2f}")
+                # Enforce minimum sell volume
+                if ask_volume < MIN_SELL_VOLUME:
+                    print(f"Debug: Aggregating Nobitex asks to reach {MIN_SELL_VOLUME} USDT")
+                    avg_price, total_volume = calculate_weighted_average(nobitex_asks, MIN_SELL_VOLUME)
+                    if avg_price is None or total_volume < MIN_SELL_VOLUME:
+                        print(f"Debug: Failed to aggregate Nobitex asks to {MIN_SELL_VOLUME} USDT")
+                    else:
+                        ask_price, ask_volume = avg_price, total_volume
+                        print(f"Debug: Aggregated Nobitex ask - Price: {ask_price:.1f}, Volume: {ask_volume:.2f}")
+                        # Compare with Bitpin sells
+                        for i, (sell_price, sell_volume) in enumerate(bitpin_sells):
+                            try:
+                                print(f"Debug: Comparing with Bitpin sell - Price: {sell_price:.1f}, Volume: {sell_volume:.2f}")
+                                volume_diff_ratio = abs(ask_volume - sell_volume) / max(ask_volume, sell_volume)
+                                if volume_diff_ratio > 0.05:
+                                    print(f"Debug: Volume difference > 5% ({volume_diff_ratio:.2%}), aggregating Bitpin sells")
+                                    avg_sell_price, total_sell_volume = calculate_weighted_average(bitpin_sells, ask_volume, i)
+                                    if avg_sell_price is None or total_sell_volume < MIN_SELL_VOLUME:
+                                        print(f"Debug: Failed to aggregate Bitpin sells to {ask_volume:.2f} USDT")
+                                        continue
+                                    price_diff = avg_sell_price - ask_price
+                                    compare_volume = total_sell_volume
+                                    compare_price = avg_sell_price
+                                    print(f"Debug: Bitpin sell aggregated - Price: {avg_sell_price:.1f}, Volume: {total_sell_volume:.2f}")
+                                else:
+                                    price_diff = sell_price - ask_price
+                                    compare_volume = sell_volume
+                                    compare_price = sell_price
+                                    print(f"Debug: Using single Bitpin sell - Price: {sell_price:.1f}, Volume: {sell_volume:.2f}")
+
+                                if price_diff > best_ask_sell_diff:
+                                    best_ask_sell_diff = price_diff
+                                    best_ask_sell_details = (ask_price, ask_volume, compare_price, compare_volume, orig_ask_price, orig_ask_volume, sell_price, sell_volume)
+                                    print(f"Debug: New best ask-sell diff: {price_diff:.1f}")
+                            except (ZeroDivisionError, TypeError) as e:
+                                log_error(f"Error comparing Bitpin sell: {e}")
+                                continue
+                else:
+                    # Use first ask directly if volume is sufficient
+                    print(f"Debug: First Nobitex ask volume sufficient, comparing with Bitpin sells")
+                    for i, (sell_price, sell_volume) in enumerate(bitpin_sells):
+                        try:
+                            print(f"Debug: Comparing with Bitpin sell - Price: {sell_price:.1f}, Volume: {sell_volume:.2f}")
+                            volume_diff_ratio = abs(ask_volume - sell_volume) / max(ask_volume, sell_volume)
+                            if volume_diff_ratio > 0.05:
+                                print(f"Debug: Volume difference > 5% ({volume_diff_ratio:.2%}), aggregating Bitpin sells")
+                                avg_sell_price, total_sell_volume = calculate_weighted_average(bitpin_sells, ask_volume, i)
+                                if avg_sell_price is None or total_sell_volume < MIN_SELL_VOLUME:
+                                    print(f"Debug: Failed to aggregate Bitpin sells to {ask_volume:.2f} USDT")
+                                    continue
+                                price_diff = avg_sell_price - ask_price
+                                compare_volume = total_sell_volume
+                                compare_price = avg_sell_price
+                                print(f"Debug: Bitpin sell aggregated - Price: {avg_sell_price:.1f}, Volume: {total_sell_volume:.2f}")
+                            else:
+                                price_diff = sell_price - ask_price
+                                compare_volume = sell_volume
+                                compare_price = sell_price
+                                print(f"Debug: Using single Bitpin sell - Price: {sell_price:.1f}, Volume: {sell_volume:.2f}")
+
+                            if price_diff > best_ask_sell_diff:
+                                best_ask_sell_diff = price_diff
+                                best_ask_sell_details = (ask_price, ask_volume, compare_price, compare_volume, orig_ask_price, orig_ask_volume, sell_price, sell_volume)
+                                print(f"Debug: New best ask-sell diff: {price_diff:.1f}")
+                        except (ZeroDivisionError, TypeError) as e:
+                            log_error(f"Error comparing Bitpin sell: {e}")
+                            continue
+            except (IndexError, TypeError) as e:
+                log_error(f"Error processing Nobitex ask: {e}")
+
+        # Process first valid Nobitex bid
+        if nobitex_bids:
+            try:
+                bid_price, bid_volume = nobitex_bids[0]
+                orig_bid_price, orig_bid_volume = bid_price, bid_volume
+                print(f"Debug: First Nobitex bid - Price: {bid_price:.1f}, Volume: {bid_volume:.2f}")
+                for i, (buy_price, buy_volume) in enumerate(bitpin_buys):
+                    try:
+                        print(f"Debug: Comparing with Bitpin buy - Price: {buy_price:.1f}, Volume: {buy_volume:.2f}")
+                        volume_diff_ratio = abs(bid_volume - buy_volume) / max(bid_volume, buy_volume)
+                        if volume_diff_ratio > 0.05:
+                            print(f"Debug: Volume difference > 5% ({volume_diff_ratio:.2%}), aggregating Bitpin buys")
+                            avg_buy_price, total_buy_volume = calculate_weighted_average(bitpin_buys, bid_volume, i)
+                            if avg_buy_price is None:
+                                print(f"Debug: Failed to aggregate Bitpin buys to {bid_volume:.2f} USDT")
+                                continue
+                            price_diff = bid_price - avg_buy_price
+                            compare_volume = total_buy_volume
+                            compare_price = avg_buy_price
+                            print(f"Debug: Bitpin buy aggregated - Price: {avg_buy_price:.1f}, Volume: {total_buy_volume:.2f}")
+                        else:
+                            price_diff = bid_price - buy_price
+                            compare_volume = buy_volume
+                            compare_price = buy_price
+                            print(f"Debug: Using single Bitpin buy - Price: {buy_price:.1f}, Volume: {buy_volume:.2f}")
+
+                        if price_diff > best_bid_buy_diff:
+                            best_bid_buy_diff = price_diff
+                            best_bid_buy_details = (bid_price, bid_volume, compare_price, compare_volume, orig_bid_price, orig_bid_volume, buy_price, buy_volume)
+                            print(f"Debug: New best bid-buy diff: {price_diff:.1f}")
+                    except (ZeroDivisionError, TypeError) as e:
+                        log_error(f"Error comparing Bitpin buy: {e}")
+                        continue
+            except (IndexError, TypeError) as e:
+                log_error(f"Error processing Nobitex bid: {e}")
+
+        # Send single Telegram message for the best opportunity
+        try:
+            best_message = None
+            if best_ask_sell_diff > best_bid_buy_diff and best_ask_sell_diff > 8000 and best_ask_sell_details:
+                ask_price, ask_volume, compare_price, compare_volume, orig_ask_price, orig_ask_volume, sell_price, sell_volume = best_ask_sell_details
+                best_message = (
+                    f"✅ موقعیت آربیتراژ یافت شد 📈\n"
+                    f"💸 خرید از نوبیتکس: {int(orig_ask_price)} ریال با حجم : {orig_ask_volume:.2f} دلار\n"
+                    f"💰 فروش در بیت پین: {int(sell_price)} ریال با حجم: {sell_volume:.2f} دلار\n"
+                    f"📊 قیمت میانگین خرید: {int(ask_price)} ریال\n"
+                    f"📊 قیمت میانگین فروش: {int(compare_price)} ریال\n"
+                    f"📏 حجم قیمت میانگین: {compare_volume:.2f} دلار\n"
+                    f"📈 تفاوت قیمت: {int(best_ask_sell_diff)} ریال"
+                )
+            elif best_bid_buy_diff > 8000 and best_bid_buy_details:
+                bid_price, bid_volume, compare_price, compare_volume, orig_bid_price, orig_bid_volume, buy_price, buy_volume = best_bid_buy_details
+                best_message = (
+                    f"✅ موقعیت آربیتراژ یافت شد 📈\n"
+                    f"💸 خرید از بیت پین: {int(orig_bid_price)} ریال با حجم : {orig_bid_volume:.2f} دلار\n"
+                    f"💰 فروش در نوبیتکس: {int(buy_price)} ریال با حجم: {buy_volume:.2f} دلار\n"
+                    f"📊 قیمت میانگین خرید: {int(bid_price)} ریال\n"
+                    f"📊 قیمت میانگین فروش: {int(compare_price)} ریال\n"
+                    f"📏 حجم قیمت میانگین: {compare_volume:.2f} دلار\n"
+                    f"📈 تفاوت قیمت: {int(best_bid_buy_diff)} ریال"
+                )
+
+            if best_message:
+                await send_telegram_message(best_message)
+            else:
+                print("No arbitrage opportunity found with price difference > 8000 IRR")
+        except Exception as e:
+            log_error(f"Error preparing Telegram message: {e}")
+
+        # Print maximum price differences
+        try:
+            if best_ask_sell_details:
+                print(
+                    f"Max Nobitex Ask - Bitpin Sell Difference: {best_ask_sell_diff:.1f} IRR\n"
+                    f"Nobitex Sell: {best_ask_sell_details[0]:.1f} IRR, Volume: {best_ask_sell_details[1]:.2f}\n"
+                    f"Bitpin Sell: {best_ask_sell_details[2]:.1f} IRR, Volume: {best_ask_sell_details[3]:.2f}\n"
+                    f"Original Nobitex Sell: {best_ask_sell_details[4]:.1f} IRR, Volume: {best_ask_sell_details[5]:.2f}"
+                )
+            else:
+                print("No valid Nobitex Ask - Bitpin Sell comparisons")
+        except Exception as e:
+            log_error(f"Error printing ask-sell differences: {e}")
+
+        try:
+            if best_bid_buy_details:
+                print(
+                    f"Max Nobitex Bid - Bitpin Buy Difference: {best_bid_buy_diff:.1f} IRR\n"
+                    f"Nobitex Buy: {best_bid_buy_details[0]:.1f} IRR, Volume: {best_bid_buy_details[1]:.2f}\n"
+                    f"Bitpin Buy: {best_bid_buy_details[2]:.1f} IRR, Volume: {best_bid_buy_details[3]:.2f}\n"
+                    f"Original Nobitex Buy: {best_bid_buy_details[4]:.1f} IRR, Volume: {best_bid_buy_details[5]:.2f}"
+                )
+            else:
+                print("No valid Nobitex Bid - Bitpin Buy comparisons")
+        except Exception as e:
+            log_error(f"Error printing bid-buy differences: {e}")
+
+    except Exception as e:
+        log_error(f"Error in check_arbitrage: {e}")
+
+async def main():
+    FPS = 1 / 30  # Check every 60 seconds
+    while True:
+        try:
+            await check_arbitrage()
+            await asyncio.sleep(1.0 / FPS)
+        except Exception as e:
+            log_error(f"Error in main loop: {e}")
+            await asyncio.sleep(1.0 / FPS)  # Continue after error
+
+if platform.system() == "Emscripten":
+    asyncio.ensure_future(main())
+else:
+    if __name__ == "__main__":
+        asyncio.run(main())

+ 98 - 0
README-fa.md

@@ -0,0 +1,98 @@
+# بررسی آربیتراژ نوبیتکس و بیت‌پین
+
+این پروژه یک اسکریپت پایتون است که فرصت‌های آربیتراژ بین صرافی‌های نوبیتکس (Nobitex) و بیت‌پین (Bitpin) را برای جفت‌ارز USDT/IRR بررسی می‌کند. اسکریپت سفارش‌های فروش (ask) نوبیتکس را با سفارش‌های فروش بیت‌پین و سفارش‌های خرید (bid) نوبیتکس را با سفارش‌های خرید بیت‌پین مقایسه می‌کند تا فرصت‌های سودآور را شناسایی کند. در صورت یافتن فرصت آربیتراژ با تفاوت قیمت بیش از ۵۰۰۰ ریال، اعلان از طریق تلگرام ارسال می‌شود.
+
+## ویژگی‌ها
+- **دریافت داده‌های بلادرنگ**: داده‌های دفتر سفارش (orderbook) از API نوبیتکس و معاملات اخیر از API بیت‌پین.
+- **مقایسه دقیق**: مقایسه سفارش‌های فروش نوبیتکس با فروش بیت‌پین (برای خرید در نوبیتکس و فروش در بیت‌پین) و سفارش‌های خرید نوبیتکس با خرید بیت‌پین (برای فروش در بیت‌پین و خرید در نوبیتکس).
+- **تجمیع سفارش‌ها**: تجمیع سفارش‌های نوبیتکس برای رسیدن به حداقل حجم ۵۰۰ USDT و تطبیق حجم‌های بیت‌پین در صورت تفاوت بیش از ۵٪.
+- **ارسال اعلان تلگرام**: ارسال یک پیام تلگرامی برای بهترین فرصت آربیتراژ در هر چرخه (تفاوت قیمت > ۵۰۰۰ ریال).
+- **مدیریت خطا**: مدیریت خطاهای شبکه، داده‌های نامعتبر و مشکلات API با ادامه اجرای اسکریپت.
+- **لاگ‌گیری جامع**: چاپ لاگ‌های دیباگ برای جزئیات تجمیع سفارش‌ها و مقایسه‌ها.
+
+## پیش‌نیازها
+- Python 3.9 یا بالاتر
+- محیط مجازی (اختیاری اما توصیه‌شده)
+- دسترسی به اینترنت برای APIهای نوبیتکس و بیت‌پین
+- حساب تلگرام و ربات تلگرام برای ارسال اعلان‌ها
+
+## نصب
+1. **مخزن را کلون کنید**:
+   ```bash
+   git clone https://github.com/salamzadeh/BitEx.git
+   cd BitEx
+   ```
+
+2. **محیط مجازی ایجاد کنید** (اختیاری):
+   ```bash
+   python -m venv venv
+   source venv/bin/activate  # در لینوکس/مک
+   venv\Scripts\activate     # در ویندوز
+   ```
+
+3. **وابستگی‌ها را نصب کنید**:
+   ```bash
+   pip install python-telegram-bot==20.7 requests
+   ```
+
+4. **ربات تلگرام را تنظیم کنید**:
+   - یک ربات تلگرام از طریق [BotFather](https://t.me/BotFather) ایجاد کنید و توکن ربات را دریافت کنید.
+   - شناسه چت (Chat ID) گروه یا کاربر تلگرام را پیدا کنید (می‌توانید از ربات‌هایی مانند `@GetIDsBot` استفاده کنید).
+
+5. **پیکربندی اسکریپت**:
+   - فایل `Arbitrage.py` را باز کنید و مقادیر زیر را به‌روزرسانی کنید:
+     ```python
+     TELEGRAM_BOT_TOKEN = "YOUR_BOT_TOKEN_HERE"  # توکن ربات تلگرام
+     TELEGRAM_CHAT_ID = "YOUR_CHAT_ID_HERE"     # شناسه چت تلگرام
+     ```
+
+## استفاده
+1. **اجرای اسکریپت**:
+   ```bash
+   python Arbitrage.py
+   ```
+
+2. **خروجی مورد انتظار**:
+   - **لاگ‌های کنسول**:
+     - موفقیت یا خطای API: مثلاً `Nobitex API read successfully at ...`
+     - جزئیات تجمیع: مثلاً `Debug: Aggregated 2 orders: [(873000.0, 127.87), ...], Total Volume: 500.00, Avg Price: 873081.9`
+     - خطاها: مثلاً `Error at 2025-07-16 14:02:23: Error fetching Nobitex data: ...`
+     - حداکثر تفاوت قیمت: مثلاً `Max Nobitex Ask - Bitpin Sell Difference: 5218.1 IRR`
+   - **اعلان تلگرام**: در صورت یافتن فرصت آربیتراژ با تفاوت قیمت > ۵۰۰۰ ریال:
+     ```
+     ✅ موقعیت آربیتراژ یافت شد 📈
+     💸 خرید از نوبیتکس: 873000 ریال با حجم نوبیتکس: 127.87 دلار
+     💰 فروش در بیت پین: 879000 ریال با حجم: 100.00 دلار
+     📊 قیمت میانگین خرید: 873081 ریال
+     📊 قیمت میانگین فروش: 878566 ریال
+     📏 حجم قیمت میانگین: 500.00 دلار
+     📈 تفاوت قیمت: 5485 ریال
+     ```
+
+3. **توقف اسکریپت**:
+   - برای توقف، از `Ctrl+C` در ترمینال استفاده کنید.
+
+## پیکربندی پیشرفته
+- **تغییر حداقل حجم**: متغیر `MIN_SELL_VOLUME` (پیش‌فرض: ۵۰۰ USDT) را در اسکریپت تغییر دهید.
+- **تغییر آستانه تفاوت قیمت**: مقدار ۵۰۰۰ در شرط `best_ask_sell_diff > 5000` را در تابع `check_arbitrage` تنظیم کنید.
+- **تغییر فرکانس بررسی**: متغیر `FPS` (پیش‌فرض: هر ۶۰ ثانیه) را در تابع `main` تغییر دهید.
+
+## دیباگ و عیب‌یابی
+- **لاگ‌های کنسول** را بررسی کنید تا جزئیات تجمیع سفارش‌ها و خطاها را ببینید.
+- در صورت بروز خطا (مثلاً مشکلات شبکه یا داده‌های نامعتبر)، اسکریپت خطا را لاگ می‌کند و ادامه می‌دهد.
+- برای پشتیبانی، لاگ‌های کنسول و پیام‌های تلگرام را به همراه نسخه‌های کتابخانه‌ها (`pip show python-telegram-bot requests`) به اشتراک بگذارید.
+
+## محدودیت‌ها
+- وابستگی به APIهای نوبیتکس و بیت‌پین (در صورت قطعی API، اسکریپت خطا را لاگ کرده و ادامه می‌دهد).
+- نیاز به اتصال اینترنت پایدار.
+- نسخه‌های قدیمی‌تر `python-telegram-bot` (<20.0) ممکن است نیاز به تغییر `from telegram import Bot` به `from telegram.bot import Bot` داشته باشند.
+
+## مشارکت
+- برای گزارش باگ یا پیشنهاد ویژگی، یک Issue در مخزن GitHub ایجاد کنید.
+- Pull Requestها برای بهبود کد یا افزودن ویژگی‌های جدید استقبال می‌شوند.
+
+## لایسنس
+این پروژه تحت [لایسنس MIT](LICENSE) منتشر شده است.
+
+## تماس
+برای سؤالات یا پشتیبانی، از طریق [ایمیل یا GitHub Issues] تماس بگیرید.

+ 98 - 0
README.md

@@ -0,0 +1,98 @@
+# Nobitex-Bitpin Arbitrage Checker
+
+This project is a Python script that identifies arbitrage opportunities between the Nobitex and Bitpin exchanges for the USDT/IRR trading pair. It compares Nobitex sell orders (asks) with Bitpin sell orders and Nobitex buy orders (bids) with Bitpin buy orders to find profitable opportunities. If an arbitrage opportunity with a price difference greater than 5000 IRR is found, a notification is sent via Telegram.
+
+## Features
+- **Real-time Data Fetching**: Retrieves orderbook data from Nobitex API and recent trades from Bitpin API.
+- **Accurate Comparisons**: Compares Nobitex asks with Bitpin sells (to buy on Nobitex and sell on Bitpin) and Nobitex bids with Bitpin buys (to sell on Bitpin and buy on Nobitex).
+- **Order Aggregation**: Aggregates Nobitex asks to reach a minimum volume of 500 USDT and matches Bitpin volumes if the difference exceeds 5%.
+- **Telegram Notifications**: Sends a single Telegram message per cycle for the best arbitrage opportunity (price difference > 5000 IRR).
+- **Robust Error Handling**: Handles network, data, and API errors, allowing the script to continue running.
+- **Comprehensive Logging**: Prints detailed debug logs for order aggregation and comparisons.
+
+## Prerequisites
+- Python 3.9 or higher
+- Virtual environment (optional but recommended)
+- Internet access for Nobitex and Bitpin APIs
+- Telegram account and bot for notifications
+
+## Installation
+1. **Clone the Repository**:
+   ```bash
+   git clone https://github.com/salamzadeh/BitEx.git
+   cd BitEx
+   ```
+
+2. **Create a Virtual Environment** (optional):
+   ```bash
+   python -m venv venv
+   source venv/bin/activate  # On Linux/Mac
+   venv\Scripts\activate     # On Windows
+   ```
+
+3. **Install Dependencies**:
+   ```bash
+   pip install python-telegram-bot==20.7 requests
+   ```
+
+4. **Set Up a Telegram Bot**:
+   - Create a Telegram bot via [BotFather](https://t.me/BotFather) and obtain the bot token.
+   - Find the chat ID for your Telegram user or group (use bots like `@GetIDsBot`).
+
+5. **Configure the Script**:
+   - Open `Arbitrage.py` and update the following:
+     ```python
+     TELEGRAM_BOT_TOKEN = "YOUR_BOT_TOKEN_HERE"  # Your Telegram bot token
+     TELEGRAM_CHAT_ID = "YOUR_CHAT_ID_HERE"     # Your Telegram chat ID
+     ```
+
+## Usage
+1. **Run the Script**:
+   ```bash
+   python Arbitrage.py
+   ```
+
+2. **Expected Output**:
+   - **Console Logs**:
+     - API success or errors: e.g., `Nobitex API read successfully at ...`
+     - Aggregation details: e.g., `Debug: Aggregated 2 orders: [(873000.0, 127.87), ...], Total Volume: 500.00, Avg Price: 873081.9`
+     - Errors: e.g., `Error at 2025-07-16 14:02:23: Error fetching Nobitex data: ...`
+     - Maximum price differences: e.g., `Max Nobitex Ask - Bitpin Sell Difference: 5218.1 IRR`
+   - **Telegram Notification**: If an arbitrage opportunity with price difference > 5000 IRR is found:
+     ```
+     ✅ Arbitrage Opportunity Found 📈
+     💸 Buy on Nobitex: 873000 IRR, Nobitex Volume: 127.87 USDT
+     💰 Sell on Bitpin: 879000 IRR, Volume: 100.00 USDT
+     📊 Average Buy Price: 873081 IRR
+     📊 Average Sell Price: 878566 IRR
+     📏 Average Volume: 500.00 USDT
+     📈 Price Difference: 5485 IRR
+     ```
+
+3. **Stop the Script**:
+   - Press `Ctrl+C` in the terminal to stop.
+
+## Advanced Configuration
+- **Change Minimum Volume**: Modify `MIN_SELL_VOLUME` (default: 500 USDT) in the script.
+- **Adjust Price Difference Threshold**: Update the 5000 IRR threshold in the `check_arbitrage` function’s condition `best_ask_sell_diff > 5000`.
+- **Modify Check Frequency**: Change `FPS` (default: every 60 seconds) in the `main` function.
+
+## Debugging and Troubleshooting
+- Check **console logs** for details on order aggregation and errors.
+- If errors occur (e.g., network issues or invalid data), the script logs them and continues running.
+- For support, share console logs, Telegram messages, and library versions (`pip show python-telegram-bot requests`).
+
+## Limitations
+- Dependent on Nobitex and Bitpin APIs (logs errors and continues if APIs are down).
+- Requires stable internet connectivity.
+- Older versions of `python-telegram-bot` (<20.0) may require changing `from telegram import Bot` to `from telegram.bot import Bot`.
+
+## Contributing
+- Report bugs or suggest features by creating an Issue on GitHub.
+- Pull Requests for code improvements or new features are welcome.
+
+## License
+This project is licensed under the [MIT License](LICENSE).
+
+## Contact
+For questions or support, reach out via [email or GitHub Issues].