consider this code . what is meant by time horizon here ? does it make prediction on the no. of day we have entered or within that no. of days ? because whatever i choose during 'use' of models, it predicts the same result :"
import sys
import yfinance as yf
import pandas as pd
import numpy as npUSE

import pandas_ta as ta  # Import pandas_ta
from PyQt5.QtWidgets import (
    QApplication, QWidget, QVBoxLayout, QHBoxLayout,
    QLabel, QPushButton, QLineEdit, QTableWidget, QTableWidgetItem,
    QHeaderView, QAbstractItemView, QMessageBox, QCheckBox
)
from PyQt5.QtGui import QIcon, QFont
from PyQt5.QtCore import Qt


nfo_stocks = [
#small_list =[
    # *************** COMPLETE NFO STOCK LIST ************* #
    "NSE:AARTIDRUGS",
    "NSE:AARTIIND",
    "NSE:AARTIPHARM","NSE:ABB",

#]
#list_1 = [ 
    "NSE:ABBOTINDIA",
    "NSE:ABCAPITAL",
    "NSE:ABFRL",
    "NSE:ACC","NSE:ADANIENT",
    "NSE:ADANIPORTS","NSE:ALKEM",
    "NSE:AMBUJACEM","NSE:APOLLOHOSP","NSE:APOLLOTYRE","NSE:ASHOKLEY",

#]

#new_list = [
    "NSE:ASIANPAINT",
    "NSE:ASTRAL",
    "NSE:ATUL","NSE:AUBANK","NSE:AUROPHARMA","NSE:AXISBANK",
    "NSE:BAJAJFINSV","NSE:BAJFINANCE","NSE:BALRAMCHIN","NSE:BANDHANBNK",
    "NSE:BANKBARODA","NSE:BATAINDIA","NSE:BEL","NSE:BERGEPAINT","NSE:BHARATFORG","NSE:BHARTIARTL","NSE:BHEL","NSE:BIOCON",
    "NSE:BOSCHLTD",
    "NSE:BPCL","NSE:BRITANNIA","NSE:BSOFT","NSE:CANBK","NSE:CANFINHOME","NSE:CHAMBLFERT","NSE:CHOLAHLDNG","NSE:CIPLA","NSE:COALINDIA",
    "NSE:COFORGE","NSE:COLPAL","NSE:CONCOR","NSE:COROMANDEL","NSE:CROMPTON","NSE:CUB","NSE:CUMMINSIND","NSE:DABUR","NSE:DALBHARAT","NSE:DEEPAKNTR",
    "NSE:DELTACORP","NSE:DIVISLAB","NSE:DIXON","NSE:DLF","NSE:DRREDDY","NSE:EICHERMOT","NSE:EXIDEIND","NSE:FEDERALBNK","NSE:FSL","NSE:GAIL",
    "NSE:GLENMARK","NSE:GMRINFRA","NSE:GNFC","NSE:GODREJCP","NSE:GODREJPROP","NSE:GRANULES","NSE:GRASIM","NSE:GUJGASLTD","NSE:HAL",
    "NSE:HAVELLS","NSE:HCLTECH","NSE:HDFCAMC","NSE:HDFCBANK","NSE:HDFCLIFE","NSE:HEROMOTOCO","NSE:HINDALCO","NSE:HINDCOPPER","NSE:HINDPETRO",
    "NSE:HINDUNILVR","NSE:HONAUT",
    "NSE:ICICIBANK","NSE:ICICIGI",
    "NSE:ICICIGOLD",
    "NSE:IDEA",
    "NSE:IDFC","NSE:IDFCFIRSTB",
    "NSE:IGL","NSE:INDHOTEL","NSE:INDIACEM",
    "NSE:INDIAMART",
    "NSE:INDIGO","NSE:INDUSINDBK","NSE:INDUSTOWER","NSE:INFY","NSE:INTELLECT","NSE:IOC",
    "NSE:IPCALAB","NSE:IRCTC","NSE:ITC","NSE:JINDALSTEL","NSE:JKCEMENT","NSE:JSWSTEEL","NSE:JUBLFOOD","NSE:KOTAKBANK",
    "NSE:LALPATHLAB",
    "NSE:LAURUSLABS","NSE:LICHSGFIN","NSE:LT","NSE:LTIM","NSE:LTTS","NSE:LUPIN","NSE:M&M","NSE:M&MFIN","NSE:MANAPPURAM","NSE:MARICO","NSE:MARUTI",
    "NSE:UNITDSPR",
    "NSE:MCX","NSE:METROPOLIS","NSE:MFSL","NSE:MGL","NSE:MOTHERSON","NSE:MPHASIS","NSE:MRF","NSE:MUTHOOTFIN","NSE:NATIONALUM",
    "NSE:NAUKRI","NSE:NAVINFLUOR",
    "NSE:NESTLEIND",
    "NSE:NMDC","NSE:NTPC","NSE:OBEROIRLTY","NSE:OFSS","NSE:ONGC",
    
    
    "NSE:PAGEIND",
    "NSE:PEL",
    "NSE:PERSISTENT","NSE:PETRONET","NSE:PFC","NSE:PIDILITIND","NSE:PIIND","NSE:PNB","NSE:POLYCAB","NSE:POWERGRID","NSE:PVRINOX","NSE:RAIN",
    "NSE:RAMCOCEM","NSE:RBLBANK","NSE:RECLTD","NSE:RELIANCE","NSE:SAIL","NSE:SBICARD","NSE:SBILIFE","NSE:SBIN","NSE:SHREECEM","NSE:SIEMENS",
    "NSE:SRF","NSE:SUNPHARMA","NSE:SUNTV","NSE:SYNGENE","NSE:TATACHEM","NSE:TATACOMM","NSE:TATACONSUM","NSE:TATAMOTORS","NSE:TATAPOWER",
    "NSE:TATASTEEL","NSE:TCS","NSE:TECHM","NSE:TITAN","NSE:TORNTPHARM",
    "NSE:TRENT",
    "NSE:TVSMOTOR","NSE:UBL","NSE:ULTRACEMCO",
    "NSE:UPL","NSE:VEDL","NSE:WIPRO","NSE:ZEEL","NSE:ZYDUSLIFE",

    "NSE:MAZDOCK","NSE:CHENNPETRO","NSE:COCHINSHIP","NSE:POCL","NSE:PGEL","NSE:WEBELSOLAR","NSE:BALKRISIND",
    "NSE:BSE","NSE:DEEPINDS","NSE:FOCUS","NSE:IEX","NSE:NIITLTD","NSE:NAVA","NSE:KAMOPAINTS","NSE:PANACEABIO","BSE:TIGERLOGS",
    "NSE:THERMAX","NSE:CHALET","NSE:MURUDCERA","NSE:MANAKCOAT","NSE:RPOWER","BSE:PROTEAN","NSE:GRAVITA","BSE:MANORG"

]

