Check the logic of each calcul in the following scripte : 

import tkinter as tk
from tkinter import ttk, messagebox
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import yfinance as yf
import threading
import queue
import numpy as np
from datetime import datetime, timedelta
import pandas as pd  # Ensure pandas is imported

# List of factors
FACTORS_LIST = [
    "Regulation",
    "Institutional Adoption",
    "Market Sentiment",
    "Economic Conditions",
    "Technological Evolution",
    "Supply and Demand",
    "Competition",
    "Financial Integration",
    "Geopolitical Factors",
    "Special Events"
]

# Define weights for each factor
WEIGHTS = {
    "Regulation": 1.0,
    "Institutional Adoption": 1.0,
    "Market Sentiment": 1.5,
    "Economic Conditions": 1.0,
    "Technological Evolution": 1.2,  # Adjust if necessary
    "Supply and Demand": 2.0,
    "Competition": 0.8,
    "Financial Integration": 1.0,  # Adjust if necessary
    "Geopolitical Factors": 0.5,
    "Special Events": 0.7
}

# Base price for normalization
BASE_PRICE = 50000

# Create a thread-safe Queue for communication
result_queue = queue.Queue()

def get_bitcoin_data():
    """
    Uses yfinance to retrieve current and historical Bitcoin data.

    Returns:
        dict: Contains current price, market sentiment, etc.
    """
    try:
        btc = yf.Ticker("BTC-USD")
        eth = yf.Ticker("ETH-USD")
        sp500 = yf.Ticker("^GSPC")
        vix = yf.Ticker("^VIX")
        
        # Define the period for historical data (150 days for better coverage)
        end_date = datetime.today()
        start_date = end_date - timedelta(days=150)
        
        # Retrieve historical data
        btc_hist = btc.history(start=start_date, end=end_date)
        eth_hist = eth.history(start=start_date, end=end_date)
        sp500_hist = sp500.history(start=start_date, end=end_date)
        vix_hist = vix.history(start=start_date, end=end_date)
        
        # Current price
        if not btc_hist.empty:
            current_price = btc_hist['Close'][-1]
        else:
            current_price = BASE_PRICE
        
        # Last 30 days history to calculate trends
        btc_recent_hist = btc_hist.tail(30)
        if not btc_recent_hist.empty:
            mean_price = btc_recent_hist['Close'].mean()
            if mean_price != 0:
                price_trend = (current_price - mean_price) / mean_price  # Normalization
            else:
                price_trend = 0.0
        else:
            price_trend = 0.0

        # Sentiment based on recent changes
        recent_changes = btc_recent_hist['Close'].pct_change().dropna()
        sentiment_score = recent_changes.mean() if not recent_changes.empty else 0.0  # Average of changes

        # Institutional Adoption: Use volumes
        avg_volume = btc_recent_hist['Volume'].mean() if not btc_recent_hist.empty else 0.0
        current_volume = btc_recent_hist['Volume'][-1] if not btc_recent_hist.empty else 0.0
        if avg_volume != 0:
            adoption_score = (current_volume / avg_volume) - 1  # Normalized
        else:
            adoption_score = 0.0

        # Economic Conditions: Use VIX as a proxy
        if not vix_hist.empty:
            avg_vix = vix_hist['Close'].mean()
            current_vix = vix_hist['Close'][-1]
            if avg_vix != 0:
                economic_conditions = (avg_vix - current_vix) / avg_vix  # Normalized
            else:
                economic_conditions = 0.0
        else:
            economic_conditions = 0.0

        # Technological Evolution: Use volatility as an additional proxy
        if not btc_recent_hist.empty:
            volatility = btc_recent_hist['Close'].std()
            mean_close = btc_recent_hist['Close'].mean()
            if mean_close != 0:
                tech_evolution = volatility / mean_close  # Normalized
            else:
                tech_evolution = 0.0
        else:
            tech_evolution = 0.0

        # Regulation: Use volatility as a proxy
        if not btc_hist.empty:
            volatility = btc_hist['Close'].std()
            # Compare current volatility with a 3-month historical average
            extended_start_date = start_date - timedelta(days=90)
            hist_extended = btc.history(start=extended_start_date, end=start_date)
            if not hist_extended.empty:
                mean_volatility = hist_extended['Close'].std()
                if mean_volatility != 0:
                    regulation_score = (volatility - mean_volatility) / mean_volatility
                else:
                    regulation_score = 0.0  # Neutral if mean_volatility is zero
            else:
                regulation_score = 0.0  # Neutral default instead of simplified
        else:
            regulation_score = 0.0

        # Competition: Compare performance with Ethereum
        if not eth_hist.empty and not btc_hist.empty:
            if not btc_hist['Close'].empty and not eth_hist['Close'].empty:
                btc_start = btc_hist['Close'].iloc[0]
                btc_end = btc_hist['Close'].iloc[-1]
                eth_start = eth_hist['Close'].iloc[0]
                eth_end = eth_hist['Close'].iloc[-1]
                if btc_start != 0 and eth_start != 0:
                    btc_return = (btc_end / btc_start) - 1
                    eth_return = (eth_end / eth_start) - 1
                    competition_score = btc_return - eth_return  # Positive if BTC performs better
                else:
                    competition_score = 0.0
            else:
                competition_score = 0.0
        else:
            competition_score = 0.0

        # Financial Integration: Correlation with the S&P 500
        if not sp500_hist.empty and not btc_hist.empty:
            # Calculate daily returns
            btc_returns = btc_hist['Close'].pct_change().dropna()
            sp500_returns = sp500_hist['Close'].pct_change().dropna()
            
            # Align the dates
            btc_returns_aligned, sp500_returns_aligned = btc_returns.align(sp500_returns, join='inner')
            
            # Check if there are enough data points
            if len(btc_returns_aligned) >= 30:
                corr = btc_returns_aligned.corr(sp500_returns_aligned)
                if pd.notnull(corr):
                    # Normalize correlation from [-1, 1] to [0, 1]
                    financial_integration = (corr + 1) / 2
                else:
                    financial_integration = 0.5  # Neutral value if corr is NaN
            else:
                financial_integration = 0.5  # Default neutral value
        else:
            financial_integration = 0.5  # Default neutral value

        # Geopolitical Factors: Invert the VIX score as a proxy
        if not vix_hist.empty:
            geopolitical = -economic_conditions  # Lower VIX means less uncertainty
        else:
            geopolitical = 0.0

        # Special Events: Use sudden price variations
        if not btc_recent_hist.empty:
            daily_changes = btc_recent_hist['Close'].pct_change().abs()
            # Define a threshold, for example, 5% price change in one day
            events = daily_changes > 0.05
            if len(btc_recent_hist) > 0:
                special_events = events.sum() / len(btc_recent_hist)  # Normalized
            else:
                special_events = 0.0
        else:
            special_events = 0.0

        # Compile the scores
        factors = {
            "Supply and Demand": price_trend,
            "Market Sentiment": sentiment_score,
            "Institutional Adoption": adoption_score,
            "Economic Conditions": economic_conditions,
            "Technological Evolution": tech_evolution,
            "Regulation": regulation_score,
            "Competition": competition_score,
            "Financial Integration": financial_integration,
            "Geopolitical Factors": geopolitical,
            "Special Events": special_events
        }

        return factors

    except Exception as e:
        print(f"Error retrieving data via yfinance: {e}")
        return {factor: 0.0 for factor in FACTORS_LIST}

