English Русский 中文 Deutsch 日本語 Português
preview
Estrategia de trading del SP500 en MQL5 para principiantes

Estrategia de trading del SP500 en MQL5 para principiantes

MetaTrader 5Ejemplos |
981 5
Gamuchirai Zororo Ndawana
Gamuchirai Zororo Ndawana

Introducción

En este artículo analizaremos cómo podemos desarrollar una estrategia comercial inteligente para operar con el índice Standard & Poor's 500 (S&P 500) utilizando MQL5. Nuestro debate demostrará cómo aprovechar la flexibilidad de MQL5 para crear un asesor experto dinámico que se base en una combinación de inteligencia artificial y análisis técnico. Nuestro razonamiento es que confiar únicamente en la IA puede llevar a estrategias inestables; sin embargo, al guiar la IA con los principios probados del análisis técnico, podemos lograr un enfoque más confiable. Esta estrategia híbrida tiene como objetivo descubrir patrones ocultos dentro de las grandes cantidades de datos disponibles en nuestras terminales MetaTrader 5, aprovechando las fortalezas de la IA, el análisis tradicional y el lenguaje MQL5 para mejorar el rendimiento comercial y la toma de decisiones.

Después de leer este artículo, usted obtendrá:

  • Buenos principios de programación para MQL5
  • Información sobre cómo los comerciantes pueden analizar fácilmente grandes conjuntos de datos de múltiples símbolos simultáneamente utilizando comandos de álgebra lineal en la API MQL5.
  • Técnicas para descubrir patrones ocultos en datos de mercado que no son inmediatamente evidentes.


Historia: ¿Qué es el Standard & Poor's 500?

El S&P 500, creado en 1957, es un índice de referencia crucial que refleja el desempeño general de la economía estadounidense. A lo largo de los años, se han añadido y eliminado numerosas empresas al índice, lo que refleja la naturaleza dinámica del mercado. Entre los miembros más antiguos se encuentran General Electric, J.P. Morgan y Goldman Sachs han mantenido sus posiciones en el índice desde su creación.

El S&P 500 representa el valor promedio de las 500 empresas más grandes de los Estados Unidos de América. Al valor de cada empresa se le asigna un peso en función de su capitalización de mercado. Por lo tanto, el S&P 500 también puede considerarse un índice ponderado por capitalización de mercado de las 500 empresas más grandes que cotizan en bolsa en el mundo.

Hoy en día, empresas pioneras como Tesla y Nvidia han redefinido el punto de referencia desde su fundación. En la actualidad, la mayor parte del peso del S&P 500 está en manos de grandes empresas tecnológicas, entre las que se incluyen Google, Apple y Microsoft, entre los gigantes antes mencionados. Estos titanes tecnológicos han transformado el panorama del índice, reflejando el cambio de la economía hacia la tecnología y la innovación.


Descripción general de nuestra estrategia comercial

Una lista de las empresas incluidas en el índice es fácilmente accesible en Internet. Podemos aprovechar nuestra comprensión de cómo se compone el índice para ayudarnos a crear una estrategia comercial. Seleccionaremos un puñado de las empresas más grandes del índice y utilizaremos el precio respectivo de cada empresa como entrada para un modelo de IA que predecirá el precio de cierre del índice basándose en nuestra muestra de empresas que tienen pesos proporcionalmente grandes. Nuestro deseo es desarrollar una estrategia que combine la IA con técnicas confiables y probadas a lo largo del tiempo.

Nuestro sistema de análisis técnico empleará principios de seguimiento de tendencias para generar señales comerciales. Necesitamos incorporar varios indicadores clave:

  • El índice de canal de productos básicos (Commodity Channel Index, CCI) sera nuestro indicador de volumen. Sólo queremos ingresar a operaciones que estén respaldadas por volumen.
  • El índice de fuerza relativa (Relative Strength Index, RSI) y el rango de porcentaje de Williams (Williams Percent Range, WPR) para medir la presión de compra o venta en el mercado.
  • Una media móvil (MA) como nuestro indicador de confirmación final.

El índice de canal de productos básicos (CCI) gira alrededor de 0. Una lectura superior a 0 sugiere un volumen fuerte para una oportunidad de compra, mientras que una lectura inferior a 0 indica un volumen de soporte para la venta. Requerimos alineación con otros indicadores para la confirmación comercial, mejorando nuestras posibilidades de encontrar configuraciones de alta probabilidad.

Por lo tanto, para que podamos comprar:

  • La lectura del CCI debe ser mayor que 0.
  • La lectura del RSI debe ser mayor que 50.
  • La lectura de WPR debe ser mayor que -50.
  • MA debe estar por debajo del precio de cierre.