#Below is the updated code:

import pandas as pd
import numpy as np
import yfinance as yf
import matplotlib.pyplot as plt
import datetime
# datetime import datetime

import sys
import os

from sklearn.preprocessing import LabelEncoder, StandardScaler
from sklearn.model_selection import train_test_split, GridSearchCV, KFold
from sklearn.metrics import (
    mean_absolute_error, mean_squared_error, r2_score
)
from sklearn.ensemble import RandomForestRegressor
from sklearn.linear_model import LinearRegression
from sklearn.svm import SVR

import xgboost as xgb

import json

# For progress bar
from tqdm import tqdm

# For audible alerts
import time
import threading

# Import TensorFlow and Keras
import tensorflow as tf
from tensorflow import keras
from keras.layers import Dense
from keras.optimizers import Adam
from keras.models import Sequential

from scikeras.wrappers import KerasRegressor


class NumpyEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, (np.integer, np.int32, np.int64)):
            return int(obj)
        elif isinstance(obj, (np.float32, np.float64)):
            return float(obj)
        elif isinstance(obj, (np.ndarray,)):
            return obj.tolist()
        elif callable(obj):  # Handle function objects
            return str(obj)
        else:
            return super(NumpyEncoder, self).default(obj)


def beep():
    duration = 0.2  # seconds
    freq = 440  # Hz
    if sys.platform == 'win32':
        import winsound
        winsound.Beep(freq, int(duration * 1000))
    else:
        # Beep not available on all systems, alternative is to print bell character
        print('\a', end='')