def evaluate_factors():
    """
    Evaluates all factors by retrieving their respective scores via yfinance.

    Returns:
        dict: Dictionary containing factor names and their corresponding scores.
    """
    factors = get_bitcoin_data()
    return factors

def calculate_index(factors):
    """
    Calculates the global index based on factor scores and their weights.

    Args:
        factors (dict): Dictionary of factor scores.

    Returns:
        float: Calculated index.
    """
    total_score = sum(score * WEIGHTS.get(factor, 1) for factor, score in factors.items())
    return total_score

def plot_factors(factors):
    """
    Displays a horizontal bar chart of factor scores.

    Args:
        factors (dict): Dictionary of factor scores.
    """
    # Clear the previous plot
    for widget in frame_plot.winfo_children():
        widget.destroy()

    factor_names = list(factors.keys())
    scores = list(factors.values())

    fig, ax = plt.subplots(figsize=(6, 4))
    colors = ['green' if x >= 0 else 'red' for x in scores]
    bars = ax.barh(factor_names, scores, color=colors)
    ax.axvline(0, color='black')
    ax.set_xlabel('Score')
    ax.set_title('Factor Evaluation')

    # Dynamically adjust label positions based on score magnitude
    x_limits = ax.get_xlim()
    for bar in bars:
        width = bar.get_width()
        if width >= 0:
            label_x = width + (x_limits[1] * 0.01)  # 1% padding
            alignment = 'left'
        else:
            label_x = width - (x_limits[1] * 0.01)  # 1% padding
            alignment = 'right'
        ax.text(label_x, 
                bar.get_y() + bar.get_height() / 2, 
                f"{width:.2f}", 
                va='center',
                ha=alignment,
                fontsize=8,
                color='black')

    plt.tight_layout()
    canvas = FigureCanvasTkAgg(fig, master=frame_plot)
    canvas.draw()
    canvas.get_tk_widget().pack()