Y a la inversa para que vendamos:

  • La lectura del CCI debe ser menor que 0.
  • La lectura del RSI debe ser inferior a 50.
  • La lectura de WPR debe ser menor que -50.
  • MA debe estar por encima del precio de cierre.

Por otro lado, para construir nuestro sistema de IA, primero necesitamos obtener los precios históricos de 12 acciones de gran capitalización de mercado incluidas en el S&P 500 y los precios de cierre del S&P 500. Estos serán nuestros datos de entrenamiento que usaremos para construir nuestro modelo de regresión lineal múltiple para pronosticar los precios futuros del índice.

A partir de ahí, el sistema de IA utilizará la regresión de mínimos cuadrados ordinarios para pronosticar el valor de cierre del S&P 500.


Figura 1: Nuestro modelo verá el S&P 500 como el resultado de una colección de estas acciones.



Una vez que tengamos los datos listos, podemos estimar los parámetros del modelo para nuestro modelo de regresión lineal utilizando la siguiente fórmula.


Figura 2: La ecuación anterior demuestra una de las muchas formas posibles de estimar los parámetros de un modelo de regresión lineal múltiple.

Interpretemos las dos ecuaciones mostradas arriba.

La expresión "Ax - y" rodeada de dos líneas verticales se conoce como la norma L2 de un vector, que se pronuncia "ell 2". En el lenguaje cotidiano, cuando preguntamos "¿cuánto mide?" sobre objetos físicos, hablamos de su tamaño o magnitud. De manera similar, cuando preguntamos "qué tan grande" es un vector, nos referimos a su norma. Un vector es esencialmente una lista de números. Si bien existen diferentes tipos de normas, la norma L1 y la norma L2 son las más comunes. Hoy nos centraremos en la norma L2, que se calcula como la raíz cuadrada de la suma de los valores al cuadrado dentro del vector.

En este contexto:
  1. La matriz "A" representa los datos de entrada, que consisten en los valores de cierre de 12 acciones diferentes.
  2. El símbolo "x" denota los valores de los coeficientes para nuestro modelo de regresión lineal múltiple, con un coeficiente por acción.
  3. El producto "Ax" representa nuestras predicciones para el precio futuro del índice S&P 500 basadas en nuestros datos de entrenamiento.
  4. La notación "y" simboliza el vector de precios de cierre reales que queríamos predecir durante el entrenamiento.
Por lo tanto, "Ax - y" significa el error en nuestro sistema de IA: la diferencia entre nuestros valores previstos y los valores reales. Este error está determinado por "x" porque "A" (datos de entrada) e "y" (datos de salida) son fijos.

En esencia, la primera ecuación nos dice que estamos buscando valores de x que minimicen la norma L2 de Ax - y. En otras palabras, buscamos los valores de x que minimicen nuestro error de predicción al pronosticar el precio de cierre del S&P 500.

La segunda ecuación revela que los valores de x que logran el error mínimo se pueden encontrar multiplicando la pseudoinversa de A por los datos de salida (y) de nuestro conjunto de entrenamiento. La operación pseudoinversa se implementa convenientemente en MQL5.

Podemos encontrar eficientemente la solución pseudo-inversa en MQL5 con solo unas pocas líneas de código.


Implementación: Estrategia de trading del SP500

Empezando

Primero debemos definir las entradas para nuestro Asesor Experto. Necesitamos entradas que permitan al usuario cambiar los períodos de nuestros indicadores técnicos.

//+------------------------------------------------------------------+
//|                                            SP500 Strategy EA.mq5 |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Gamuchirai Zororo Ndawana"
#property link      "https://www.mql5.com"
#property version   "1.00"
/*
The goal of this application is to implement a strategy to trade the SP500 based
on the performance of a sample of stocks that hold proportionally large weights in the index and classical technical analysis.

We will use the following stocks:
1)Microsoft
2)Apple
3)Google
4)Meta
5)Visa
6)JPMorgan
7)Nvidia
8)Tesla
9)Johnsons N Johnsons
10)Home Depot
11)Proctor & Gamble
12)Master Card

A strategy that relies solely on AI  may prove to be unstable.
We want to use a combination of AI guided by trading strategies that have been trusted over time.

Our AI will be a linear system mapping the price of each stock to the closing price of the index.
Recall that financial time series data can be very noisy. Using simpler models may be beneficial.
We will attempt to find linear coefficients using ordinary least squares.
We will use the pseudo inverse solution to find our model parameters.

Our technical analysis will be based on trend following principles:
1)We must observe increasing volume backing a trade before we enter.
2)We want 2 confirmation indicators, RSI and WPR, confirming the trend.
3)We want price to break above the previous week high or low.
4)We want price above or below our MA before we trade.

Our goal is to find an optimal combination of statistical insight,
and classical technical analysis to hopefully build a strategy with an edge.

Gamuchirai Zororo Ndawana
Selebi Phikwe
Botswana
Monday 8 July 2024 21:03
*/
//Inputs
//How far ahead into the future should we forecast?
input int look_ahead = 10;
//Our moving average period
input int ma_period = 120;
//Our RSI period
input int rsi_period = 10;
//Our WPR period
input int wpr_period = 30;
//Our CCI period
input int cci_period = 40;
//How big should our lot sizes be?
int input  lot_multiple = 20;

