preview
Portfolio Optimization in Python and MQL5

Portfolio Optimization in Python and MQL5

MetaTrader 5Statistics and analysis | 15 July 2024, 12:44
3 185 0
Javier Santiago Gaston De Iriarte Cabrera
Javier Santiago Gaston De Iriarte Cabrera

Introduction

Introducing two innovative portfolio optimization programs designed to revolutionize trading strategies and maximize returns while minimizing risk The first a Python-based solution leverages the power of MetaTrader 5 integration alongside advanced libraries such as pandas Numpy and cvxpy to analyze historical data optimize asset allocation and visualize results with Matplotlib. The second a similar implementation crafted in MQL5 harnesses the native capabilities of the MetaTrader 5 platform offering traders a seamless experience directly within their preferred trading environment. Both programs exemplify the cutting-edge intersection of quantitative finance and technology empowering traders with sophisticated tools to make data-driven decisions in an ever-evolving market landscape.


Why do we need Portfolio Optimization?

Portfolio optimization programs serve as essential tools in modern financial management addressing the critical need for efficient risk-adjusted returns in an increasingly complex and volatile investment landscape. By leveraging advanced mathematical models and computational power these programs enable investors and financial professionals to make data-driven decisions tailored to their specific risk tolerances and investment objectives. Such programs systematically analyze vast amounts of historical data market trends and asset correlations to determine optimal asset allocations that maximize potential returns while minimizing overall portfolio risk.

This scientific approach to portfolio construction helps mitigate human biases and emotional decision-making often associated with traditional investment strategies Furthermore portfolio optimization programs facilitate dynamic rebalancing allowing investors to adapt swiftly to changing market conditions and maintain alignment with their long-term financial goals In an era of global economic interconnectedness and rapid information flow these sophisticated tools provide a competitive edge enabling investors to navigate uncertainties and capitalize on opportunities across diverse asset classes ultimately fostering more robust and resilient investment strategies.


Why should we use Python?

Python is a good way to quickly try strategies or ideas, it has a big community and runs fast. It also counts with MetraTrader5 library, to download data or even make trades. So, we will firstly use python to see if their idea is consistent, and we should use different % of the account with different symbols.

This is the python script we are going to use:

# Import necessary libraries
import MetaTrader5 as mt5
import pandas as pd
import numpy as np
import cvxpy as cp
import matplotlib.pyplot as plt
from datetime import datetime

# Function to obtain historical data from MT5
def get_mt5_data(symbols, from_date, to_date):
    # Establish connection with MetaTrader 5
    if not mt5.initialize():
        print("Error: Could not connect to MetaTrader 5")
        mt5.shutdown()
        return None
    
    data = {}
    
    for symbol in symbols:
        # Get historical price data
        rates = mt5.copy_rates_range(symbol, mt5.TIMEFRAME_D1, from_date, to_date)
        
        if rates is not None:
            # Convert to Pandas DataFrame
            df = pd.DataFrame(rates)
            df['time'] = pd.to_datetime(df['time'], unit='s')
            df.set_index('time', inplace=True)
            df.drop(['tick_volume', 'spread', 'real_volume'], axis=1, inplace=True)
            
            # Calculate daily returns
            df['return'] = df['close'].pct_change().fillna(0)
            
            # Save in the data dictionary
            data[symbol] = df
    
    # Close the connection with MetaTrader 5
    mt5.shutdown()
    
    return data

