import pandas as pd
import wbdata
import MetaTrader5 as mt5
from catboost import CatBoostRegressor
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error
import warnings
import matplotlib.pyplot as plt
import seaborn as sns
from datetime import datetime, timedelta

# Disable warnings
warnings.filterwarnings("ignore", category=UserWarning, module="wbdata")

# Download World Bank data
indicators = {
    "NY.GDP.MKTP.KD.ZG": "GDP growth",
    "FP.CPI.TOTL.ZG": "Inflation",
    "FR.INR.RINR": "Real interest rate",
    "NE.EXP.GNFS.ZS": "Exports",  # % of GDP
    "NE.IMP.GNFS.ZS": "Imports",  # % of GDP
    "BN.CAB.XOKA.GD.ZS": "Current account balance",  # % of GDP
    "GC.DOD.TOTL.GD.ZS": "Government debt",  # % of GDP
    "SL.UEM.TOTL.ZS": "Unemployment rate",  # % of working-age population
    "NY.GNP.PCAP.CD": "GNI per capita",  # current USD
    "NY.GDP.PCAP.KD.ZG": "GDP per capita growth",  # Constant 2010 USD
    "NE.RSB.GNFS.ZS": "Reserves in months of imports",
    "NY.GDP.DEFL.KD.ZG": "GDP deflator",  # Constant 2010 USD
    "NY.GDP.PCAP.KD": "GDP per capita (constant 2015 US$)",
    "NY.GDP.PCAP.PP.CD": "GDP per capita, PPP (current international $)",
    "NY.GDP.PCAP.PP.KD": "GDP per capita, PPP (constant 2017 international $)",
    "NY.GDP.PCAP.CN": "GDP per capita (current LCU)",
    "NY.GDP.PCAP.KN": "GDP per capita (constant LCU)",
    "NY.GDP.PCAP.CD": "GDP per capita (current US$)",
    "NY.GDP.PCAP.KD": "GDP per capita (constant 2010 US$)",
    "NY.GDP.PCAP.KD.ZG": "GDP per capita growth (annual %)",
    "NY.GDP.PCAP.KN.ZG": "GDP per capita growth (constant LCU)",
}

# Get data for each indicator separately
data_frames = []
for indicator, name in indicators.items():
    try:
        data_frame = wbdata.get_dataframe({indicator: name}, country="all")
        data_frames.append(data_frame)
    except Exception as e:
        print(f"Error fetching data for indicator '{indicator}': {e}")

# Combine data into a single DataFrame
if data_frames:
    data = pd.concat(data_frames, axis=1)

    # Display info on available indicators and their data
    print("Available indicators and their data:")
    print(data.columns)
    print(data.head())

    # Save data to CSV
    data.to_csv("economic_data.csv", index=True)

    # Display statistics
    print("Economic Data Statistics:")
    print(data.describe())
else:
    print(
        "No data was fetched. Please check your internet connection and the availability of the World Bank API."
    )
    data = (
        pd.DataFrame()
    )  # Create an empty DataFrame to avoid errors in the rest of the script

# Download data from MetaTrader5
if not mt5.initialize():
    print("initialize() failed")
    mt5.shutdown()

# Get all currency pairs
symbols = mt5.symbols_get()
symbol_names = [symbol.name for symbol in symbols]

# Load historical data for each currency pair
historical_data = {}
for symbol in symbol_names:
    rates = mt5.copy_rates_from_pos(symbol, mt5.TIMEFRAME_D1, 0, 1000)
    df = pd.DataFrame(rates)
    df["time"] = pd.to_datetime(df["time"], unit="s")
    df.set_index("time", inplace=True)
    historical_data[symbol] = df


# Prepare data for forecasting
def prepare_data(symbol_data, economic_data):
    data = symbol_data.copy()
    data["close_diff"] = data["close"].diff()
    data["close_corr"] = data["close"].rolling(window=30).corr(data["close"].shift(1))

    for indicator in indicators.keys():
        if indicator in economic_data.columns:
            # Use forward fill method to fill missing values
            data[indicator] = economic_data[indicator].ffill()
        else:
            print(f"Warning: Data for indicator '{indicator}' is not available.")

    data.dropna(inplace=True)
    return data


# Prepare data for all currency pairs
prepared_data = {}
for symbol, df in historical_data.items():
    prepared_data[symbol] = prepare_data(df, data)