A continuación necesitamos importar nuestra biblioteca comercial para que podamos administrar nuestras posiciones.

//Libraries
//Trade class
#include <Trade/Trade.mqh>
CTrade Trade;

Continuando, necesitamos definir las variables globales que se utilizan en todo nuestro programa.

//Global variables
//Smallest lot size
double minimum_volume;
//Ask
double ask_price;
//Bid
double bid_price;
//How much data should we fetch?
int fetch = 20;
//Determine the starting date for our inputs
int inupt_start;
//Determine the starting date for our outputs
int output_start;
//Which symbols are we going to use to forecast the SP500 close?
string desired_symbols[] = {"MSFT","AAPL","NVDA","GOOG","TSLA","META","JPM","JNJ","V","PG","HD","MA"};
//Define our input matrix
matrix input_matrix = matrix::Ones(fetch,(ArraySize(desired_symbols))-1);
//Define our output matrix, our output matrix has one column
matrix output_matrix = matrix::Ones(fetch,1);
//A vector to store the initial values of each column
vector initiall_input_values = vector::Ones((ArraySize(desired_symbols)));
//A variable to store the initial value of the output
double initiall_output_value = 0.0;
//Defining the matrix that will store our model parameters
matrix b;
//Define our target symbol
string target_symbol = "US_500";
//A vector to temporarily store the historic closing price of each stock
vector symbol_history = vector::Zeros(fetch);
//A flag to let us know when our model is ready
bool model_initialized = false;
//A flag to let us know if the training process has started
bool model_being_trained = false;
//Our model's prediction
double model_forecast = 0;
//Handlers for our technical indicators
int cci_handler,rsi_handler,wpr_handler,ma_handler;
//Buffers for our technical indicator values
double cci_buffer[],rsi_buffer[],wpr_buffer[],ma_buffer[];

Ahora que hemos llegado hasta aquí, debemos configurar nuestros indicadores técnicos en el controlador de inicialización.

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
//Let's setup our technical indicators
   rsi_handler = iRSI(target_symbol,PERIOD_CURRENT,rsi_period,PRICE_CLOSE);
   ma_handler = iMA(target_symbol,PERIOD_CURRENT,ma_period,0,MODE_EMA,PRICE_CLOSE);
   wpr_handler = iWPR(target_symbol,PERIOD_CURRENT,wpr_period);
   cci_handler = iCCI(target_symbol,PERIOD_CURRENT,cci_period,PRICE_CLOSE);
//Get market data
   minimum_volume = SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN);
//---
   return(INIT_SUCCEEDED);
  }

Necesitamos funciones auxiliares. Inicialmente, definimos un procedimiento para inicializar nuestro modelo de regresión lineal múltiple. Nuestro procedimiento de ajuste comienza colocando una bandera para indicar que nuestro modelo está en entrenamiento. Las funciones adicionales manejan tareas como la recuperación de datos de entrenamiento y el ajuste del modelo. Al dividir nuestro código en funciones más pequeñas, evitaremos escribir las mismas líneas de código varias veces. 

//+------------------------------------------------------------------+
//This function defines the procedure for fitting our model
void model_initialize(void)
  {
   /*
   Our function will run the initialization procedure as follows:
   1)Update the training flag to show that the model has been trained
   2)Fetch the necessary input and output data
   3)Preprocess the data, normalization and scaling etc.
   4)Fit the model
   5)Update the flags to show that the model has been trained.
Desde allí nuestra aplicación tendrá la capacidad de comenzar a operar.
   */
//First let us set our flag to denote that the model is being trained
   model_being_trained = true;
//Defining the input start time and the output start time
//Remember the input data should not contain the most recent bars
//The most recent bars must only be included in the output start time
//The input data should be older than the output data.
   inupt_start = 1+look_ahead;
   output_start = 1;
//Fetch the input data
   fetch_input_data(inupt_start,fetch);
//Fetch the output data
   fetch_output_data(output_start,fetch);
//Prepare the data
   prepare_data(0);
//Fit our model
   fit_linear_model();
//The model is ready
   model_ready();
  }

Ahora, procedemos a definir la función responsable de obtener nuestros datos de entrada. Nuestra matriz de entrada incluirá una columna de unos en la primera columna para representar el punto de intersección o el precio de cierre promedio del S&P 500 cuando todos los valores de las acciones son cero. Aunque este escenario no tiene sentido en el mundo financiero, ya que si todos los valores de las acciones fueran 0, el precio de cierre del S&P 500 también sería 0.

