#orderbook.py
import asyncio
import logging
import os
import time
from typing import Dict, List, Tuple

import numpy as np
from scipy import stats
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score
from sklearn.preprocessing import StandardScaler
import pandas as pd

# Django setup
import django
from django.utils import timezone

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'binancebot.settings')
django.setup()

from .models import Kline, OrderBook, Signal, TradingPair  # Assuming these models exist

# Logger setup
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

class OrderbookBot:
    def __init__(self, short_period: int = 100, long_period: int = 500, volume_threshold: float = 2, ob_imbalance_threshold: float = 0.7):
        self.short_period = short_period
        self.long_period = long_period
        self.volume_threshold = volume_threshold
        self.ob_imbalance_threshold = ob_imbalance_threshold
        self.pair_data: Dict[str, Dict] = {}

    def initialize_pair(self, pair: str):
        self.pair_data[pair] = {
            'volume_history': [],
            'ob_imbalance_history': [],
            'price_history': [],
            'volatility_history': []
        }

    async def get_candles_df(self, pair: str, timeframe: str, num_candles: int = 200) -> pd.DataFrame:
        """Fetches recent candle data for the specified trading pair and timeframe."""
        klines = Kline.objects.filter(symbol=pair, timeframe=timeframe).order_by("-timestamp")[:num_candles]
        df = pd.DataFrame.from_records(klines.values())
        df.set_index("timestamp", inplace=True)
        df.sort_index(inplace=True)  # Ensure chronological order for analysis
        return df

    async def get_order_book(self, pair: str) -> Tuple[List[Tuple[float, float]], List[Tuple[float, float]]]:
        """Fetches the latest order book data for the specified trading pair."""
        try:
            order_book_record = OrderBook.objects.filter(symbol=pair).latest('timestamp')
            bids = order_book_record.bids
            asks = order_book_record.asks
            return bids, asks
        except OrderBook.DoesNotExist:
            logger.warning(f"No order book data for {pair}")
            return [], []

    def calculate_volume_zscore(self, pair: str, current_volume: float) -> float:
        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(self.pair_data[pair]['volume_history'])[-1]

    def calculate_ob_imbalance(self, bids: List[Tuple[float, float]], asks: List[Tuple[float, float]]) -> float:
        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:
        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(self.pair_data[pair]['ob_imbalance_history'])[-1]

    def calculate_volatility(self, pair: str, current_price: float) -> float:
        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(self.pair_data[pair]['price_history']) / self.pair_data[pair]['price_history'][:-1]
        return np.std(returns) * np.sqrt(len(returns))

    async def generate_signal(self, pair: str, timeframe: str) -> Tuple[str, Dict]:
        try:
            df = await self.get_candles_df(pair, timeframe)
            if df.empty:
                return "Neutral", {}

            current_volume = df['volume'].iloc[-1]
            current_price = df['close'].iloc[-1]

            # Get order book data
            bids, asks = await self.get_order_book(pair)

            if not bids or not asks:
                return "Neutral", {}

            ob_imbalance = self.calculate_ob_imbalance(bids, asks)
            volume_zscore = self.calculate_volume_zscore(pair, current_volume)
            ob_imbalance_zscore = self.calculate_ob_imbalance_zscore(pair, ob_imbalance)
            volatility = self.calculate_volatility(pair, current_price)

            # Determine adaptive thresholds
            market_regime = "medium_volatility"  # Simplified; enhance with models if needed
            adaptive_volume_threshold, adaptive_ob_threshold = self.adaptive_thresholds(market_regime)

            signal = "Neutral"
            if volume_zscore > adaptive_volume_threshold and ob_imbalance > adaptive_ob_threshold:
                if ob_imbalance_zscore > 0:
                    signal = "Buy"
                elif ob_imbalance_zscore < 0:
                    signal = "Sell"

            # Calculate support and resistance levels
            support_level, resistance_level = df['low'].min(), df['high'].max()
            stop_loss, take_profit = self.calculate_stop_loss_take_profit(signal, current_price, support_level, resistance_level)
            
            metrics = {
                'volume_zscore': volume_zscore,
                'ob_imbalance': ob_imbalance,
                'ob_imbalance_zscore': ob_imbalance_zscore,
                'volatility': volatility,
                'stop_loss': stop_loss,
                'take_profit': take_profit,
                'support': support_level,
                'resistance': resistance_level
            }

            logger.info(f"Generated signal for {pair}: {signal}")
            return signal, metrics
        except Exception as e:
            logger.error(f"Error generating signal for {pair}: {str(e)}")
            return "Neutral", {}

    def calculate_stop_loss_take_profit(self, signal: str, current_price: float, support: float, resistance: float) -> Tuple[float, float]:
        if signal == "Buy":
            return support, resistance
        elif signal == "Sell":
            return resistance, support
        else:
            return support, resistance

    async def save_signal(self, pair: str, timeframe: str, signal: str, metrics: Dict[str, float]):
        try:
            Signal.objects.create(
                symbol=pair,
                timestamp=timezone.now(),
                timeframe=timeframe,
                signal_type=signal,
                price=metrics.get("current_price"),
                stop_loss=metrics.get("stop_loss"),
                take_profit=metrics.get("take_profit")
            )
            logger.info(f"Signal for {pair} {timeframe} saved to the database.")
        except Exception as e:
            logger.error(f"Error saving signal for {pair} {timeframe}: {e}")

    async def run_bot(self):
        while True:
            logger.info("Starting bot cycle...")
            pairs = TradingPair.objects.values_list("symbol", flat=True)
            timeframes = ["1m", "5m", "15m", "1h"]
            tasks = []

            for pair in pairs:
                for timeframe in timeframes:
                    task = self.process_pair_timeframe(pair, timeframe)
                    tasks.append(task)

            await asyncio.gather(*tasks)

            time.sleep(60)  # Adjust sleep time according to your needs

    async def process_pair_timeframe(self, pair: str, timeframe: str):
        signal, metrics = await self.generate_signal(pair, timeframe)
        await self.save_signal(pair, timeframe, signal, metrics)

