English Русский Deutsch 日本語
preview
Automatización del indicador del sentimiento del mercado

Automatización del indicador del sentimiento del mercado

MetaTrader 5 — Sistemas comerciales |
26 0
Hlomohang John Borotho
Hlomohang John Borotho

Introducción

En el artículo anterior, desarrollamos un indicador personalizado del sentimiento del mercado, que combina señales de varios marcos temporales en un único panel de fácil lectura. Asimismo, analizamos las tendencias en los marcos temporales superiores junto con las estructuras de los marcos temporales inferiores —examinando la acción del precio, los máximos y mínimos de oscilación, las medias móviles y la volatilidad— para clasificar el mercado en cinco categorías de sentimiento bien definidas: alcista, bajista, de apetito por el riesgo, de aversión al riesgo o neutral.

La automatización del indicador personalizado aporta varias ventajas clave a la hora de operar. Garantiza un seguimiento continuo y objetivo del sentimiento del mercado, sin sesgos emocionales, lo que permite ejecutar operaciones con mayor rapidez ante los cambios del mercado. Podemos mantener la coherencia del análisis entre distintos marcos temporales, aprovechar oportunidades tempranas mediante la detección sistemática de señales de ruptura o de confirmación de tendencias y gestionar el riesgo con mayor eficacia. En definitiva, la automatización convierte lo que sería un proceso laborioso y propenso a errores en un asesor experto (EA) eficiente que facilita la toma de decisiones basada en datos.



Lógica de ejecución

Sentimiento alcista/de apetito por el riesgo:

El sentimiento alcista refleja un fuerte impulso alcista, en el que las tendencias y las estructuras de precios en marcos temporales más amplios confirman que los compradores tienen el control, mientras que el sentimiento de apetito por el riesgo indica una mayor confianza de los inversores y un mayor apetito por el riesgo, lo que a menudo impulsa al alza los precios. En ambos casos, el entorno del mercado favorece un movimiento al alza, y nuestra estrategia consiste en ejecutar una operación de compra siempre que el indicador señale un sentimiento alcista o de apetito por el riesgo, ya que es más probable que el precio siga subiendo.

Sentimiento bajista/de aversión al riesgo:

El sentimiento bajista refleja un fuerte impulso a la baja, en el que las tendencias y las estructuras de precios en marcos temporales superiores confirman que los vendedores dominan el mercado, mientras que el sentimiento de aversión al riesgo indica un giro hacia la seguridad, ya que los inversores evitan los activos más arriesgados y empujan los precios a la baja. En ambos casos, el entorno del mercado favorece un movimiento a la baja, y nuestra estrategia consiste en ejecutar una operación de venta siempre que el indicador señale un sentimiento bajista o de aversión al riesgo, ya que es más probable que el precio siga bajando.

Sentimiento neutral:

El sentimiento neutral o errático se da cuando el mercado carece de una dirección definida y los precios se consolidan dentro de un rango o fluctúan de forma impredecible entre distintos niveles. En estos periodos de incertidumbre, la probabilidad de que se produzcan señales falsas y vaivenes del precio es mayor, por lo que utilizamos la lectura neutral como filtro para evitar entrar en operaciones. Al mantenernos al margen del mercado en estas condiciones, protegemos el capital y esperamos una señal más clara de sentimiento alcista, bajista, de apetito por el riesgo o de aversión al riesgo antes de actuar.



Primeros pasos

//+------------------------------------------------------------------+
//|                                               Mark_Sent_Auto.mq5 |
//|                        GIT under Copyright 2025, MetaQuotes Ltd. |
//|                     https://www.mql5.com/en/users/johnhlomohang/ |
//+------------------------------------------------------------------+
#property copyright "GIT under Copyright 2025, MetaQuotes Ltd."
#property link      "https://www.mql5.com/en/users/johnhlomohang/"
#property version   "1.00"
#include <Trade/Trade.mqh>

CTrade trade;

En este código, la línea #include &lt;Trade/Trade.mqh&gt; incorpora la biblioteca de trading de MetaTrader 5, que proporciona la clase integrada CTrade. Esta clase contiene todas las funciones esenciales necesarias para automatizar la ejecución de operaciones, como abrir, cerrar, modificar o gestionar posiciones y órdenes. Tras incluir la biblioteca, creamos una instancia de la clase con CTrade trade;. Este objeto actúa como nuestra interfaz con el sistema de negociación; a través de él, podemos invocar métodos como trade.Buy(), trade.Sell() o trade.PositionClose(). Como nuestro objetivo consiste en automatizar las decisiones de negociación a partir del indicador de sentimiento del mercado, es necesario incluir esta biblioteca y definir un objeto CTrade para poder enviar y gestionar las operaciones mediante programación.

//+------------------------------------------------------------------+
//| Input parameters                                                 |
//+------------------------------------------------------------------+
input group "Timeframe Settings"
input ENUM_TIMEFRAMES HigherTF = PERIOD_H4;     // Higher Timeframe
input ENUM_TIMEFRAMES LowerTF1 = PERIOD_H1;     // Lower Timeframe 1
input ENUM_TIMEFRAMES LowerTF2 = PERIOD_M30;    // Lower Timeframe 2

input group "Indicator Settings"
input int MAPeriod = 200;                       // EMA Period
input int SwingLookback = 5;                    // Swing Lookback Bars
input double ATRThreshold = 0.002;              // ATR Threshold for Neutral

input group "Trading Settings"
input double LotSize = 0.1;                     // Trade Lot Size
input int StopLossPips = 50;                    // Stop Loss in Pips
input int TakeProfitPips = 100;                 // Take Profit in Pips
input int MagicNumber = 12345;                  // Magic Number for trades
input int Slippage = 3;                         // Slippage in points

input group "Trailing Stop Parameters"
input bool UseTrailingStop = true;              // Enable trailing stops
input int BreakEvenAtPips = 500;                // Move to breakeven at this profit (pips) 
input int TrailStartAtPips = 600;               // Start trailing at this profit (pips)
input int TrailStepPips = 100;                  // Trail by this many pips 

input group "Risk Management"
input bool UseRiskManagement = true;            // Enable Risk Management
input double RiskPercent = 2.0;                 // Risk Percentage per Trade
input int MinBarsBetweenTrades = 3;             // Minimum bars between trades