def play_sound_async():
    threading.Thread(target=beep).start()


def transform_stock_name(name):
    # Remove the prefix 'NSE:' or 'BSE:' and add appropriate suffix
    if 'NSE:' in name:
        name = name.split('NSE:')[1]
        if '-' in name:
            name = name.split('-')[0]
        name += '.NS'
    elif 'BSE:' in name:
        name = name.split('BSE:')[1]
        name += '.BO'
    else:
        # Assume it's a ticker symbol and append '.NS' (adjust as needed)
        name += '.NS'
    return name


def fetch_stock_data(stock_list, start_date, end_date):
    stock_data = {}
    print("Fetching stock data...")
    for stock in tqdm(stock_list):
        try:
            df = yf.download(stock, start=start_date, end=end_date)
            if not df.empty:
                stock_data[stock] = df
            else:
                print(f"No data for {stock}")
        except Exception as e:
            print(f"Error downloading {stock}: {e}")
    return stock_data


def calculate_indicators(df):
    df = df.copy()
    # Simple Moving Averages
    df['SMA_5'] = df['Close'].rolling(window=5).mean()
    df['SMA_10'] = df['Close'].rolling(window=10).mean()
    df['SMA_20'] = df['Close'].rolling(window=20).mean()
    # Exponential Moving Averages
    df['EMA_5'] = df['Close'].ewm(span=5, adjust=False).mean()
    df['EMA_10'] = df['Close'].ewm(span=10, adjust=False).mean()
    # Relative Strength Index (RSI)
    delta = df['Close'].diff()
    gain = (delta.where(delta > 0, 0)).fillna(0)
    loss = (-delta.where(delta < 0, 0)).fillna(0)
    average_gain = gain.rolling(window=14).mean()
    average_loss = loss.rolling(window=14).mean()
    rs = average_gain / average_loss
    df['RSI'] = 100 - (100 / (1 + rs))
    # Bollinger Bands
    df['BB_Middle'] = df['Close'].rolling(window=20).mean()
    df['BB_Std'] = df['Close'].rolling(window=20).std()
    df['BB_Upper'] = df['BB_Middle'] + (df['BB_Std'] * 2)
    df['BB_Lower'] = df['BB_Middle'] - (df['BB_Std'] * 2)
    # MACD
    ema_12 = df['Close'].ewm(span=12, adjust=False).mean()
    ema_26 = df['Close'].ewm(span=26, adjust=False).mean()
    df['MACD'] = ema_12 - ema_26
    df['MACD_Signal'] = df['MACD'].ewm(span=9, adjust=False).mean()
    # Stochastic Oscillator
    low_min = df['Low'].rolling(window=14).min()
    high_max = df['High'].rolling(window=14).max()
    df['Stochastic'] = ((df['Close'] - low_min) / (high_max - low_min)) * 100
    # Fill any remaining NaN values
    df.fillna(method='bfill', inplace=True)
    return df


def prepare_dataset(stock_data):
    df_list = []
    for symbol, data in stock_data.items():
        data = data.copy()
        data['Symbol'] = symbol
        data.reset_index(inplace=True)
        df_list.append(data)
    dataset = pd.concat(df_list, ignore_index=True)
    return dataset


def define_target_variable(dataset, prediction_horizon=15):
    dataset = dataset.sort_values(['Symbol', 'Date'])
    dataset['Future_Close'] = dataset.groupby('Symbol')['Close'].shift(-prediction_horizon)
    dataset['Future_Return'] = (dataset['Future_Close'] - dataset['Close']) / dataset['Close']
    # Drop rows with NaN values in Future_Close (the last 'n' rows for each symbol)
    dataset = dataset.dropna(subset=['Future_Close'])
    return dataset