# Function to optimize the portfolio
def optimize_portfolio(data):
    symbols = list(data.keys())
    n_assets = len(symbols)
    
    # Find the minimum data length among all assets
    min_length = min(len(data[symbol]) for symbol in symbols)
    
    # Adjust and normalize returns
    returns = np.zeros((min_length, n_assets))
    for i, symbol in enumerate(symbols):
        # Adjust data length
        df = data[symbol].iloc[:min_length]
        returns[:, i] = df['return'].values
    
    # Calculate covariance matrix and expected returns
    cov_matrix = np.cov(returns, rowvar=False)
    expected_returns = np.mean(returns, axis=0)
    
    # Optimization variables
    weights = cp.Variable(n_assets)
    risk = cp.quad_form(weights, cov_matrix)
    objective = cp.Maximize(expected_returns @ weights - 0.5 * risk)
    
    # Constraints
    constraints = [cp.sum(weights) == 1, weights >= 0]
    
    # Solve the optimization problem
    prob = cp.Problem(objective, constraints)
    prob.solve()
    
    # Display optimization results
    print("\nOptimization Results:")
    for i, symbol in enumerate(symbols):
        print(f"{symbol}: {weights.value[i]}")
    
    # Calculate minimum variance and expected return for the portfolio
    min_variance = cp.sqrt(cp.quad_form(weights.value, cov_matrix)).value
    expected_return_portfolio = expected_returns @ weights.value
    
    print(f"\nExpected portfolio return: {expected_return_portfolio:.4f}")
    print(f"Minimum portfolio variance: {min_variance:.4f}")
    
    return symbols, weights.value

# Function to visualize results
def visualize_results(symbols, weights):
    # Plot weights of each asset in the portfolio
    plt.figure(figsize=(10, 6))
    plt.bar(symbols, weights, color='blue')
    plt.xlabel('Assets')
    plt.ylabel('Weights')
    plt.title('Asset Weights in Optimized Portfolio')
    plt.show()

# Execute the main script
if __name__ == "__main__":
    # Define parameters
    symbols = ["EURUSD", "GBPUSD", "#AMZN", "#AAPL"]  # Asset symbols
    from_date = datetime(2023, 1, 1)  # Start date
    to_date = datetime(2023, 12, 31)  # End date
    
    # Get historical data from MT5
    print(f"Obtaining historical data from {from_date} to {to_date}...")
    data = get_mt5_data(symbols, from_date, to_date)
    
    if data:
        # Optimize the portfolio
        symbols, weights = optimize_portfolio(data)
        
        # Visualize the results
        visualize_results(symbols, weights)

Results look like this:

Obtaining historical data from 2023-01-01 00:00:00 to 2023-12-31 00:00:00...

Optimization Results:
EURUSD: 1.9303305369616842e-23
GBPUSD: 1.9417113191106993e-23
#AMZN: 1.0
#AAPL: -1.3370355361690525e-23

Expected portfolio return: 0.0025
Minimum portfolio variance: 0.0205

and this is the graph:

Graph 1

This results tell us to only invest in Amazon, but, this lets add a strategy to the script, to see if values change, and results ask us to invest in other symbols.

We can do this by adding this to the script (we will use a simple strategy of two MAS crossing), you can modify this strategy to use the one you need:

# Function to apply the moving average crossover strategy
def apply_sma_strategy(data, short_window=12, long_window=26):
    for symbol, df in data.items():
        df['SMA_50'] = df['close'].rolling(window=short_window).mean()
        df['SMA_200'] = df['close'].rolling(window=long_window).mean()
        df['signal'] = 0
        df.loc[df.index[short_window:], 'signal'] = np.where(
            df.loc[df.index[short_window:], 'SMA_50'] > df.loc[df.index[short_window:], 'SMA_200'], 1, 0
        )
        df['position'] = df['signal'].shift(1).fillna(0)
    return data

# Function to adjust returns according to the strategy
def adjust_returns(data):
    for symbol, df in data.items():
        df['adjusted_return'] = df['return'] * df['position']
    return data

and adding also this at the main:

        # Apply the moving average crossover strategy
        data = apply_sma_strategy(data)
        
        # Adjust returns according to the strategy
        data = adjust_returns(data)

Results are shown as this:

Obtaining historical data from 2023-01-01 00:00:00 to 2023-12-31 00:00:00...

Optimization Results:
EURUSD: -5.669275045708089e-25
GBPUSD: 5.494697501444607e-23
#AMZN: 1.0
#AAPL: -5.59465620602481e-23

Expected portfolio return: 0.0006
Minimum portfolio variance: 0.0151

Figure 2

There have been some small changes, but, overall results look the same.

This is because, AMZN has had a giant trend over all that period.

AMZN

You must be aware of this, and watch for the trend to stop, but, you can comprehend why the risk management is so important.


MQL5 Script