input group "Visual Settings"
input bool ShowPanel = true;                    // Show Information Panel
input int PanelCorner = 1;                      // Panel Corner: 0=TopLeft,1=TopRight,2=BottomLeft,3=BottomRight
input int PanelX = 10;                          // Panel X Position
input int PanelY = 10;                          // Panel Y Position
input string FontFace = "Arial";                // Font Face
input int FontSize = 10;                        // Font Size
input color BullishColor = clrLimeGreen;        // Bullish Color
input color BearishColor = clrRed;              // Bearish Color
input color RiskOnColor = clrDodgerBlue;        // Risk-On Color
input color RiskOffColor = clrDarkGray;         // Risk-Off Color
input color NeutralColor = clrGold;             // Neutral Color

Aquí, la primera sección agrupa los parámetros de entrada relacionados con la configuración de los marcos temporales y los ajustes del indicador. Al definir HigherTF, LowerTF1 y LowerTF2, el asesor experto puede analizar el sentimiento del mercado en múltiples niveles de la acción del precio, como un marco temporal superior para determinar la dirección de la tendencia y marcos temporales inferiores para confirmar la estructura. En la configuración del indicador, parámetros como MAPeriod (el periodo de la media móvil exponencial), SwingLookback (el número de barras que se analizan para detectar máximos y mínimos de oscilación) y ATRThreshold (utilizado para detectar mercados neutros o erráticos) ofrecen a los traders flexibilidad para ajustar con precisión la forma en que se detecta el sentimiento del mercado.

La segunda sección regula el comportamiento en las operaciones y la gestión de riesgos. Los parámetros de entrada como LotSize, StopLossPips y TakeProfitPips definen los parámetros básicos de ejecución de las operaciones, mientras que MagicNumber ayuda al EA a distinguir sus operaciones de las demás y Slippage controla la tolerancia en la ejecución de las órdenes. Los parámetros del stop dinámico (UseTrailingStop, BreakEvenAtPips, TrailStartAtPips, TrailStepPips) permiten proteger de forma dinámica las ganancias una vez que una operación evoluciona favorablemente. Además, el bloque de gestión de riesgos (UseRiskManagement, RiskPercent, MinBarsBetweenTrades) garantiza que el tamaño de las posiciones se ajuste al patrimonio de la cuenta, evita la sobreexposición y establece un intervalo mínimo entre operaciones para mantener la disciplina.

La última sección se centra en la presentación visual del indicador de sentimiento del mercado. Opciones como ShowPanel, PanelCorner, PanelX y PanelY permiten al operador controlar dónde aparece el panel de sentimiento del mercado en el gráfico. La personalización de la fuente (FontFace, FontSize) garantiza la legibilidad, mientras que los parámetros de entrada que definen los colores (BullishColor, BearishColor, RiskOnColor, RiskOffColor, NeutralColor) ofrecen indicaciones visuales inmediatas para cada tipo de sentimiento del mercado. Esta personalización mejora la claridad y la facilidad de uso, para que el trader evalúe de un vistazo las condiciones del mercado.

//+------------------------------------------------------------------+
//| Global variables                                                 |
//+------------------------------------------------------------------+
int higherTFHandle, lowerTF1Handle, lowerTF2Handle;
double higherTFMA[], lowerTF1MA[], lowerTF2MA[];
datetime lastUpdateTime = 0;
string indicatorName = "MarketSentEA";
int currentSentiment = 0;
int previousSentiment = 0;
datetime lastTradeTime = 0;
double global_PipValueInPoints; // Stores pip value in points
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
    indicatorName = "MarketSentEA_" + IntegerToString(ChartID());
    
    // Create handles for MA indicators on different timeframes
    higherTFHandle = iMA(_Symbol, HigherTF, MAPeriod, 0, MODE_EMA, PRICE_CLOSE);
    lowerTF1Handle = iMA(_Symbol, LowerTF1, MAPeriod, 0, MODE_EMA, PRICE_CLOSE);
    lowerTF2Handle = iMA(_Symbol, LowerTF2, MAPeriod, 0, MODE_EMA, PRICE_CLOSE);
    
    // Set array as series
    ArraySetAsSeries(higherTFMA, true);
    ArraySetAsSeries(lowerTF1MA, true);
    ArraySetAsSeries(lowerTF2MA, true);
    
     //global_PipValueInPoints = GetPipValueInPoints();
    
    // Create panel if enabled
    if(ShowPanel)
        CreatePanel();
    
    // Check for trading permission
    if(!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED))
        Print("Error: Trading is not allowed! Check AutoTrading permission.");
    
    return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
    // Delete all objects
    ObjectsDeleteAll(0, indicatorName);
    IndicatorRelease(higherTFHandle);
    IndicatorRelease(lowerTF1Handle);
    IndicatorRelease(lowerTF2Handle);
}

Aquí definimos las variables globales que el asesor experto utilizará a lo largo de su ejecución. Así, se crean los identificadores (higherTFHandle, lowerTF1Handle y lowerTF2Handle) para almacenar referencias a los indicadores de media móvil (MA) en diferentes marcos temporales. Las matrices (higherTFMA, lowerTF1MA, lowerTF2MA) contienen los valores calculados de estos indicadores para su posterior análisis. Variables como «lastUpdateTime», «lastTradeTime» y «currentSentiment»/«previousSentiment» ayudan a realizar un seguimiento de cuándo se realizaron por última vez los cálculos o las operaciones, así como a gestionar los cambios en el sentimiento del mercado. La cadena «indicatorName» identifica de forma única los objetos dibujados en el gráfico, mientras que «global_PipValueInPoints» almacenará las conversiones de pips a puntos para los cálculos de riesgo y tamaño de la operación.