if __name__ == "__main__":
    bot = OrderbookBot()
    try:
        asyncio.run(bot.run_bot())
    except KeyboardInterrupt:
        logger.info("Stopping bot...")

Can you analyze orderbook.py above and neworderbook.py code below and tell me what code is more robust and wold produce more accurate results based on features provided. Provide a rough probable success rate in percentage for each based on the features. 

#neworderbook.py
import asyncio
import logging
import os
import time
from typing import Dict, List, Tuple, Optional
from dataclasses import dataclass
from enum import Enum
import numpy as np
import pandas as pd
from scipy import stats
from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler
from datetime import datetime, timedelta
import talib

# Django setup
import django
from django.utils import timezone
from django.db.models import Avg, Max, Min

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'binancebot.settings')
django.setup()

from .models import Kline, OrderBook, Signal, TradingPair

# Enhanced logging
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler('trading_signals.log'),
        logging.StreamHandler()
    ]
)
logger = logging.getLogger(__name__)

class SignalType(Enum):
    STRONG_BUY = "Strong Buy"
    BUY = "Buy"
    NEUTRAL = "Neutral"
    SELL = "Sell"
    STRONG_SELL = "Strong Sell"
    NO_SIGNAL = "No Signal"

@dataclass
class SignalMetrics:
    signal_time: datetime
    chart_timestamp: datetime
    symbol: str
    current_price: float
    price_24h_change: float
    signal_type: SignalType
    stop_loss: float
    take_profit: float
    success_rate: float
    result: str
    volume_analysis: Dict
    orderbook_analysis: Dict
    technical_analysis: Dict

