Consider the following program, which is meant for stock price prediction , the various modules of the program are as below. The program is situated in a directory named stock_prediction, where all these files are located and it also contains one directory named models, which contains info about models, their python files and their prediction models are stored. Please go through the following so that we can improve the system further.

# gui.py
import sys
from PyQt5.QtWidgets import (
    QApplication, QWidget, QVBoxLayout, QLabel, QPushButton, 
    QFileDialog, QLineEdit, QMessageBox, QTextEdit, QComboBox, 
    QHBoxLayout
)
from PyQt5.QtCore import Qt, QThread, pyqtSignal
from main import multi_stock_prediction
from config import START_DATE, END_DATE, INTERVAL
from utils import setup_logging, log_message
import os
import pandas as pd  # Added for saving predictions
 
class WorkerThread(QThread):
    # Define custom signals
    progress = pyqtSignal(str)
    finished = pyqtSignal(dict)
    
    def __init__(self, stock_symbols, start_date, end_date, interval, train_mode):
        super().__init__()
        self.stock_symbols = stock_symbols
        self.start_date = start_date
        self.end_date = end_date
        self.interval = interval
        self.train_mode = train_mode
        self.predictions = {}
    
    def run(self):
        log_message("WorkerThread started.")
        self.predictions = multi_stock_prediction(
            self.stock_symbols, 
            self.start_date, 
            self.end_date, 
            self.interval, 
            self.train_mode,
            progress_callback=self.emit_progress
        )
        self.finished.emit(self.predictions)
        log_message("WorkerThread finished.")
    
    def emit_progress(self, message: str):
        self.progress.emit(message)