La función OnInit() inicializa el asesor experto (EA). Esta establece un nombre de indicador único (vinculado al ID del gráfico), crea identificadores de medias móviles para los marcos temporales seleccionados y configura matrices para los datos de series temporales. También incluye la lógica necesaria para crear un panel. La función OnDeinit() se encarga de la limpieza cuando se retira el EA del gráfico: elimina los objetos del gráfico asociados al indicador y libera los identificadores de las medias móviles para liberar recursos. En conjunto, estas funciones garantizan que el EA se inicie correctamente y se cierre sin dejar residuos ni consumir memoria innecesariamente.

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
    // Update only on new bar or every 10 seconds
    if(TimeCurrent() - lastUpdateTime < 10)
        return;
    
    lastUpdateTime = TimeCurrent();
    
    // Get MA values
    if(CopyBuffer(higherTFHandle, 0, 0, 3, higherTFMA) < 3 ||
       CopyBuffer(lowerTF1Handle, 0, 0, 3, lowerTF1MA) < 3 ||
       CopyBuffer(lowerTF2Handle, 0, 0, 3, lowerTF2MA) < 3)
    {
        Print("Error copying indicator buffers");
        return;
    }
    
    // Get current prices
    double higherTFPrice = iClose(_Symbol, HigherTF, 0);
    double lowerTF1Price = iClose(_Symbol, LowerTF1, 0);
    double lowerTF2Price = iClose(_Symbol, LowerTF2, 0);
    
    // Determine higher timeframe bias
    int higherTFBias = GetHigherTFBias(higherTFPrice, higherTFMA[0]);
    
    // Determine lower timeframe structures
    bool lowerTF1Bullish = IsBullishStructure(LowerTF1, SwingLookback);
    bool lowerTF1Bearish = IsBearishStructure(LowerTF1, SwingLookback);
    bool lowerTF2Bullish = IsBullishStructure(LowerTF2, SwingLookback);
    bool lowerTF2Bearish = IsBearishStructure(LowerTF2, SwingLookback);
    
    // Check for breakouts (BOS)
    bool lowerTF1Breakout = HasBreakout(LowerTF1, SwingLookback, higherTFBias);
    bool lowerTF2Breakout = HasBreakout(LowerTF2, SwingLookback, higherTFBias);
    
    // Determine final sentiment
    previousSentiment = currentSentiment;
    currentSentiment = DetermineSentiment(
        higherTFBias, 
        lowerTF1Bullish, lowerTF1Bearish, lowerTF1Breakout,
        lowerTF2Bullish, lowerTF2Bearish, lowerTF2Breakout
    );
    
    // Update panel if enabled
    if(ShowPanel)
        UpdatePanel(higherTFBias, currentSentiment);
    
    // Check if we should trade
    CheckForTradingOpportunity();
    
    if(UseTrailingStop) ManageOpenTrades();
}

La función OnTick() es el bucle de ejecución principal del asesor experto (EA) y se ejecuta cada vez que el mercado genera un nuevo tick. Para optimizar el rendimiento, se actualiza solo en una nueva barra o, como mínimo, una vez cada 10 segundos, lo que evita cálculos innecesarios. En primer lugar, recupera los valores de las medias móviles de los marcos temporales predefinidos mediante CopyBuffer y los compara con los precios actuales obtenidos a través de iClose(). A partir de estos datos, se determina el sesgo direccional del marco temporal superior, al tiempo que se identifican las estructuras alcistas y bajistas en los marcos temporales inferiores, junto con posibles rupturas.

Luego la función determina el sentimiento general del mercado combinando estas condiciones, actualiza el panel del gráfico si está activado y busca posibles oportunidades de negociación. Por último, si los stops dinámicos están activados, gestiona las operaciones abiertas de forma dinámica para proteger los beneficios. Esto garantiza que el EA analice continuamente el sentimiento del mercado, actualice los elementos visuales y ejecute operaciones de forma estructurada y automatizada.

//+------------------------------------------------------------------+
//| Determine higher timeframe bias                                  |
//+------------------------------------------------------------------+
int GetHigherTFBias(double price, double maValue)
{
    double deviation = MathAbs(price - maValue) / maValue;
    
    if(price > maValue && deviation > ATRThreshold)
        return 1; // Bullish
    else if(price < maValue && deviation > ATRThreshold)
        return -1; // Bearish
    else
        return 0; // Neutral
}

//+------------------------------------------------------------------+
//| Check for bullish structure (HH, HL)                             |
//+------------------------------------------------------------------+
bool IsBullishStructure(ENUM_TIMEFRAMES tf, int lookback)
{
    // Get swing highs and lows
    int swingHighIndex = iHighest(_Symbol, tf, MODE_HIGH, lookback * 2, 1);
    int swingLowIndex = iLowest(_Symbol, tf, MODE_LOW, lookback * 2, 1);
    
    // Get previous swings for comparison
    int prevSwingHighIndex = iHighest(_Symbol, tf, MODE_HIGH, lookback, lookback + 1);
    int prevSwingLowIndex = iLowest(_Symbol, tf, MODE_LOW, lookback, lookback + 1);
    
    if(swingHighIndex == -1 || swingLowIndex == -1 || 
       prevSwingHighIndex == -1 || prevSwingLowIndex == -1)
        return false;
    
    double swingHigh = iHigh(_Symbol, tf, swingHighIndex);
    double swingLow = iLow(_Symbol, tf, swingLowIndex);
    double prevSwingHigh = iHigh(_Symbol, tf, prevSwingHighIndex);
    double prevSwingLow = iLow(_Symbol, tf, prevSwingLowIndex);
    
    // Check for higher high and higher low
    return (swingHigh > prevSwingHigh && swingLow > prevSwingLow);
}

//+------------------------------------------------------------------+
//| Check for bearish structure (LH, LL)                             |
//+------------------------------------------------------------------+
bool IsBearishStructure(ENUM_TIMEFRAMES tf, int lookback)
{
    // Get swing highs and lows
    int swingHighIndex = iHighest(_Symbol, tf, MODE_HIGH, lookback * 2, 1);
    int swingLowIndex = iLowest(_Symbol, tf, MODE_LOW, lookback * 2, 1);
    
    // Get previous swings for comparison
    int prevSwingHighIndex = iHighest(_Symbol, tf, MODE_HIGH, lookback, lookback + 1);
    int prevSwingLowIndex = iLowest(_Symbol, tf, MODE_LOW, lookback, lookback + 1);
    
    if(swingHighIndex == -1 || swingLowIndex == -1 || 
       prevSwingHighIndex == -1 || prevSwingLowIndex == -1)
        return false;
    
    double swingHigh = iHigh(_Symbol, tf, swingHighIndex);
    double swingLow = iLow(_Symbol, tf, swingLowIndex);
    double prevSwingHigh = iHigh(_Symbol, tf, prevSwingHighIndex);
    double prevSwingLow = iLow(_Symbol, tf, prevSwingLowIndex);
    
    // Check for lower high and lower low
    return (swingHigh < prevSwingHigh && swingLow < prevSwingLow);
}