We have just seen how to develop this risk management in python, we will now go and reproduce the same script in MQL5 because MQL5 is more accurate.

Here's the script:

//+------------------------------------------------------------------+
//|                                        optimizacion_carteras.mq5 |
//|       Copyright 2024, Javier Santiago Gaston de Iriarte Cabrera. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
// Define the assets to consider
string symbols[] = {"EURUSD", "GBPUSD", "#AAPL", "#AMZN"};

// Date parameters to obtain historical data
datetime from_date = D'2023.01.01 00:00';
datetime to_date = D'2024.01.01 00:00';

//+------------------------------------------------------------------+
//| Function to obtain historical data from MetaTrader 5             |
//+------------------------------------------------------------------+
void obtenerDatosHistoricos(string symbol, datetime from, datetime to, double &out[][6])
  {
   // Declare an array to store the historical rates
   MqlRates rates[];
   
   // Copy historical data for the specified symbol and date range
   int copied = CopyRates(symbol, PERIOD_D1, from, to, rates);

   // Check if data copying was successful
   if(copied <= 0)
      Print("Failed to copy price data ", GetLastError());
   else
      Print("Copied ", ArraySize(rates), " bars");

   // Resize the output array to match the size of copied data
   ArrayResize(out, copied);
   Print("Copied ", copied, " data points for ", symbol);

   // Transfer data to the output array
   for(int i = 0; i < copied; i++)
     {
      out[i][0] = (double)rates[i].time;
      out[i][1] = rates[i].open;
      out[i][2] = rates[i].high;
      out[i][3] = rates[i].low;
      out[i][4] = rates[i].close;
      out[i][5] = (double)rates[i].tick_volume;
     }
  }
//+------------------------------------------------------------------+
//| Function to calculate daily returns                              |
//+------------------------------------------------------------------+
void calcularRendimientos(double &data[][6], double &returns[])
  {
   // Determine the number of data points
   int total = ArrayRange(data, 0);
   
   // Resize the returns array to accommodate the calculated returns
   ArrayResize(returns, total - 1);
   Print("Calculating returns for ", total, " data points");

   // Initialize variables to track maximum and minimum returns
   double max_return = -DBL_MAX;
   double min_return = DBL_MAX;
   int problematic_index = -1;
   int valid_returns_count = 0;

   // Iterate through the data points to calculate returns
   for(int i = 1; i < total; i++)
     {
      // Ensure the previous closing price is not zero to avoid division by zero
      if(data[i - 1][4] != 0.0)
        {
         // Calculate the return as the percentage change in closing prices
         double retorno = (data[i][4] - data[i - 1][4]) / data[i - 1][4];
         returns[i - 1] = retorno;
         valid_returns_count++;

         // Update maximum and minimum returns if applicable
         if(retorno > max_return)
            max_return = retorno;
         if(retorno < min_return)
            min_return = retorno;

         // Identify and log suspicious returns
         if(MathAbs(retorno) > 1.0)
           {
            Print("Suspicious return at index ", i, ": ", retorno);
            Print("Data[", i - 1, "][4] = ", data[i - 1][4], ", Data[", i, "][4] = ", data[i][4]);
            problematic_index = i;
           }

         // Periodically print return values for verification
         if(i % 50 == 0 || i == total - 1)
           {
            Print("Return for index ", i - 1, ": ", retorno);
           }
        }
      else
        {
         // If the previous closing price is zero, set the return to zero
         returns[i - 1] = 0.0;
         Print("Zero price found at index ", i - 1);
        }
     }

   // Print the maximum and minimum returns
   Print("Max return: ", max_return, ", Min return: ", min_return);
   
   // Log the index of any problematic returns
   if(problematic_index != -1)
     {
      Print("Problematic return found at index: ", problematic_index);
     }
  }