# Updated forecast function
def forecast(symbol, symbol_data):
    if len(symbol_data) < 50:  # Check if data is sufficient for forecasting
        print(f"Not enough data for {symbol}. Skipping forecast.")
        return None, None

    X = symbol_data.drop(columns=["close"])
    y = symbol_data["close"]

    X_train, X_test, y_train, y_test = train_test_split(
        X, y, test_size=0.2, shuffle=False
    )

    if len(X_train) == 0 or len(X_test) == 0:
        print(f"Not enough data for {symbol} after splitting. Skipping forecast.")
        return None, None

    model = CatBoostRegressor(
        iterations=1000, learning_rate=0.1, depth=8, loss_function="RMSE", verbose=100
    )
    model.fit(X_train, y_train, verbose=False)

    predictions = model.predict(X_test)
    mse = mean_squared_error(y_test, predictions)
    print(f"Mean Squared Error for {symbol}: {mse}")

    # Analyze feature importance
    feature_importance = model.feature_importances_
    feature_names = X.columns
    importance_df = pd.DataFrame(
        {"feature": feature_names, "importance": feature_importance}
    )
    importance_df = importance_df.sort_values("importance", ascending=False)
    print(f"Top 10 Feature Importance for {symbol}:")
    print(importance_df.head(10))

    # Forecasting one month ahead
    future_data = symbol_data.tail(30).copy()
    future_predictions = model.predict(future_data.drop(columns=["close"]))

    return future_predictions, importance_df


# Forecasting for all currency pairs
forecasts = {}
feature_importances = {}
for symbol, df in prepared_data.items():
    try:
        forecast_result, importance_df = forecast(symbol, df)
        if forecast_result is not None and importance_df is not None:
            forecasts[symbol] = forecast_result
            feature_importances[symbol] = importance_df
    except Exception as e:
        print(f"Error forecasting for {symbol}: {e}")


# Visualize forecasts
def visualize_forecast(symbol, forecast):
    plt.figure(figsize=(12, 6))
    plt.plot(range(len(forecast)), forecast, label="Forecast")
    plt.title(f"Forecast for {symbol}")
    plt.xlabel("Days")
    plt.ylabel("Price")
    plt.legend()
    plt.savefig(f"{symbol}_forecast.png")
    plt.close()


# Visualize feature importance
def visualize_feature_importance(symbol, importance_df):
    if importance_df is None or importance_df.empty:
        print(f"No feature importance data available for {symbol}")
        return

    plt.figure(figsize=(12, 6))
    sns.barplot(x="importance", y="feature", data=importance_df.head(10))
    plt.title(f"Top 10 Feature Importance for {symbol}")
    plt.xlabel("Importance")
    plt.ylabel("Feature")
    plt.tight_layout()
    plt.savefig(f"{symbol}_feature_importance.png")
    plt.close()


# Create visualizations
for symbol, forecast in forecasts.items():
    visualize_forecast(symbol, forecast)
    if symbol in feature_importances:
        visualize_feature_importance(symbol, feature_importances[symbol])


# Interpret results
def interpret_results(symbol, forecast, importance_df):
    if forecast is None or importance_df is None:
        return f"Insufficient data for interpretation of {symbol}"

    trend = "upward" if forecast[-1] > forecast[0] else "downward"
    volatility = "high" if forecast.std() / forecast.mean() > 0.1 else "low"
    top_feature = importance_df.iloc[0]["feature"]

    interpretation = f"""
    Interpretation for {symbol}:
    1. Price Trend: The forecast shows a {trend} trend for the next 30 days.
    2. Volatility: The predicted price movement shows {volatility} volatility.
    3. Key Influencing Factor: The most important feature for this forecast is '{top_feature}'.
    4. Economic Implications:
       - If GDP growth is a top factor, it suggests strong economic performance is influencing the currency.
       - High importance of inflation rate might indicate monetary policy changes are affecting the currency.
       - If trade balance factors are crucial, international trade dynamics are likely driving currency movements.
    5. Trading Implications:
       - {trend.capitalize()} trend suggests potential for {'long' if trend == 'upward' else 'short'} positions.
       - {'Consider using tight stop-losses due to high volatility.' if volatility == 'high' else 'Lower volatility might allow for wider stop-losses.'}
    6. Risk Assessment:
       - Always consider the model's limitations and potential for unexpected market events.
       - Past performance doesn't guarantee future results.
    """
    return interpretation


# Generate and print interpretations
for symbol in forecasts.keys():
    interpretation = interpret_results(
        symbol, forecasts[symbol], feature_importances.get(symbol)
    )
    print(interpretation)

    # Save interpretation to a text file
    with open(f"{symbol}_interpretation.txt", "w") as f:
        f.write(interpretation)

# MetaTrader 5 shutdown
mt5.shutdown()

print(
    "Analysis complete. Check the generated PNG files for visualizations and TXT files for interpretations."
)