def handle_missing_values(dataset):
    # Fill NaNs with forward/backward fill
    dataset.fillna(method='ffill', inplace=True)
    dataset.fillna(method='bfill', inplace=True)
    return dataset


def encode_categorical_variables(dataset):
    le = LabelEncoder()
    dataset['Symbol_Encoded'] = le.fit_transform(dataset['Symbol'])
    return dataset, le


def select_features(dataset):
    features = dataset.drop(columns=['Date', 'Symbol', 'Future_Close', 'Future_Return'])
    target = dataset['Future_Return']
    return features, target


def scale_features(features):
    scaler = StandardScaler()
    features_scaled = scaler.fit_transform(features)
    feature_names = features.columns
    return features_scaled, scaler, feature_names


def split_dataset(features, target):
    X_train, X_test, y_train, y_test = train_test_split(
        features, target, test_size=0.2, random_state=42
    )
    return X_train, X_test, y_train, y_test


def tune_random_forest_regressor(X_train, y_train):
    param_grid = {
        'n_estimators': [100, 200],
        'max_depth': [None, 5, 10],
        'min_samples_split': [2, 5],
    }
    rf = RandomForestRegressor(random_state=42)
    grid_search = GridSearchCV(
        estimator=rf, param_grid=param_grid, cv=3, scoring='neg_mean_squared_error', n_jobs=-1
    )
    grid_search.fit(X_train, y_train)
    print("Best parameters for Random Forest Regressor:", grid_search.best_params_)
    best_rf = grid_search.best_estimator_
    return best_rf


def tune_xgboost_regressor(X_train, y_train):
    param_grid = {
        'n_estimators': [100, 200],
        'max_depth': [3, 5, 7],
        'learning_rate': [0.01, 0.1],
    }
    xgb_reg = xgb.XGBRegressor(eval_metric='rmse', use_label_encoder=False, random_state=42)
    grid_search = GridSearchCV(
        estimator=xgb_reg, param_grid=param_grid, cv=3, scoring='neg_mean_squared_error', n_jobs=-1
    )
    grid_search.fit(X_train, y_train)
    print("Best parameters for XGBoost Regressor:", grid_search.best_params_)
    best_xgb = grid_search.best_estimator_
    return best_xgb


def train_neural_network_regressor(X_train, y_train):
    def create_nn_model():
        model = Sequential()
        model.add(Dense(64, input_dim=X_train.shape[1], activation='relu'))
        model.add(Dense(32, activation='relu'))
        model.add(Dense(1))  # No activation for regression output
        optimizer = Adam(learning_rate=0.001)
        model.compile(
            loss='mean_squared_error', optimizer=optimizer, metrics=['mean_absolute_error']
        )
        return model

    nn_model = KerasRegressor(model=create_nn_model, epochs=10, batch_size=32, verbose=0)
    nn_model.fit(X_train, y_train)
    return nn_model


def train_linear_regression(X_train, y_train):
    lr_regressor = LinearRegression()
    lr_regressor.fit(X_train, y_train)
    return lr_regressor


def train_svr(X_train, y_train):
    svr_regressor = SVR()
    svr_regressor.fit(X_train, y_train)
    return svr_regressor


def plot_feature_importance(model, feature_names):
    if hasattr(model, 'feature_importances_'):
        importances = model.feature_importances_
        indices = np.argsort(importances)[::-1]

        plt.figure(figsize=(12, 6))
        plt.title("Feature Importance")
        plt.bar(range(len(importances)), importances[indices], align='center')
        plt.xticks(
            range(len(importances)), [feature_names[i] for i in indices],
            rotation=90
        )
        plt.tight_layout()
##########################################################################
        # Save the plot to a file instead of displaying it
        model_name = type(model).__name__
        #model_name = type(model).name

        output_dir ='c:/users/swarn/python_codes/python_try/hugging/plots'
        #'c:/users/swarn/screener/test_files/current_dataframe/current_dataframe.csv'
        filename = f'feature_importance_{model_name}.png'
        filepath = os.path.join(output_dir, filename)
        plt.savefig(filepath)
        #plt.savefig(f'feature_importance_{model_name}.png')
        plt.close()  # Close the figure to free up memory
    else:
        print("Model does not have feature_importances_ attribute")