//+------------------------------------------------------------------+
//| Main function                                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   // Declare arrays to store historical data and returns
   double data[][6];
   double returns[];

   // Loop through each symbol to obtain historical data and calculate returns
   for(int i = 0; i < ArraySize(symbols); i++)
     {
      obtenerDatosHistoricos(symbols[i], from_date, to_date, data);

      // Determine the size of the data array
      int data_size = ArraySize(data);
      Print("Data size for ", symbols[i], ": ", data_size);

      // Ensure there is enough data to calculate returns
      if(data_size > 1 && ArrayRange(data, 1) == 6)
        {
         calcularRendimientos(data, returns);
         int returns_size = ArraySize(returns);
         Print("Returns size for ", symbols[i], ": ", returns_size);

         // Initialize variables to calculate the expected return and variance
         double sum_returns = 0;
         int valid_returns = 0;

         // Sum the valid returns and count them
         for(int j = 0; j < returns_size; j++)
           {
            if(MathIsValidNumber(returns[j]) && MathAbs(returns[j]) <= 1.0)
              {
               sum_returns += returns[j];
               valid_returns++;
              }
            else
              {
               Print("Invalid or extreme return at index ", j, ": ", returns[j]);
              }
           }

         // Calculate the mean return
         double mean_return = (valid_returns > 0) ? sum_returns / valid_returns : 0;

         // Calculate the variance of the returns
         double variance = 0;
         for(int j = 0; j < valid_returns; j++)
           {
            if(MathIsValidNumber(returns[j]) && MathAbs(returns[j]) <= 1.0)
              {
               variance += MathPow(returns[j] - mean_return, 2);
              }
           }
         variance = (valid_returns > 0) ? variance / valid_returns : 0;

         // Display the results in the console
         Print("Results for ", symbols[i]);
         Print("Expected return: ", mean_return);
         Print("Variance: ", variance);
         Print("Sum of returns: ", sum_returns);
         Print("Valid returns: ", valid_returns, " out of ", returns_size);
         Print("-----------------------");
        }
      else
        {
         // Log if there were insufficient data or an incorrect data format
         Print("Could not obtain enough data for ", symbols[i], " or the data format is incorrect");
         Print("-----------------------");
        }
     }
  }

Firstly, the script defines a list of assets to be considered, represented by their symbols. It also sets the date range for obtaining historical data, specifying a start and end date.

The function obtenerDatosHistoricos is responsible for fetching historical data for a given asset within the specified date range. It utilizes the CopyRates function to retrieve the data and store it in an array of MqlRates structures. The function then copies this data into an output array, ensuring the correct format and size. If the data retrieval fails, an error message is printed, while successful retrieval results in a confirmation message indicating the number of data points copied.

The calcularRendimientos function calculates daily returns from the historical data. It iterates through the data points, calculating the return as the percentage change in closing prices between consecutive days. The function handles potential errors, such as zero closing prices, and logs suspicious returns that exceed a predefined threshold. It tracks the maximum and minimum returns encountered during the calculations and prints periodic updates to verify the calculated returns.

The main function OnStart initializes arrays to store historical data and returns. It iterates through each asset symbol, calling obtenerDatosHistoricos to fetch the data and then calcularRendimientos to calculate the returns. After calculating the returns, the function proceeds to compute the expected return and variance. It sums the valid returns, calculates the mean return, and then computes the variance of the returns. The results, including the expected return, variance, sum of returns, and the count of valid returns, are printed to the console for each asset. If insufficient data is available or the data format is incorrect, an appropriate message is logged.

Throughout the script, extensive logging is implemented to ensure transparency and facilitate debugging. Messages indicate the progress and results of data retrieval, return calculation, and statistical analysis, allowing the user to track the execution flow and identify any issues that arise.

Results, to run this scripts, just load the script to a window, and you will see this results :

