#machine_model.py
import aiohttp
import numpy as np
import pandas as pd
from sklearn.feature_selection import SelectKBest, mutual_info_regression
from sklearn.metrics import mean_absolute_error
from sklearn.model_selection import RandomizedSearchCV, train_test_split, TimeSeriesSplit
from sklearn.preprocessing import StandardScaler
from sklearn.ensemble import RandomForestRegressor, GradientBoostingRegressor
from sklearn.svm import SVR
from xgboost import XGBRegressor
from lightgbm import LGBMRegressor
import tensorflow as tf
from keras import Sequential
from tensorflow.python.keras.layers.recurrent import LSTM
from tensorflow.python.keras.layers import Dense, Dropout, Attention, Conv1D
from keras.src.callbacks import EarlyStopping
from tensorflow.python.keras.optimizer_v1 import Adam
from statsmodels.tsa.arima.model import ARIMA
from prophet import Prophet
import talib
import asyncio
from typing import Dict, List, Tuple
import logging
from .fetch_data import DataFetcher  # Assume this is appropriately implemented to fetch data
from .models import Signal  # Assume this is your Django model for saving signals
import json

# Logger configuration
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

class MachineTradingBot:
    def __init__(self, symbols: List[str]):
        self.models = {
            'random_forest': RandomForestRegressor(random_state=42),
            'svm': SVR(),
            'xgboost': XGBRegressor(random_state=42),
            'lightgbm': LGBMRegressor(random_state=42),
            'gradient_boosting': GradientBoostingRegressor(random_state=42),
            'lstm': None,
            'hybrid_cnn_lstm': None,
            'arima': None,
            'prophet': Prophet(),
        }
        self.scaler = StandardScaler()
        self.best_params = {}
        self.feature_importances = {}
        self.model_weights = {model: 1 / len(self.models) for model in self.models}
        self.model_performance = {model: [] for model in self.models}
        self.timeframes = ['5m', '15m', '1h']
        self.symbols = symbols
        self.session = None
        self.fetcher = DataFetcher(symbols=symbols, timeframes=self.timeframes)

    async def initialize(self):
        self.session = aiohttp.ClientSession()
        await self.fetcher.initialize_exchange()

    async def fetch_data(self, symbol: str, timeframe: str) -> pd.DataFrame:
        # Fetch historical data using DataFetcher
        await self.fetcher.fetch_historical_data()
        # Placeholder for fetching real-time updates

    def preprocess_data(self, data: pd.DataFrame) -> pd.DataFrame:
        data = self.add_technical_indicators(data)
        data = self.add_fourier_features(data)
        data = self.add_lagged_features(data)
        data = self.add_market_regime_features(data)
        data.dropna(inplace=True)
        return data

    def add_technical_indicators(self, data: pd.DataFrame) -> pd.DataFrame:
        for col in ['open', 'high', 'low', 'close', 'volume']:
            if col not in data.columns:
                logger.warning(f"Column {col} not found in data. Skipping technical indicators.")
                return data

        try:
            data['rsi'] = talib.RSI(data['close'])
            data['macd'], data['macd_signal'], _ = talib.MACD(data['close'])
            data['bbands_upper'], data['bbands_middle'], data['bbands_lower'] = talib.BBANDS(data['close'])
            data['adx'] = talib.ADX(data['high'], data['low'], data['close'])
            data['cci'] = talib.CCI(data['high'], data['low'], data['close'])
            data['mfi'] = talib.MFI(data['high'], data['low'], data['close'], data['volume'])
        except Exception as e:
            logger.error(f"Error calculating technical indicators: {str(e)}")
        return data

    def add_fourier_features(self, data: pd.DataFrame) -> pd.DataFrame:
        for period in [5, 10, 22]:
            for order in range(1, 3):
                data[f'fourier_sin_{period}_{order}'] = np.sin(2 * np.pi * order * data.index.dayofyear / period)
                data[f'fourier_cos_{period}_{order}'] = np.cos(2 * np.pi * order * data.index.dayofyear / period)
        return data

    def add_lagged_features(self, data: pd.DataFrame) -> pd.DataFrame:
        for lag in [1, 3, 5, 7, 14, 30]:
            data[f'close_lag_{lag}'] = data['close'].shift(lag)
            data[f'volume_lag_{lag}'] = data['volume'].shift(lag)
        return data

    def add_market_regime_features(self, data: pd.DataFrame) -> pd.DataFrame:
        returns = data['close'].pct_change()
        volatility = returns.rolling(window=22).std()
        data['market_regime'] = pd.cut(volatility, bins=3, labels=['low', 'medium', 'high'])
        data = pd.get_dummies(data, columns=['market_regime'])
        return data

    def prepare_features(self, data: pd.DataFrame) -> Tuple[pd.DataFrame, pd.Series]:
        features = data.drop(['open', 'high', 'low', 'close', 'volume'], axis=1)
        target = data['close'].shift(-1)
        selector = SelectKBest(mutual_info_regression, k=50)
        features = pd.DataFrame(selector.fit_transform(features, target), columns=features.columns[selector.get_support()])
        return features, target

    async def train_models(self, data: pd.DataFrame):
        features, target = self.prepare_features(data)
        X_train, X_test, y_train, y_test = train_test_split(features, target, test_size=0.2, shuffle=False)
        X_train_scaled = self.scaler.fit_transform(X_train)
        X_test_scaled = self.scaler.transform(X_test)

        tasks = []
        for model_name in ['random_forest', 'svm', 'xgboost', 'lightgbm', 'gradient_boosting']:
            tasks.append(self.tune_and_train(model_name, X_train_scaled, y_train, X_test_scaled, y_test))

        tasks.append(self.train_lstm(X_train_scaled, y_train, X_test_scaled, y_test))
        tasks.append(self.train_hybrid_cnn_lstm(X_train_scaled, y_train, X_test_scaled, y_test))
        tasks.append(self.train_arima(data['close']))

        prophet_data = pd.DataFrame({'ds': data.index, 'y': data['close']})
        tasks.append(self.train_prophet(prophet_data))

        await asyncio.gather(*tasks)
        self.calculate_feature_importances(features.columns)

    async def tune_and_train(self, model_name: str, X_train: np.ndarray, y_train: pd.Series, X_test: np.ndarray, y_test: pd.Series):
        logger.info(f"Tuning and training {model_name}...")
        param_distributions = self.get_param_distributions(model_name)

        tscv = TimeSeriesSplit(n_splits=5)
        search = RandomizedSearchCV(
            self.models[model_name],
            param_distributions=param_distributions,
            n_iter=50,
            cv=tscv,
            random_state=42,
            n_jobs=-1,
            scoring='neg_mean_absolute_error'
        )
        search.fit(X_train, y_train)
        self.models[model_name] = search.best_estimator_
        self.best_params[model_name] = search.best_params_

        y_pred = self.models[model_name].predict(X_test)
        mae = mean_absolute_error(y_test, y_pred)
        self.model_performance[model_name].append(mae)

        logger.info(f"Best parameters for {model_name}: {search.best_params_}")
        logger.info(f"MAE for {model_name}: {mae}")

    async def train_lstm(self, X_train: np.ndarray, y_train: pd.Series, X_test: np.ndarray, y_test: pd.Series):
        model = Sequential([
            LSTM(100, activation='relu', input_shape=(X_train.shape[1], 1), return_sequences=True),
            Attention(),
            Dropout(0.2),
            LSTM(50, activation='relu'),
            Dropout(0.2),
            Dense(1)
        ])
        model.compile(optimizer=Adam(learning_rate=0.001), loss='mse')

        X_train_reshaped = X_train.reshape((X_train.shape[0], X_train.shape[1], 1))
        X_test_reshaped = X_test.reshape((X_test.shape[0], X_test.shape[1], 1))

        early_stopping = EarlyStopping(patience=10, restore_best_weights=True)

        model.fit(
            X_train_reshaped, y_train,
            epochs=100,
            batch_size=32,
            validation_split=0.2,
            callbacks=[early_stopping],
            verbose=0
        )
        y_pred = model.predict(X_test_reshaped)
        mae = mean_absolute_error(y_test, y_pred)
        self.model_performance['lstm'].append(mae)
        logger.info(f"MAE for LSTM: {mae}")

        self.models['lstm'] = model

    async def train_hybrid_cnn_lstm(self, X_train: np.ndarray, y_train: pd.Series, X_test: np.ndarray, y_test: pd.Series):
        model = Sequential([
            Conv1D(filters=64, kernel_size=3, activation='relu', input_shape=(X_train.shape[1], 1)),
            Conv1D(filters=64, kernel_size=3, activation='relu'),
            LSTM(50, activation='relu', return_sequences=True),
            Attention(),
            LSTM(50, activation='relu'),
            Dense(1)
        ])
        model.compile(optimizer=Adam(learning_rate=0.001), loss='mse')

        X_train_reshaped = X_train.reshape((X_train.shape[0], X_train.shape[1], 1))
        X_test_reshaped = X_test.reshape((X_test.shape[0], X_test.shape[1], 1))

        early_stopping = EarlyStopping(patience=10, restore_best_weights=True)

        model.fit(
            X_train_reshaped, y_train,
            epochs=100,
            batch_size=32,
            validation_split=0.2,
            callbacks=[early_stopping],
            verbose=0
        )

        y_pred = model.predict(X_test_reshaped)
        mae = mean_absolute_error(y_test, y_pred)
        self.model_performance['hybrid_cnn_lstm'].append(mae)
        logger.info(f"MAE for Hybrid CNN-LSTM: {mae}")

        self.models['hybrid_cnn_lstm'] = model

    async def train_arima(self, time_series: pd.Series):
        model = ARIMA(time_series, order=(5, 1, 0))
        fitted = model.fit()

        # Evaluate performance
        y_pred = fitted.forecast(steps=len(time_series))
        mae = mean_absolute_error(time_series, y_pred)
        self.model_performance['arima'].append(mae)
        logger.info(f"MAE for ARIMA: {mae}")

        self.models['arima'] = fitted

    async def train_prophet(self, prophet_data: pd.DataFrame):
        model = Prophet()
        model.fit(prophet_data)

        future = model.make_future_dataframe(periods=len(prophet_data))
        forecast = model.predict(future)

        y_pred = forecast['yhat'][-len(prophet_data):]
        mae = mean_absolute_error(prophet_data['y'], y_pred)
        self.model_performance['prophet'].append(mae)
        logger.info(f"MAE for Prophet: {mae}")

        self.models['prophet'] = model

    async def generate_signals(self, latest_data: pd.DataFrame) -> Tuple[Dict[str, str], Dict[str, float]]:
        features, _ = self.prepare_features(latest_data)
        scaled_features = self.scaler.transform(features)

        signals = {}
        confidences = {}

        for model_name in ['random_forest', 'svm', 'xgboost', 'lightgbm', 'gradient_boosting']:
            prediction = self.models[model_name].predict(scaled_features)
            signal_strength = prediction[-1] - latest_data['close'].iloc[-1]
            category = "strong buy" if signal_strength > 0.005 else "weak buy" if signal_strength > 0 else "strong sell" if signal_strength < -0.005 else "weak sell"
            signals[model_name] = category
            confidences[model_name] = abs(signal_strength) / latest_data['close'].iloc[-1]

        # LSTM Model
        lstm_features = scaled_features.reshape((scaled_features.shape[0], scaled_features.shape[1], 1))
        lstm_prediction = self.models['lstm'].predict(lstm_features)
        signal_strength_lstm = lstm_prediction[-1][0] - latest_data['close'].iloc[-1]
        category_lstm = "strong buy" if signal_strength_lstm > 0.005 else "weak buy" if signal_strength_lstm > 0 else "strong sell" if signal_strength_lstm < -0.005 else "weak sell"
        signals['lstm'] = category_lstm
        confidences['lstm'] = abs(signal_strength_lstm) / latest_data['close'].iloc[-1]

        # Hybrid CNN-LSTM Model
        hybrid_prediction = self.models['hybrid_cnn_lstm'].predict(lstm_features)
        signal_strength_hybrid = hybrid_prediction[-1][0] - latest_data['close'].iloc[-1]
        category_hybrid = "strong buy" if signal_strength_hybrid > 0.005 else "weak buy" if signal_strength_hybrid > 0 else "strong sell" if signal_strength_hybrid < -0.005 else "weak sell"
        signals['hybrid_cnn_lstm'] = category_hybrid
        confidences['hybrid_cnn_lstm'] = abs(signal_strength_hybrid) / latest_data['close'].iloc[-1]

        # ARIMA Model
        arima_forecast = self.models['arima'].forecast(steps=1)
        signal_strength_arima = arima_forecast[0] - latest_data['close'].iloc[-1]
        category_arima = "strong buy" if signal_strength_arima > 0.005 else "weak buy" if signal_strength_arima > 0 else "strong sell" if signal_strength_arima < -0.005 else "weak sell"
        signals['arima'] = category_arima
        confidences['arima'] = abs(signal_strength_arima) / latest_data['close'].iloc[-1]

        # Prophet Model
        prophet_forecast = self.models['prophet'].predict(pd.DataFrame({'ds': [latest_data.index[-1] + pd.Timedelta(minutes=5)]}))
        signal_strength_prophet = prophet_forecast['yhat'].iloc[0] - latest_data['close'].iloc[-1]
        category_prophet = "strong buy" if signal_strength_prophet > 0.005 else "weak buy" if signal_strength_prophet > 0 else "strong sell" if signal_strength_prophet < -0.005 else "weak sell"
        signals['prophet'] = category_prophet
        confidences['prophet'] = abs(signal_strength_prophet) / latest_data['close'].iloc[-1]

        return signals, confidences

    async def concurrent_signal_generation(self, symbol: str):
        tasks = []
        for timeframe in self.timeframes:
            data = await self.fetch_data(symbol, timeframe)
            preprocessed_data = self.preprocess_data(data)
            await self.train_models(preprocessed_data)
            task = self.generate_signals(preprocessed_data.iloc[-30:])
            tasks.append(task)

        results = await asyncio.gather(*tasks)
        return results

    async def save_signals_to_db(self, symbol: str, timeframe: str, final_decision: str, signals: Dict[str, str], confidences: Dict[str, float]):
        Signal.objects.update_or_create(
            symbol=symbol,
            timeframe=timeframe,
            defaults={
                'decision': final_decision,
                'signals': json.dumps(signals),
                'confidences': json.dumps(confidences),
            }
        )
        logger.info(f"Signals saved for {symbol} {timeframe}")

    async def run(self):
        await self.initialize()
        for symbol in self.symbols:
            results = await self.concurrent_signal_generation(symbol)
            for timeframe, (signals, confidences) in zip(self.timeframes, results):
                final_decision = "buy" if sum(1 for s in signals.values() if 'buy' in s) > len(signals) / 2 else "sell"
                await self.save_signals_to_db(symbol, timeframe, final_decision, signals, confidences)
        await self.session.close()