##########################################################################
    #    plt.show()
    #else:
    #    print("Model does not have feature_importances_ attribute")


# Function to save models
def save_model(model, model_name):
    import joblib
    filename = f"{model_name}_model.pkl"
    joblib.dump(model, filename)
    print(f"{model_name} model saved to {filename}")


# Function to load models
def load_model(model_name):
    import joblib
    filename = f"{model_name}_model.pkl"
    if os.path.exists(filename):
        model = joblib.load(filename)
        print(f"{model_name} model loaded from {filename}")
        return model
    else:
        print(f"No saved model found for {model_name}. Please train the model first.")
        return None


def save_keras_model(model, model_name):
    filename = f"{model_name}model.h5"
    model.model_.save(filename)
    print(f"{model_name} model saved to {filename}")


def load_keras_model(model_name):
    filename = f"{model_name}model.h5"
    if os.path.exists(filename):
        from keras.models import load_model
        keras_model = load_model(filename)
        print(f"{model_name} model loaded from {filename}")
        # Wrap the loaded Keras model back into a KerasRegressor
        from scikeras.wrappers import KerasRegressor

        def create_nn_model():
            return keras_model

        nn_model = KerasRegressor(model=create_nn_model)
        nn_model.model_ = keras_model  # Set the trained model
        return nn_model
    else:
        print(f"No saved model found for {model_name}. Please train the model first.")
        return None


def save_scaler(scaler):
    import joblib
    joblib.dump(scaler, "scaler.pkl")
    print("Scaler saved to scaler.pkl")


def load_scaler():
    import joblib
    if os.path.exists("scaler.pkl"):
        scaler = joblib.load("scaler.pkl")
        print("Scaler loaded from scaler.pkl")
        return scaler
    else:
        print("No saved scaler found.")
        return None


def save_label_encoder(le):
    import joblib
    joblib.dump(le, "label_encoder.pkl")
    print("Label encoder saved to label_encoder.pkl")


def load_label_encoder():
    import joblib
    if os.path.exists("label_encoder.pkl"):
        le = joblib.load("label_encoder.pkl")
        print("Label encoder loaded from label_encoder.pkl")
        return le
    else:
        print("No saved label encoder found.")
        return None


# Function to sanitize parameters for JSON serialization
def sanitize_parameters(params):
    sanitized_params = {}
    for key, value in params.items():
        try:
            json.dumps(value)  # Check if value is JSON serializable
            sanitized_params[key] = value
        except TypeError:
            sanitized_params[key] = str(value)
    return sanitized_params