Dejando esto de lado, nuestro procedimiento para obtener datos de entrada es sencillo:

  1. Inicializa la matriz de entrada llena de unos.
  2. Reformar la matriz
  3. Complete el valor de cierre de cada acción, comenzando desde la segunda columna
  4. Asegúrese de que la primera columna esté llena de unos.
//This function will get our input data ready
void fetch_input_data(int f_input_start,int f_fetch)
  {
   /*
      To get the input data ready we will to cycle through the symbols available and copy the requested
      amount of data to the globally scoped input matrix.
      Our function parameters are:
      1)f_input_start: this is where we should get our input data, for the current data pass 0 and only fetch 1.
      2)f_fetch: this is how much data should be fetched and copied into the input matrix.
   */
   Print("Preparing to fetch input data");
//Let's observe the original state of our input matrix
   Print("We will reset the input matrix before fetching the data we need: ");
   input_matrix = matrix::Ones(f_fetch,(ArraySize(desired_symbols))-1);
   Print(input_matrix);
//We need to reshape our matrix
   input_matrix.Reshape(f_fetch,13);
//Filling the input matrix
//Then we need to prepare our input matrix.
//The first column must be full of 1's because it represents the intercept.
//Therefore we will skip 0, and start filling input values from column 1. This is not a mistake.
   for(int i=1; i <= ArraySize(desired_symbols);i++)
     {
      //Copy the input data we need
      symbol_history.CopyRates(desired_symbols[i-1],PERIOD_CURRENT,COPY_RATES_CLOSE,f_input_start,f_fetch);
      //Insert the input data into our input matrix
      input_matrix.Col(symbol_history,i);
     }
//Ensure that the first column is full of ones for our intercept
   vector intercept = vector::Ones(f_fetch);
   input_matrix.Col(intercept,0);
//Let us see what our input matrix looks like now.
   Print("Final state of our input matrix: ");
   Print(input_matrix);
  }
Nuestro procedimiento para obtener datos de salida será más o menos el mismo que usamos para obtener datos de entrada.
//This function will get our output data ready
void fetch_output_data(int f_output_start, int f_fetch)
  {
   /*
      This function will fetch our output data for training our model and copy it into
      our globally defined output matrix. The model has only 2 parameters:
      1)f_output_start: where should we start copying our output data
      2)f_fetch: amount of data to copy
   */
   Print("Preparing to fetch output data");
//Let's observe the original state of our output matrix
   Print("Ressetting output matrix before fetching the data we need: ");
//Reset the output matrix
   output_matrix = matrix::Ones(f_fetch,1);
   Print(output_matrix);
//We need to reshape our matrix
   output_matrix.Reshape(f_fetch,1);
//Output data
//Copy the output data we need
   symbol_history.CopyRates(target_symbol,PERIOD_CURRENT,COPY_RATES_CLOSE,f_output_start,f_fetch);
//Insert the output data into our input matrix
   output_matrix.Col(symbol_history,0);
//Let us see what our input matrix looks like now.
   Print("Final state of our output matrix: ");
   Print(output_matrix);
  }

Antes de ajustar nuestro modelo, necesitamos estandarizar y escalar nuestros datos. Este es un paso importante porque hace que sea más fácil para nuestra aplicación aprender las relaciones ocultas en las tasas de cambio.

Tenga en cuenta que nuestro procedimiento de ajuste tiene dos fases distintas: en la fase 1, cuando se entrena el modelo por primera vez, necesitamos almacenar los valores que usamos para escalar cada columna en un vector. Cada columna se dividirá por el primer valor que contenga, lo que significa que el primer valor de cada columna será uno.

A partir de ese momento, si observamos cualquier valor menor a uno en esa columna, entonces el precio bajó, y valores mayores a uno indican que el precio aumentó. Si observamos una lectura de 0,72, entonces el precio cayó un 28% y si observamos un valor de 1,017, entonces el precio aumentó un 1,7%.

Solo debemos almacenar el valor inicial de cada columna una vez, de ahí en adelante todas las entradas futuras deben dividirse por la misma cantidad para garantizar la consistencia.  

//This function will normalize our input data so that our model can learn better
//We will also scale our output so that it shows the growth in price
void prepare_data(int f_flag)
  {
   /*
      This function is responsible for normalizing our inputs and outputs.
      We want to first store the initial value of each column.
      Then we will divide each column by its first value, so that the first value in each column is one.
      The above steps are performed only once, when the model is initialized. All subsequent inputs will
      simply be divided by the initial values of that column.
   */

   Print("Normalizing and scaling the data");
//This part of the procedure should only be performed once
   if(f_flag == 0)
     {
      Print("Preparing to normalize the data for training the model");
      //Normalizing the inputs
      //Store the initial value of each column
      for(int i=0;i<ArraySize(desired_symbols);i++)
        {
         //First we store the initial value of each column
         initiall_input_values[i] = input_matrix[0][i+1];
         //Now temporarily copy the column, so that we can normalize the entire column at once
         vector temp_vector = input_matrix.Col(i+1);
         //Now divide that entire column by its initial value, if done correctly the first value of each column should be 1
         temp_vector = temp_vector / initiall_input_values[i];
         //Now reinsert the normalized column
         input_matrix.Col(temp_vector,i+1);
        }
      //Print the initial input values
      Print("Our initial input values for each column ");
      Print(initiall_input_values);

      //Scale the output
      //Store the initial output value
      initiall_output_value = output_matrix[0][0];
      //Make a temporary copy of the entire output column
      vector temp_vector_output = output_matrix.Col(0);
      //Divide the entire column by its initial value, so that the first value in the column is one
      //This means that any time the value is less than 1, price fell
      //And every time the value is greater than 1, price rose.
      //A value of 1.023... means price increased by 2.3%
      //A value of 0.8732.. would mean price fell by 22.67%
      temp_vector_output = temp_vector_output / initiall_output_value;
      //Reinsert the column back into the output matrix
      output_matrix.Col(temp_vector_output,0);
      //Shpw the scaled data
      Print("Data has been normlised and the output has been scaled:");
      Print("Input matrix: ");
      Print(input_matrix);
      Print("Output matrix: ");
      Print(output_matrix);
     }

//Normalize the data using the initial values we have allready calculated
   if(f_flag == 1)
     {
      //Divide each of the input values by its corresponding input value
      Print("Preparing to normalize the data for prediction");
      for(int i = 0; i < ArraySize(desired_symbols);i++)
        {
         input_matrix[0][i+1] = input_matrix[0][i+1]/initiall_input_values[i];
        }
      Print("Input data being used for prediction");
      Print(input_matrix);
     }
  }

A continuación, debemos definir una función que establecerá las condiciones de bandera necesarias para permitir que nuestra aplicación comience a operar.

//This function will allow our program to switch states and begin forecasting and looking for trades
void model_ready(void)
  {
//Our model is ready.
//We need to switch these flags to give the system access to trading functionality
   Print("Giving the system permission to begin trading");
   model_initialized = true;
   model_being_trained = false;
  }

Para obtener una predicción de nuestro Asesor Experto, primero debemos obtener los datos más recientes que tenemos disponibles, preprocesar los datos y luego usar la ecuación de regresión lineal para obtener un pronóstico de nuestro modelo.

//This function will obtain a prediction from our model
double model_predict(void)
  {
//First we have to fetch current market data
   Print("Obtaining a forecast from our model");
   fetch_input_data(0,1);
//Now we need to normalize our data using the values we calculated when we initialized the model
   prepare_data(1);
//Now we can return our model's forecast
   return((b[0][0] +
           (b[1][0]*input_matrix[0][0]) +
           (b[2][0]*input_matrix[0][1]) +
           (b[3][0]*input_matrix[0][2]) +
           (b[4][0]*input_matrix[0][3]) +
           (b[5][0]*input_matrix[0][4]) +
           (b[6][0]*input_matrix[0][5]) +
           (b[7][0]*input_matrix[0][6]) +
           (b[8][0]*input_matrix[0][7]) +
           (b[9][0]*input_matrix[0][8]) +
           (b[10][0]*input_matrix[0][9]) +
           (b[11][0]*input_matrix[0][10])+
           (b[12][0]*input_matrix[0][11])));
  }

Necesitamos implementar un procedimiento para ajustar nuestro modelo lineal utilizando las ecuaciones que definimos y explicamos anteriormente. Tenga en cuenta que nuestra API MQL5 nos permite la flexibilidad de ejecutar nuestros comandos de álgebra lineal de una manera flexible y fácil de usar, podemos encadenar operaciones fácilmente, haciendo que nuestros productos sean de alto rendimiento.

Esto es especialmente importante si desea vender sus productos en el mercado, evite usar bucles for siempre que podamos realizar operaciones vectoriales en su lugar porque las operaciones vectoriales son más rápidas que los bucles. Sus usuarios finales experimentarán una aplicación muy responsiva que no se retrasa y disfrutarán de su experiencia de usuario.

//This function will fit our linear model using the pseudo inverse solution
void fit_linear_model(void)
  {
   /*
      The pseudo inverse solution finds a list of values that maps our training data inputs to the output by minimizing the error, RSS.
      These coefficient values can easily be estimated using linear algebra, however these coefficients minimize our error on training data
      and on not unseen data!
   */
   Print("Attempting to find OLS solutions.");
//Now we can estimate our model parameters
   b = input_matrix.PInv().MatMul(output_matrix);
//Let's see our model parameters
   Print("Our model parameters ");
   Print(b);
  }

