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

Analyze the code above and tell me a rough estimate of the accuracy of its signals in percentage based on features and engineering. What can I do to increase the accuracy of the signals