![Portfolio Optimization in Python and MQL5](https://c.mql5.com/2/84/Portfolio_Optimization_in_Python_and_MQL5_600x314.jpg)
Portfolio Optimization in Python and MQL5
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:
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
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.
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](https://c.mql5.com/2/84/Developing_an_Expert_Advisor_based_on_the_Consolidation_Range_Breakout_strategy_in_MQL5___LOGO.png)
![Cascade Order Trading Strategy Based on EMA Crossovers for MetaTrader 5](https://c.mql5.com/2/84/Cascade_Order_Trading_Strategy_Based_on_EMA_Crossovers___LOGO.png)
![SP500 Trading Strategy in MQL5 For Beginners](https://c.mql5.com/2/84/SP500_Trading_Strategy_in_MQL5____LOGO.png)
![Price Driven CGI Model: Theoretical Foundation](https://c.mql5.com/2/84/Price_Driven_CGI_Model___Theoretical_Foundation___LOGO.png)
![MQL5 - Language of trade strategies built-in the MetaTrader 5 client terminal](https://c.mql5.com/i/registerlandings/logo-2.png)
- Free trading apps
- Over 8,000 signals for copying
- Economic news for exploring financial markets
You agree to website policy and terms of use