consider the code and help it improve :
import os
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import tensorflow as tf
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error
from xgboost import XGBRegressor
from sklearn.ensemble import RandomForestRegressor
from sklearn.linear_model import LinearRegression
import yfinance as yf
import logging

# Define constants
MODEL_DIR = 'c:/users/swarn/python_codes/python_try/hugging/lstm/models/'



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

def log_message(message):
    logging.info(message)

# Function to fetch stock data
def fetch_stock_data(stock_symbol, start_date, end_date, interval):
    log_message(f"Fetching data for {stock_symbol}")
    try:
        stock_data = yf.download(stock_symbol, start=start_date, end=end_date, interval=interval)
        if stock_data.empty:
            log_message(f"No data found for {stock_symbol}")
            return None
        stock_data['Date'] = stock_data.index
        return stock_data
    except Exception as e:
        log_message(f"Error fetching data: {str(e)}")
        return None

# Function to preprocess data and calculate technical indicators
def preprocess_data(stock_data):
    stock_data['SMA'] = stock_data['Close'].rolling(window=14).mean()
    stock_data['EMA'] = stock_data['Close'].ewm(span=14, adjust=False).mean()
    stock_data['RSI'] = calculate_rsi(stock_data['Close'])
    stock_data['MACD'] = calculate_macd(stock_data['Close'])
    stock_data['Bollinger High'], stock_data['Bollinger Low'] = calculate_bollinger_bands(stock_data['Close'])
    stock_data['ATR'] = calculate_atr(stock_data)
    stock_data.dropna(inplace=True)
    return stock_data

# Technical indicators calculation functions (add more as needed)
def calculate_rsi(prices, n=14):
    delta = prices.diff()
    gain = np.where(delta > 0, delta, 0)
    loss = np.where(delta < 0, -delta, 0)
    avg_gain = pd.Series(gain).rolling(window=n).mean()
    avg_loss = pd.Series(loss).rolling(window=n).mean()
    rs = avg_gain / avg_loss
    return 100 - (100 / (1 + rs))

def calculate_macd(prices, slow=26, fast=12, signal=9):
    exp1 = prices.ewm(span=fast, adjust=False).mean()
    exp2 = prices.ewm(span=slow, adjust=False).mean()
    macd = exp1 - exp2
    signal_line = macd.ewm(span=signal, adjust=False).mean()
    return macd - signal_line

def calculate_bollinger_bands(prices, n=20):
    sma = prices.rolling(window=n).mean()
    std = prices.rolling(window=n).std()
    bollinger_high = sma + (std * 2)
    bollinger_low = sma - (std * 2)
    return bollinger_high, bollinger_low

def calculate_atr(stock_data, n=14):
    high_low = stock_data['High'] - stock_data['Low']
    high_close = np.abs(stock_data['High'] - stock_data['Close'].shift())
    low_close = np.abs(stock_data['Low'] - stock_data['Close'].shift())
    ranges = pd.concat([high_low, high_close, low_close], axis=1)
    true_range = ranges.max(axis=1)
    return true_range.rolling(window=n).mean()

# Function to train models
def train_lstm_model(X_train, y_train, X_test, y_test, stock_symbol):
    model = tf.keras.Sequential([
        tf.keras.layers.LSTM(50, return_sequences=True, input_shape=(X_train.shape[1], 1)),
        tf.keras.layers.LSTM(50, return_sequences=False),
        tf.keras.layers.Dense(25),
        tf.keras.layers.Dense(1)
    ])
    model.compile(optimizer='adam', loss='mean_squared_error')
    model.fit(X_train, y_train, epochs=10, batch_size=64, verbose=1)
    model.save(os.path.join(MODEL_DIR, f'{stock_symbol}_lstm_model.h5'))
    return model, model.evaluate(X_test, y_test)

def train_xgboost_model(X_train, y_train, X_test, y_test, stock_symbol):
    model = XGBRegressor(n_estimators=100, learning_rate=0.1, max_depth=5)
    model.fit(X_train, y_train)
    model.save_model(os.path.join(MODEL_DIR, f'{stock_symbol}_xgboost_model.json'))
    return model

def train_random_forest_model(X_train, y_train, X_test, y_test, stock_symbol):
    model = RandomForestRegressor(n_estimators=100)
    model.fit(X_train, y_train)
    pd.to_pickle(model, os.path.join(MODEL_DIR, f'{stock_symbol}_random_forest_model.pkl'))
    return model

def train_linear_regression_model(X_train, y_train, stock_symbol):
    model = LinearRegression()
    model.fit(X_train, y_train)
    pd.to_pickle(model, os.path.join(MODEL_DIR, f'{stock_symbol}_linear_regression_model.pkl'))
    return model
###############################################################################################

# Function to calculate weighted average prediction
def weighted_average_prediction(models, X_test, y_test):
    predictions = np.zeros(X_test.shape[0])
    total_weight = 0
    for model, model_name in models:
        y_pred = model.predict(X_test)
        weight = 1 / mean_squared_error(y_test, y_pred, squared=False)
        predictions += y_pred * weight
        total_weight += weight
    return predictions / total_weight



def multi_stock_prediction(stock_symbols, start_date, end_date, interval, train_mode):
    predictions = {}
    for stock_symbol in stock_symbols:
        log_message(f"Processing stock: {stock_symbol}")
        stock_data = fetch_stock_data(stock_symbol, start_date, end_date, interval)
        
        if stock_data is None or stock_data.empty:
            log_message(f"No data found for {stock_symbol}. Skipping...")
            continue
        
        log_message(f"Data fetched for {stock_symbol}: {stock_data.head()}")
        stock_data = preprocess_data(stock_data)
        log_message(f"Data after preprocessing for {stock_symbol}: {stock_data.head()}")
        
        X = stock_data[['SMA', 'EMA', 'RSI', 'MACD', 'Bollinger High', 'Bollinger Low', 'ATR']].values
        y = stock_data['Close'].values
        
        X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, shuffle=False)
        log_message(f"Split data for {stock_symbol} into train and test sets.")
        
        if train_mode == 'train':
            lstm_model, _ = train_lstm_model(X_train, y_train, X_test, y_test, stock_symbol)
            xgboost_model = train_xgboost_model(X_train, y_train, X_test, y_test, stock_symbol)
            rf_model = train_random_forest_model(X_train, y_train, X_test, y_test, stock_symbol)
            lr_model = train_linear_regression_model(X_train, y_train, stock_symbol)
            models = [(lstm_model, 'lstm'), (xgboost_model, 'xgboost'), (rf_model, 'random_forest'), (lr_model, 'linear_regression')]
            log_message(f"Models trained for {stock_symbol}.")
        else:
            models = load_pretrained_models(stock_symbol)
            log_message(f"Loaded pretrained models for {stock_symbol}.")

        weighted_pred = weighted_average_prediction(models, X_test, y_test)
        predictions[stock_symbol] = weighted_pred[-1]
        log_message(f"Prediction for {stock_symbol}: {weighted_pred[-1]}")
        
    return predictions


# Function to visualize predictions
def visualize_predictions(stock_symbol, actual, predicted):
    plt.figure(figsize=(10, 6))
    plt.plot(actual, color='blue', label='Actual Prices')
    plt.plot(predicted, color='red', label='Predicted Prices')
    plt.title(f'{stock_symbol} Actual vs Predicted Prices')
    plt.xlabel('Time')
    plt.ylabel('Stock Price')
    plt.legend()
    plt.show()

# Function to send real-time alerts
def real_time_alert(stock_symbol, actual_price, predicted_price, threshold=0.05):
    deviation = abs((actual_price - predicted_price) / predicted_price)
    if deviation > threshold:
        log_message(f"ALERT: Significant deviation for {stock_symbol}: Actual: {actual_price}, Predicted: {predicted_price}")

# Function to load pre-trained models
def load_pretrained_models(stock_symbol):
    models = []
    lstm_model = tf.keras.models.load_model(os.path.join(MODEL_DIR, f'{stock_symbol}_lstm_model.h5'))
    xgboost_model = XGBRegressor()
    xgboost_model.load_model(os.path.join(MODEL_DIR, f'{stock_symbol}_xgboost_model.json'))
    rf_model = pd.read_pickle(os.path.join(MODEL_DIR, f'{stock_symbol}_random_forest_model.pkl'))
    lr_model = pd.read_pickle(os.path.join(MODEL_DIR, f'{stock_symbol}_linear_regression_model.pkl'))
    models.append((lstm_model, 'lstm'))
    models.append((xgboost_model, 'xgboost'))
    models.append((rf_model, 'random_forest'))
    models.append((lr_model, 'linear_regression'))
    return models

def main():
    # Example stock symbols, replace with actual user input as needed
    stock_symbols = ['HAL.NS', 'MAZDOCK.NS', 'MARUTI.NS']
    start_date = '2022-01-01'
    end_date = '2023-12-31'
    interval = '1d'  # or '5m' for intraday
    train_mode = 'train'  # or 'use' for using pre-trained models

    predictions = multi_stock_prediction(stock_symbols, start_date, end_date, interval, train_mode)
    log_message(f"Final Predictions: {predictions}")

if __name__ == "__main__":
    main()