import asyncio
import json
import websockets
import logging
from binance.client import Client
from binance.exceptions import BinanceAPIException

# Set up your Binance API keys (for this example, it's hardcoded)
api_key = 'your_api_key'
api_secret = 'your_api_secret'

# Initialize the Binance client
client = Client(api_key, api_secret)

# Initialize logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

# Constants
SPOT_FEE = 0.0007  # 0.07% spot trading fee
FUTURES_MAKER_FEE = 0.0001  # 0.01% futures maker fee
FUTURES_TAKER_FEE = 0.0004  # 0.04% futures taker fee
TRADE_AMOUNT = 100  # 100 USDT trade size
MIN_PROFIT_THRESHOLD = 0.2  # Minimum profit of 20 cents
DRY_RUN = False  # Set to False when ready to execute trades live

# Global variables
spot_prices = {}
futures_prices = {}

# Function to get tradable pairs
def get_tradable_pairs():
    logging.info("Fetching tradable pairs...")
    try:
        spot_info = client.get_exchange_info()
        futures_info = client.futures_exchange_info()

        spot_symbols = set([s['symbol'] for s in spot_info['symbols'] if s['symbol'].endswith('USDT') and s['status'] == 'TRADING'])
        futures_symbols = set([s['symbol'] for s in futures_info['symbols'] if s['symbol'].endswith('USDT') and s['status'] == 'TRADING'])

        tradable_pairs = list(spot_symbols.intersection(futures_symbols))
        logging.info(f"Found {len(tradable_pairs)} tradable pairs: {', '.join(tradable_pairs)}")
        return tradable_pairs
    except BinanceAPIException as e:
        logging.error(f"Error fetching tradable pairs: {e}")
        return []

# Function to handle WebSocket messages with reconnection
async def handle_socket_message(pairs):
    global spot_prices, futures_prices
    spot_streams = '/'.join([f'{pair.lower()}@ticker' for pair in pairs])
    futures_streams = '/'.join([f'{pair.lower()}@markPrice' for pair in pairs])

    spot_url = f"wss://stream.binance.com:9443/ws/{spot_streams}"
    futures_url = f"wss://fstream.binance.com/ws/{futures_streams}"

    logging.info("Initializing WebSocket connections for real-time price updates...")

    async def retry_connection(func, retries=5, delay=5):
        for i in range(retries):
            try:
                return await func()
            except Exception as e:
                logging.error(f"Error: {e}. Retrying {i + 1}/{retries} in {delay} seconds...")
                await asyncio.sleep(delay)
                delay *= 2  # Exponential backoff
        raise Exception("Max retries reached")

    async def spot_ws():
        while True:
            try:
                async with websockets.connect(spot_url) as websocket:
                    logging.info("Connected to Spot WebSocket")
                    while True:
                        message = await websocket.recv()
                        data = json.loads(message)
                        symbol, price = data['s'], float(data['c'])
                        spot_prices[symbol] = price
            except Exception as e:
                logging.error(f"Spot WebSocket error: {e}")
                await asyncio.sleep(5)

    async def futures_ws():
        while True:
            try:
                async with websockets.connect(futures_url) as websocket:
                    logging.info("Connected to Futures WebSocket")
                    while True:
                        message = await websocket.recv()
                        data = json.loads(message)
                        symbol, price = data['s'], float(data['p'])
                        futures_prices[symbol] = price
            except Exception as e:
                logging.error(f"Futures WebSocket error: {e}")
                await asyncio.sleep(5)

    await retry_connection(lambda: asyncio.gather(spot_ws(), futures_ws()))

# Function to calculate arbitrage opportunity
def calculate_arbitrage(symbol, amt_usdt):
    spot_price = spot_prices.get(symbol)
    futures_price = futures_prices.get(symbol)

    if not spot_price or not futures_price:
        return 0, False

    # Scenario 1: Buy Futures, Sell Spot
    scenario1_profit = (spot_price - futures_price) * (amt_usdt / futures_price)
    scenario1_profit -= (amt_usdt * futures_taker_fee) + (amt_usdt * spot_fee)

    # Scenario 2: Buy Spot, Sell Futures
    scenario2_profit = (futures_price - spot_price) * (amt_usdt / spot_price)
    scenario2_profit -= (amt_usdt * spot_fee) + (amt_usdt * futures_taker_fee)

    if scenario1_profit > scenario2_profit:
        return scenario1_profit, True  # True means buy on Futures
    else:
        return scenario2_profit, False  # False means buy on Spot

# Function to execute trades and rebalance wallets
async def execute_trade(symbol, amt_usdt, buy_futures):
    try:
        if buy_futures:
            logging.info(f"Executing trade for {symbol}: Buy Futures, Sell Spot")
            
            futures_order = client.futures_create_order(
                symbol=symbol,
                side='BUY',
                type='MARKET',
                quantity=amt_usdt / futures_prices[symbol]
            )
            spot_order = client.order_market_sell(symbol=symbol, quantity=float(futures_order['executedQty']))

            # Transfer all profit and trade amount from spot back to futures
            transfer_amount = float(spot_order['cummulativeQuoteQty'])
            client.futures_account_transfer(asset='USDT', amount=transfer_amount, type=1)  # Spot to Futures transfer
            logging.info(f"Transferred {transfer_amount:.2f} USDT to Futures wallet")

        else:
            logging.info(f"Executing trade for {symbol}: Buy Spot, Sell Futures")

            spot_order = client.order_market_buy(symbol=symbol, quoteOrderQty=amt_usdt)
            futures_order = client.futures_create_order(
                symbol=symbol,
                side='SELL',
                type='MARKET',
                quantity=float(spot_order['executedQty'])
            )

            # Transfer all profit and trade amount from futures back to spot
            transfer_amount = float(futures_order['cumQuote'])
            client.futures_account_transfer(asset='USDT', amount=transfer_amount, type=2)  # Futures to Spot transfer
            logging.info(f"Transferred {transfer_amount:.2f} USDT to Spot wallet")

        logging.info(f"Trade completed for {symbol}.")
    except BinanceAPIException as e:
        logging.error(f"Error executing trade for {symbol}: {e}")

# Main loop for arbitrage detection
async def main_arbitrage_loop():
    tradable_pairs = get_tradable_pairs()
    if not tradable_pairs:
        logging.error("No tradable pairs available. Exiting.")
        return

    asyncio.create_task(handle_socket_message(tradable_pairs))

    logging.info("Starting main arbitrage detection loop...")
    while True:
        try:
            best_trade = max(tradable_pairs, key=lambda x: calculate_arbitrage(x, TRADE_AMOUNT)[0])
            profit, buy_futures = calculate_arbitrage(best_trade, TRADE_AMOUNT)

            if profit > MIN_PROFIT_THRESHOLD:
                logging.info(f"Profitable arbitrage found for {best_trade}: {profit:.2f} USDT profit")
                if not DRY_RUN:
                    await execute_trade(best_trade, TRADE_AMOUNT, buy_futures)
            else:
                logging.info(f"No profitable arbitrage found. Best opportunity: {best_trade} with {profit:.2f} USDT profit")

            await asyncio.sleep(1)
        except Exception as e:
            logging.error(f"Error in main loop: {e}")
            await asyncio.sleep(5)

if __name__ == "__main__":
    logging.info("Arbitrage Bot Starting...")
    asyncio.run(main_arbitrage_loop())
am trying to make a bot as you can see and i have no knowledeg of coding is there is somthing wrong with it and can you add some suggestion to the code to make it profitable as possible