class EnhancedOrderbookBot:
    def __init__(self):
        self.short_period = 100
        self.long_period = 500
        self.volume_threshold = 2
        self.ob_imbalance_threshold = 0.7
        self.pair_data = {}
        self.vwap_periods = [20, 50, 200]
        self.rsi_period = 14
        self.bb_period = 20
        
    def initialize_pair(self, pair: str):
        self.pair_data[pair] = {
            'volume_data': pd.DataFrame(),
            'orderbook_data': pd.DataFrame(),
            'price_data': pd.DataFrame(),
            'volatility_history': [],
            'support_resistance_levels': [],
            'liquidity_zones': []
        }

    async def analyze_volume_profile(self, df: pd.DataFrame) -> Dict:
        """Advanced volume profile analysis"""
        volume_profile = {
            'buying_pressure': 0,
            'selling_pressure': 0,
            'volume_trend': '',
            'abnormal_volume': False,
            'volume_concentration': [],
            'delta_volume': 0
        }

        if df.empty:
            return volume_profile

        # Calculate volume-weighted metrics
        df['typical_price'] = (df['high'] + df['low'] + df['close']) / 3
        df['volume_price'] = df['typical_price'] * df['volume']
        
        # Volume trend analysis
        recent_volume = df['volume'].tail(20).mean()
        historical_volume = df['volume'].mean()
        volume_profile['volume_trend'] = 'Increasing' if recent_volume > historical_volume * 1.2 else 'Decreasing' if recent_volume < historical_volume * 0.8 else 'Stable'
        
        # Delta volume (buying vs selling pressure)
        df['delta_volume'] = df.apply(
            lambda x: x['volume'] if x['close'] > x['open'] else -x['volume'],
            axis=1
        )
        volume_profile['delta_volume'] = df['delta_volume'].sum()
        
        # Volume concentration analysis
        price_volume_clusters = self.analyze_price_volume_clusters(df)
        volume_profile['volume_concentration'] = price_volume_clusters
        
        return volume_profile

    def analyze_price_volume_clusters(self, df: pd.DataFrame) -> List[Dict]:
        """Analyze price levels where volume is concentrated"""
        price_volume = pd.DataFrame({
            'price': df['typical_price'],
            'volume': df['volume']
        })
        
        # Use KMeans to identify volume clusters
        X = price_volume.values
        n_clusters = min(5, len(X) // 10)  # Adaptive number of clusters
        if n_clusters < 2:
            return []
            
        kmeans = KMeans(n_clusters=n_clusters, random_state=42)
        clusters = kmeans.fit_predict(X)
        
        cluster_info = []
        for i in range(n_clusters):
            cluster_data = X[clusters == i]
            cluster_info.append({
                'price_level': float(cluster_data[:, 0].mean()),
                'volume_concentration': float(cluster_data[:, 1].sum()),
                'strength': len(cluster_data) / len(X)
            })
            
        return sorted(cluster_info, key=lambda x: x['volume_concentration'], reverse=True)

    async def analyze_orderbook_dynamics(self, pair: str) -> Dict:
        """Advanced order book analysis"""
        try:
            order_book_record = await self.get_order_book(pair)
            if not order_book_record:
                return {}

            bids, asks = order_book_record
            
            analysis = {
                'bid_ask_imbalance': 0,
                'spread': 0,
                'depth_analysis': {},
                'price_walls': [],
                'liquidity_analysis': {},
                'order_book_pressure': 0
            }
            
            if not bids or not asks:
                return analysis
                
            # Basic metrics
            best_bid = float(bids[0][0])
            best_ask = float(asks[0][0])
            analysis['spread'] = (best_ask - best_bid) / best_bid * 100
            
            # Cumulative volume analysis
            bid_volumes = np.cumsum([float(bid[1]) for bid in bids])
            ask_volumes = np.cumsum([float(ask[1]) for ask in asks])
            
            analysis['depth_analysis'] = {
                'bid_depth': float(bid_volumes[-1]),
                'ask_depth': float(ask_volumes[-1]),
                'depth_ratio': float(bid_volumes[-1] / ask_volumes[-1]) if ask_volumes[-1] != 0 else 1
            }
            
            # Identify price walls (large orders)
            threshold = np.percentile([float(x[1]) for x in bids + asks], 90)
            analysis['price_walls'] = [
                {'side': 'bid', 'price': float(bid[0]), 'volume': float(bid[1])}
                for bid in bids if float(bid[1]) > threshold
            ] + [
                {'side': 'ask', 'price': float(ask[0]), 'volume': float(ask[1])}
                for ask in asks if float(ask[1]) > threshold
            ]
            
            # Calculate order book pressure
            bid_pressure = sum(float(bid[1]) / (abs(float(bid[0]) - best_bid) + 1) for bid in bids)
            ask_pressure = sum(float(ask[1]) / (abs(float(ask[0]) - best_ask) + 1) for ask in asks)
            analysis['order_book_pressure'] = float((bid_pressure - ask_pressure) / (bid_pressure + ask_pressure))
            
            return analysis
            
        except Exception as e:
            logger.error(f"Error in orderbook analysis for {pair}: {str(e)}")
            return {}

    async def generate_comprehensive_signal(self, pair: str, timeframe: str) -> Tuple[SignalType, SignalMetrics]:
        try:
            # Fetch and prepare data
            df = await self.get_candles_df(pair, timeframe)
            if df.empty:
                return SignalType.NO_SIGNAL, None

            current_price = df['close'].iloc[-1]
            price_24h_ago = df['close'].iloc[-1440] if len(df) > 1440 else df['close'].iloc[0]
            price_24h_change = ((current_price - price_24h_ago) / price_24h_ago) * 100

            # Comprehensive analysis
            volume_analysis = await self.analyze_volume_profile(df)
            orderbook_analysis = await self.analyze_orderbook_dynamics(pair)
            technical_analysis = self.perform_technical_analysis(df)

            # Signal generation
            signal_type, metrics = self.evaluate_signal_strength(
                volume_analysis,
                orderbook_analysis,
                technical_analysis,
                current_price
            )

            # Calculate success rate
            success_rate = self.calculate_signal_success_rate(
                signal_type,
                volume_analysis,
                orderbook_analysis,
                technical_analysis
            )

            # Generate stop loss and take profit levels
            stop_loss, take_profit = self.calculate_optimal_levels(
                signal_type,
                current_price,
                technical_analysis['support_levels'],
                technical_analysis['resistance_levels'],
                df
            )

            signal_metrics = SignalMetrics(
                signal_time=timezone.now(),
                chart_timestamp=df.index[-1],
                symbol=pair,
                current_price=current_price,
                price_24h_change=price_24h_change,
                signal_type=signal_type,
                stop_loss=stop_loss,
                take_profit=take_profit,
                success_rate=success_rate,
                result='Pending',
                volume_analysis=volume_analysis,
                orderbook_analysis=orderbook_analysis,
                technical_analysis=technical_analysis
            )

            return signal_type, signal_metrics

        except Exception as e:
            logger.error(f"Error generating signal for {pair}: {str(e)}")
            return SignalType.NO_SIGNAL, None

    def perform_technical_analysis(self, df: pd.DataFrame) -> Dict:
        """Comprehensive technical analysis"""
        analysis = {}
        
        # Calculate basic indicators
        analysis['rsi'] = talib.RSI(df['close'].values, timeperiod=self.rsi_period)[-1]
        upper, middle, lower = talib.BBANDS(df['close'].values, timeperiod=self.bb_period)
        analysis['bollinger_bands'] = {
            'upper': upper[-1],
            'middle': middle[-1],
            'lower': lower[-1]
        }
        
        # Calculate VWAP for different periods
        analysis['vwap'] = {}
        for period in self.vwap_periods:
            df_period = df.tail(period)
            analysis['vwap'][period] = (df_period['typical_price'] * df_period['volume']).sum() / df_period['volume'].sum()
        
        # Support and resistance levels
        analysis['support_levels'] = self.identify_support_levels(df)
        analysis['resistance_levels'] = self.identify_resistance_levels(df)
        
        # Trend analysis
        analysis['trend'] = self.analyze_trend(df)
        
        return analysis

    def evaluate_signal_strength(self, volume_analysis: Dict, orderbook_analysis: Dict, technical_analysis: Dict, current_price: float) -> Tuple[SignalType, Dict]:
        """Evaluate overall signal strength and determine signal type"""
        # Initialize scoring system
        buy_score = 0
        sell_score = 0
        
        # Volume analysis scoring
        if volume_analysis['delta_volume'] > 0:
            buy_score += 1
        else:
            sell_score += 1
            
        if volume_analysis['volume_trend'] == 'Increasing':
            if volume_analysis['delta_volume'] > 0:
                buy_score += 2
            else:
                sell_score += 2
                
        # Orderbook analysis scoring
        if orderbook_analysis.get('order_book_pressure', 0) > 0.3:
            buy_score += 2
        elif orderbook_analysis.get('order_book_pressure', 0) < -0.3:
            sell_score += 2
            
        # Technical analysis scoring
        if technical_analysis['rsi'] < 30:
            buy_score += 2
        elif technical_analysis['rsi'] > 70:
            sell_score += 2
            
        if current_price < technical_analysis['bollinger_bands']['lower']:
            buy_score += 1
        elif current_price > technical_analysis['bollinger_bands']['upper']:
            sell_score += 1
            
        # Evaluate trend
        if technical_analysis['trend']['direction'] == 'upward':
            buy_score += 2
        elif technical_analysis['trend']['direction'] == 'downward':
            sell_score += 2
            
        # Determine signal type
        if buy_score - sell_score >= 4:
            return SignalType.STRONG_BUY, {'buy_score': buy_score, 'sell_score': sell_score}
        elif buy_score - sell_score >= 2:
            return SignalType.BUY, {'buy_score': buy_score, 'sell_score': sell_score}
        elif sell_score - buy_score >= 4:
            return SignalType.STRONG_SELL, {'buy_score': buy_score, 'sell_score': sell_score}
        elif sell_score - buy_score >= 2:
            return SignalType.SELL, {'buy_score': buy_score, 'sell_score': sell_score}
        elif abs(buy_score - sell_score) < 2 and technical_analysis['trend']['strength'] == 'sideways':
            return SignalType.NEUTRAL, {'buy_score': buy_score, 'sell_score': sell_score}
        else:
            return SignalType.NO_SIGNAL, {'buy_score': buy_score, 'sell_score': sell_score}

    def calculate_signal_success_rate(self, signal_type: SignalType, volume_analysis: Dict, orderbook_analysis: Dict, technical_analysis: Dict) -> float:
        """Calculate probability of signal success"""
        base_rate = 50.0  # Base success rate
        
        # Adjust based on signal type
        if signal_type in [SignalType.STRONG_BUY, SignalType.STRONG_SELL]:
            base_rate += 10
        elif signal_type in [SignalType.BUY, SignalType.SELL]:
            base_rate += 5
            
        # Volume confidence
        if volume_analysis['volume_trend'] == 'Increasing':
            base_rate += 5
        if abs(volume_analysis['delta_volume']) > 1000000:  # Significant volume
            base_rate += 5
            
        # Orderbook confidence
        if abs(orderbook_analysis.get('order_book_pressure', 0)) > 0.5:
            base_rate += 5
            
        # Technical confidence
        if technical_analysis['trend']['strength'] == 'strong':
            base_rate += 10
        elif technical_analysis['trend']['strength'] == 'moderate':
            base_rate += 5
            
        # Cap the success rate
        return min(95.0, max(5.0, base_rate))

    def calculate_optimal_levels(self, signal_type: SignalType, current_price: float, support_levels: List[float], resistance_levels: List[float], df: pd.DataFrame) -> Tuple[float, float]:
        """Calculate optimal stop loss and take profit levels"""
        atr = talib.ATR(df['high'].values, df['low'].values, df['close'].values, timeperiod=14)[-1]
        volatility_factor = 1.5

        if signal_type in [SignalType.STRONG_BUY, SignalType.BUY]:
            # Find nearest support for stop loss
            valid_supports = [s for s in support_levels if s < current_price]
            stop_loss = max(valid_supports) if valid_supports else current_price - (atr * volatility_factor)
            
            # Find resistance for take profit
            valid_resistances = [r for r in resistance_levels if r > current_price]
            take_profit = min(valid_resistances) if valid_resistances else current_price + (atr * 2.5)
            
            # Ensure minimum risk-reward ratio of 1:2
            if (take_profit - current_price) < 2 * (current_price - stop_loss):
                take_profit = current_price + 2 * (current_price - stop_loss)

        elif signal_type in [SignalType.STRONG_SELL, SignalType.SELL]:
            # Find nearest resistance for stop loss
            valid_resistances = [r for r in resistance_levels if r > current_price]
            stop_loss = min(valid_resistances) if valid_resistances else current_price + (atr * volatility_factor)
            
            # Find support for take profit
            valid_supports = [s for s in support_levels if s < current_price]
            take_profit = max(valid_supports) if valid_supports else current_price - (atr * 2.5)
            
            # Ensure minimum risk-reward ratio of 1:2
            if (current_price - take_profit) < 2 * (stop_loss - current_price):
                take_profit = current_price - 2 * (stop_loss - current_price)

        else:  # NEUTRAL or NO_SIGNAL
            stop_loss = current_price - (atr * volatility_factor)
            take_profit = current_price + (atr * volatility_factor)

        return round(stop_loss, 8), round(take_profit, 8)

    def identify_support_levels(self, df: pd.DataFrame) -> List[float]:
        """Identify key support levels using multiple methods"""
        levels = set()
        
        # Method 1: Local minima
        window = 20
        for i in range(window, len(df) - window):
            if min(df['low'].iloc[i-window:i+window]) == df['low'].iloc[i]:
                levels.add(float(df['low'].iloc[i]))
        
        # Method 2: Volume Price Analysis (VPA)
        price_volume = pd.DataFrame({
            'price': df['low'],
            'volume': df['volume']
        })
        grouped = price_volume.groupby('price')['volume'].sum()
        high_volume_prices = grouped[grouped > grouped.quantile(0.8)].index.values
        levels.update(high_volume_prices)
        
        # Method 3: Historical price levels with multiple touches
        price_touches = {}
        for price in df['low']:
            rounded_price = round(price, 4)  # Round to reduce noise
            price_touches[rounded_price] = price_touches.get(rounded_price, 0) + 1
        
        multiple_touches = [price for price, touches in price_touches.items() if touches >= 3]
        levels.update(multiple_touches)
        
        return sorted(list(levels))

    def identify_resistance_levels(self, df: pd.DataFrame) -> List[float]:
        """Identify key resistance levels using multiple methods"""
        levels = set()
        
        # Method 1: Local maxima
        window = 20
        for i in range(window, len(df) - window):
            if max(df['high'].iloc[i-window:i+window]) == df['high'].iloc[i]:
                levels.add(float(df['high'].iloc[i]))
        
        # Method 2: Volume Price Analysis (VPA)
        price_volume = pd.DataFrame({
            'price': df['high'],
            'volume': df['volume']
        })
        grouped = price_volume.groupby('price')['volume'].sum()
        high_volume_prices = grouped[grouped > grouped.quantile(0.8)].index.values
        levels.update(high_volume_prices)
        
        # Method 3: Historical price levels with multiple touches
        price_touches = {}
        for price in df['high']:
            rounded_price = round(price, 4)
            price_touches[rounded_price] = price_touches.get(rounded_price, 0) + 1
        
        multiple_touches = [price for price, touches in price_touches.items() if touches >= 3]
        levels.update(multiple_touches)
        
        return sorted(list(levels))

    def analyze_trend(self, df: pd.DataFrame) -> Dict:
        """Analyze price trend using multiple methods"""
        # Calculate EMAs
        ema20 = talib.EMA(df['close'].values, timeperiod=20)
        ema50 = talib.EMA(df['close'].values, timeperiod=50)
        ema200 = talib.EMA(df['close'].values, timeperiod=200)
        
        # Current values
        current_ema20 = ema20[-1]
        current_ema50 = ema50[-1]
        current_ema200 = ema200[-1]
        current_price = df['close'].iloc[-1]
        
        # Determine trend direction
        if current_ema20 > current_ema50 > current_ema200:
            direction = 'upward'
        elif current_ema20 < current_ema50 < current_ema200:
            direction = 'downward'
        else:
            direction = 'sideways'
        
        # Calculate trend strength
        adr = (df['high'] - df['low']).mean()  # Average Daily Range
        volatility = df['close'].pct_change().std()
        
        if direction == 'sideways':
            if volatility < 0.01:  # Low volatility
                strength = 'weak'
            else:
                strength = 'moderate'
        else:
            # Calculate momentum using RSI
            rsi = talib.RSI(df['close'].values, timeperiod=14)[-1]
            
            if direction == 'upward':
                if rsi > 70 and volatility > 0.02:
                    strength = 'strong'
                elif rsi > 60:
                    strength = 'moderate'
                else:
                    strength = 'weak'
            else:  # downward
                if rsi < 30 and volatility > 0.02:
                    strength = 'strong'
                elif rsi < 40:
                    strength = 'moderate'
                else:
                    strength = 'weak'
        
        return {
            'direction': direction,
            'strength': strength,
            'volatility': float(volatility),
            'adr': float(adr),
            'ema_alignment': {
                'ema20': float(current_ema20),
                'ema50': float(current_ema50),
                'ema200': float(current_ema200)
            }
        }

    async def update_signal_results(self):
        """Update the results of pending signals"""
        pending_signals = Signal.objects.filter(result='Pending')
        
        for signal in pending_signals:
            current_price = await self.get_current_price(signal.symbol)
            
            if current_price >= signal.take_profit:
                signal.result = 'Won'
                signal.save()
            elif current_price <= signal.stop_loss:
                signal.result = 'Lost'
                signal.save()

    async def save_signal(self, metrics: SignalMetrics):
        """Save signal with comprehensive metrics to database"""
        try:
            signal = Signal(
                symbol=metrics.symbol,
                signal_generated_time=metrics.signal_time,
                chart_timestamp=metrics.chart_timestamp,
                timeframe=self.timeframe,
                signal_type=metrics.signal_type.value,
                price=metrics.current_price,
                price_24h_change=metrics.price_24h_change,
                stop_loss=metrics.stop_loss,
                take_profit=metrics.take_profit,
                success_rate=metrics.success_rate,
                result=metrics.result,
                volume_analysis=metrics.volume_analysis,
                orderbook_analysis=metrics.orderbook_analysis,
                technical_analysis=metrics.technical_analysis
            )
            await sync_to_async(signal.save)()
            
            logger.info(f"Saved {metrics.signal_type.value} signal for {metrics.symbol} at {metrics.signal_time}")
            
        except Exception as e:
            logger.error(f"Error saving signal: {str(e)}")

    async def run_bot(self):
        """Main bot loop"""
        while True:
            try:
                logger.info("Starting analysis cycle...")
                pairs = await sync_to_async(list)(TradingPair.objects.values_list("symbol", flat=True))
                timeframes = ["1m", "5m", "15m", "1h"]
                
                for pair in pairs:
                    for timeframe in timeframes:
                        self.timeframe = timeframe
                        signal_type, metrics = await self.generate_comprehensive_signal(pair, timeframe)
                        
                        if signal_type != SignalType.NO_SIGNAL and metrics:
                            await self.save_signal(metrics)
                
                # Update results of pending signals
                await self.update_signal_results()
                
                await asyncio.sleep(60)  # Adjust based on your needs
                
            except Exception as e:
                logger.error(f"Error in main bot loop: {str(e)}")
                await asyncio.sleep(60)

if __name__ == "__main__":
    bot = EnhancedOrderbookBot()
    try:
        asyncio.run(bot.run_bot())
    except KeyboardInterrupt:
        logger.info("Stopping bot...")