English Русский Español Deutsch 日本語 Português
preview
用Python和MQL5进行投资组合优化

用Python和MQL5进行投资组合优化

MetaTrader 5统计分析 | 14 二月 2025, 11:13
239 0
Javier Santiago Gaston De Iriarte Cabrera
Javier Santiago Gaston De Iriarte Cabrera

引言

介绍两种创新的投资组合优化程序,旨在彻底革新交易策略,最大化回报的同时最小化风险。第一个是基于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

这是图形:

Graph 1

这些结果告诉我们只投资于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

图 2

虽然有些小修改,但是结果总体上看差不多。

这是因为在整个期间,亚马逊(AMZN)呈现出了显著的趋势。

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

S&amp;P 500交易策略在MQL5中的实现(适合初学者) S&amp;P 500交易策略在MQL5中的实现(适合初学者)
了解如何利用MQL5精准预测标普500指数,结合经典技术分析以增强稳定性,并将算法与经过时间验证的原则相结合,以获得稳健的市场洞察。
MQL5 交易工具包(第 2 部分):扩展和实现仓位管理 EX5 库 MQL5 交易工具包(第 2 部分):扩展和实现仓位管理 EX5 库
了解如何在 MQL5 代码或项目中导入和使用 EX5 库。在这篇续文中,我们将通过向现有库中添加更多仓位管理功能并创建两个 EA 交易系统来扩展 EX5 库。第一个例子将使用可变指数动态平均(Variable Index Dynamic Average,VIDYA)技术指标来开发追踪止损交易策略 EA 交易,而第二个例子将利用交易面板来监控、开仓、平仓和修改仓位。这两个例子将演示如何使用和实现升级后的 EX5 仓位管理库。
适应性社会行为优化(ASBO):两阶段演变 适应性社会行为优化(ASBO):两阶段演变
我们继续探讨生物体的社会行为及其对新数学模型 ASBO(适应性社会行为优化)开发的影响。我们将深入研究两阶段演变,测试算法并得出结论。正如在自然界中,一群生物体共同努力生存一样,ASBO 使用集体行为原理来解决复杂的优化问题。
在MetaTrader 5中实现基于EMA交叉的级联订单交易策略 在MetaTrader 5中实现基于EMA交叉的级联订单交易策略
本文介绍一个基于EMA交叉信号的自动交易算法,该算法适用于MetaTrader 5平台。文章详细阐述了在MQL5中开发一个EA所需的方方面面,以及在MetaTrader 5中进行测试的过程——从分析价格区间行为到风险管理。