class StockPredictionApp(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Stock Prediction Application")
        self.setGeometry(100, 100, 800, 600)
        self.init_ui()
        self.worker = None

    def init_ui(self):
        layout = QVBoxLayout()
        
        # Mode Selection
        mode_layout = QHBoxLayout()
        mode_label = QLabel("Select Mode:")
        self.mode_combo = QComboBox()
        self.mode_combo.addItems(['TRAIN', 'USE'])
        mode_layout.addWidget(mode_label)
        mode_layout.addWidget(self.mode_combo)
        layout.addLayout(mode_layout)
        
        # Stock Input
        stock_layout = QHBoxLayout()
        stock_label = QLabel("Stock Symbols (comma-separated):")
        self.stock_input = QLineEdit()
        stock_layout.addWidget(stock_label)
        stock_layout.addWidget(self.stock_input)
        layout.addLayout(stock_layout)
        
        # Or Load from File
        file_layout = QHBoxLayout()
        load_button = QPushButton("Load Stocks from File")
        load_button.clicked.connect(self.load_stocks_from_file)
        file_layout.addWidget(load_button)
        layout.addLayout(file_layout)
        
        # Model Selection (Display)
        model_layout = QVBoxLayout()
        model_label = QLabel("Available Models and Parameters:")
        self.model_text = QTextEdit()
        self.model_text.setReadOnly(True)
        self.display_models()
        model_layout.addWidget(model_label)
        model_layout.addWidget(self.model_text)
        layout.addLayout(model_layout)
        
        # Start Button
        self.start_button = QPushButton("Start")
        self.start_button.clicked.connect(self.start_process)
        layout.addWidget(self.start_button)
        
        # Progress Display
        progress_label = QLabel("Progress:")
        self.progress_text = QTextEdit()
        self.progress_text.setReadOnly(True)
        layout.addWidget(progress_label)
        layout.addWidget(self.progress_text)
        
        # Prediction Output
        output_label = QLabel("Final Predictions:")
        self.output_text = QTextEdit()
        self.output_text.setReadOnly(True)
        layout.addWidget(output_label)
        layout.addWidget(self.output_text)
        
        # Set Layout
        self.setLayout(layout)
    
    def display_models(self):
        models_info = """
 Available Models:
 
 1. LSTM
    - Layers: 2 LSTM layers (50 units each), 2 Dense layers
    - Optimizer: Adam
    - Loss: Mean Squared Error
    - Epochs: 100
    - Batch Size: 64
    
 2. XGBoost
    - n_estimators: 100
    - learning_rate: 0.1
    - max_depth: 5
    
 3. Random Forest
    - n_estimators: 100
    - max_depth: None
    - min_samples_split: 2
    
 4. Linear Regression
    - No hyperparameters
 """
        self.model_text.setText(models_info)
    
    def load_stocks_from_file(self):
        options = QFileDialog.Options()
        filename, _ = QFileDialog.getOpenFileName(self, "Select Stock List File", "", "Text Files (*.txt);;All Files (*)", options=options)
        if filename:
            try:
                with open(filename, 'r') as file:
                    stocks = file.read().splitlines()
                self.stock_input.setText(','.join(stocks))
                log_message(f"Loaded stocks from file: {filename}")
            except Exception as e:
                QMessageBox.critical(self, "Error", f"Failed to load file: {e}")
    
    def start_process(self):
        stock_text = self.stock_input.text().strip()
        if not stock_text:
            QMessageBox.warning(self, "Input Error", "Please enter at least one stock symbol.")
            return
        stock_symbols = [s.strip() for s in stock_text.split(',') if s.strip()]
        if not stock_symbols:
            QMessageBox.warning(self, "Input Error", "Please enter valid stock symbols.")
            return
        
        mode = self.mode_combo.currentText().lower()
        if mode not in ['train', 'use']:
            QMessageBox.warning(self, "Input Error", "Invalid mode selected.")
            return
        
        # Disable UI elements during processing
        self.start_button.setDisabled(True)
        self.progress_text.clear()
        self.output_text.clear()
        
        # Start Worker Thread
        self.worker = WorkerThread(stock_symbols, START_DATE, END_DATE, INTERVAL, mode)
        self.worker.progress.connect(self.update_progress)
        self.worker.finished.connect(self.process_finished)
        self.worker.start()
        log_message("Started processing in WorkerThread.")
    
    def update_progress(self, message: str):
        self.progress_text.append(message)
    
    def process_finished(self, predictions: dict):
        for stock, pred in predictions.items():
            self.output_text.append(f"{stock}: {pred}")
        # Save predictions to CSV
        if predictions:
            pred_df = pd.DataFrame(list(predictions.items()), columns=['Stock', 'Prediction'])
            pred_df.to_csv(os.path.join('models', 'predictions.csv'), index=False)
            log_message("Saved predictions to predictions.csv")
        self.start_button.setDisabled(False)
        log_message("Processing completed.")

if __name__ == "__main__":
    # Initialize logging before launching the GUI
    from utils import setup_logging
    setup_logging()
    
    app = QApplication(sys.argv)
    window = StockPredictionApp()
    window.show()
    sys.exit(app.exec_())

# main.py
import os
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from utils import setup_logging, log_message, set_seeds
from preprocess import preprocess_data
from data_fetcher import fetch_stock_data
from models.lstm import train_lstm_model
from models.xgboost_model import train_xgboost_model
from models.random_forest import train_random_forest_model
from models.linear_regression import train_linear_regression_model
from config import MODEL_DIR
from sklearn.metrics import mean_squared_error
import matplotlib.pyplot as plt
from typing import List, Dict, Tuple, Optional
from concurrent.futures import ThreadPoolExecutor, as_completed
from xgboost import XGBRegressor

def load_pretrained_models(stock_symbol: str, progress_callback=None) -> List[Tuple]:
    """
    Loads pre-trained models for a given stock symbol.

    Parameters:
        stock_symbol (str): The ticker symbol of the stock.
        progress_callback (callable): Function to send progress updates.

    Returns:
        List[Tuple]: List of tuples containing models and their names.
    """
    models = []
    try:
        # Load LSTM Model
        lstm_path = os.path.join(MODEL_DIR, f'{stock_symbol}_lstm_model.h5')
        if os.path.exists(lstm_path):
            import tensorflow as tf
            lstm_model = tf.keras.models.load_model(lstm_path)
            models.append((lstm_model, 'lstm'))
            if progress_callback:
                progress_callback(f"LSTM model loaded for {stock_symbol}.")
        else:
            if progress_callback:
                progress_callback(f"LSTM model not found for {stock_symbol}.")

        # Load XGBoost Model
        xgb_path = os.path.join(MODEL_DIR, f'{stock_symbol}_xgboost_model.json')
        if os.path.exists(xgb_path):
            xgb_model = XGBRegressor()
            xgb_model.load_model(xgb_path)
            models.append((xgb_model, 'xgboost'))
            if progress_callback:
                progress_callback(f"XGBoost model loaded for {stock_symbol}.")
        else:
            if progress_callback:
                progress_callback(f"XGBoost model not found for {stock_symbol}.")

        # Load Random Forest Model
        rf_path = os.path.join(MODEL_DIR, f'{stock_symbol}_random_forest_model.pkl')
        if os.path.exists(rf_path):
            rf_model = pd.read_pickle(rf_path)
            models.append((rf_model, 'random_forest'))
            if progress_callback:
                progress_callback(f"Random Forest model loaded for {stock_symbol}.")
        else:
            if progress_callback:
                progress_callback(f"Random Forest model not found for {stock_symbol}.")

        # Load Linear Regression Model
        lr_path = os.path.join(MODEL_DIR, f'{stock_symbol}_linear_regression_model.pkl')
        if os.path.exists(lr_path):
            lr_model = pd.read_pickle(lr_path)
            models.append((lr_model, 'linear_regression'))
            if progress_callback:
                progress_callback(f"Linear Regression model loaded for {stock_symbol}.")
        else:
            if progress_callback:
                progress_callback(f"Linear Regression model not found for {stock_symbol}.")
    except Exception as e:
        if progress_callback:
            progress_callback(f"Error loading models for {stock_symbol}: {e}")

    return models

def evaluate_model(model, X_test, y_test, model_name: str, stock_symbol: str, progress_callback=None) -> Optional[np.ndarray]:
    """
    Evaluates a given model and returns predictions.

    Parameters:
        model: Trained model.
        X_test (array-like): Test features.
        y_test (array-like): Test target.
        model_name (str): Name of the model.
        stock_symbol (str): Stock symbol.
        progress_callback (callable): Function to send progress updates.

    Returns:
        np.ndarray: Predictions.
    """
    try:
        if model_name == 'lstm':
            y_pred = model.predict(X_test).flatten()
        else:
            y_pred = model.predict(X_test)
        mse = mean_squared_error(y_test, y_pred)
        if progress_callback:
            progress_callback(f"{model_name.upper()} Evaluation for {stock_symbol} - MSE: {mse}")
        return y_pred
    except Exception as e:
        if progress_callback:
            progress_callback(f"Error evaluating {model_name} for {stock_symbol}: {e}")
        return None

def weighted_average_prediction(models: List[Tuple], X_test, y_test, stock_symbol: str, progress_callback=None) -> Optional[np.ndarray]:
    """
    Calculates a weighted average of predictions from multiple models based on inverse MSE.

    Parameters:
        models (List[Tuple]): List of tuples containing models and their names.
        X_test (array-like): Test features.
        y_test (array-like): Test target.
        stock_symbol (str): Stock symbol.
        progress_callback (callable): Function to send progress updates.

    Returns:
        np.ndarray: Weighted average predictions.
    """
    predictions = []
    weights = []

    for model, model_name in models:
        try:
            if model_name == 'lstm':
                y_pred = model.predict(X_test).flatten()
            else:
                y_pred = model.predict(X_test)
            mse = mean_squared_error(y_test, y_pred)
            weight = 1 / mse if mse != 0 else 0
            predictions.append(y_pred * weight)
            weights.append(weight)
            if progress_callback:
                progress_callback(f"Model: {model_name}, MSE: {mse}, Weight: {weight}")
        except Exception as e:
            if progress_callback:
                progress_callback(f"Error predicting with {model_name}: {e}")

    if not weights:
        if progress_callback:
            progress_callback("No valid model predictions available.")
        return None

    weighted_sum = np.sum(predictions, axis=0)
    total_weight = np.sum(weights)
    final_prediction = weighted_sum / total_weight if total_weight != 0 else None

    return final_prediction

def visualize_predictions(stock_symbol: str, actual: np.ndarray, predicted: np.ndarray, save: bool = False, progress_callback=None):
    """
    Plots actual vs predicted stock prices.

    Parameters:
        stock_symbol (str): The ticker symbol of the stock.
        actual (np.ndarray): Actual closing prices.
        predicted (np.ndarray): Predicted closing prices.
        save (bool): Whether to save the plot as an image.
        progress_callback (callable): Function to send progress updates.
    """
    try:
        plt.figure(figsize=(14, 7))
        plt.plot(actual, color='blue', label='Actual Prices')
        plt.plot(predicted, color='red', linestyle='--', label='Predicted Prices')
        plt.title(f'{stock_symbol} Actual vs Predicted Prices')
        plt.xlabel('Time')
        plt.ylabel('Stock Price')
        plt.legend()
        plt.grid(True)

        if save:
            plot_path = os.path.join(MODEL_DIR, f'{stock_symbol}_prediction.png')
            plt.savefig(plot_path)
            log_message(f"Saved plot for {stock_symbol} at {plot_path}")
            if progress_callback:
                progress_callback(f"Saved plot for {stock_symbol} at {plot_path}")

        plt.close()
    except Exception as e:
        if progress_callback:
            progress_callback(f"Error visualizing predictions for {stock_symbol}: {e}")

def process_stock(stock_symbol: str, start_date: str, end_date: str, interval: str, train_mode: str, progress_callback=None) -> Optional[float]:
    """
    Processes a single stock: fetches data, preprocesses, trains/loads models, makes predictions, and visualizes results.

    Parameters:
        stock_symbol (str): The ticker symbol of the stock.
        start_date (str): Start date for data fetching.
        end_date (str): End date for data fetching.
        interval (str): Data interval.
        train_mode (str): Mode - 'train' or 'use'.
        progress_callback (callable): Function to send progress updates.

    Returns:
        Optional[float]: Final prediction for the stock, or None if failed.
    """
    progress_callback(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:
        progress_callback(f"No data found for {stock_symbol}. Skipping...")
        return None

    stock_data, scaler = preprocess_data(stock_data)
    X = stock_data[['SMA', 'EMA', 'RSI', 'MACD', 'Bollinger High', 'Bollinger Low', 'ATR']].values
    y = stock_data['Close'].values

    # Split into training and testing
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, shuffle=False)

    # Reshape for LSTM
    X_train_lstm = X_train.reshape((X_train.shape[0], X_train.shape[1], 1))
    X_test_lstm = X_test.reshape((X_test.shape[0], X_test.shape[1], 1))

    models = []

    if train_mode == 'train':
        # Train LSTM Model
        if progress_callback:
            progress_callback("Training LSTM Model...")
        lstm_model, lstm_mse = train_lstm_model(X_train_lstm, y_train, X_test_lstm, y_test, stock_symbol)
        if lstm_model:
            models.append((lstm_model, 'lstm'))

        # Train XGBoost Model
        if progress_callback:
            progress_callback("Training XGBoost Model...")
        xgb_model, xgb_mse = train_xgboost_model(X_train, y_train, X_test, y_test, stock_symbol)
        if xgb_model:
            models.append((xgb_model, 'xgboost'))

        # Train Random Forest Model
        if progress_callback:
            progress_callback("Training Random Forest Model...")
        rf_model, rf_mse = train_random_forest_model(X_train, y_train, X_test, y_test, stock_symbol)
        if rf_model:
            models.append((rf_model, 'random_forest'))

        # Train Linear Regression Model
        if progress_callback:
            progress_callback("Training Linear Regression Model...")
        lr_model, lr_mse = train_linear_regression_model(X_train, y_train, stock_symbol)
        if lr_model:
            models.append((lr_model, 'linear_regression'))

        progress_callback(f"All models trained for {stock_symbol}.")
    else:
        # Load pre-trained models
        if progress_callback:
            progress_callback("Loading pre-trained models...")
        models = load_pretrained_models(stock_symbol, progress_callback)
        if not models:
            progress_callback(f"No pre-trained models available for {stock_symbol}. Skipping prediction.")
            return None
        progress_callback(f"Pre-trained models loaded for {stock_symbol}.")

    # Make Predictions
    if not models:
        progress_callback(f"No models available for {stock_symbol}. Skipping prediction.")
        return None

    # Make predictions and compute weighted average
    progress_callback("Making Predictions...")
    final_prediction = weighted_average_prediction(models, X_test, y_test, stock_symbol, progress_callback)

    if final_prediction is not None:
        final_pred_value = final_prediction[-1]
        progress_callback(f"Final Weighted Prediction for {stock_symbol}: {final_pred_value}")
        # Visualize Predictions
        progress_callback("Visualizing Predictions...")
        visualize_predictions(stock_symbol, y_test, final_prediction, save=True, progress_callback=progress_callback)
        return final_pred_value
    else:
        progress_callback(f"Failed to compute final prediction for {stock_symbol}.")
        return None

def multi_stock_prediction(stock_symbols: List[str], start_date: str, end_date: str, interval: str, train_mode: str, progress_callback=None) -> Dict[str, float]:
    """
    Processes multiple stocks either by training models or using pre-trained models.

    Parameters:
        stock_symbols (List[str]): List of stock ticker symbols.
        start_date (str): Start date for data fetching.
        end_date (str): End date for data fetching.
        interval (str): Data interval.
        train_mode (str): Mode - 'train' or 'use'.
        progress_callback (callable): Function to send progress updates.

    Returns:
        Dict[str, float]: Final predictions per stock.
    """
    predictions = {}
    for stock in stock_symbols:
        pred = process_stock(stock, start_date, end_date, interval, train_mode, progress_callback)
        if pred is not None:
            predictions[stock] = pred
    return predictions

# data_fetcher.py
import yfinance as yf
import pandas as pd
from typing import Optional
from utils import log_message

def fetch_stock_data(stock_symbol: str, start_date: str, end_date: str, interval: str) -> Optional[pd.DataFrame]:
    """
    Fetches stock data for a given symbol between start_date and end_date with the specified interval.

    Parameters:
        stock_symbol (str): The ticker symbol of the stock.
        start_date (str): The start date in 'YYYY-MM-DD' format.
        end_date (str): The end date in 'YYYY-MM-DD' format.
        interval (str): Data interval (e.g., '1d', '5m').

    Returns:
        pd.DataFrame: DataFrame containing stock data or None if fetch fails.
    """
    log_message(f"Fetching data for {stock_symbol}")
    try:
        # Remove the dtype parameter as it's not supported by yfinance's download()
        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.reset_index(inplace=True)
        return stock_data
    except Exception as e:
        log_message(f"Error fetching data for {stock_symbol}: {e}")
        return None

# preprocess.py
import pandas as pd
import numpy as np
from sklearn.preprocessing import StandardScaler
from typing import Tuple
from utils import log_message

def calculate_rsi(prices: pd.Series, n: int = 14) -> pd.Series:
    """
    Calculates the Relative Strength Index (RSI).

    Parameters:
        prices (pd.Series): Series of closing prices.
        n (int): Window size.

    Returns:
        pd.Series: RSI values.
    """
    delta = prices.diff()
    gain = delta.clip(lower=0)
    loss = -delta.clip(upper=0)
    avg_gain = gain.rolling(window=n, min_periods=1).mean()
    avg_loss = loss.rolling(window=n, min_periods=1).mean()
    rs = avg_gain / avg_loss
    rsi = 100 - (100 / (1 + rs))
    return rsi

def calculate_macd(prices: pd.Series, slow: int = 26, fast: int = 12, signal: int = 9) -> pd.Series:
    """
    Calculates the Moving Average Convergence Divergence (MACD).

    Parameters:
        prices (pd.Series): Series of closing prices.
        slow (int): Slow EMA period.
        fast (int): Fast EMA period.
        signal (int): Signal line period.

    Returns:
        pd.Series: MACD histogram values.
    """
    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: pd.Series, n: int = 20) -> Tuple[pd.Series, pd.Series]:
    """
    Calculates Bollinger Bands.

    Parameters:
        prices (pd.Series): Series of closing prices.
        n (int): Window size.

    Returns:
        Tuple[pd.Series, pd.Series]: Upper and lower Bollinger Bands.
    """
    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: pd.DataFrame, n: int = 14) -> pd.Series:
    """
    Calculates the Average True Range (ATR).

    Parameters:
        stock_data (pd.DataFrame): DataFrame containing 'High', 'Low', and 'Close' columns.
        n (int): Window size.

    Returns:
        pd.Series: ATR values.
    """
    high_low = stock_data['High'] - stock_data['Low']
    high_close = (stock_data['High'] - stock_data['Close'].shift()).abs()
    low_close = (stock_data['Low'] - stock_data['Close'].shift()).abs()
    ranges = pd.concat([high_low, high_close, low_close], axis=1)
    true_range = ranges.max(axis=1)
    atr = true_range.rolling(window=n, min_periods=1).mean()
    return atr

