
Оптимизация портфеля на языках Python и MQL5
Введение
Представляем две инновационные программы оптимизации портфеля для кардинального изменения торговых стратегий и доведения доходности до максимума при минимальных рисках. Первое решение (на базе языка Python) использует возможности интеграции платформы MetaTrader 5 с такими передовыми библиотеками, как Pandas, Numpy и cvxpy, для анализа исторических данных, оптимизации распределения активов и визуализации результатов с помощью Matplotlib. Вторая похожая реализация, созданная на языке MQL5, использует собственные возможности платформы MetaTrader 5, предлагая трейдерам опыт бесперебойной работы в предпочитаемой ими торговой среде. Обе программы представляют собой примеры передового взаимодействия финансовой математики и технологий, снабжая трейдеров сложными инструментами для принятия решений на основе данных в постоянно меняющейся рыночной среде.
Для чего нам нужна оптимизация портфеля?
Программы для оптимизации портфеля являются важнейшими инструментами современного финансового менеджмента, удовлетворяя острую потребность в эффективной доходности с поправкой на риск в условиях все более сложной и неустойчивой инвестиционной среды. Используя передовые математические модели и вычислительные мощности, эти программы позволяют инвесторам и специалистам по финансам принимать решения на основе данных, учитывающие конкретные уровни риска и инвестиционные цели. Такие программы систематически анализируют огромные объемы исторических данных о рыночных трендах и корреляции активов для выявления возможностей оптимального распределения активов, обеспечивая максимальную потенциальную доходность и сводя к минимуму общий риск портфеля.
Этот научный подход к формированию портфеля помогает смягчить влияние человеческого фактора и принятие решений на основе эмоций, часто присущие традиционным стратегиям инвестирования. Более того, программы оптимизации портфеля упрощают процесс динамического изменения баланса активов, позволяя инвесторам быстро адаптироваться к меняющимся условиям рынка и достигать долгосрочных финансовых целей. В эпоху глобальной экономической взаимосвязанности и быстрого обмена информацией эти тщательно разработанные инструменты обеспечивают инвесторам конкуретное преимущество, давая им возможность ориентироваться в условиях неопределенности и извлекать пользу и прибыль из возможностей, предоставляемых активами разных классов, в конечном итоге способствуя созданию более надежных и устойчивых инвестиционных стратегий.
Зачем нам Python?
Python — хороший способ быстро опробовать стратегии или идеи, у этого языка большое сообщество, и он быстро работает. Кроме того, он подходит для библиотеки MetraTrader5, позволяя загружать данные или даже совершать сделки. Итак, сначала воспользуемся языком Python, чтобы проверить, насколько последовательна идея, и с разными символами нам следует использовать разный % счета.
Вот какой скрипт на Python мы собираемся применить:
# 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)
Результаты выглядят следующим образом:
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
а вот график:
Результаты говорят нам, что инвестировать нужно только в Amazon, но это позволяет добавить в сценарий стратегию, чтобы увидеть, изменятся ли значения, и результаты указывают, что следует инвестировать в другой символ.
Мы можем сделать это, добавив его в скрипт (воспользуемся простой стратегией пересечения двух MAS), и вы можете изменить эту стратегию, чтобы применить тот, который вам нужен:
# 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
а также добавив следующее в основную часть:
# Apply the moving average crossover strategy data = apply_sma_strategy(data) # Adjust returns according to the strategy data = adjust_returns(data)
Результаты показаны следующим образом:
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
Произошли некоторые небольшие изменения, но в целом результаты выглядят прежними.
Это связано с тем, что на протяжении всего этого периода AMZN демонстрировал огромный тренд.
Вы должны знать об этом и следить за трендом, чтобы вовремя остановиться, не вы можете понять, почему так важно управление рисками.
Скрипт на MQL5
Мы только что проверили, как разрабатывать этот сценарий управления рисками на языке Python, а теперь воспроизведем тот же скрипт на MQL5, так как язык MQL5 точнее.
Вот этот скрипт:
//+------------------------------------------------------------------+ //| 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("-----------------------"); } } }
Во-первых, скрипт определяет список представленных соответствующими символами активов, которые необходимо учитывать. Кроме того, он устанавливает диапазон дат для получения исторических данных, указывая начальную и конечную даты.
Функция obtenerDatosHistoricos отвечает за получение исторических данных для указанного актива в заданном диапазоне дат. Она использует функцию CopyRates для извлечения данных и сохранения их в массиве структур MqlRates. Затем функция копирует эти данные в выходной массив, обеспечивая правильные формат и размер. Если извлечь данные не удается, печатается сообщение об ошибке, тогда как при успешном извлечении выводится подтверждающее сообщение с указанием количества скопированных точек данных.
Функция calcularRendimientos рассчитывает ежедневную доходность на основе исторических данных. Она выполняет итерацию по точкам данных, рассчитывая доходность как изменение процентного соотношения цен закрытия между днями, следующими друг за другом. Функция обрабатывает потенциальные ошибки, такие как нулевые цены закрытия, и регистрирует подозрительную доходность, превышающую предварительно заданный порог. Она отслеживает максимальную и минимальную доходность, полученную при расчетах, и печатает периодические обновления для проверки рассчитанной доходности.
Основная функция OnStart инициализирует массивы для хранения исторических данных и данных о доходности. Она проходит по каждому символу актива, вызывая функцию obtenerDatosHistoricos для извлечения данных, а затем — calcularRendimientos для расчета доходности. После расчета доходности функция переходит к вычислению ожидаемой доходности и дисперсии. Она суммирует валидные значения доходности, вычисляет среднюю доходность, а затем вычисляет дисперсию доходности. Результаты, включая ожидаемую доходность, дисперсию, сумму значений доходности и количество валидных значений доходности, выводятся на консоль по каждому активу. Если данных недостаточно или их формат неверный, регистрируется соответствующее сообщение.
По всему скрипту реализуется развернутая регистрация для обеспечения прозрачности и упрощения отладки. Сообщения отображают ход и результаты извлечения данных, расчета доходности и статистического анализа, позволяя пользователю отслеживать процесс исполнения и выявлять любые возникающие проблемы.
Результаты
Для запуска этих скриптов просто загрузите скрипт в окно, и вы увидите следующие результаты:
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) -----------------------
Как видно из результатов, у AAPL должно быть около 40% инвестиций, AMZN — 60%, EURUSD — 3%, а GBPUSD — 6%. Эти значения — ориентировочные, никакие стратегии не использовались.
Добавление стратегии в скрипт MQL5
В первый скрипт необходимо добавить следующее:
Функция расчета скользящих средних
Эта функция вычисляет скользящее среднее для заданного набора данных за указанный период. Она выполняет итерации по точкам данных, суммируя цены закрытия за определенный период, а затем делит на период для получения среднего значения. Если данных недостаточно для расчета скользящего среднего, функция устанавливает значение равным нулю.
//+------------------------------------------------------------------+
//| 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
}
}
}
И интегрируем это в функцию 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("-----------------------"); } } }
Эта функция генерирует торговые сигналы на основе пересечения двух скользящих средних (с коротким периодом и с длинным периодом). Она выполняет итерации по данным, проверяя случаи, когда скользящая средняя с коротким периодом пересекает скользящую среднюю с длинным периодом снизу вверх или сверху вниз. Пересечение снизу вверх генерирует сигнал на покупку, а пересечение сверху вниз — сигнал на продажу. Если пересечения нет, сигнал не генерируется.
Интеграция в основную функцию OnStart
В основной функции для каждого символа происходит получение исторических данных, при этом рассчитывается доходность. Затем для каждого символа рассчитываются скользящие средние и торговые сигналы. Скользящие средние получаются с помощью функции calcularMediaMovil и для коротких, и для длинных периодов. Функция generarSenales используется для генерации сигналов на покупку и продажу на основе пересечений скользящих средних. Результаты, включая вычисленные сигналы, выводятся на консоль для просмотра.
Теперь этот код включает в себя стратегию пересечений простых скользящих средних, которая генерирует сигналы на покупки и продажи. Торговые сигналы выводятся на консоль для просмотра, позволяя при необходимости вносить корректировки в периоды скользящих средних.Результаты выглядят следующим образом:
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
В статье представлено комплексное исследование методов оптимизации портфеля с использованием обоих языков программирования (Python и MQL5) на платформе MetaTrader 5, подчеркивающее важную роль принятия решений на основе данных в современном финансовом менеджменте. Кроме того, в ней описан процесс разработки и реализации сложных алгоритмов, которые анализируют исторические данные, оптимизируют распределение активов и генерируют торговые сигналы на основе пересечения скользящих средних.
Авторы делают акцент на значении оптимизации портфеля как важнейшего инструмента для достижения эффективной доходности с поправкой на риск в условиях все более сложной и неустойчивой инвестиционной среды. Используя передовые математические модели и вычислительные мощности, эти программы позволяют инвесторам принимать обоснованные решения, отвечающие именно их допустимым уровням риска и инвестиционным целям. Статья демонстрирует, как такие методы оптимизации могут систематически анализировать огромные объемы исторических данных о рыночных трендах и корреляции активов для определения оптимального распределения активов, которое увеличивает потенциальную доходность до максимума и сводит общий риск портфеля к минимуму.
Двойной подход с использованием языка Python для первоначального тестирования стратегии и языка MQL5 для беспрепятственной интеграции с платформой MetaTrader 5 демонстрирует универсальность и мощь объединения разных сред программирования. Такая методология позволяет трейдерам извлекать пользу из обширных библиотек и аналитических возможностей языка Python, одновременно используя собственные функции платформы MetaTrader 5.
Необходимость организации оптимизации портфеля в трейдинге нельзя переоценить, поскольку она обеспечивает научный подход к формированию портфеля, помогая нивелировать влияние человеческой предвзятости и принятия эмоциональных решений, часто связанное с традиционными стратегиями в инвестировании. В эпоху глобального экономического взаимовлияния и быстрого обмена информацией такие сложные инструменты предлагают конкурентное преимущество, позволяя инвесторам ориентироваться в условиях неопределенности и извлекать выгоду из возможностей, которые предоставляют активы разных классов.
Статья представляет подробное пошаговое руководство процесса реализации, включая фрагментами кодов на обоих языках, Python и MQL5, иллюстрируя способы получения исторических данных, генерации сигналов на основе пересечения скользящих средних и оптимизации распределения активов. Кроме того, она показывает, как визуализировать результаты, что имеет решающее значение для эффективной интерпретации и передачи результатов.
Подводя итог, можно сказать, что статья представляет собой ценный ресурс для трейдеров и специалистов в области финансов, стремящихся улучшить свои инвестиционные стратегии с помощью передовых методов оптимизации портфеля. В ней подчеркивается синергия между финансовой математикой и технологиями, а также предлагаются практические идеи по разработке надежных и гибких торговых систем. В заключение авторы подчеркивают незаменимый характер таких инструментов в достижении эффективной доходности с поправкой на риск и построении устойчивых инвестиционных стратегий на современных динамичных финансовых рынках.
Заключение
В заключение отметим, что интеграция Python и MQL5 для оптимизации портфеля представляет собой значительный шаг вперед в деле разработки торговых стратегий, объединяя аналитические возможности Python с возможностями бесперебойной торговли на платформе MetaTrader 5. Эти инновационные программы призваны дать трейдерам возможность эффективно анализировать исторические данные, оптимизировать распределение активов и визуализировать результаты, повышая тем самым качество принятия решений на основе данных о финансовых рынках.
Используя обширные библиотеки Python, включая Pandas, Numpy и cvxpy, а также встроенные MQL5-скрипты платформы MetaTrader 5, трейдеры могут эффективно управлять своими портфелями и адаптировать их к изменяющимся условиям рынка. Такой двойной подход не только облегчает первоначальное тестирование стратегий на языке Python, но и обеспечивает их практическое применение на практике и точность в среде MetaTrader 5. Поскольку финансовый ландшафт становится все более сложным и волатильным, такие сложные инструменты становятся незаменимыми в достижении эффективной доходности с поправкой на риск. Смягчая влияние человеческого фактора и используя передовые математические модели, эти программы оптимизации портфеля предлагают надежную основу для построения устойчивых инвестиционных стратегий.
Благодарим за интерес к этой статье и рекомендуем глубже изучить эти инструменты для повышения эффективности торговли и стратегического финансового менеджмента. Спасибо и всего наилучшего.
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/15288
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.





- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования