import numpy as np
from scipy import stats
import pandas as pd
from collections import deque
from sklearn.cluster import KMeans
from typing import Dict, List, Tuple

class MultiCryptoSignalBot:
    def __init__(self, short_period: int = 100, long_period: int = 500, volume_threshold: float = 2, ob_imbalance_threshold: float = 0.7):
        """
        Initialize the MultiCryptoSignalBot with parameters for signal generation.

        :param short_period: Number of data points for short-term calculations
        :param long_period: Number of data points for long-term calculations
        :param volume_threshold: Threshold for considering volume as significant
        :param ob_imbalance_threshold: Threshold for considering order book imbalance as significant
        """
        self.short_period = short_period
        self.long_period = long_period
        self.volume_threshold = volume_threshold
        self.ob_imbalance_threshold = ob_imbalance_threshold
        
        # Dictionary to store historical data for each trading pair
        self.pair_data: Dict[str, Dict] = {}

    def initialize_pair(self, pair: str):
        """
        Initialize data structures for a new trading pair.

        :param pair: Trading pair symbol (e.g., 'BTCUSDT')
        """
        self.pair_data[pair] = {
            'volume_history': deque(maxlen=self.long_period),
            'ob_imbalance_history': deque(maxlen=self.long_period),
            'price_history': deque(maxlen=self.long_period),
            'volatility_history': deque(maxlen=self.long_period)
        }

    def calculate_volume_zscore(self, pair: str, current_volume: float) -> float:
        """
        Calculate the z-score of the current volume compared to historical volumes.

        :param pair: Trading pair symbol
        :param current_volume: Current trading volume
        :return: Z-score of the current volume
        """
        self.pair_data[pair]['volume_history'].append(current_volume)
        if len(self.pair_data[pair]['volume_history']) < self.short_period:
            return 0
        return stats.zscore(list(self.pair_data[pair]['volume_history']))[-1]

    def calculate_ob_imbalance(self, bids: List[Tuple[float, float]], asks: List[Tuple[float, float]]) -> float:
        """
        Calculate the order book imbalance.

        :param bids: List of (price, volume) tuples for bids
        :param asks: List of (price, volume) tuples for asks
        :return: Order book imbalance ratio
        """
        total_bid_volume = sum(bid[1] for bid in bids)
        total_ask_volume = sum(ask[1] for ask in asks)
        return total_bid_volume / (total_bid_volume + total_ask_volume)

    def calculate_ob_imbalance_zscore(self, pair: str, current_imbalance: float) -> float:
        """
        Calculate the z-score of the current order book imbalance.

        :param pair: Trading pair symbol
        :param current_imbalance: Current order book imbalance
        :return: Z-score of the current order book imbalance
        """
        self.pair_data[pair]['ob_imbalance_history'].append(current_imbalance)
        if len(self.pair_data[pair]['ob_imbalance_history']) < self.short_period:
            return 0
        return stats.zscore(list(self.pair_data[pair]['ob_imbalance_history']))[-1]

    def calculate_volume_weighted_price(self, trades: List[Dict[str, float]]) -> float:
        """
        Calculate the volume-weighted average price (VWAP) from recent trades.

        :param trades: List of recent trades, each a dict with 'price' and 'volume' keys
        :return: Volume-weighted average price
        """
        return sum(trade['price'] * trade['volume'] for trade in trades) / sum(trade['volume'] for trade in trades)

    def calculate_ob_depth(self, order_book: Dict[str, List[Tuple[float, float]]], levels: int = 10) -> Tuple[float, float]:
        """
        Calculate the cumulative depth of the order book up to a specified number of levels.

        :param order_book: Dictionary with 'bids' and 'asks' keys, each containing a list of (price, volume) tuples
        :param levels: Number of levels to consider in the order book
        :return: Tuple of (bid_depth, ask_depth)
        """
        bid_depth = sum(bid[1] for bid in order_book['bids'][:levels])
        ask_depth = sum(ask[1] for ask in order_book['asks'][:levels])
        return bid_depth, ask_depth

    def detect_spoofing(self, order_book: Dict[str, List[Tuple[float, float]]], trades: List[Dict[str, float]], time_window: int = 60) -> bool:
        """
        Detect potential spoofing activity in the order book.

        :param order_book: Dictionary with 'bids' and 'asks' keys, each containing a list of (price, volume) tuples
        :param trades: List of recent trades, each a dict with 'price' and 'volume' keys
        :param time_window: Time window (in seconds) to consider for spoofing detection
        :return: Boolean indicating whether potential spoofing is detected
        """
        large_orders = [order for order in order_book['bids'] + order_book['asks'] if order[1] > self.volume_threshold * np.mean([trade['volume'] for trade in trades])]
        return any(order[0] not in [trade['price'] for trade in trades] for order in large_orders)

    def calculate_volatility(self, pair: str, current_price: float) -> float:
        """
        Calculate the current volatility based on historical price data.

        :param pair: Trading pair symbol
        :param current_price: Current price of the trading pair
        :return: Calculated volatility
        """
        self.pair_data[pair]['price_history'].append(current_price)
        if len(self.pair_data[pair]['price_history']) < self.short_period:
            return 0
        returns = np.diff(list(self.pair_data[pair]['price_history'])) / list(self.pair_data[pair]['price_history'])[:-1]
        volatility = np.std(returns) * np.sqrt(len(returns))
        self.pair_data[pair]['volatility_history'].append(volatility)
        return volatility

    def detect_market_regime(self, pair: str) -> str:
        """
        Detect the current market regime based on recent volatility.

        :param pair: Trading pair symbol
        :return: String indicating the detected market regime ('unknown', 'high_volatility', 'medium_volatility', or 'low_volatility')
        """
        if len(self.pair_data[pair]['volatility_history']) < self.short_period:
            return 'unknown'
        recent_volatility = list(self.pair_data[pair]['volatility_history'])[-self.short_period:]
        kmeans = KMeans(n_clusters=3, random_state=0).fit(np.array(recent_volatility).reshape(-1, 1))
        current_regime = kmeans.predict(np.array([recent_volatility[-1]]).reshape(-1, 1))[0]
        if current_regime == kmeans.labels_.max():
            return 'high_volatility'
        elif current_regime == kmeans.labels_.min():
            return 'low_volatility'
        else:
            return 'medium_volatility'

    def adaptive_thresholds(self, market_regime: str) -> Tuple[float, float]:
        """
        Adjust thresholds based on the current market regime.

        :param market_regime: String indicating the current market regime
        :return: Tuple of (adaptive_volume_threshold, adaptive_ob_threshold)
        """
        if market_regime == 'high_volatility':
            return self.volume_threshold * 1.5, self.ob_imbalance_threshold * 1.2
        elif market_regime == 'low_volatility':
            return self.volume_threshold * 0.75, self.ob_imbalance_threshold * 0.9
        else:
            return self.volume_threshold, self.ob_imbalance_threshold

    def generate_signal(self, pair: str, current_volume: float, bids: List[Tuple[float, float]], asks: List[Tuple[float, float]], 
                        trades: List[Dict[str, float]], order_book: Dict[str, List[Tuple[float, float]]], current_price: float) -> Tuple[str, Dict]:
        """
        Generate a trading signal for a given trading pair based on current market data.

        :param pair: Trading pair symbol
        :param current_volume: Current trading volume
        :param bids: List of (price, volume) tuples for bids
        :param asks: List of (price, volume) tuples for asks
        :param trades: List of recent trades, each a dict with 'price' and 'volume' keys
        :param order_book: Dictionary with 'bids' and 'asks' keys, each containing a list of (price, volume) tuples
        :param current_price: Current price of the trading pair
        :return: Tuple of (signal, metrics dictionary)
        """
        if pair not in self.pair_data:
            self.initialize_pair(pair)

        volume_zscore = self.calculate_volume_zscore(pair, current_volume)
        ob_imbalance = self.calculate_ob_imbalance(bids, asks)
        ob_imbalance_zscore = self.calculate_ob_imbalance_zscore(pair, ob_imbalance)
        vwap = self.calculate_volume_weighted_price(trades)
        bid_depth, ask_depth = self.calculate_ob_depth(order_book)
        spoofing_detected = self.detect_spoofing(order_book, trades)
        volatility = self.calculate_volatility(pair, current_price)
        market_regime = self.detect_market_regime(pair)
        
        adaptive_volume_threshold, adaptive_ob_threshold = self.adaptive_thresholds(market_regime)

        # Determine the signal based on multiple factors
        if volume_zscore > adaptive_volume_threshold and ob_imbalance > adaptive_ob_threshold:
            if ob_imbalance_zscore > 0 and bid_depth > ask_depth:
                signal = "Buy"
            elif ob_imbalance_zscore < 0 and ask_depth > bid_depth:
                signal = "Sell"
            else:
                signal = "Neutral"
        else:
            signal = "Neutral"

        # Cancel signal if spoofing is detected
        if spoofing_detected:
            signal = "Neutral"

        signal_strength = abs(volume_zscore * ob_imbalance_zscore)

        metrics = {
            'volume_zscore': volume_zscore,
            'ob_imbalance': ob_imbalance,
            'ob_imbalance_zscore': ob_imbalance_zscore,
            'vwap': vwap,
            'bid_depth': bid_depth,
            'ask_depth': ask_depth,
            'spoofing_detected': spoofing_detected,
            'volatility': volatility,
            'market_regime': market_regime,
            'signal_strength': signal_strength
        }

        return signal, metrics

def get_binance_futures_usdt_pairs():
    """
    Function to fetch all USDT pairs from Binance Futures.
    This is a placeholder function. In a real implementation, you would use the Binance API to fetch this data.

    :return: List of USDT trading pairs
    """
    # Placeholder list of some USDT pairs. In reality, you'd fetch this from Binance API.
    return ['BTCUSDT', 'ETHUSDT', 'BNBUSDT', 'ADAUSDT', 'DOTUSDT', 'XRPUSDT', 'LINKUSDT', 'LTCUSDT', 'BCHUSDT', 'TRXUSDT']

# Example usage
bot = MultiCryptoSignalBot()
usdt_pairs = get_binance_futures_usdt_pairs()

# Simulated market data (in a real scenario, you'd fetch this data from an exchange API)
for pair in usdt_pairs:
    current_volume = np.random.randint(1000, 10000)
    current_price = np.random.uniform(100, 10000)
    bids = [(current_price * (1 - np.random.uniform(0, 0.01)), np.random.randint(1, 100)) for _ in range(10)]
    asks = [(current_price * (1 + np.random.uniform(0, 0.01)), np.random.randint(1, 100)) for _ in range(10)]
    trades = [{'price': current_price * (1 + np.random.uniform(-0.001, 0.001)), 'volume': np.random.randint(1, 10)} for _ in range(20)]
    order_book = {'bids': bids, 'asks': asks}

    signal, metrics = bot.generate_signal(pair, current_volume, bids, asks, trades, order_book, current_price)
    print(f"Pair: {pair}")
    print(f"Signal: {signal}")
    print(f"Metrics: {metrics}")
    print("---")
Analyze the code to identify strengths and weaknesses and what I can do to improve it