import MetaTrader5 as mt5
import pandas as pd
import numpy as np
from scipy.stats import norm
from scipy.optimize import minimize

# Initialize MetaTrader 5
if not mt5.initialize():
    print("MetaTrader 5 initialization error")
    mt5.shutdown()

# Parameters
symbols = [
    "EURUSD",
    "GBPUSD",
    "AUDUSD",
    "NZDUSD",
    "USDCAD",
    "USDCHF",
    "USDJPY",
    "EURCHF",
    "EURAUD",
    "EURGBP",
]
timeframe = mt5.TIMEFRAME_H1
start_date = pd.Timestamp("2017-01-01")
end_date = pd.Timestamp.now()

# Limitations
min_weight = 0.01
max_weight = 0.55
min_lot = 0.1


# Functions for receiving data
def get_data(symbol, timeframe, start_date, end_date):
    rates = mt5.copy_rates_range(symbol, timeframe, start_date, end_date)
    df = pd.DataFrame(rates)
    df["time"] = pd.to_datetime(df["time"], unit="s")
    df.set_index("time", inplace=True)
    df["returns"] = df["close"].pct_change()
    return df


# Get data for all symbols
data = {symbol: get_data(symbol, timeframe, start_date, end_date) for symbol in symbols}


# VaR calculation function
def calculate_var(returns, confidence_level=0.90, holding_period=190):
    var = norm.ppf(1 - confidence_level) * returns.std() * np.sqrt(holding_period)
    return var


# Portfolio VaR calculation function
def portfolio_var(weights, returns, confidence_level=0.90, holding_period=183):
    portfolio_returns = returns.dot(weights)
    return calculate_var(portfolio_returns, confidence_level, holding_period)


# Portfolio optimization function
def optimize_portfolio(
    returns, risk_free_rate, target_return, confidence_level=0.95, holding_period=190
):
    n = len(returns.columns)

    def objective(weights):
        return portfolio_var(weights, returns, confidence_level, holding_period)

    def constraint1(weights):
        return np.sum(weights) - 1.0

    def constraint2(weights):
        return np.sum(returns.mean() * weights) - target_return

    constraints = (
        {"type": "eq", "fun": constraint1},
        {"type": "eq", "fun": constraint2},
    )
    bounds = tuple((min_weight, max_weight) for _ in range(n))

    result = minimize(
        objective, n * [1.0 / n], method="SLSQP", bounds=bounds, constraints=constraints
    )
    return result.x


# Function for calculating optimal position and stop loss size
def calculate_position_size(
    symbol, direction, deposit, leverage, risk_per_trade, stop_loss_pips, weight
):
    symbol_info = mt5.symbol_info(symbol)
    contract_size = symbol_info.trade_contract_size
    pip_value = (
        symbol_info.trade_tick_value * 10
    )  # Multiply by 10 since pips are usually calculated with the accuracy of 0.1 point

    max_loss = deposit * risk_per_trade
    position_size = max_loss / (stop_loss_pips * pip_value)

    # Consider leverage
    max_position_size = deposit * leverage / (symbol_info.ask * contract_size)

    position_size = min(position_size, max_position_size)

    # Limit minimum lot
    position_size = max(position_size, min_lot)

    # Reduce to portfolio weight
    position_size = position_size * weight

    # Debugging data
    print(f"  Debug - {symbol}:")
    print(f"    Contract size: {contract_size}")
    print(f"    Pip value: {pip_value}")
    print(f"    Max loss: {max_loss}")
    print(f"    Stop loss pips: {stop_loss_pips}")
    print(f"    Calculated position size: {position_size}")
    print(f"    Max position size due to leverage: {max_position_size}")

    return position_size


# Stop loss calculation function in points
def calculate_stop_loss(symbol, var, direction):
    symbol_info = mt5.symbol_info(symbol)
    point = symbol_info.point

    if direction == "buy":
        stop_loss_pips = abs(var) / point
    else:  # "sell"
        stop_loss_pips = abs(var) / point

    return stop_loss_pips


# New take profit calculation function
def calculate_take_profit(stop_loss_pips, risk_reward_ratio=8):
    return stop_loss_pips * risk_reward_ratio