//+------------------------------------------------------------------+
//| Check for breakout of structure                                  |
//+------------------------------------------------------------------+
bool HasBreakout(ENUM_TIMEFRAMES tf, int lookback, int higherTFBias)
{
    // Get recent swing points
    int swingHighIndex = iHighest(_Symbol, tf, MODE_HIGH, lookback, 1);
    int swingLowIndex = iLowest(_Symbol, tf, MODE_LOW, lookback, 1);
    
    if(swingHighIndex == -1 || swingLowIndex == -1)
        return false;
    
    double swingHigh = iHigh(_Symbol, tf, swingHighIndex);
    double swingLow = iLow(_Symbol, tf, swingLowIndex);
    double currentPrice = iClose(_Symbol, tf, 0);
    
    // Check for breakout based on higher timeframe bias
    if(higherTFBias == 1) // Bullish bias - look for breakout above swing high
        return (currentPrice > swingHigh);
    else if(higherTFBias == -1) // Bearish bias - look for breakout below swing low
        return (currentPrice < swingLow);
    
    return false;
}

La función GetHigherTFBias() compara el precio actual con el valor de una media móvil en el marco temporal superior para determinar la tendencia dominante. Al calcular la desviación relativa entre ambos, se garantiza que las pequeñas fluctuaciones cercanas a la media móvil se consideren neutras. Si el precio se sitúa notablemente por encima de la media móvil, la función devuelve 1, lo que indica un sesgo direccional alcista, mientras que, si el precio se sitúa significativamente por debajo, devuelve -1, lo que indica un sesgo direccional bajista. Si no se cumple ninguna de las dos condiciones, el sesgo direccional se establece por defecto en 0, lo que representa un estado neutro. Esto proporciona al EA un contexto direccional claro antes de analizar los marcos temporales inferiores.

Las funciones IsBullishStructure() e IsBearishStructure() analizan los máximos y mínimos de oscilación en el marco temporal especificado para confirmar los patrones estructurales. En el caso de una estructura alcista, el código comprueba si el máximo de oscilación más reciente es superior al máximo de oscilación anterior y si el mínimo de oscilación más reciente también es superior al mínimo de oscilación anterior, lo que indica máximos y mínimos cada vez más altos (HH, HL). Por el contrario, la comprobación bajista verifica que los máximos y mínimos sean cada vez más bajos (LH, LL). Estas validaciones estructurales aportan un nivel adicional de confirmación, lo que garantiza que las operaciones se ajusten no solo al sesgo direccional de la media móvil, sino también a la estructura real del mercado.

La función HasBreakout() busca rupturas del precio de los niveles de oscilación recientes en la dirección del sesgo direccional del marco temporal superior. Por ejemplo, con un sesgo direccional alcista, confirma si el precio actual ha superado el máximo de oscilación más reciente, lo que indica una continuación de la tendencia. Con un sesgo direccional bajista, comprueba si el precio ha caído por debajo del mínimo de oscilación más reciente. Si no existe un sesgo direccional claro, ninguna ruptura se considera válida. Esta función evita las operaciones prematuras al garantizar que las entradas solo se produzcan cuando la evolución del precio se ajuste tanto a la estructura como al impulso del marco temporal superior, lo que refuerza la fiabilidad de las señales.

//+------------------------------------------------------------------+
//| Determine final sentiment                                        |
//+------------------------------------------------------------------+
int DetermineSentiment(int higherTFBias, 
                      bool tf1Bullish, bool tf1Bearish, bool tf1Breakout,
                      bool tf2Bullish, bool tf2Bearish, bool tf2Breakout)
{
    // Bullish sentiment
    if(higherTFBias == 1 && tf1Bullish && tf2Bullish)
        return 1; // Bullish
    
    // Bearish sentiment
    if(higherTFBias == -1 && tf1Bearish && tf2Bearish)
        return -1; // Bearish
    
    // Risk-on sentiment
    if(higherTFBias == 1 && (tf1Breakout || tf2Breakout))
        return 2; // Risk-On
    
    // Risk-off sentiment
    if(higherTFBias == -1 && (tf1Breakout || tf2Breakout))
        return -2; // Risk-Off
    
    // Neutral/choppy sentiment
    return 0; // Neutral
}

//+------------------------------------------------------------------+
//| Check for trading opportunity                                    |
//+------------------------------------------------------------------+
void CheckForTradingOpportunity()
{
    // Check if enough time has passed since last trade
    if(TimeCurrent() - lastTradeTime < PeriodSeconds(_Period) * MinBarsBetweenTrades)
        return;
    
    // Check if sentiment has changed
    if(currentSentiment == previousSentiment)
        return;
    
    // Close existing positions if sentiment changed significantly
    if((previousSentiment > 0 && currentSentiment < 0) || 
       (previousSentiment < 0 && currentSentiment > 0))
    {
        CloseAllPositions();
    }
    
    // Execute new trades based on sentiment
    switch(currentSentiment)
    {
        case 1: // Bullish - Buy
        case 2: // Risk-On - Buy
            ExecuteTrade(ORDER_TYPE_BUY, _Symbol);
            lastTradeTime = TimeCurrent();
            break;
            
        case -1: // Bearish - Sell
        case -2: // Risk-Off - Sell
            ExecuteTrade(ORDER_TYPE_SELL, _Symbol);
            lastTradeTime = TimeCurrent();
            break;
            
        case 0: // Neutral - Close all positions
            CloseAllPositions();
            break;
    }
}

//+------------------------------------------------------------------+
//| Execute trade with risk parameters                               |
//+------------------------------------------------------------------+
void ExecuteTrade(ENUM_ORDER_TYPE tradeType, string symbol)
{
   double point = SymbolInfoDouble(symbol, SYMBOL_POINT);
   double price = (tradeType == ORDER_TYPE_BUY) ? SymbolInfoDouble(symbol, SYMBOL_ASK) :
                                                SymbolInfoDouble(symbol, SYMBOL_BID);

   // Convert StopLoss and TakeProfit from pips to actual price distances
   double sl_distance = StopLossPips * point;
   double tp_distance = TakeProfitPips * point;
   
   double sl = (tradeType == ORDER_TYPE_BUY) ? price - sl_distance :
                                             price + sl_distance;
   
   double tp = (tradeType == ORDER_TYPE_BUY) ? price + tp_distance :
                                             price - tp_distance;

   trade.PositionOpen(symbol, tradeType, LotSize, price, sl, tp, NULL);
}