def main_machine_learning():
    import numpy as np
    import matplotlib.pyplot as plt
    from sklearn.model_selection import cross_val_score, KFold

    # Audible beep at the start
    play_sound_async()
    print("Program started...")

    # Provide option to Train or Use
    action = input("Do you want to 'Train' the models or 'Use' existing models? Enter 'Train' or 'Use': ").strip().lower()

    if action not in ['train', 'use']:
        print("Invalid input. Exiting the program.")
        return
    if action == 'use' :
    # Prompt user for input stocks and prediction horizon
        user_stock_symbols = input("Enter the stock symbols separated by commas (e.g., AAPL, GOOGL, MSFT): ")
        user_stock_list = [symbol.strip() for symbol in user_stock_symbols.split(',')]

    elif action == 'train' :
        user_stock_list = []
        user_stock_list = nfo_stocks.copy()
    # Transform stock names to Yahoo Finance format
    stock_list = []
    #user_stock_list = []
    #user_stock_list = nfo_stocks.copy() # taking all the stocks into consideration.    

    for stock in user_stock_list:
        new_name = transform_stock_name(stock)
        stock_list.append(new_name)


    # Prompt user for prediction horizon
    prediction_horizon_input = input("Enter the prediction horizon in days (e.g., 5): ")
    try:
        prediction_horizon = int(prediction_horizon_input)
    except ValueError:
        print("Invalid prediction horizon input. Using default value of 5 days.")
        prediction_horizon = 5

    # Prompt user for data period
    data_period_input = input("Enter the number of years of data to use for training (e.g., 5 or 10): ")
    try:
        data_period_years = int(data_period_input)
    except ValueError:
        print("Invalid data period input. Using default value of 5 years.")
        data_period_years = 5

    end_date = datetime.datetime.now()
    start_date = end_date - datetime.timedelta(days=data_period_years*365)
    
    start_time = datetime.datetime.now()
    print(f'Training the Models on {len(stock_list)} stocks data')
    print("Estimated Time in minutes = ",len(stock_list))
    print("Time of start = ",start_time)


    if action == 'train':
        # Fetch data
        stock_data = fetch_stock_data(stock_list, start_date.strftime('%Y-%m-%d'), end_date.strftime('%Y-%m-%d'))

        # Check if any data was fetched
        if not stock_data:
            print("No stock data fetched. Please check your stock symbols and internet connection.")
            return

        # Calculate indicators
        print("Calculating technical indicators...")
        for symbol in tqdm(stock_data):
            stock_data[symbol] = calculate_indicators(stock_data[symbol])

        # Prepare dataset
        dataset = prepare_dataset(stock_data)
        dataset = define_target_variable(dataset, prediction_horizon=prediction_horizon)
        dataset = handle_missing_values(dataset)

        # Encode categorical variables
        dataset, le = encode_categorical_variables(dataset)
        features, target = select_features(dataset)

        # Save label encoder for future use
        save_label_encoder(le)

        # Add feature names for plotting
        feature_names = features.columns

        # Scale features
        features_scaled, scaler, feature_names = scale_features(features)

        # Save scaler and feature names for future use
        save_scaler(scaler)
        with open('feature_names.json', 'w') as f:
            json.dump(feature_names.tolist(), f)

        # Split dataset
        X_train, X_test, y_train, y_test = split_dataset(features_scaled, target)

        # Remove balancing step as it's not applicable for regression
        X_train_balanced, y_train_balanced = X_train, y_train

        # Train regression models
        print("Training regression models...")
        rf_model = tune_random_forest_regressor(X_train_balanced, y_train_balanced)
        xgb_model = tune_xgboost_regressor(X_train_balanced, y_train_balanced)
        nn_model = train_neural_network_regressor(X_train_balanced, y_train_balanced)
        lr_model = train_linear_regression(X_train_balanced, y_train_balanced)
        svr_model = train_svr(X_train_balanced, y_train_balanced)

        # Save models
        save_model(rf_model, "RandomForestRegressor")
        save_model(xgb_model, "XGBoostRegressor")
        save_keras_model(nn_model, "NeuralNetworkRegressor")
        save_model(lr_model, "LinearRegression")
        save_model(svr_model, "SVR")

        # Evaluate models
        models = {
            'Random Forest Regressor': rf_model,
            'XGBoost Regressor': xgb_model,
            'Neural Network Regressor': nn_model,
            'Linear Regression': lr_model,
            'Support Vector Regressor': svr_model
        }

        model_performance = {}  # Dictionary to store model performance

        for model_name, model in models.items():
            print(f"\n{model_name} Evaluation:")

            # Predictions
            y_pred = model.predict(X_test)

            # Compute evaluation metrics
            mae = mean_absolute_error(y_test, y_pred)
            mse = mean_squared_error(y_test, y_pred)
            rmse = mse ** 0.5
            r2 = r2_score(y_test, y_pred)

            print(f"Mean Absolute Error (MAE): {mae:.4f}")
            print(f"Mean Squared Error (MSE): {mse:.6f}")
            print(f"Root Mean Squared Error (RMSE): {rmse:.4f}")
            print(f"R-squared (R²): {r2:.4f}")

            # Cross-validation
            print(f"Performing cross-validation for {model_name}...")
            cv = KFold(n_splits=5, shuffle=True, random_state=42)
            mse_scores = -cross_val_score(model, X_train_balanced, y_train_balanced, cv=cv, scoring='neg_mean_squared_error', n_jobs=-1)

            mean_mse = float(mse_scores.mean())
            print(f"Cross-validated MSE scores for {model_name}: {mse_scores}")
            print(f"Mean MSE: {mean_mse:.6f}")

            # Store performance metrics
            if hasattr(model, 'get_params'):
                parameters = model.get_params()
                parameters = sanitize_parameters(parameters)
            else:
                parameters = None

            model_performance[model_name] = {
                'MAE': mae,
                'MSE': mse,
                'RMSE': rmse,
                'R2': r2,
                'Parameters': parameters
            }

            if model_name in ['Random Forest Regressor', 'XGBoost Regressor']:
                plot_feature_importance(model, feature_names)

        # Save model performance metrics
        with open('model_performance_regression.json', 'w') as f:
            json.dump(model_performance, f, cls=NumpyEncoder)
        print("Model performance metrics saved to model_performance_regression.json")

        # Audible beep at the end of training
        play_sound_async()
        print("Training completed.")
        end_time = datetime.datetime.now()
        print("Start Time =", start_time)
        print("End Time = ",end_time)
    elif action == 'use':
        # Load models
        print("Loading saved models...")
        rf_model = load_model("RandomForestRegressor")
        xgb_model = load_model("XGBoostRegressor")
        nn_model = load_keras_model("NeuralNetworkRegressor")
        lr_model = load_model("LinearRegression")
        svr_model = load_model("SVR")

        # Load scaler and label encoder
        scaler = load_scaler()
        le = load_label_encoder()

        # Load feature names
        with open('feature_names.json', 'r') as f:
            feature_names = json.load(f)

        # Load model performance metrics
        if os.path.exists('model_performance_regression.json'):
            with open('model_performance_regression.json', 'r') as f:
                model_performance = json.load(f)
        else:
            print("Model performance metrics not found.")
            model_performance = {}

        # Display models and their performance
        print("\nAvailable models:")
        for idx, model_name in enumerate(model_performance.keys()):
            performance = model_performance[model_name]
            print(f"{idx+1}. {model_name} - RMSE: {performance['RMSE']:.6f}")

        # Allow user to select a model
        model_choice_input = input("Enter the number corresponding to the model you want to use: ")
        try:
            model_choice = int(model_choice_input) - 1
            selected_model_name = list(model_performance.keys())[model_choice]
            print(f"You have selected: {selected_model_name}")
        except (ValueError, IndexError):
            print("Invalid choice. Exiting the program.")
            return

        # Get the selected model
        models = {
            'Random Forest Regressor': rf_model,
            'XGBoost Regressor': xgb_model,
            'Neural Network Regressor': nn_model,
            'Linear Regression': lr_model,
            'Support Vector Regressor': svr_model
        }
        best_model = models[selected_model_name]

        # Fetch recent data for prediction
        recent_end_date = datetime.datetime.now()
        recent_start_date = recent_end_date - datetime.timedelta(days=60)
        recent_stock_data = fetch_stock_data(
            stock_list, recent_start_date.strftime('%Y-%m-%d'), recent_end_date.strftime('%Y-%m-%d')
        )

        if not recent_stock_data:
            print("No recent stock data fetched. Please check your stock symbols and internet connection.")
            return

        # Calculate indicators
        print("Calculating technical indicators on recent data...")
        for symbol in tqdm(recent_stock_data):
            recent_stock_data[symbol] = calculate_indicators(recent_stock_data[symbol])

        # Prepare recent data
        recent_dataset = prepare_dataset(recent_stock_data)
        recent_dataset = handle_missing_values(recent_dataset)

        # Encode categorical variables
        recent_dataset['Symbol_Encoded'] = le.transform(recent_dataset['Symbol'])
        recent_features = recent_dataset.drop(columns=['Date', 'Symbol'])

        # Ensure recent_features has the same columns as during training
        # Add missing columns with default values
        missing_cols = set(feature_names) - set(recent_features.columns)
        for col in missing_cols:
            recent_features[col] = 0

        # Remove any extra columns not in original features
        extra_cols = set(recent_features.columns) - set(feature_names)
        recent_features.drop(columns=extra_cols, inplace=True)

        # Reorder columns to match the original order
        recent_features = recent_features[feature_names]

        # Now transform
        recent_features_scaled = scaler.transform(recent_features)

        # Predict expected returns using the best model
        predictions = best_model.predict(recent_features_scaled)

        recent_dataset['Predicted_Return'] = predictions
        recent_dataset['Predicted_Future_Price'] = recent_dataset['Close'] * (1 + recent_dataset['Predicted_Return'])
        recent_dataset['Predicted_Return_Percentage'] = recent_dataset['Predicted_Return'] * 100

        # Get latest date's predictions
        idx = recent_dataset.groupby('Symbol')['Date'].idxmax()
        latest_predictions = recent_dataset.loc[idx]

        # Calculate the predicted profit per share
        latest_predictions['Predicted_Profit'] = (
            latest_predictions['Predicted_Future_Price'] - latest_predictions['Close']
        )
        latest_predictions['Predicted_Return_Percentage'] = latest_predictions['Predicted_Return'] * 100

        # Round the relevant columns to 2 decimal places
        latest_predictions['Close'] = latest_predictions['Close'].round(2)
        latest_predictions['Predicted_Return'] = latest_predictions['Predicted_Return'].round(2)
        latest_predictions['Predicted_Future_Price'] = latest_predictions['Predicted_Future_Price'].round(2)
        latest_predictions['Predicted_Profit'] = latest_predictions['Predicted_Profit'].round(2)
        latest_predictions['Predicted_Return_Percentage'] = latest_predictions['Predicted_Return_Percentage'].round(2)



        # Sort latest_predictions by Predicted_Profit in descending order
        latest_predictions = latest_predictions.sort_values(by='Predicted_Return', ascending=False)

        print(f"\nPredicted returns for the next {prediction_horizon} days (sorted by predicted profits):")
        print(latest_predictions[['Symbol', 'Date', 'Close', 'Predicted_Return_Percentage', 'Predicted_Future_Price', 'Predicted_Profit']])