2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Copied 259 bars
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Copied 259 data points for EURUSD
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Data size for EURUSD: 1554
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Calculating returns for 259 data points
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Return for index 49: 0.008422556659553942
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Return for index 99: 0.0005552522233225886
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Return for index 149: -0.0016251305097825213
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Return for index 199: -0.0018138190337636214
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Return for index 249: 0.002726296367691935
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Return for index 257: -0.0023503674709141444
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Max return: 0.016843640170492922, Min return: -0.014629419109562279
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Returns size for EURUSD: 258
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Results for EURUSD
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Expected return: 0.00014629557982735741
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Variance: 0.000021906221916670055
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Sum of returns: 0.03774425959545821
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Valid returns: 258 out of 258
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     -----------------------
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Copied 259 bars
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Copied 259 data points for GBPUSD
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Data size for GBPUSD: 1554
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Calculating returns for 259 data points
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Return for index 49: 0.01253428642673093
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Return for index 99: -0.00020901161622245828
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Return for index 149: -0.0009419054513750523
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Return for index 199: 0.00011442115156718484
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Return for index 249: -0.0024846582214581455
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Return for index 257: 0.000015710672259594492
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Max return: 0.01795095252445456, Min return: -0.016589470883191758
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Returns size for GBPUSD: 258
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Results for GBPUSD
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Expected return: 0.0002283507472210021
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Variance: 0.000026680765574142948
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Sum of returns: 0.058914492783018545
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Valid returns: 258 out of 258
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     -----------------------
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Copied 250 bars
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Copied 250 data points for #AAPL
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Data size for #AAPL: 1500
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Calculating returns for 250 data points
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Return for index 49: 0.026341719766143464
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Return for index 99: 0.010473614547965662
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Return for index 149: -0.006613315549627641
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Return for index 199: -0.0011390170283046702
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Return for index 248: -0.006298074441174946
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Max return: 0.047845736667670974, Min return: -0.04621826746924895
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Returns size for #AAPL: 249
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Results for #AAPL
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Expected return: 0.0018199882676706617
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Variance: 0.00016500191971009266
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Sum of returns: 0.4531770786499948
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Valid returns: 249 out of 249
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     -----------------------
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Copied 250 bars
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Copied 250 data points for #AMZN
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Data size for #AMZN: 1500
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Calculating returns for 250 data points
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Return for index 49: 0.04365079365079357
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Return for index 99: 0.04105902777777781
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Return for index 149: -0.00952790314492451
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Return for index 199: -0.0033604251328539594
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Return for index 248: -0.009568443663346995
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Max return: 0.08595143898844165, Min return: -0.07525053686471019
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Returns size for #AMZN: 249
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Results for #AMZN
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Expected return: 0.002502188742255484
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Variance: 0.0004212375364322232
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Sum of returns: 0.6230449968216155
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     Valid returns: 249 out of 249
2024.07.11 21:46:42.458 optimizacion_carteras_v2 english (#AMZN,W1)     -----------------------

As you can see from results, AAPL should have around a 40% of invest, and AMZN a 60%, EURUSD a 3% and GBPUSD a 6%. These values are orientated, and no strategies have been used. 


Adding a Strategy to MQL5 Script

You must add this to the first script:

Moving Average Calculation Function

This function calculates the moving average for a given data set over a specified period. It iterates through the data points, summing the closing prices over the defined period, and then divides by the period to obtain the average. If there is insufficient data to calculate the moving average, the function sets the value to zero.

//+------------------------------------------------------------------+
//| Function to calculate moving average                             |
//+------------------------------------------------------------------+
void calcularMediaMovil(double &data[][6], int period, double &ma[])
  {
   int total = ArrayRange(data, 0);
   ArrayResize(ma, total);

   for(int i = 0; i < total; i++)
     {
      if(i >= period - 1)
        {
         double sum = 0;
         for(int j = i; j > i - period; j--)
           {
            sum += data[j][4]; // Closing price
           }
         ma[i] = sum / period;
        }
      else
        {
         ma[i] = 0.0; // Not enough data for moving average
        }
     }
  }
//+------------------------------------------------------------------+
//| Function to generate trading signals based on moving average cross |
//+------------------------------------------------------------------+
void generarSenales(double &data[][6], double &ma_rapida[], double &ma_lenta[], double &senales[])
  {
   int total = ArrayRange(data, 0);
   ArrayResize(senales, total);

   for(int i = 1; i < total; i++)
     {
      if(ma_rapida[i - 1] <= ma_lenta[i - 1] && ma_rapida[i] > ma_lenta[i])
        {
         senales[i] = 1; // Buy signal
        }
      else if(ma_rapida[i - 1] >= ma_lenta[i - 1] && ma_rapida[i] < ma_lenta[i])
        {
         senales[i] = -1; // Sell signal
        }
      else
        {
         senales[i] = 0; // No signal
        }
     }
  }

And integrate this to the OnStart:

void OnStart()
  {
   double data[][6];
   double returns[];

   for(int i = 0; i < ArraySize(symbols); i++)
     {
      obtenerDatosHistoricos(symbols[i], from_date, to_date, data);
      int data_size = ArraySize(data);

      if(data_size > 1 && ArrayRange(data, 1) == 6)
        {
         calcularRendimientos(data, returns);
         int returns_size = ArraySize(returns);

         double sum_returns = 0;
         int valid_returns = 0;

         for(int j = 0; j < returns_size; j++)
           {
            if(MathIsValidNumber(returns[j]) && MathAbs(returns[j]) <= 1.0)
              {
               sum_returns += returns[j];
               valid_returns++;
              }
           }

         double mean_return = (valid_returns > 0) ? sum_returns / valid_returns : 0;
         double variance = 0;
         for(int j = 0; j < valid_returns; j++)
           {
            if(MathIsValidNumber(returns[j]) && MathAbs(returns[j]) <= 1.0)
              {
               variance += MathPow(returns[j] - mean_return, 2);
              }
           }
         variance = (valid_returns > 0) ? variance / valid_returns : 0;

         Print("Results for ", symbols[i]);
         Print("Expected return: ", mean_return);
         Print("Variance: ", variance);
         Print("Sum of returns: ", sum_returns);
         Print("Valid returns: ", valid_returns, " out of ", returns_size);
         Print("-----------------------");

         // Calculate moving averages and trading signals
         double ma_rapida[];
         double ma_lenta[];
         double senales[];

         int periodo_rapido = 10; // Short period moving average
         int periodo_lento = 50;  // Long period moving average

         calcularMediaMovil(data, periodo_rapido, ma_rapida);
         calcularMediaMovil(data, periodo_lento, ma_lenta);
         generarSenales(data, ma_rapida, ma_lenta, senales);

         // Log trading signals
         for(int k = 0; k < ArraySize(senales); k++)
           {
            if(senales[k] != 0)
              {
               string tipo_senal = (senales[k] == 1) ? "Compra" : "Venta";
               Print("Signal for ", symbols[i], " on ", TimeToString((datetime)data[k][0]), ": ", tipo_senal);
              }
           }
        }
      else
        {
         Print("Could not obtain enough data for ", symbols[i], " or the data format is incorrect");
         Print("-----------------------");
        }
     }
  }

This function generates trading signals based on the crossing of two moving averages (a short period and a long period). It iterates through the data, checking for instances where the short-period moving average crosses above or below the long-period moving average. A cross above generates a buy signal, while a cross below generates a sell signal. If there is no crossover, no signal is generated.


Integration in the Main Function OnStart

In the main function, historical data is obtained for each symbol, and returns are calculated. Then, the moving averages and trading signals are computed for each symbol. The moving averages are obtained using the calcularMediaMovil function for both the short and long periods. The generarSenales function is used to generate buy and sell signals based on the moving average crossovers. The results, including the calculated signals, are printed to the console for review.

This code now includes a simple moving average crossover strategy that generates buy and sell signals. The trading signals are printed to the console for review, allowing adjustments to the periods of the moving averages as needed.

Results look like this:

2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Copied 259 bars
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Copied 259 data points for EURUSD
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Calculating returns for 259 data points
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Return for index 49: 0.008422556659553942
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Return for index 99: 0.0005552522233225886
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Return for index 149: -0.0016251305097825213
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Return for index 199: -0.0018138190337636214
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Return for index 249: 0.002726296367691935
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Return for index 257: -0.0023503674709141444
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Max return: 0.016843640170492922, Min return: -0.014629419109562279
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Results for EURUSD
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Expected return: 0.00014629557982735741
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Variance: 0.000021906221916670055
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Sum of returns: 0.03774425959545821
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Valid returns: 258 out of 258
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     -----------------------
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Signal for EURUSD on 2023.01.13 00:00: Compra
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Signal for EURUSD on 2023.03.10 00:00: Venta
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Signal for EURUSD on 2023.03.27 00:00: Compra
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Signal for EURUSD on 2023.05.19 00:00: Venta
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Signal for EURUSD on 2023.06.22 00:00: Compra
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Signal for EURUSD on 2023.08.14 00:00: Venta
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Signal for EURUSD on 2023.11.08 00:00: Compra
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Copied 259 bars
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Copied 259 data points for GBPUSD
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Calculating returns for 259 data points
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Return for index 49: 0.01253428642673093
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Return for index 99: -0.00020901161622245828
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Return for index 149: -0.0009419054513750523
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Return for index 199: 0.00011442115156718484
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Return for index 249: -0.0024846582214581455
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Return for index 257: 0.000015710672259594492
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Max return: 0.01795095252445456, Min return: -0.016589470883191758
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Results for GBPUSD
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Expected return: 0.0002283507472210021
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Variance: 0.000026680765574142948
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Sum of returns: 0.058914492783018545
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Valid returns: 258 out of 258
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     -----------------------
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Signal for GBPUSD on 2023.01.13 00:00: Compra
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Signal for GBPUSD on 2023.03.10 00:00: Venta
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Signal for GBPUSD on 2023.03.23 00:00: Compra
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Signal for GBPUSD on 2023.05.26 00:00: Venta
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Signal for GBPUSD on 2023.06.12 00:00: Compra
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Signal for GBPUSD on 2023.08.10 00:00: Venta
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Signal for GBPUSD on 2023.11.14 00:00: Compra
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Copied 250 bars
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Copied 250 data points for #AAPL
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Calculating returns for 250 data points
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Return for index 49: 0.026341719766143464
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Return for index 99: 0.010473614547965662
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Return for index 149: -0.006613315549627641
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Return for index 199: -0.0011390170283046702
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Return for index 248: -0.006298074441174946
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Max return: 0.047845736667670974, Min return: -0.04621826746924895
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Results for #AAPL
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Expected return: 0.0018199882676706617
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Variance: 0.00016500191971009266
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Sum of returns: 0.4531770786499948
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Valid returns: 249 out of 249
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     -----------------------
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Signal for #AAPL on 2023.01.17 00:00: Compra
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Signal for #AAPL on 2023.08.10 00:00: Venta
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Signal for #AAPL on 2023.10.18 00:00: Compra
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Signal for #AAPL on 2023.10.20 00:00: Venta
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Signal for #AAPL on 2023.11.10 00:00: Compra
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Copied 250 bars
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Copied 250 data points for #AMZN
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Calculating returns for 250 data points
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Return for index 49: 0.04365079365079357
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Return for index 99: 0.04105902777777781
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Return for index 149: -0.00952790314492451
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Return for index 199: -0.0033604251328539594
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Return for index 248: -0.009568443663346995
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Max return: 0.08595143898844165, Min return: -0.07525053686471019
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Results for #AMZN
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Expected return: 0.002502188742255484
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Variance: 0.0004212375364322232
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Sum of returns: 0.6230449968216155
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Valid returns: 249 out of 249
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     -----------------------
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Signal for #AMZN on 2023.01.17 00:00: Compra
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Signal for #AMZN on 2023.03.15 00:00: Venta
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Signal for #AMZN on 2023.03.24 00:00: Compra
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Signal for #AMZN on 2023.09.27 00:00: Venta
2024.07.11 22:02:03.328 optimizacion_carteras_v3 english (#AMZN,W1)     Signal for #AMZN on 2023.11.07 00:00: Compra

The article presents a comprehensive exploration of portfolio optimization techniques utilizing both Python and MQL5 programming languages with the MetaTrader 5 platform emphasizing the critical role of data-driven decision-making in modern financial management It elucidates the process of developing and implementing sophisticated algorithms that analyze historical data optimize asset allocation and generate trading signals based on moving average crossovers.

The author's underscore the importance of portfolio optimization as an essential tool for achieving efficient risk-adjusted returns in an increasingly complex and volatile investment landscape By leveraging advanced mathematical models and computational power these programs enable investors to make informed decisions tailored to their specific risk tolerances and investment objectives. The article demonstrates how such optimization techniques can systematically analyze vast amounts of historical data market trends and asset correlations to determine optimal asset allocations that maximize potential returns while minimizing overall portfolio risk.

The dual approach of using Python for initial strategy testing and MQL5 for seamless integration with MetaTrader 5 showcases the versatility and power of combining different programming environments. This methodology allows traders to benefit from Python's extensive libraries and analytical capabilities while harnessing the native functionalities of the MetaTrader 5 platform.

The necessity of employing portfolio optimization in trading cannot be overstated as it provides a scientific approach to portfolio construction helping mitigate human biases and emotional decision-making often associated with traditional investment strategies In an era of global economic interconnectedness and rapid information flow these sophisticated tools offer a competitive edge enabling investors to navigate uncertainties and capitalize on opportunities across diverse asset classes.

The article provides a detailed walkthrough of the implementation process including code snippets for both Python and MQL5 illustrating how to obtain historical data calculate returns generate trading signals based on moving average crossovers and optimize asset allocation It also demonstrates how to visualize results which is crucial for interpreting and communicating findings effectively.

In summary the article serves as a valuable resource for traders and financial professionals seeking to enhance their investment strategies through advanced portfolio optimization techniques. It highlights the synergy between quantitative finance and technology offering practical insights into developing robust and adaptable trading systems. The authors conclusion emphasizes the indispensable nature of such tools in achieving efficient risk-adjusted returns and constructing resilient investment strategies in today's dynamic financial markets.


Conclusion

In conclusion, the integration of Python and MQL5 for portfolio optimization represents a significant advancement in trading strategy development, blending the analytical prowess of Python with the seamless trading capabilities of MetaTrader 5. These innovative programs are designed to empower traders with the ability to analyze historical data, optimize asset allocation, and visualize results effectively, thus enhancing data-driven decision-making in the financial markets.

Utilizing Python's extensive libraries, including Pandas, Numpy, and cvxpy, along with MetaTrader 5's native MQL5 scripting, traders can efficiently manage and adapt their portfolios to changing market conditions. This dual approach not only facilitates the initial testing of strategies in Python but also ensures their practical application and accuracy within the MetaTrader 5 environment. As the financial landscape becomes increasingly complex and volatile, such sophisticated tools are indispensable for achieving efficient risk-adjusted returns. By mitigating human biases and leveraging advanced mathematical models, these portfolio optimization programs offer a robust framework for constructing resilient investment strategies.

We appreciate your interest in this article and encourage you to explore these tools further for enhanced trading performance and strategic financial management. Thank you and best regards.

Developing an Expert Advisor (EA) based on the Consolidation Range Breakout strategy in MQL5 Developing an Expert Advisor (EA) based on the Consolidation Range Breakout strategy in MQL5
This article outlines the steps to create an Expert Advisor (EA) that capitalizes on price breakouts after consolidation periods. By identifying consolidation ranges and setting breakout levels, traders can automate their trading decisions based on this strategy. The Expert Advisor aims to provide clear entry and exit points while avoiding false breakouts
Cascade Order Trading Strategy Based on EMA Crossovers for MetaTrader 5 Cascade Order Trading Strategy Based on EMA Crossovers for MetaTrader 5
The article guides in demonstrating an automated algorithm based on EMA Crossovers for MetaTrader 5. Detailed information on all aspects of demonstrating an Expert Advisor in MQL5 and testing it in MetaTrader 5 - from analyzing price range behaviors to risk management.
SP500 Trading Strategy in MQL5 For Beginners SP500 Trading Strategy in MQL5 For Beginners
Discover how to leverage MQL5 to forecast the S&P 500 with precision, blending in classical technical analysis for added stability and combining algorithms with time-tested principles for robust market insights.
Price Driven CGI Model: Theoretical Foundation Price Driven CGI Model: Theoretical Foundation
Let's discuss the data manipulation algorithm, as we dive deeper into conceptualizing the idea of using price data to drive CGI objects. Think about transferring the effects of events, human emotions and actions on financial asset prices to a real-life model. This study delves into leveraging price data to influence the scale of a CGI object, controlling growth and emotions. These visible effects can establish a fresh analytical foundation for traders. Further insights are shared in the article.