def update_results():
    """
    Retrieves factor scores, calculates the index, and queues the results for GUI update.
    """
    try:
        factors = evaluate_factors()
        index = calculate_index(factors)
        # Queue the results for the main thread to process
        result_queue.put((factors, index))
    except Exception as e:
        print(f"Error updating results: {e}")
        result_queue.put(("error", str(e)))

def process_queue():
    """
    Processes items in the result_queue and updates the GUI accordingly.
    This function is scheduled to run periodically using Tkinter's after method.
    """
    try:
        while not result_queue.empty():
            result = result_queue.get_nowait()
            if result[0] == "error":
                # Handle the error
                messagebox.showerror("Error", f"An error occurred while updating results: {result[1]}")
                label_result.config(text="Predictive Index: Error", fg="red")
            else:
                factors, index = result
                # Display factor scores
                for factor, score in factors.items():
                    tree.set(factor, "Score", f"{score:.4f}")  # Increase precision if necessary

                # Display the index
                if index > 0:
                    trend = "Uptrend Predicted 📈"
                    color = "green"
                elif index < 0:
                    trend = "Downtrend Predicted 📉"
                    color = "red"
                else:
                    trend = "Stable ➖"
                    color = "gray"

                label_result.config(text=f"Predictive Index: {trend} (Score: {index:.2f})", fg=color)

                # Display the chart
                plot_factors(factors)
    except Exception as e:
        print(f"Error processing queue: {e}")
    finally:
        # Schedule the next queue check
        root.after(100, process_queue)

def update_results_threaded():
    """
    Starts the update_results function in a separate thread to keep the GUI responsive.
    """
    # Disable the update button to prevent multiple threads
    button_update.config(state='disabled')
    def target():
        try:
            update_results()
        finally:
            # Re-enable the update button after processing
            button_update.config(state='normal')
    thread = threading.Thread(target=target)
    thread.start()

# Configure the main window
root = tk.Tk()
root.title("Bitcoin Price Analysis")

# Configure layout
root.geometry("800x800")  # Increase size for better visibility
root.resizable(False, False)

# Title
title = ttk.Label(root, text="Bitcoin Trend Predictor", font=("Helvetica", 16))
title.pack(pady=10)

# Factors table
columns = ("Factor", "Score")
tree = ttk.Treeview(root, columns=columns, show='headings', height=10)
tree.heading("Factor", text="Factor")
tree.heading("Score", text="Score")
tree.column("Factor", width=200, anchor='center')
tree.column("Score", width=100, anchor='center')

# Insert factors with `iid` corresponding to the factor name
for factor in FACTORS_LIST:
    tree.insert('', tk.END, iid=factor, values=(factor, "0.0000"))

tree.pack(pady=10)

# Index result (using tkinter.Label)
label_result = tk.Label(root, text="Predictive Index: ", font=("Helvetica", 14))
label_result.pack(pady=10)

# Update button
button_update = ttk.Button(root, text="Evaluate", command=update_results_threaded)
button_update.pack(pady=10)

# Frame for the chart
frame_plot = ttk.Frame(root)
frame_plot.pack(pady=10, fill='both', expand=True)

# Initial call to process_queue to start periodic checking
root.after(100, process_queue)

# Run the main loop
root.mainloop()