
用Python和MQL5进行投资组合优化
引言
介绍两种创新的投资组合优化程序,旨在彻底革新交易策略,最大化回报的同时最小化风险。第一个是基于Python的解决方案,它利用MetaTrader 5的集成以及pandas、Numpy和cvxpy等高级库的力量,分析历史数据、优化资产配置,并通过Matplotlib可视化结果。第二个是用MQL5实现的类似程序,它利用MetaTrader 5平台的原生功能,为交易者提供在其首选交易环境中无缝使用的体验。这两个程序都体现了量化金融与技术的前沿交叉,为交易者提供了强大的工具,使他们能够在不断变化的市场环境中做出数据驱动的决策。
为什么我们需要投资组合优化?
投资组合优化程序是现代金融管理中的重要工具,它满足了在日益复杂和动荡的投资环境中对高效风险调整收益的迫切需求。通过利用先进的数学模型和计算能力,这些程序使投资者和金融专业人士能够根据其特定的风险承受能力和投资目标,做出数据驱动的决策。这些程序系统地分析大量的历史数据、市场趋势和资产相关性,以确定最优的资产配置,从而在最小化整体投资组合风险的同时最大化潜在收益。
这种科学的投资组合构建方法有助于减少传统投资策略中常见的由人类偏见和情绪驱动的决策。此外,投资组合优化程序还便于动态再平衡,使投资者能够迅速适应市场变化,并保持与长期财务目标的一致性。在全球经济相互联系日益紧密、信息流动迅速的时代,这些复杂的工具为投资者提供了竞争优势,使他们能够在多样化的资产类别中应对不确定性并抓住机会,从而促进更具稳健性和韧性的投资策略的形成。
为什么我们应该使用Python?
Python是一种快速尝试策略或想法的好方法,它拥有庞大的社区支持,并且运行速度较快。它还配备了MetaTrader5库,可用于下载数据甚至执行交易。因此,我们首先会使用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,但我们可以为脚本添加一个策略,看看数值是否会改变,结果可能会要求我们投资其他交易品种。
我们可以通过在脚本中添加以下内容来实现这一点(我们将使用一个简单的双移动平均线交叉策略),你可以根据需要修改这个策略:
# 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等丰富库,以及MetaTrader 5的MQL5脚本,交易者可以高效地管理和适应市场变化的投资组合。这种双重方法不仅便于在Python中进行策略的初步测试,还确保了策略在MetaTrader 5环境中的实际应用和准确性。随着金融环境日益复杂和动荡,这些复杂的工具对于实现高效风险调整收益至关重要。通过减少人类偏见并利用先进的数学模型,这些投资组合优化程序为构建韧性投资策略提供了坚实的框架。
感谢您对本文的兴趣,我们鼓励您进一步探索这些工具,以提升交易表现和战略金融管理能力。谢谢,祝好!
本文由MetaQuotes Ltd译自英文
原文地址: https://www.mql5.com/en/articles/15288