def preprocess_data(stock_data: pd.DataFrame) -> Tuple[pd.DataFrame, StandardScaler]:
    """
    Preprocesses stock data by calculating technical indicators and scaling features.

    Parameters:
        stock_data (pd.DataFrame): Raw stock data.

    Returns:
        Tuple[pd.DataFrame, StandardScaler]: Preprocessed data and the scaler object.
    """
    log_message("Preprocessing data...")
    stock_data['SMA'] = stock_data['Close'].rolling(window=14, min_periods=1).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)
    
    # Feature Scaling
    scaler = StandardScaler()
    feature_cols = ['SMA', 'EMA', 'RSI', 'MACD', 'Bollinger High', 'Bollinger Low', 'ATR']
    stock_data[feature_cols] = scaler.fit_transform(stock_data[feature_cols])
    
    log_message("Preprocessing completed.")
    return stock_data, scaler

# utils.py
import logging
from config import LOG_FILE
import random
import numpy as np
import tensorflow as tf

def setup_logging():
    """
    Sets up logging to both console and a file with appropriate formatting.
    """
    logger = logging.getLogger()
    logger.setLevel(logging.INFO)

    # Prevent adding multiple handlers in interactive environments
    if not logger.handlers:
        # Formatter
        formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')

        # File Handler
        file_handler = logging.FileHandler(LOG_FILE)
        file_handler.setLevel(logging.INFO)
        file_handler.setFormatter(formatter)

        # Console Handler
        console_handler = logging.StreamHandler()
        console_handler.setLevel(logging.INFO)
        console_handler.setFormatter(formatter)

        # Add Handlers
        logger.addHandler(file_handler)
        logger.addHandler(console_handler)