# Modified main function
def main(
    deposit,
    leverage,
    risk_per_trade,
    confidence_level=0.90,
    holding_period=190,
    risk_reward_ratio=8,
):
    # Collect profitability data
    returns = pd.DataFrame(
        {symbol: data[symbol]["returns"] for symbol in symbols}
    ).dropna()

    # Portfolio optimization
    risk_free_rate = 0.02  # Offered risk-free rate
    target_return = (
        returns.mean().mean()
    )  # Target profitability - average by all symbols
    weights = optimize_portfolio(
        returns, risk_free_rate, target_return, confidence_level, holding_period
    )

    # Calculate portfolio VaR
    portfolio_var_value = portfolio_var(
        weights, returns, confidence_level, holding_period
    )

    print(f"Optimal portfolio weights: {dict(zip(symbols, weights))}")
    print(f"Portfolio VaR: {portfolio_var_value:.4f}")
    total_risk_currency = 0
    total_risk_pips = 0
    total_potential_profit_currency = 0
    total_potential_profit_pips = 0

    # Calculate position, stop loss and take profit for each symbol
    for symbol, weight in zip(symbols, weights):
        symbol_var = calculate_var(returns[symbol], confidence_level, holding_period)

        long_stop_loss_pips = calculate_stop_loss(symbol, symbol_var, "buy")
        short_stop_loss_pips = calculate_stop_loss(symbol, symbol_var, "sell")

        long_take_profit_pips = calculate_take_profit(
            long_stop_loss_pips, risk_reward_ratio
        )
        short_take_profit_pips = calculate_take_profit(
            short_stop_loss_pips, risk_reward_ratio
        )

        long_position_size = calculate_position_size(
            symbol,
            "buy",
            deposit,
            leverage,
            risk_per_trade,
            long_stop_loss_pips,
            weight,
        )
        short_position_size = calculate_position_size(
            symbol,
            "sell",
            deposit,
            leverage,
            risk_per_trade,
            short_stop_loss_pips,
            weight,
        )

        symbol_info = mt5.symbol_info(symbol)
        pip_value = symbol_info.trade_tick_value * 10

        long_risk_currency = long_position_size * long_stop_loss_pips * pip_value
        short_risk_currency = short_position_size * short_stop_loss_pips * pip_value

        long_potential_profit_currency = (
            long_position_size * long_take_profit_pips * pip_value
        )
        short_potential_profit_currency = (
            short_position_size * short_take_profit_pips * pip_value
        )

        total_risk_currency += max(long_risk_currency, short_risk_currency)
        total_risk_pips += max(long_stop_loss_pips, short_stop_loss_pips)
        total_potential_profit_currency += max(
            long_potential_profit_currency, short_potential_profit_currency
        )
        total_potential_profit_pips += max(
            long_take_profit_pips, short_take_profit_pips
        )

        print(f"\n{symbol}:")
        print(f"  Portfolio weight: {weight:.4f}")
        print(f"  VaR: {symbol_var:.4f}")
        print(f"  Stop loss (Long): {long_stop_loss_pips:.2f} pips")
        print(f"  Stop loss (Short): {short_stop_loss_pips:.2f} pips")
        print(f"  Take profit (Long): {long_take_profit_pips:.2f} pips")
        print(f"  Take profit (Short): {short_take_profit_pips:.2f} pips")
        print(f"  Position size (Long): {long_position_size:.4f} lots")
        print(f"  Position size (Short): {short_position_size:.4f} lots")
        print(
            f"  Maximum risk (Long): {long_risk_currency:.2f} USD, {(long_risk_currency/deposit)*100:.2f}%"
        )
        print(
            f"  Maximum risk (Short): {short_risk_currency:.2f} USD, {(short_risk_currency/deposit)*100:.2f}%"
        )
        print(
            f"  Potential profit (Long): {long_potential_profit_currency:.2f} USD, {(long_potential_profit_currency/deposit)*100:.2f}%"
        )
        print(
            f"  Potential profit (Short): {short_potential_profit_currency:.2f} USD, {(short_potential_profit_currency/deposit)*100:.2f}%"
        )

    expected_outcome = total_potential_profit_currency - total_risk_currency

    print("\nTotal portfolio risk:")
    print(f"  In currency: {total_risk_currency:.2f} USD")
    print(f"  In deposit %: {(total_risk_currency/deposit)*100:.2f}%")
    print(f"  In points (sum of all pairs): {total_risk_pips:.2f}")

    print("\nTotal potential portfolio profit:")
    print(f"  In currency: {total_potential_profit_currency:.2f} USD")
    print(f"  In deposit %: {(total_potential_profit_currency/deposit)*100:.2f}%")
    print(f"  In points (sum of all pairs): {total_potential_profit_pips:.2f}")

    print(f"\nExpected portfolio outcome: {expected_outcome:.2f} USD")
    print(f"Expected outcome in deposit %: {(expected_outcome/deposit)*100:.2f}%")


# Launch main function
if __name__ == "__main__":
    deposit = 100000  # Deposit in USD
    leverage = 500  # Leverage 1:100
    risk_per_trade = 0.02  # Risk per trade 2%
    risk_reward_ratio = 8  # Risk/reward ratio

    main(deposit, leverage, risk_per_trade, risk_reward_ratio=risk_reward_ratio)

mt5.shutdown()