A medida que avanzamos, necesitamos una función responsable de actualizar nuestros indicadores técnicos y obtener datos actuales del mercado.

//Update our technical indicator values
void update_technical_indicators()
  {
//Get market data
   ask_price = SymbolInfoDouble(_Symbol,SYMBOL_ASK);
   bid_price = SymbolInfoDouble(_Symbol,SYMBOL_BID);
//Copy indicator buffers
   CopyBuffer(rsi_handler,0,0,10,rsi_buffer);
   CopyBuffer(cci_handler,0,0,10,cci_buffer);
   CopyBuffer(wpr_handler,0,0,10,wpr_buffer);
   CopyBuffer(ma_handler,0,0,10,ma_buffer);
//Set the arrays as series
   ArraySetAsSeries(rsi_buffer,true);
   ArraySetAsSeries(cci_buffer,true);
   ArraySetAsSeries(wpr_buffer,true);
   ArraySetAsSeries(ma_buffer,true);
  }

Luego, escribiremos una función que ejecutará nuestras configuraciones comerciales por nosotros. Nuestra función solo podrá ejecutar operaciones si ambos sistemas están alineados.

//Let's try find an entry signal
void find_entry(void)
  {
//Our sell setup
   if(bearish_sentiment())
     {
      Trade.Sell(minimum_volume * lot_multiple,_Symbol,bid_price);
     }
//Our buy setup
   else
      if(bullish_sentiment())
        {
         Trade.Buy(minimum_volume * lot_multiple,_Symbol,ask_price);
        }
  }

Esto es posible porque tenemos funciones que definen el sentimiento bajista según nuestras descripciones anteriores.

//This function tells us if all our tools align for a sell setup
bool bearish_sentiment(void)
  {
   /*
      For a sell setup, we want the following conditions to be satisfied:
      1) CCI reading should be less than 0
      2) RSI reading should be less than 50
      3) WPR reading should be less than -50
      4) Our model forecast should be less than 1
      5) MA reading should be below the moving average
   */
   return((iClose(target_symbol,PERIOD_CURRENT,0) < ma_buffer[0]) &&
          (cci_buffer[0] < 0) &&
          (rsi_buffer[0] < 50) &&
          (wpr_buffer[0] < -50) &&
          (model_forecast < 1));
  }

Y lo mismo ocurre con nuestra función que define el sentimiento alcista.

//This function tells us if all our tools align for a buy setup
bool bullish_sentiment(void)
  {
   /*
      For a sell setup, we want the following conditions to be satisfied:
      1) CCI reading should be greater than 0
      2) RSI reading should be greater than 50
      3) WPR reading should be greater than -50
      4) Our model forecast should be greater than 1
      5) MA reading should be above the moving average
   */
   return((iClose(target_symbol,PERIOD_CURRENT,0) > ma_buffer[0]) &&
          (cci_buffer[0] > 0) &&
          (rsi_buffer[0] > 50) &&
          (wpr_buffer[0] > -50) &&
          (model_forecast > 1));
  }

Por último, necesitamos una función auxiliar para actualizar nuestros valores de stop loss y take profit.