#        print(f"\nPredicted returns for the next {prediction_horizon} days:")
#        print(latest_predictions[['Symbol', 'Date', 'Close', 'Predicted_Return', 'Predicted_Future_Price']])

        # Add code to save the predictions to a file
        # Define output directory for predictions
        predictions_dir = 'c:/users/swarn/python_codes/python_try/hugging/predictions'
        os.makedirs(predictions_dir, exist_ok=True)

        # Get current date as a string
        #current_date = datetime.now().strftime('%Y-%m-%d')
        
        #current_date = datetime.datetime.today() # Gives time also so not suitable for naming a file
        current_date = datetime.date.today()
        print("Current Date = ",current_date)

        # Define filename with current date
        filename = f'Predictions_{current_date}.csv'
        filepath = os.path.join(predictions_dir, filename)

        # Select relevant columns to save
        #columns_to_save = ['Symbol', 'Date', 'Close', 'Predicted_Return', 'Predicted_Future_Price']
        columns_to_save = ['Symbol', 'Date', 'Close', 'Predicted_Return_Percentage', 'Predicted_Future_Price', 'Predicted_Profit']

        # Save the latest predictions to a CSV file
        latest_predictions[columns_to_save].to_csv(filepath, index=False)

        print(f"\nPredictions saved to {filepath}")




        # Audible beep at the end of program
        play_sound_async()
        print("Program execution completed.")

    # Provide guidance on number of stocks and data periods
    print("\nGuidance on model training:")
    print("1. Training with more stocks generally improves model generalization but increases training time.")
    print("2. Using data from longer periods (e.g., 10 years) can capture more market cycles but may include outdated patterns.")
    print("3. Balance between the number of stocks, data period, and computational resources for optimal performance.")


if __name__ == "__main__":
    main_machine_learning()