if __name__ == "__main__":
    symbols = ['BTC/USDT', 'ETH/USDT']  # Example list of symbols
    bot = MachineTradingBot(symbols)
    asyncio.run(bot.run())

Between the code above and the one below, wic one is likely to produce more accurate signals. What are the strengths and weaknesses of each and what would you recommend I use. Also, anything that I can do to improve it's accuracy further

import asyncio
import numpy as np
import pandas as pd
from datetime import datetime, timedelta
from sklearn.ensemble import RandomForestRegressor, GradientBoostingRegressor
from sklearn.preprocessing import StandardScaler, RobustScaler
from sklearn.model_selection import TimeSeriesSplit
from sklearn.metrics import mean_absolute_error, precision_score, recall_score, f1_score
import lightgbm as lgb
from xgboost import XGBRegressor
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import LSTM, Dense, Dropout
from tensorflow.keras.callbacks import EarlyStopping
from tensorflow.keras.optimizers import Adam
import logging
from typing import Dict, List, Tuple, Optional
from django.utils import timezone
from django.db.models import Q
from decimal import Decimal

# Assume these are your Django models
from .models import PriceData, Signal, PerformanceMetrics

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

class EnhancedTradingBot:
    def __init__(self, symbols: List[str]):
        self.symbols = symbols
        self.timeframes = ['1m', '5m', '15m', '1h']
        self.models = self._initialize_models()
        self.scalers = {
            'price': RobustScaler(),
            'volume': RobustScaler(),
            'features': StandardScaler()
        }
        self.performance_metrics = {}
        self.volatility_windows = {'1m': 60, '5m': 24, '15m': 16, '1h': 24}
        self.min_success_rate = 65  # Minimum success rate to generate signals

    def _initialize_models(self) -> Dict:
        return {
            'random_forest': RandomForestRegressor(
                n_estimators=100,
                max_depth=10,
                min_samples_split=5,
                min_samples_leaf=2,
                n_jobs=-1,
                random_state=42
            ),
            'lightgbm': lgb.LGBMRegressor(
                n_estimators=100,
                learning_rate=0.05,
                max_depth=7,
                num_leaves=31,
                n_jobs=-1,
                random_state=42
            ),
            'xgboost': XGBRegressor(
                n_estimators=100,
                learning_rate=0.05,
                max_depth=7,
                n_jobs=-1,
                random_state=42
            ),
            'gradient_boosting': GradientBoostingRegressor(
                n_estimators=100,
                learning_rate=0.05,
                max_depth=5,
                random_state=42
            ),
            'lstm': None  # Will be initialized during training
        }

    async def fetch_historical_data(self, symbol: str, timeframe: str) -> pd.DataFrame:
        """Fetch historical data from Django ORM"""
        queryset = PriceData.objects.filter(
            symbol=symbol,
            timeframe=timeframe
        ).order_by('timestamp')
        
        df = pd.DataFrame.from_records(
            queryset.values('timestamp', 'open', 'high', 'low', 'close', 'volume')
        )
        df.set_index('timestamp', inplace=True)
        return df

    def calculate_dynamic_thresholds(self, data: pd.DataFrame, timeframe: str) -> Tuple[float, float]:
        """Calculate dynamic thresholds based on volatility"""
        returns = data['close'].pct_change()
        volatility = returns.rolling(window=self.volatility_windows[timeframe]).std()
        current_volatility = volatility.iloc[-1]
        
        # Adjust thresholds based on volatility
        base_threshold = 0.005
        volatility_multiplier = current_volatility / returns.std()
        
        buy_threshold = base_threshold * volatility_multiplier
        sell_threshold = -base_threshold * volatility_multiplier
        
        return buy_threshold, sell_threshold

    def create_features(self, data: pd.DataFrame) -> pd.DataFrame:
        """Create advanced features for ML models"""
        df = data.copy()
        
        # Price-based features
        df['returns'] = df['close'].pct_change()
        df['log_returns'] = np.log1p(df['returns'])
        
        # Volatility features
        df['volatility'] = df['returns'].rolling(window=20).std()
        df['volatility_change'] = df['volatility'].pct_change()
        
        # Volume-based features
        df['volume_ma'] = df['volume'].rolling(window=20).mean()
        df['volume_std'] = df['volume'].rolling(window=20).std()
        df['volume_ratio'] = df['volume'] / df['volume_ma']
        
        # Price momentum
        for window in [5, 10, 20, 30]:
            df[f'momentum_{window}'] = df['close'].pct_change(periods=window)
            df[f'volume_momentum_{window}'] = df['volume'].pct_change(periods=window)
        
        # Price differences
        df['price_range'] = df['high'] - df['low']
        df['price_range_ratio'] = df['price_range'] / df['close']
        
        # Target variable (future returns)
        df['target'] = df['close'].shift(-1) / df['close'] - 1
        
        return df.dropna()

    def prepare_lstm_data(self, data: pd.DataFrame, lookback: int = 60) -> Tuple[np.ndarray, np.ndarray]:
        """Prepare data for LSTM model"""
        features = data.drop(['target'], axis=1).values
        target = data['target'].values
        
        X, y = [], []
        for i in range(len(features) - lookback):
            X.append(features[i:(i + lookback)])
            y.append(target[i + lookback])
            
        return np.array(X), np.array(y)

    def create_lstm_model(self, input_shape: Tuple) -> Sequential:
        """Create LSTM model with optimized architecture"""
        model = Sequential([
            LSTM(50, input_shape=input_shape, return_sequences=True),
            Dropout(0.2),
            LSTM(30, return_sequences=False),
            Dropout(0.2),
            Dense(1)
        ])
        
        model.compile(
            optimizer=Adam(learning_rate=0.001),
            loss='mse',
            metrics=['mae']
        )
        return model

    async def train_models(self, data: pd.DataFrame, timeframe: str):
        """Train all models with the provided data"""
        features_df = self.create_features(data)
        
        # Split features and target
        X = features_df.drop(['target'], axis=1)
        y = features_df['target']
        
        # Scale features
        X_scaled = self.scalers['features'].fit_transform(X)
        
        # Time series split
        tscv = TimeSeriesSplit(n_splits=5)
        
        # Train traditional ML models
        for name, model in self.models.items():
            if name != 'lstm':
                logger.info(f"Training {name} for {timeframe}")
                model.fit(X_scaled, y)
                
        # Train LSTM
        X_lstm, y_lstm = self.prepare_lstm_data(features_df)
        self.models['lstm'] = self.create_lstm_model((X_lstm.shape[1], X_lstm.shape[2]))
        
        early_stopping = EarlyStopping(
            monitor='val_loss',
            patience=5,
            restore_best_weights=True
        )
        
        self.models['lstm'].fit(
            X_lstm, y_lstm,
            epochs=50,
            batch_size=32,
            validation_split=0.2,
            callbacks=[early_stopping],
            verbose=0
        )

    def calculate_support_resistance(self, data: pd.DataFrame) -> Tuple[float, float]:
        """Calculate support and resistance levels"""
        price_range = data['high'].max() - data['low'].min()
        support = data['low'].iloc[-1] - (price_range * 0.1)
        resistance = data['high'].iloc[-1] + (price_range * 0.1)
        return support, resistance

    def calculate_success_rate(self, predictions: Dict[str, float], confidence_scores: Dict[str, float]) -> float:
        """Calculate success rate based on model predictions and confidence"""
        weighted_confidence = sum(pred * conf for pred, conf in zip(predictions.values(), confidence_scores.values()))
        return min(abs(weighted_confidence) * 100, 95)  # Cap at 95%

    async def generate_signals(self, symbol: str, timeframe: str) -> Optional[Dict]:
        """Generate trading signals with comprehensive analysis"""
        try:
            # Fetch and prepare data
            data = await self.fetch_historical_data(symbol, timeframe)
            features_df = self.create_features(data)
            
            if len(features_df) < 100:  # Minimum required data
                return None
                
            X = features_df.drop(['target'], axis=1).iloc[-1:]
            X_scaled = self.scalers['features'].transform(X)
            
            # Get predictions from all models
            predictions = {}
            confidence_scores = {}
            
            for name, model in self.models.items():
                if name != 'lstm':
                    pred = model.predict(X_scaled)[0]
                    predictions[name] = pred
                    confidence_scores[name] = abs(pred)
                else:
                    X_lstm = self.prepare_lstm_data(features_df.iloc[-60:], lookback=60)[0]
                    pred = model.predict(X_lstm)[-1][0]
                    predictions[name] = pred
                    confidence_scores[name] = abs(pred)
            
            # Calculate dynamic thresholds
            buy_threshold, sell_threshold = self.calculate_dynamic_thresholds(data, timeframe)
            
            # Calculate weighted prediction
            weights = {
                'random_forest': 0.2,
                'lightgbm': 0.25,
                'xgboost': 0.25,
                'gradient_boosting': 0.15,
                'lstm': 0.15
            }
            
            weighted_prediction = sum(pred * weights[name] for name, pred in predictions.items())
            
            # Calculate success rate
            success_rate = self.calculate_success_rate(predictions, confidence_scores)
            
            if success_rate < self.min_success_rate:
                return None
                
            # Determine signal type
            signal_type = 'neutral'
            if weighted_prediction > buy_threshold:
                signal_type = 'strong_buy' if weighted_prediction > 2 * buy_threshold else 'buy'
            elif weighted_prediction < sell_threshold:
                signal_type = 'strong_sell' if weighted_prediction < 2 * sell_threshold else 'sell'
            
            # Calculate support and resistance
            support, resistance = self.calculate_support_resistance(data)
            
            # Prepare signal details
            current_price = float(data['close'].iloc[-1])
            
            signal_details = {
                'symbol': symbol,
                'timeframe': timeframe,
                'timestamp': timezone.now(),
                'price_timestamp': data.index[-1],
                'price': current_price,
                'change_24h': float(data['close'].pct_change(periods=24).iloc[-1] * 100),
                'signal': signal_type,
                'stop_loss': support if signal_type in ['buy', 'strong_buy'] else resistance,
                'take_profit': resistance if signal_type in ['buy', 'strong_buy'] else support,
                'success_rate': success_rate,
                'model_predictions': predictions,
                'confidence_scores': confidence_scores,
                'weighted_prediction': weighted_prediction,
                'thresholds': {
                    'buy': buy_threshold,
                    'sell': sell_threshold
                }
            }
            
            return signal_details
            
        except Exception as e:
            logger.error(f"Error generating signal for {symbol} {timeframe}: {str(e)}")
            return None

    async def save_signal(self, signal_details: Dict):
        """Save signal to database"""
        try:
            Signal.objects.create(
                symbol=signal_details['symbol'],
                timeframe=signal_details['timeframe'],
                timestamp=signal_details['timestamp'],
                price_timestamp=signal_details['price_timestamp'],
                price=Decimal(str(signal_details['price'])),
                change_24h=Decimal(str(signal_details['change_24h'])),
                signal_type=signal_details['signal'],
                stop_loss=Decimal(str(signal_details['stop_loss'])),
                take_profit=Decimal(str(signal_details['take_profit'])),
                success_rate=Decimal(str(signal_details['success_rate'])),
                details=signal_details
            )
        except Exception as e:
            logger.error(f"Error saving signal: {str(e)}")

    async def run(self):
        """Main execution loop"""
        try:
            tasks = []
            for symbol in self.symbols:
                for timeframe in self.timeframes:
                    # Train models
                    data = await self.fetch_historical_data(symbol, timeframe)
                    await self.train_models(data, timeframe)
                    
                    # Generate and save signals
                    tasks.append(self.generate_signals(symbol, timeframe))
            
            signals = await asyncio.gather(*tasks)
            
            # Save valid signals
            save_tasks = []
            for signal in signals:
                if signal:
                    save_tasks.append(self.save_signal(signal))
            
            if save_tasks:
                await asyncio.gather(*tasks)
                
        except Exception as e:
            logger.error(f"Error in main execution loop: {str(e)}")

if __name__ == "__main__":
    symbols = ['BTC/USDT', 'ETH/USDT']  # Example symbols
    bot = EnhancedTradingBot(symbols)
    asyncio.run(bot.run())