English Deutsch
preview
PythonとMQL5によるポートフォリオ最適化

PythonとMQL5によるポートフォリオ最適化

MetaTrader 5統計と分析 | 5 9月 2024, 10:30
22 0
Javier Santiago Gaston De Iriarte Cabrera
Javier Santiago Gaston De Iriarte Cabrera

はじめに

取引戦略を刷新し、リスクを最小限に抑えつつリターンを最大化するための2つの革新的なポートフォリオ最適化プログラムを紹介します。最初のPythonベースのソリューションでは、pandas、Numpy、cvxpyなどの高度なライブラリを活用し、MetaTrader 5との統合により、過去データを分析して最適な資産配分をおこない、Matplotlibで結果を可視化します。次に、MQL5を使用した同様の実装では、MetaTrader 5のネイティブ機能を活かし、トレーダーの好む取引環境でシームレスな体験を提供します。両プログラムは、クオンツファイナンスとテクノロジーの最先端を体現しており、トレーダーは進化し続ける市場環境に対応したデータ主導の意思決定をおこなうための強力なツールを手にすることができます。


なぜポートフォリオ最適化が必要なのか?

ポートフォリオ最適化プログラムは、複雑さが増し、変動の激しい現代の投資環境で、効率的なリスク調整後リターンを追求するために不可欠なツールです。これらのプログラムは、数学的モデルと高い計算能力を駆使し、投資家や金融専門家がリスク許容度や投資目標に合わせたデータ主導の意思決定をおこなうのを支援します。過去の膨大なデータを体系的に分析し、マーケットトレンドや資産間の相関関係を評価することで、ポートフォリオ全体のリスクを最小化しつつ、最大のリターンを追求する最適な資産配分を決定します。

この科学的なポートフォリオ構築アプローチは、従来の投資戦略にしばしば見られる人間の偏見や感情的な意思決定を軽減する効果があります。さらに、ポートフォリオ最適化プログラムは動的なリバランスを可能にし、投資家が変化する市場状況に迅速に対応しながら、長期的な財務目標との整合性を維持できるよう支援します。世界経済が密接に相互接続し、情報が急速に伝わる時代において、これらの洗練されたツールは投資家に競争優位をもたらし、不確実性を乗り越えつつ多様な資産クラスの機会を最大限に活用できるようにします。最終的には、より堅牢で回復力のある投資戦略の構築を促進します。


なぜPythonを使う必要があるのか?

Pythonは、戦略やアイデアを素早く試すための優れた手段です。大規模なコミュニティに支えられ、実行スピードも速いです。さらに、MetaTrader 5ライブラリを活用すれば、データのダウンロードや取引の実行も可能です。まず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

そしてこれがグラフです。

グラフ1

この結果は、現在アマゾンのみに投資するよう指示していますが、スクリプトに戦略を追加することで、値動きに応じて他の銘柄にも投資するように変更できます。

この目的を達成するために、スクリプトに以下を追加します(ここでは2つの移動平均線が交差するシンプルな戦略を使用)。

# 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("-----------------------");
        }
     }
  }

この関数は、2本の移動平均線(短期間と長期間)のクロスに基づいて売買シグナルを生成します。短期間移動平均が長期間移動平均を上回ったり、下回ったりしていないか、データを反復処理して確認します。上にクロスすれば買いシグナル、下にクロスすれば売りシグナルとなります。クロスオーバーがなければ、シグナルは生成されません。


メイン関数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という2つのプログラミング言語とMetaTrader 5プラットフォームを活用して、ポートフォリオ最適化手法を深く掘り下げ、現代の金融管理におけるデータ駆動型の意思決定の重要性を明らかにします。過去のデータを分析し、資産配分を最適化するための高度なアルゴリズムを開発し、移動平均クロスオーバーに基づく売買シグナルの生成プロセスを詳細に解説します。

著者は、複雑化し変動が激しい投資環境において、リスク調整後リターンの最適化におけるポートフォリオ最適化の重要性を強調しています。高度な数学モデルと計算能力を駆使することで、これらのプログラムは、投資家がリスク許容度や投資目的に応じた情報に基づいた意思決定をおこなうための強力なツールとなります。この記事では、最適化技術が膨大な過去データを用いて市場動向や資産相関を体系的に分析し、ポートフォリオ全体のリスクを最小限に抑えながら潜在的リターンを最大化する方法を示しています。

戦略の初期テストにはPythonを使用し、MetaTrader 5とのシームレスな統合にはMQL5を活用するという2つのアプローチは、異なるプログラミング環境を組み合わせることで得られる多様性とパワーを示しています。この方法により、トレーダーはMetaTrader 5のネイティブ機能を利用しつつ、Pythonの豊富なライブラリと分析機能を最大限に活用することができます。

ポートフォリオ最適化を取引に取り入れることの重要性は、従来の投資戦略に見られる人間のバイアスや感情的な意思決定を減少させる科学的アプローチの提供にあります。グローバル経済の相互接続と情報の迅速な流れの時代において、これらの洗練されたツールは、投資家が不確実性を乗り越え、多様な資産クラスの機会を最大限に活用するための競争力を提供します。

この記事では、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

初心者のためのMQL5におけるファンダメンタル分析とテクニカル分析戦略の組み合わせ 初心者のためのMQL5におけるファンダメンタル分析とテクニカル分析戦略の組み合わせ
この記事では、トレンドフォローとファンダメンタル分析の原則を1つのエキスパートアドバイザー(EA)にシームレスに統合し、より強固な取引戦略を構築する方法について説明します。MQL5を活用して、誰でも簡単にカスタマイズされた取引アルゴリズムを作成できることを紹介します。
MQL5でインタラクティブなグラフィカルユーザーインターフェイスを作成する(第2回):コントロールと応答性の追加 MQL5でインタラクティブなグラフィカルユーザーインターフェイスを作成する(第2回):コントロールと応答性の追加
ダイナミックな機能でMQL5のGUIパネルを強化することで、ユーザーの取引体験を大幅に向上させることができます。インタラクティブな要素、ホバー効果、リアルタイムのデータ更新を取り入れることで、パネルは現代のトレーダーにとって強力なツールとなるでしょう。
エラー 146 (「トレードコンテキスト ビジー」) と、その対処方法 エラー 146 (「トレードコンテキスト ビジー」) と、その対処方法
この記事では、MT4において複数のEAの衝突をさける方法を扱います。ターミナルの操作、MQL4の基本的な使い方がわかる人にとって、役に立つでしょう。
データサイエンスと機械学習(第26回):時系列予測における究極の戦い - LSTM対GRUニューラルネットワーク データサイエンスと機械学習(第26回):時系列予測における究極の戦い - LSTM対GRUニューラルネットワーク
前回の記事では、データの長期的な依存関係をうまく捉えられないにもかかわらず、利益を上げる戦略を構築できる単純RNNについて説明しました。この記事では、LSTM (Long-Short Term Memory)とGRU (Gated Recurrent Unit)の両方について説明します。この2つは、単純RNNの欠点を克服し、それを凌駕するために紹介されました。