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())