def log_message(message: str):
    """
    Logs a message at the INFO level.
    """
    logging.info(message)

def set_seeds(seed: int = 42):
    """
    Sets random seeds for reproducibility.
    """
    np.random.seed(seed)
    tf.random.set_seed(seed)
    random.seed(seed)

# config.py
import os

# Base Directory
BASE_DIR = os.path.dirname(os.path.abspath(__file__))

# Directories
MODEL_DIR = os.path.join(BASE_DIR, 'models')
LOG_FILE = os.path.join(BASE_DIR, 'app.log')

# Stock Parameters
DEFAULT_STOCK_SYMBOLS = ['HAL.NS', 'MAZDOCK.NS', 'MARUTI.NS']
START_DATE = '2023-09-26'  
END_DATE = '2024-09-25'    #'2023-12-31'
INTERVAL = '1d'  # Options: '1d', '5m', etc.
TRAIN_MODE = 'train'  # Options: 'train', 'use'

# Model Hyperparameters
LSTM_EPOCHS = 100
LSTM_BATCH_SIZE = 64
XGBOOST_PARAMS = {
    'n_estimators': 100,
    'learning_rate': 0.1,
    'max_depth': 5
}
RANDOM_FOREST_PARAMS = {
    'n_estimators': 100,
    'max_depth': None,
    'min_samples_split': 2
}