La función DetermineSentiment() combina las señales de los marcos temporales superiores e inferiores en una única clasificación del sentimiento del mercado. Si el sesgo direccional del marco temporal superior es alcista y ambos marcos temporales inferiores confirman estructuras alcistas, la función devuelve un sentimiento alcista. Del mismo modo, si todas las condiciones apuntan a una tendencia bajista, esto indica un sentimiento bajista. Cuando el marco temporal superior es alcista, pero en los marcos temporales inferiores solo se producen rupturas, el sentimiento se clasifica como de apetito por el riesgo, mientras que un sesgo direccional bajista con rupturas indica un sentimiento de aversión al riesgo. Si no se cumple ninguna de estas condiciones, la función adopta por defecto el valor «neutral», lo que indica un mercado errático y sin una dirección clara o indeciso. Esta jerarquía estructurada garantiza que el sentimiento sea coherente, tenga en cuenta el contexto y refleje tanto las condiciones de tendencia como las de ruptura.

La función CheckForTradingOpportunity() utiliza las señales del sentimiento del mercado para automatizar las operaciones. Aplica reglas como espaciar las operaciones a lo largo del tiempo (MinBarsBetweenTrades) y reaccionar únicamente cuando el sentimiento cambia con respecto al estado anterior. Si el sentimiento cambia de dirección, las posiciones existentes se cierran antes de abrir nuevas operaciones, lo que garantiza la adaptación a las últimas perspectivas del mercado. En función del sentimiento del mercado, el EA ejecuta órdenes de compra en situaciones alcistas o de apetito por el riesgo, órdenes de venta en situaciones bajistas o de aversión al riesgo, o cierra todas las posiciones durante las fases neutras. La función ExecuteTrade() gestiona la colocación de órdenes con controles de riesgo mediante el cálculo de los niveles de stop-loss y take-profit en términos de precio, mientras que la función auxiliar PipsToPrice() garantiza la compatibilidad con símbolos que usan distintos formatos de pips (por ejemplo, cotizaciones de 3 o 5 dígitos). En conjunto, estas funciones crean un ciclo completo que abarca desde la detección del sentimiento del mercado hasta la ejecución automatizada de operaciones con gestión de riesgos.

//+------------------------------------------------------------------+
//| Trailing stop function                                           |
//+------------------------------------------------------------------+
void ManageOpenTrades()
{
   if(!UseTrailingStop) return;

   int total = PositionsTotal();
   for(int i = total - 1; i >= 0; i--)
   {
      // get ticket (PositionGetTicket returns ulong; it also selects the position)
      ulong ticket = PositionGetTicket(i);                  // correct usage. :contentReference[oaicite:3]{index=3}
      if(ticket == 0) continue;

      // ensure the position is selected (recommended)
      if(!PositionSelectByTicket(ticket)) continue;

      // Optional: only operate on same symbol or your EA's magic number
      if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
      // if(PositionGetInteger(POSITION_MAGIC) != MyMagicNumber) continue;

      // read position properties AFTER selecting
      double open_price   = PositionGetDouble(POSITION_PRICE_OPEN);
      double current_price= PositionGetDouble(POSITION_PRICE_CURRENT); // current price for the position. :contentReference[oaicite:4]{index=4}
      double current_sl   = PositionGetDouble(POSITION_SL);
      double current_tp   = PositionGetDouble(POSITION_TP);
      ENUM_POSITION_TYPE pos_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);

      // pip size
      double pip_price = PipsToPrice(1);

      // profit in pips (use current_price returned above)
      double profit_price = (pos_type == POSITION_TYPE_BUY) ? (current_price - open_price)
                                                             : (open_price - current_price);
      double profit_pips = profit_price / pip_price;
      if(profit_pips <= 0) continue;

      // get broker min stop distance (in price units)
      double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
      double stop_level_points = (double)SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL); // integer property. :contentReference[oaicite:5]{index=5}
      double stopLevelPrice = stop_level_points * point;

      // get market Bid/Ask for stop-level checks
      double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
      double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);

      // -------------------------
      // 1) Move to breakeven
      // -------------------------
      if(profit_pips >= BreakEvenAtPips)
      {
         double breakeven = open_price;
         // small adjustment to help account for spread/commissions (optional)
         if(pos_type == POSITION_TYPE_BUY)  breakeven += point; 
         else                                breakeven -= point;

         // Check stop-level rules: for BUY SL must be >= (bid - stopLevelPrice) distance below bid
         if(pos_type == POSITION_TYPE_BUY)
         {
            if((bid - breakeven) >= stopLevelPrice) // allowed by server
            {
               if(breakeven > current_sl) // only move SL up
               {
                  if(!trade.PositionModify(ticket, NormalizeDouble(breakeven, _Digits), current_tp))
                     PrintFormat("PositionModify failed (BE) ticket %I64u error %d", ticket, GetLastError());
               }
            }
         }
         else // SELL
         {
            if((breakeven - ask) >= stopLevelPrice)
            {
               if(current_sl == 0.0 || breakeven < current_sl) // move SL down
               {
                  if(!trade.PositionModify(ticket, NormalizeDouble(breakeven, _Digits), current_tp))
                     PrintFormat("PositionModify failed (BE) ticket %I64u error %d", ticket, GetLastError());
               }
            }
         }
      } // end breakeven

      // -------------------------
      // 2) Trailing in steps after TrailStartAtPips
      // -------------------------
      if(profit_pips >= TrailStartAtPips)
      {
         double extra_pips = profit_pips - TrailStartAtPips;
         int step_count = (int)(extra_pips / TrailStepPips);

         // compute desired SL relative to open_price (as per your original request)
         double desiredOffsetPips = (double)(TrailStartAtPips + step_count * TrailStepPips);
         double new_sl_price;

         if(pos_type == POSITION_TYPE_BUY)
         {
            new_sl_price = open_price + PipsToPrice((int)desiredOffsetPips);
            // ensure new SL respects server min distance from current Bid
            if((bid - new_sl_price) < stopLevelPrice)
               new_sl_price = bid - stopLevelPrice;

            if(new_sl_price > current_sl) // only move SL up
            {
               if(!trade.PositionModify(ticket, NormalizeDouble(new_sl_price, _Digits), current_tp))
                  PrintFormat("PositionModify failed (Trail Buy) ticket %I64u error %d", ticket, GetLastError());
            }
         }
         else // SELL
         {
            new_sl_price = open_price - PipsToPrice((int)desiredOffsetPips);
            // ensure new SL respects server min distance from current Ask
            if((new_sl_price - ask) < stopLevelPrice)
               new_sl_price = ask + stopLevelPrice;

            if(current_sl == 0.0 || new_sl_price < current_sl) // only move SL down (more profitable)
            {
               if(!trade.PositionModify(ticket, NormalizeDouble(new_sl_price, _Digits), current_tp))
                  PrintFormat("PositionModify failed (Trail Sell) ticket %I64u error %d", ticket, GetLastError());
            }
         }
      }
   } 
}