//+----------------------------------------------------------------------+
//|This function is responsible for calculating our SL & TP values       |
//+----------------------------------------------------------------------+
void update_stoploss(void)
  {

//First we iterate over the total number of open positions
   for(int i = PositionsTotal() -1; i >= 0; i--)
     {

      //Then we fetch the name of the symbol of the open position
      string symbol = PositionGetSymbol(i);

      //Before going any furhter we need to ensure that the symbol of the position matches the symbol we're trading
      if(_Symbol == symbol)
        {
         //Now we get information about the position
         ulong ticket = PositionGetInteger(POSITION_TICKET); //Position Ticket
         double position_price = PositionGetDouble(POSITION_PRICE_OPEN); //Position Open Price
         long type = PositionGetInteger(POSITION_TYPE); //Position Type
         double current_stop_loss = PositionGetDouble(POSITION_SL); //Current Stop loss value

         //If the position is a buy
         if(type == POSITION_TYPE_BUY)
           {

            //The new stop loss value is just the ask price minus the ATR stop we calculated above
            double atr_stop_loss = NormalizeDouble(ask_price - ((min_distance * sl_width)/2),_Digits);
            //The new take profit is just the ask price plus the ATR stop we calculated above
            double atr_take_profit = NormalizeDouble(ask_price + (min_distance * sl_width),_Digits);

            //If our current stop loss is less than our calculated ATR stop loss
            //Or if our current stop loss is 0 then we will modify the stop loss and take profit
            if((current_stop_loss < atr_stop_loss) || (current_stop_loss == 0))
              {
               Trade.PositionModify(ticket,atr_stop_loss,atr_take_profit);
              }
           }

         //If the position is a sell
         else
            if(type == POSITION_TYPE_SELL)
              {
               //The new stop loss value is just the ask price minus the ATR stop we calculated above
               double atr_stop_loss = NormalizeDouble(bid_price + ((min_distance * sl_width)/2),_Digits);
               //The new take profit is just the ask price plus the ATR stop we calculated above
               double atr_take_profit = NormalizeDouble(bid_price - (min_distance * sl_width),_Digits);

               //If our current stop loss is greater than our calculated ATR stop loss
               //Or if our current stop loss is 0 then we will modify the stop loss and take profit
               if((current_stop_loss > atr_stop_loss) || (current_stop_loss == 0))
                 {
                  Trade.PositionModify(ticket,atr_stop_loss,atr_take_profit);
                 }
              }
        }
     }

Todas nuestras funciones auxiliares serán llamadas en el momento apropiado por nuestro controlador OnTick, que es responsable de controlar el flujo de eventos dentro de nuestra terminal cada vez que cambia el precio.

void OnTick()
  {
//Our model must be initialized before we can begin trading.
   switch(model_initialized)
     {
      //Our model is ready
      case(true):
         //Update the technical indicator values
         update_technical_indicators();
         //If we have no open positions, let's make a forecast using our model
         if(PositionsTotal() == 0)
           {
            //Let's obtain a prediction from our model
            model_forecast = model_predict();
            //Now that we have sentiment from our model let's try find an entry
            find_entry();
            Comment("Model forecast: ",model_forecast);
           }

         //If we have an open position, we need to manage it
         if(PositionsTotal() > 0)
           {
            //Update our stop loss
            update_stoploss();
           }
         break;

      //Default case
      //Our model is not yet ready.
      default:
         //If our model is not being trained, train it.
         if(!model_being_trained)
           {
            Print("Our model is not ready. Starting the training procedure");
            model_initialize();
           }
         break;
     }

  }

Nuestra aplicación en acción

Figura 3: Nuestra aplicación en acción.


Limitaciones

Existen algunas limitaciones en cuanto al enfoque de modelado que hemos elegido para nuestros modelos de IA; destaquemos algunas de las más importantes:

1.1 Entradas correlacionadas

Los problemas causados por las entradas correlacionadas no son exclusivos de los modelos lineales, este problema afecta a muchos modelos de aprendizaje automático. Algunas de las acciones que tenemos en nuestra matriz de entrada operan en la misma industria y sus precios tienden a subir y bajar al mismo tiempo, esto puede dificultar que nuestro modelo aísle el efecto que cada acción tiene sobre el rendimiento del índice porque los movimientos de los precios de las acciones pueden enmascararse entre sí.

1.2 Función objetivo no lineal

Los conjuntos de datos financieros que son inherentemente ruidosos suelen ser más adecuados para modelos más simples, como la regresión lineal múltiple. Sin embargo, en la mayoría de los casos de la vida real las funciones reales que se aproximan casi nunca son lineales. Por lo tanto, cuanto más lejos esté la función real de la linealidad, mayor será la variación que se observará en la precisión del modelo.

1.3 Limitaciones de un enfoque de modelado directo

Por último, si incluyéramos todas las acciones que cotizan en el S&P 500 en nuestro modelo, terminaríamos con un modelo que contendría 500 parámetros. Optimizar un modelo de este tipo demandaría importantes recursos computacionales y la interpretación de sus resultados plantearía desafíos. Las soluciones de modelado directo no se escalan adecuadamente en este contexto; a medida que agreguemos más acciones en el futuro, la cantidad de parámetros a optimizar podría volverse rápidamente inmanejable.


Conclusión

En este artículo, demostramos lo sencillo que es comenzar a crear modelos de IA que integren análisis técnico para tomar decisiones comerciales informadas. A medida que avanzamos hacia modelos más avanzados, la mayoría de los pasos tratados hoy permanecerán sin cambios. Esto debería brindarles confianza a los principiantes, sabiendo que han completado un proyecto de aprendizaje automático de principio a fin y, con suerte, han comprendido la lógica detrás de cada decisión.

Toda nuestra aplicación utiliza código MQL5 nativo y aprovecha los indicadores técnicos estándar disponibles en cada instalación de MetaTrader 5. Esperamos que este artículo le inspire a explorar más posibilidades con sus herramientas existentes y continuar avanzando en el dominio de MQL5.

Traducción del inglés realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/en/articles/14815

Archivos adjuntos |
linfo2
linfo2 | 18 jul 2024 en 02:56

Impresionante gracias por su código interesante y bien documentado , Mirando hacia adelante a conseguir que funcione . Hay al menos una trampa con para los novatos , que es cambiar el símbolo de destino en la línea 92 al símbolo de sus corredores para sp500 , también en la línea 84 para que coincida con los símbolos de las acciones de su corredor tiene.

¿Qué debería devolver la línea 286 "Nuestros parámetros del modelo"? Obtengo [-nan(ind)], el resto parece funcionar perfectamente.

linfo2
linfo2 | 18 jul 2024 en 07:10
Creo que es porque no tengo los datos descargados para el SP500 todavía lo siento, debería haber comprobado que en primer lugar
Gamuchirai Zororo Ndawana
Gamuchirai Zororo Ndawana | 22 jul 2024 en 10:16
linfo2 #:

Impresionante gracias por su código interesante y bien documentado , Mirando hacia adelante a conseguir que funcione . Hay al menos una trampa con para los novatos , que es cambiar el símbolo de destino en la línea 92 a su símbolo de corredores para sp500 , también en la línea 84 para que coincida con los símbolos de las acciones de su corredor tiene.

¿Qué debería devolver la línea 286 "Nuestros parámetros del modelo"? Obtengo [-nan(ind)], el resto parece funcionar perfectamente.

Tienes razón, es un truco sutil con el que hay que tener cuidado. Mi corredor tiene los nombres más extraños para ciertos símbolos, me alegro de que se las arregló para resolver el problema.
Malcolm Campbell
Malcolm Campbell | 7 ago 2024 en 00:01

Tenía la esperanza de que esto realmente podría funcionar. Gracias al autor por compartirlo a pesar de todo.

no se puede cargar el indicador 'Relative Strength Index' [4302]
no se puede cargar el indicador 'Moving Average' [4302]
no se puede cargar el indicador 'Williams' Percent Range' [4302]
no se puede cargar el indicador 'Commodity Channel Index' [4302]

Entiendo que los indicadores deberían cargarse y mostrarse automáticamente.(falla)

He colocado manualmente los indicadores en el gráfico e incluso he creado una plantilla con todos los indicadores incluidos. Incluso cuando el gráfico de cada acción está abierto y todos los indicadores se colocan en el gráfico manualmente, el EA no puede leer los indicadores por sí mismo.

Gamuchirai Zororo Ndawana
Gamuchirai Zororo Ndawana | 7 ago 2024 en 18:06
Malcolm Campbell Relative Strength Index' [4302]
no se puede cargar el indicador 'Moving Average' [4302]
no se puede cargar el indicador 'Williams' Percent Range' [4302]
no se puede cargar el indicador 'Commodity Channel Index' [4302]

Entiendo que los indicadores deberían cargarse y mostrarse automáticamente.(falla)

He colocado manualmente los indicadores en el gráfico e incluso he creado una plantilla con todos los indicadores incluidos. Incluso cuando el gráfico de cada acción está abierto y todos los indicadores se colocan en el gráfico manualmente, el EA no puede leer los indicadores por sí mismo.

Hey Malcom, ¿podrías compartir una captura de pantalla de tu experiencia?
Desarrollamos un asesor experto multidivisa (Parte 14): Cambio de volumen adaptable en el gestor de riesgos Desarrollamos un asesor experto multidivisa (Parte 14): Cambio de volumen adaptable en el gestor de riesgos
El gestor de riesgos que hemos desarrollado en los últimos artículos solo contiene funciones básicas. Hoy trataremos de analizar sus posibles formas de desarrollo, lo que nos permitirá aumentar los resultados comerciales sin interferir con la lógica de las estrategias de negociación.
Trading con spreads en el mercado Fórex utilizando el factor de estacionalidad Trading con spreads en el mercado Fórex utilizando el factor de estacionalidad
El en presente artículo analizaremos las posibilidades de formar y proporcionar datos sobre el uso del factor de estacionalidad al negociar con spreads en el mercado Fórex.
Aprendiendo MQL5 de principiante a profesional (Parte III): Tipos de datos complejos y archivos de inclusión Aprendiendo MQL5 de principiante a profesional (Parte III): Tipos de datos complejos y archivos de inclusión
Este artículo es el tercero de una serie de materiales sobre los principales aspectos de la programación en MQL5. Aquí nos encargaremos de tipos de datos complejos que no describimos en el artículo anterior, como estructuras, uniones, clases y el tipo de datos "función". También veremos cómo añadir modularidad a nuestro programa utilizando la directiva #include del preprocesador.
Optimización de carteras en Python y MQL5 Optimización de carteras en Python y MQL5
Este artículo explora técnicas avanzadas de optimización de cartera utilizando Python y MQL5 con MetaTrader 5. Demuestra cómo desarrollar algoritmos para el análisis de datos, la asignación de activos y la generación de señales comerciales, enfatizando la importancia de la toma de decisiones basada en datos en la gestión financiera moderna y la mitigación de riesgos.