//+------------------------------------------------------------------+
//| Close all positions                                              |
//+------------------------------------------------------------------+
void CloseAllPositions()
{
    for(int i = PositionsTotal() - 1; i >= 0; i--)
    {
        ulong ticket = PositionGetTicket(i);
        if(PositionSelectByTicket(ticket))
        {
            if(PositionGetInteger(POSITION_MAGIC) == MagicNumber)
            {
                MqlTradeRequest request = {};
                MqlTradeResult result = {};
                
                request.action = TRADE_ACTION_DEAL;
                request.symbol = _Symbol;
                request.volume = PositionGetDouble(POSITION_VOLUME);
                request.type = (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) ? 
                              ORDER_TYPE_SELL : ORDER_TYPE_BUY;
                request.price = SymbolInfoDouble(_Symbol, 
                    (request.type == ORDER_TYPE_BUY) ? SYMBOL_ASK : SYMBOL_BID);
                request.deviation = Slippage;
                request.magic = MagicNumber;
                request.comment = "MarketSentEA Close";
                
                if(OrderSend(request, result))
                {
                    if(result.retcode == TRADE_RETCODE_DONE)
                        Print("Position closed successfully. Ticket: ", ticket);
                    else
                        Print("Error closing position. Code: ", result.retcode);
                }
            }
        }
    }
}

Este apartado del EA se centra en dos mecanismos principales de gestión de riesgos: la gestión del stop dinámico y el cierre forzoso de posiciones. La función ManageOpenTrades() comprueba primero que los stops dinámicos solo se apliquen cuando estén activados y, a continuación, recorre todas las posiciones abiertas. Comprueba el beneficio en pips, aplica un stop en el punto de equilibrio una vez alcanzado el beneficio mínimo y continúa ajustando el stop dinámico por pasos a medida que aumenta el beneficio de la posición. Se han integrado las restricciones del bróker relativas al nivel de stop y las consideraciones sobre el spread para garantizar que todas las modificaciones del stop sean válidas y seguras.

En conjunto, estas funciones crean un sólido sistema de salida y protección para las operaciones, lo que reduce el riesgo y maximiza las ganancias. Al combinar la lógica del punto de equilibrio, el stop dinámico por pasos y un mecanismo fiable para cerrar todas las posiciones cuando así lo exijan las reglas del sentimiento del mercado o de la estrategia, el EA puede proteger el capital en condiciones adversas y asegurar los beneficios a medida que las operaciones evolucionan favorablemente. En definitiva, esto mejora la coherencia, la resiliencia y la rentabilidad a largo plazo de la estrategia.

//+------------------------------------------------------------------+
//| Calculate lot size with risk management                          |
//+------------------------------------------------------------------+
double CalculateLotSize(double stopLossPrice)
{
    if(!UseRiskManagement)
        return LotSize;
    
    double accountBalance = AccountInfoDouble(ACCOUNT_BALANCE);
    double riskAmount = accountBalance * RiskPercent / 100.0;
    
    double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
    double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
    
    // Calculate stop loss in points
    double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
    double slPoints = MathAbs(currentPrice - stopLossPrice) / point;
    
    // Calculate lot size
    double lotSize = riskAmount / (slPoints * tickValue);
    
    // Normalize lot size
    double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
    double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
    double lotStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
    
    lotSize = MathMax(minLot, MathMin(maxLot, lotSize));
    lotSize = MathRound(lotSize / lotStep) * lotStep;
    
    return lotSize;
}

//+------------------------------------------------------------------+
//| Helper: convert timeframe to string                              |
//+------------------------------------------------------------------+
string TFtoString(int tf)
{
   switch(tf)
   {
      case PERIOD_M1:  return "M1";
      case PERIOD_M5:  return "M5";
      case PERIOD_M15: return "M15";
      case PERIOD_M30: return "M30";
      case PERIOD_H1:  return "H1";
      case PERIOD_H4:  return "H4";
      case PERIOD_D1:  return "D1";
      case PERIOD_W1:  return "W1";
      case PERIOD_MN1: return "MN";
      default: return "TF?";
   }
}

//+------------------------------------------------------------------+
//| Create information panel                                         |
//+------------------------------------------------------------------+
void CreatePanel()
{
   //--- background panel
   string bg = indicatorName + "_BG";
   ObjectCreate(0, bg, OBJ_RECTANGLE_LABEL, 0, 0, 0);
   ObjectSetInteger(0, bg, OBJPROP_XDISTANCE, PanelX);
   ObjectSetInteger(0, bg, OBJPROP_YDISTANCE, PanelY);
   ObjectSetInteger(0, bg, OBJPROP_XSIZE, 200);
   ObjectSetInteger(0, bg, OBJPROP_YSIZE, 120);
   ObjectSetInteger(0, bg, OBJPROP_CORNER, PanelCorner);
   ObjectSetInteger(0, bg, OBJPROP_BGCOLOR, clrBlack);
   ObjectSetInteger(0, bg, OBJPROP_BORDER_TYPE, BORDER_FLAT);
   ObjectSetInteger(0, bg, OBJPROP_BORDER_COLOR, clrWhite);
   ObjectSetInteger(0, bg, OBJPROP_BACK, true);
   ObjectSetInteger(0, bg, OBJPROP_SELECTABLE, false);
   ObjectSetInteger(0, bg, OBJPROP_HIDDEN, true);
   ObjectSetInteger(0, bg, OBJPROP_ZORDER, 0);
   
   //--- title
   string title = indicatorName + "_Title";
   ObjectCreate(0, title, OBJ_LABEL, 0, 0, 0);
   ObjectSetInteger(0, title, OBJPROP_XDISTANCE, PanelX + 10);
   ObjectSetInteger(0, title, OBJPROP_YDISTANCE, PanelY + 10);
   ObjectSetInteger(0, title, OBJPROP_CORNER, PanelCorner);
   ObjectSetString (0, title, OBJPROP_TEXT, "Market Sentiment EA");
   ObjectSetInteger(0, title, OBJPROP_COLOR, clrWhite);
   ObjectSetString (0, title, OBJPROP_FONT, FontFace);
   ObjectSetInteger(0, title, OBJPROP_FONTSIZE, FontSize);
   ObjectSetInteger(0, title, OBJPROP_BACK, false);
   ObjectSetInteger(0, title, OBJPROP_SELECTABLE, false);
   ObjectSetInteger(0, title, OBJPROP_HIDDEN, true);
   ObjectSetInteger(0, title, OBJPROP_ZORDER, 1);
   
   //--- timeframe labels + sentiment placeholders
   string timeframes[3] = { TFtoString(HigherTF), TFtoString(LowerTF1), TFtoString(LowerTF2) };
   for(int i=0; i<3; i++)
   {
      string tfLabel = indicatorName + "_TF" + IntegerToString(i);
      ObjectCreate(0, tfLabel, OBJ_LABEL, 0, 0, 0);
      ObjectSetInteger(0, tfLabel, OBJPROP_XDISTANCE, PanelX + 10);
      ObjectSetInteger(0, tfLabel, OBJPROP_YDISTANCE, PanelY + 30 + i * 20);
      ObjectSetInteger(0, tfLabel, OBJPROP_CORNER, PanelCorner);
      ObjectSetString (0, tfLabel, OBJPROP_TEXT, timeframes[i] + ":");
      ObjectSetInteger(0, tfLabel, OBJPROP_COLOR, clrLightGray);
      ObjectSetString (0, tfLabel, OBJPROP_FONT, FontFace);
      ObjectSetInteger(0, tfLabel, OBJPROP_FONTSIZE, FontSize);
      ObjectSetInteger(0, tfLabel, OBJPROP_BACK, false);
      ObjectSetInteger(0, tfLabel, OBJPROP_SELECTABLE, false);
      ObjectSetInteger(0, tfLabel, OBJPROP_HIDDEN, true);
      ObjectSetInteger(0, tfLabel, OBJPROP_ZORDER, 1);
      
      string sentLabel = indicatorName + "_Sentiment" + IntegerToString(i);
      ObjectCreate(0, sentLabel, OBJ_LABEL, 0, 0, 0);
      ObjectSetInteger(0, sentLabel, OBJPROP_XDISTANCE, PanelX + 100);
      ObjectSetInteger(0, sentLabel, OBJPROP_YDISTANCE, PanelY + 30 + i * 20);
      ObjectSetInteger(0, sentLabel, OBJPROP_CORNER, PanelCorner);
      ObjectSetString (0, sentLabel, OBJPROP_TEXT, "N/A");
      ObjectSetInteger(0, sentLabel, OBJPROP_COLOR, NeutralColor);
      ObjectSetString (0, sentLabel, OBJPROP_FONT, FontFace);
      ObjectSetInteger(0, sentLabel, OBJPROP_FONTSIZE, FontSize);
      ObjectSetInteger(0, sentLabel, OBJPROP_BACK, false);
      ObjectSetInteger(0, sentLabel, OBJPROP_SELECTABLE, false);
      ObjectSetInteger(0, sentLabel, OBJPROP_HIDDEN, true);
      ObjectSetInteger(0, sentLabel, OBJPROP_ZORDER, 1);
   }
   
   //--- final sentiment label
   string fnl = indicatorName + "_Final";
   ObjectCreate(0, fnl, OBJ_LABEL, 0, 0, 0);
   ObjectSetInteger(0, fnl, OBJPROP_XDISTANCE, PanelX + 10);
   ObjectSetInteger(0, fnl, OBJPROP_YDISTANCE, PanelY + 100);
   ObjectSetInteger(0, fnl, OBJPROP_CORNER, PanelCorner);
   ObjectSetString (0, fnl, OBJPROP_TEXT, "Final: Neutral");
   ObjectSetInteger(0, fnl, OBJPROP_COLOR, NeutralColor);
   ObjectSetString (0, fnl, OBJPROP_FONT, FontFace);
   ObjectSetInteger(0, fnl, OBJPROP_FONTSIZE, FontSize + 2);
   ObjectSetInteger(0, fnl, OBJPROP_BACK, false);
   ObjectSetInteger(0, fnl, OBJPROP_SELECTABLE, false);
   ObjectSetInteger(0, fnl, OBJPROP_HIDDEN, true);
   ObjectSetInteger(0, fnl, OBJPROP_ZORDER, 1);
}

//+------------------------------------------------------------------+
//| Update information panel                                         |
//+------------------------------------------------------------------+
void UpdatePanel(int higherTFBias, int sentiment)
{
    // Update higher timeframe sentiment
    string higherTFSentiment = "Neutral";
    color higherTFColor = NeutralColor;
    
    if(higherTFBias == 1)
    {
        higherTFSentiment = "Bullish";
        higherTFColor = BullishColor;
    }
    else if(higherTFBias == -1)
    {
        higherTFSentiment = "Bearish";
        higherTFColor = BearishColor;
    }
    
    ObjectSetString(0, indicatorName + "_Sentiment0", OBJPROP_TEXT, higherTFSentiment);
    ObjectSetInteger(0, indicatorName + "_Sentiment0", OBJPROP_COLOR, higherTFColor);
    
    // Update final sentiment
    string finalSentiment = "Neutral";
    color finalColor = NeutralColor;
    
    switch(sentiment)
    {
        case 1:
            finalSentiment = "Bullish";
            finalColor = BullishColor;
            break;
        case -1:
            finalSentiment = "Bearish";
            finalColor = BearishColor;
            break;
        case 2:
            finalSentiment = "Risk-On";
            finalColor = RiskOnColor;
            break;
        case -2:
            finalSentiment = "Risk-Off";
            finalColor = RiskOffColor;
            break;
        default:
            finalSentiment = "Neutral";
            finalColor = NeutralColor;
    }
    
    ObjectSetString(0, indicatorName + "_Final", OBJPROP_TEXT, "Final: " + finalSentiment);
    ObjectSetInteger(0, indicatorName + "_Final", OBJPROP_COLOR, finalColor);
}

La función CalculateLotSize permite determinar dinámicamente el tamaño de la posición en función del saldo de la cuenta y el porcentaje de riesgo. En lugar de utilizar un tamaño de lote fijo, calcula el volumen adecuado midiendo la distancia entre el punto de entrada y el stop-loss, convirtiéndola en puntos y dividiendo el capital de riesgo entre la pérdida potencial por pip. Esto garantiza que en cada operación se arriesgue una proporción constante del patrimonio de la cuenta, independientemente de la volatilidad o del precio del instrumento, al tiempo que se ajusta el tamaño final del lote a los límites del bróker, como el mínimo, el máximo y el incremento permitido.

La función auxiliar TFtoString convierte las constantes numéricas de los marcos temporales en cadenas legibles para el usuario. Esto resulta fundamental para mostrar información relevante en paneles, cuadros de mando o registros, lo que permite a los traders comprender rápidamente a qué marco temporal corresponden el sesgo direccional o las señales a los que se hace referencia. Al convertir estos códigos enteros en texto claro, como «M5» o «H1», el código resulta más fácil de entender y se evita la confusión a la hora de realizar un seguimiento de varios marcos temporales.

Las funciones CreatePanel y UpdatePanel se encargan de la interfaz visual del EA. CreatePanel inicializa un panel de sentimiento de diseño sencillo con un fondo, un título y marcadores de posición para el sentimiento del mercado de cada marco temporal supervisado, además de un estado de sentimiento final. UpdatePanel actualiza dinámicamente estas etiquetas con el sesgo direccional actual del marco temporal superior y el sentimiento final calculado, modificando tanto el texto como el color para representar visualmente los estados alcista, bajista, de apetito por el riesgo, de aversión al riesgo o neutral. Juntas, crean un panel de seguimiento intuitivo y en tiempo real que permite a los traders evaluar rápidamente las condiciones del mercado sin tener que analizar datos sin procesar.



Resultados de las pruebas con datos históricos

La prueba con datos históricos se evaluó en el marco temporal de 1H durante un periodo de prueba de aproximadamente dos meses (del 17 de febrero de 2025 al 6 de mayo de 2025), con los siguientes parámetros:



Conclusión

En resumen, hemos diseñado e implementado un marco completo para automatizar el indicador del sentimiento del mercado. Hemos incluido el desarrollo de una lógica de detección del sentimiento del mercado en múltiples marcos temporales, la definición de categorías claras como alcista, bajista, de apetito por el riesgo, de aversión al riesgo y neutral, y la programación de las reglas de ejecución de operaciones para actuar en función de esas señales. También hemos integrado funciones esenciales como la gestión de riesgos mediante el ajuste dinámico del tamaño del lote, funciones de stop dinámico para asegurar las ganancias y un panel de información en tiempo real que muestra visualmente las condiciones del sentimiento del mercado. En conjunto, estos componentes han transformado el indicador en un sistema de negociación totalmente automatizado, capaz tanto de realizar análisis como de ejecutar operaciones.

En conclusión, esta automatización ayuda a los traders al eliminar la toma de decisiones basada en las emociones y garantizar la coherencia en la forma en que se realizan y gestionan las operaciones. Combinando el análisis del sentimiento del mercado, las reglas de negociación estructuradas y la gestión de riesgos integrada, los traders pueden desenvolverse en el mercado con un enfoque más disciplinado. El sistema ofrece una interfaz visual clara que aporta transparencia, al tiempo que ejecuta operaciones de forma automática en consonancia con el sentimiento del mercado, lo cual permite a los traders ahorrar tiempo, minimizar los errores y trabajar con una herramienta de nivel profesional que se adapta a las condiciones cambiantes del mercado.

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

Archivos adjuntos |
Mark_Sent_Auto.mq5 (62.08 KB)
Redes neuronales en el trading: Señales de trading robustas en cualquier régimen de mercado (Conclusión) Redes neuronales en el trading: Señales de trading robustas en cualquier régimen de mercado (Conclusión)
El artículo ofrece un análisis detallado de la integración de los enfoques del framework ST-Expert en la arquitectura Extralonger, lo que permite analizar simultáneamente las representaciones vectoriales temporales y espaciales de los datos. Se presentan los resultados de las pruebas realizadas con datos históricos reales, que demuestran la eficacia del modelo y su robustez frente a las anomalías del mercado. El artículo describe la estructura modular del framework, que garantiza la reproducibilidad, la flexibilidad para la investigación y la capacidad de optimizar los componentes por etapas.
Creación de sistemas de trading basados en IA en MQL5 (Parte 2): Desarrollo de un programa integrado con ChatGPT y con interfaz de usuario Creación de sistemas de trading basados en IA en MQL5 (Parte 2): Desarrollo de un programa integrado con ChatGPT y con interfaz de usuario
En este artículo, desarrollaremos un programa en MQL5 integrado con ChatGPT y dotado de una interfaz de usuario, aprovechando el framework para el procesamiento de JSON de la primera parte para enviar prompts a la API de OpenAI y mostrar las respuestas en un gráfico de MetaTrader 5. Asismo, implementaremos un panel de control con un campo de entrada, un botón de envío y un área de visualización de la respuesta, gestionando la comunicación con la API y el ajuste de texto para la interacción con el usuario.
Redes neuronales en el trading: señales de trading estables en cualquier régimen de mercado (Módulos de atención) Redes neuronales en el trading: señales de trading estables en cualquier régimen de mercado (Módulos de atención)
En este artículo continuaremos con la implementación de los enfoques del framework ST-Expert, centrándonos en los aspectos prácticos de su uso mediante MQL5. En artículos anteriores, hemos analizado los fundamentos teóricos y los componentes clave del modelo; ahora pasaremos a trabajar directamente con los algoritmos de atención de grafos y de distribución de atención local y global. El objetivo principal de este artículo es mostrar cómo las ideas conceptuales de ST-Expert se convierten en soluciones funcionales para el análisis y la previsión de series financieras.
Modificación del algoritmo de optimización de Dingo — Dingo Optimization Algorithm M (DOAm) Modificación del algoritmo de optimización de Dingo — Dingo Optimization Algorithm M (DOAm)
La modificación del algoritmo de Dingo propuesta por el autor en este artículo ha elevado considerablemente el listón en la búsqueda del mejor de los mejores algoritmos de optimización. ¿Es posible obtener resultados aún mejores?