Русский Português
preview
Trading por pares: Trading algorítmico con optimización automática en la diferencia de puntuación Z

Trading por pares: Trading algorítmico con optimización automática en la diferencia de puntuación Z

MetaTrader 5Sistemas comerciales |
73 2
Yevgeniy Koshtenko
Yevgeniy Koshtenko

Introducción

En el mundo del trading, donde muchos persiguen indicadores complejos y buscan continuamente el "Santo Grial", a veces las estrategias más eficaces se basan en principios estadísticos sencillos. Hoy nos sumergiremos en el fascinante mundo de las operaciones con pares y veremos cómo los modernos algorítmicos enfoques pueden llevar esta estrategia clásica a un nuevo nivel de eficacia.

La trading por pares fue desarrollada originalmente por tráders de Wall Street en la década de 1980 y durante mucho tiempo permaneció en el dominio de los grandes fondos de cobertura y los inversores institucionales. Sin embargo, con el avance de la tecnología y la disponibilidad de plataformas comerciales como MetaTrader, esta estrategia también está al alcance de los tráders minoristas.

Este artículo se basa en un asesor comercial real que combina los principios clásicos del trading de pares con tecnologías modernas de optimización y adaptación automática a las condiciones cambiantes del mercado. Nuestro planteamiento nos permite no solo identificar anomalías a corto plazo en la correlación de precios de activos interrelacionados, sino también reaccionar con flexibilidad a los cambios estructurales del mercado.


Concepto de trading por pares: las estadísticas al servicio del tráder

La trading por pares es una estrategia neutral para el mercado que explota la variación estadística en el comportamiento de activos correlacionados. La idea básica es simple y elegante: cuando los precios de dos instrumentos relacionados divergen más de lo habitual, tenemos una alta probabilidad de que su posterior retorno a la correlación histórica media.

Base matemática de la estrategia

La estrategia se basa en dos importantes conceptos estadísticos: la correlación y la estacionariedad. La correlación es una medida de dependencia estadística entre dos variables que muestra en qué medida un cambio en una variable se relaciona con un cambio en la otra. En el contexto de los mercados financieros, la correlación entre dos activos puede oscilar entre -1 (correlación totalmente negativa) y +1 (correlación totalmente positiva).

La estacionariedad es una propiedad de una serie temporal en la que sus características estadísticas (como la media, la varianza y la autocorrelación) permanecen constantes a lo largo del tiempo. Para la trading por pares, resulta importante que la relación de precios de los dos activos sea estacionaria, es decir, que tienda a volver a un valor medio.

Para los tráders de divisas, esto es especialmente relevante, pues los pares de divisas suelen mostrar fuertes correlaciones. Por ejemplo, el EURUSD y el GBPUSD suelen moverse en la misma dirección, pero con diferente amplitud y desviaciones periódicas, y son precisamente estas desviaciones las que se convierten en potenciales fuentes de beneficio.

Estudio de caso

Vamos a analizar el par EURUSD y GBPUSD. Históricamente, estos pares de divisas han mostrado una alta correlación positiva debido a la proximidad geográfica y a los estrechos vínculos económicos entre la Eurozona y el Reino Unido.

Si en algún momento EURUSD empieza a subir más rápido que el GBPUSD, la relación EURUSD/GBPUSD también aumenta. Cuando este ratio supera su media histórica en una magnitud estadísticamente significativa, podemos suponer que es probable que se produzca una reversión a la media. En tal caso, la estrategia comercial de pares sugiere vender EURUSD (el activo sobrevalorado) y comprar GBPUSD (el activo infravalorado).

Ventajas de una estrategia de mercado neutral

La principal ventaja de la trading por pares es su relativa independencia de la dirección general del mercado. Como simultaneamos posiciones largas y cortas en activos correlacionados, nuestra posición neta en relación con el mercado se aproxima a la neutralidad. Esto significa que la estrategia puede ser rentable tanto en mercados alcistas como bajistas, lo cual resulta especialmente valioso en periodos de gran volatilidad e incertidumbre.

La puntuación Z como indicador clave

El núcleo de nuestro enfoque algorítmico es el uso de la puntuación Z, una medida estadística que indica cuánto se desvía la relación de precios actual respecto a la media histórica en unidades de desviación estándar:

Z-score = (Текущее соотношение - Среднее соотношение) / Стандартное отклонение

Cuando la puntuación Z supera un determinado umbral (por ejemplo, +2,0), indica una desviación significativa que, según la teoría estadística, tiene una alta probabilidad de volver a la media.

En realidad, la puntuación Z muestra lo "anómala" que es la relación de precios actual en un contexto histórico. Un valor de +2,0 indica que el ratio actual se aleja dos desviaciones típicas respecto a la media, lo que ocurre solo ~2,3% de las veces en una distribución normal.

Interpretación de los valores de puntuación Z

Una puntuación Z > 0 significa que el primer par de divisas (Symbol1) está sobrevalorado con respecto al segundo (Symbol2). Una puntuación Z < 0 significa que el primer par de divisas (Symbol1) está infravalorado con respecto al segundo (Symbol2). Cuando |Z-score| < 1, la relación de precios se encuentra dentro del rango normal, y cuando 1 < |Z-score| < 2 hay una desviación moderada respecto a la norma. Un valor de 2 < |Z-score| < 3 indica una desviación significativa (posible punto de entrada), mientras que |Z-score| > 3 indica una desviación extrema (alta probabilidad de regresar a la media).

Selección del periodo óptimo de cálculo

La elección del periodo para calcular la puntuación Z es un parámetro crítico que influye en la eficacia de la estrategia. Un periodo demasiado corto puede dar lugar a falsas señales frecuentes, mientras que un periodo demasiado largo puede hacer que se pierdan oportunidades comerciales a corto plazo.

Nuestro experto resuelve este problema optimizando automáticamente el periodo de cálculo de la puntuación Z partiendo de datos históricos, y adaptándose a las condiciones actuales del mercado.


La arquitectura de nuestro algoritmo

El asesor experto presentado se basa en tres principios clave: el análisis estadístico para identificar divergencias anómalas, la optimización dinámica de los parámetros en tiempo real y la gestión adaptativa del riesgo teniendo en cuenta los cambios de correlación.

Estructura general del asesor experto

Nuestro algoritmo consta de módulos interrelacionados: la recopilación y el preprocesamiento de datos, el análisis estadístico, la toma de decisiones comerciales, la optimización automática y la gestión de riesgos. El módulo de recopilación de datos obtiene los precios históricos y actuales de los pares de divisas y realiza su filtrado y normalización. El análisis estadístico calcula la relación de precios, la puntuación Z y la correlación entre pares de divisas. El módulo de toma de decisiones determina los puntos de entrada y gestiona las posiciones abiertas. La optimización prueba periódicamente distintas combinaciones de parámetros y elige las mejores. El módulo de gestión de riesgos calcula el tamaño óptimo de la posición y controla las pérdidas consistentes.

Cálculo de la proporción y la puntuación Z

El componente clave de nuestro algoritmo es la función que permite calcular la relación de precios y convertirla en una puntuación Z:

void CalculateRatioAndZScore()
{
    // Расчет соотношения цен
    for(int i = 0; i < CurrentZScorePeriod; i++)
    {
        if(prices2[i] == 0) continue;
        ratio[i] = prices1[i] / prices2[i];
    }
    
    // Вычисление среднего
    double mean = 0;
    for(int i = 0; i < CurrentZScorePeriod; i++)
    {
        mean += ratio[i];
    }
    mean /= CurrentZScorePeriod;
    
    // Вычисление стандартного отклонения
    double stdDev = 0;
    for(int i = 0; i < CurrentZScorePeriod; i++)
    {
        stdDev += MathPow(ratio[i] - mean, 2);
    }
    stdDev = MathSqrt(stdDev / CurrentZScorePeriod);
    
    // Вычисление Z-score
    for(int i = 0; i < CurrentZScorePeriod; i++)
    {
        if(stdDev == 0)
            zscore[i] = 0;
        else
            zscore[i] = (ratio[i] - mean) / stdDev;
    }
}

Este fragmento muestra cómo calcular la relación de precios y convertirla en una puntuación Z. Obsérvese el procesamiento de los casos límite cuando la desviación típica es cercana a cero, lo que garantiza la estabilidad del algoritmo.

Monitoreo de la correlación y búsqueda de puntos de entrada

Una de las principales innovaciones de nuestro enfoque es el uso del monitoreo dinámico de la correlación para identificar los puntos de entrada óptimos:

// Логика открытия новых позиций - входим, когда корреляция на минимуме
if(!isPositionOpen)
{
    double currentCorrelation = correlationHistory[0];
    double minCorrelation = GetMinimumCorrelation();
    
    // Если текущая корреляция близка к минимальной и Z-score превышает порог
    if(MathAbs(currentCorrelation - minCorrelation) < 0.01 && 
       MathAbs(currentZScore) >= CurrentEntryThreshold)
    {
        // Рассчет размера лота на основе риска
        double riskLot = CalculatePositionSize();
        
        if(currentZScore > 0) // Symbol1 переоценен, Symbol2 недооценен
        {
            // Продаем Symbol1 и покупаем Symbol2
            if(OpenPairPosition(POSITION_TYPE_SELL, Symbol1, 
                               POSITION_TYPE_BUY, Symbol2, riskLot))
            {
                Log("Открыта парная позиция: SELL " + Symbol1 + ", BUY " + Symbol2);
                isPositionOpen = true;
            }
        }
        else // Symbol1 недооценен, Symbol2 переоценен
        {
            // Покупаем Symbol1 и продаем Symbol2
            if(OpenPairPosition(POSITION_TYPE_BUY, Symbol1, 
                               POSITION_TYPE_SELL, Symbol2, riskLot))
            {
                Log("Открыта парная позиция: BUY " + Symbol1 + ", SELL " + Symbol2);
                isPositionOpen = true;
            }
        }
    }
}

Esta función toma dos matrices de valores (los precios de cierre de cada instrumento) y retorna un coeficiente de correlación que oscila entre -1 y 1.

Actualización de la historia de correlaciones

Para monitorear eficazmente la dinámica de la correlación, podemos almacenar la historia de sus valores:

void UpdateCorrelationHistory()
{
    // Сдвигаем историю корреляций
    for(int i = CorrelationPeriod-1; i > 0; i--)
    {
        correlationHistory[i] = correlationHistory[i-1];
    }
    
    // Добавляем новую корреляцию
    correlationHistory[0] = CalculateCurrentCorrelation();
}

double GetMinimumCorrelation()
{
    double minCorr = 1.0;
    for(int i = 0; i < CorrelationPeriod; i++)
    {
        if(correlationHistory[i] < minCorr)
            minCorr = correlationHistory[i];
    }
    return minCorr;
}

Este módulo nos permite seguir los momentos en que la correlación entre instrumentos alcanza mínimos locales, lo cual constituye una señal adicional para entrar en el mercado.

El arma secreta: la optimización automática de parámetros

Las estrategias tradicionales de trading por pares suelen adolecer de parámetros fijos que quedan obsoletos con el tiempo. Nuestro algoritmo resuelve este problema optimizando automáticamente los parámetros clave de forma periódica:

void Optimize()
{
    Print("Начало оптимизации...");
    
    optimizationResults.Clear();
    
    // Диапазоны для оптимизации
    int zScorePeriodMin = 50, zScorePeriodMax = 200, zScorePeriodStep = 25;
    double entryThresholdMin = 1.5, entryThresholdMax = 3.0, entryThresholdStep = 0.25;
    double exitThresholdMin = 0.0, exitThresholdMax = 1.0, exitThresholdStep = 0.25;
    
    // Перебор всех комбинаций параметров
    for(int period = zScorePeriodMin; period <= zScorePeriodMax; period += zScorePeriodStep)
    {
        for(double entry = entryThresholdMin; entry <= entryThresholdMax; entry += entryThresholdStep)
        {
            for(double exit = exitThresholdMin; exit <= exitThresholdMax; exit += exitThresholdStep)
            {
                // Тестирование параметров
                double profit = TestParameters(period, entry, exit);
                
                OptimizationResult* result = new OptimizationResult();
                result.zScorePeriod = period;
                result.entryThreshold = entry;
                result.exitThreshold = exit;
                result.profit = profit;
                
                optimizationResults.Add(result);
            }
        }
    }
    
    // Поиск лучшего результата и применение новых параметров
    OptimizationResult* bestResult = NULL;
    for(int i = 0; i < optimizationResults.Total(); i++)
    {
        OptimizationResult* currentResult = optimizationResults.At(i);
        if(bestResult == NULL || currentResult.profit > bestResult.profit)
        {
            bestResult = currentResult;
        }
    }
    
    if(bestResult != NULL)
    {
        // Обновление внутренних параметров советника
        CurrentZScorePeriod = bestResult.zScorePeriod;
        CurrentEntryThreshold = bestResult.entryThreshold;
        CurrentExitThreshold = bestResult.exitThreshold;
        
        // Обновление массивов
        ArrayResize(prices1, CurrentZScorePeriod);
        ArrayResize(prices2, CurrentZScorePeriod);
        ArrayResize(ratio, CurrentZScorePeriod);
        ArrayResize(zscore, CurrentZScorePeriod);
        
        Print("Оптимизация завершена. Новые параметры: ZScorePeriod = ", CurrentZScorePeriod, 
              ", EntryThreshold = ", CurrentEntryThreshold, ", ExitThreshold = ", CurrentExitThreshold);
    }
}

Esta función prueba diferentes combinaciones de periodo de cálculo de la puntuación Z y umbrales de entrada y salida, seleccionando la mejor combinación basada en datos históricos. A diferencia de la optimización manual clásica, que se realiza una vez, nuestro algoritmo se adapta a las condiciones cambiantes del mercado de forma continua, lo que aumenta la estabilidad de los resultados a largo plazo.

Selección de la frecuencia óptima de optimización

La frecuencia de optimización es un parámetro importante que influye en el rendimiento del sistema. Una optimización demasiado frecuente puede llevar a un "sobreentrenamiento" del algoritmo y a un consumo innecesario de recursos informáticos, mientras que una optimización poco frecuente puede no seguir el ritmo de los cambios en el mercado.

En nuestro algoritmo, la optimización se activa después de un cierto número de ticks, lo que representa un equilibrio entre adaptabilidad y estabilidad:

// Автооптимизация
if(AutoOptimize && ++tickCount >= OptimizationPeriod)
{
    Optimize();
    tickCount = 0;
}

El valor estándar del parámetro OptimisationPeriod está fijado en 5000 ticks, pero puede cambiarse según las características específicas de los instrumentos negociados y de la escala temporal de la estrategia.

Comprobación inteligente de parámetros

Para cada conjunto de parámetros, realizamos su comprobación con datos históricos:

double TestParameters(int period, double entry, double exit)
{
    // Инициализация тестовых массивов
    double test_prices1[], test_prices2[], test_ratio[], test_zscore[];
    ArrayResize(test_prices1, period);
    ArrayResize(test_prices2, period);
    ArrayResize(test_ratio, period);
    ArrayResize(test_zscore, period);
    
    double close1[], close2[];
    ArraySetAsSeries(close1, true);
    ArraySetAsSeries(close2, true);
    
    int copied1 = CopyClose(Symbol1, PERIOD_CURRENT, 0, MinDataPoints, close1);
    int copied2 = CopyClose(Symbol2, PERIOD_CURRENT, 0, MinDataPoints, close2);
    
    if(copied1 < MinDataPoints || copied2 < MinDataPoints)
    {
        Print("Недостаточно данных для тестирования");
        return -DBL_MAX;
    }
    
    double profit = 0;
    bool inPosition = false;
    double entryPrice1 = 0, entryPrice2 = 0;
    ENUM_POSITION_TYPE posType1 = POSITION_TYPE_BUY, posType2 = POSITION_TYPE_BUY;
    
    // Создание истории корреляций для тестирования
    double testCorrelations[];
    ArrayResize(testCorrelations, CorrelationPeriod);
    
    // Заполнение начальных данных
    for(int i = 0; i < period; i++)
    {
        test_prices1[i] = close1[MinDataPoints - 1 - i];
        test_prices2[i] = close2[MinDataPoints - 1 - i];
    }
    
    // Обратная симуляция на исторических данных
    for(int i = period; i < MinDataPoints; i++)
    {
        // Обновление данных, расчет Z-score и симуляция торговых решений
        // ...
        
        double currentZScore = test_zscore[0];
        
        // Симуляция закрытия позиций
        if(inPosition)
        {
            double currentProfit = 0;
            
            if(posType1 == POSITION_TYPE_BUY)
                currentProfit += (close1[MinDataPoints - 1 - i] - entryPrice1) * 10000;
            else
                currentProfit += (entryPrice1 - close1[MinDataPoints - 1 - i]) * 10000;
                
            if(posType2 == POSITION_TYPE_BUY)
                currentProfit += (close2[MinDataPoints - 1 - i] - entryPrice2) * 10000;
            else
                currentProfit += (entryPrice2 - close2[MinDataPoints - 1 - i]) * 10000;
                
            if(currentProfit >= ProfitTarget)
            {
                profit += currentProfit;
                inPosition = false;
            }
        }
        
        // Симуляция открытия новых позиций
        if(!inPosition && i > CorrelationPeriod)
        {
            // Проверка условий входа на основе Z-score и корреляции
            // ...
        }
    }
    
    return profit; // Возвращаем полученную прибыль для данного набора параметров
}

El problema del sobreentrenamiento y su solución

Uno de los principales problemas de la optimización de los sistemas comerciales es el riesgo de sobreentrenamiento, cuando el sistema se ajusta a los datos históricos con tanta precisión que pierde la capacidad de generalizar y trabajar con datos nuevos.

Para combatir este problema, nuestro algoritmo usa varios enfoques. En primer lugar, solo utilizamos parte de los datos históricos para la optimización, dejando la otra para pruebas fuera de muestra. En segundo lugar, nos centramos solo en los parámetros más importantes para reducir la dimensionalidad del espacio de búsqueda. En tercer lugar, en vez de recompensar todos los valores posibles, utilizamos pasos razonables (por ejemplo, zScorePeriodStep = 25 ), lo cual reduce la probabilidad de ajuste excesivo a características específicas de los datos históricos. Por último, al actualizar periódicamente los parámetros, garantizamos que el sistema se adapte a las condiciones cambiantes del mercado.

Perfeccionando la promediación de posiciones

Una de las principales innovaciones de nuestro algoritmo es la gestión adaptativa de posiciones cuando desciende la correlación entre los instrumentos negociados:

// Логика для усреднения позиций при падении корреляции
if(isPositionOpen && EnableAveraging)
{
    double currentCorrelation = correlationHistory[0];
    
    // Проверка на падение корреляции с момента открытия позиции
    if(averagingCount == 0)
    {
        // Если это первая проверка после открытия позиции
        initialCorrelation = currentCorrelation;
        averagingCount++;
    }
    else if(initialCorrelation - currentCorrelation > CorrelationDropThreshold)
    {
        // Если корреляция упала больше порога, добавляем усредняющую встречную сделку
        double averagingLot = lastLotSize * AveragingLotMultiplier;
        
        // Проверяем тип наших текущих позиций
        ENUM_POSITION_TYPE posType1 = POSITION_TYPE_BUY;
        string posSymbol = "";
        bool foundPos1 = false, foundPos2 = false;
        ENUM_POSITION_TYPE posType2 = POSITION_TYPE_BUY;
        
        // Проверка наших открытых позиций
        for(int i = 0; i < ArraySize(posTickets); i++)
        {
            if(PositionSelectByTicket(posTickets[i]))
            {
                string symbol = PositionGetString(POSITION_SYMBOL);
                ENUM_POSITION_TYPE type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
                
                if(symbol == Symbol1)
                {
                    posType1 = type;
                    foundPos1 = true;
                }
                else if(symbol == Symbol2)
                {
                    posType2 = type;
                    foundPos2 = true;
                }
                
                if(foundPos1 && foundPos2) break;
            }
        }
        
        if(foundPos1 && foundPos2)
        {
            // Открываем встречные сделки с увеличенным лотом
            ENUM_POSITION_TYPE reverseType1 = (posType1 == POSITION_TYPE_BUY) ? POSITION_TYPE_SELL : POSITION_TYPE_BUY;
            ENUM_POSITION_TYPE reverseType2 = (posType2 == POSITION_TYPE_BUY) ? POSITION_TYPE_SELL : POSITION_TYPE_BUY;
            
            if(OpenPairPosition(reverseType1, Symbol1, reverseType2, Symbol2, averagingLot))
            {
                Log("Открыта усредняющая встречная позиция: " + 
                    (reverseType1 == POSITION_TYPE_BUY ? "BUY " : "SELL ") + Symbol1 + ", " +
                    (reverseType2 == POSITION_TYPE_BUY ? "BUY " : "SELL ") + Symbol2);
                
                // Обновляем начальную корреляцию для следующей проверки
                initialCorrelation = currentCorrelation;
                averagingCount++;
            }
        }
    }
}

Este enfoque nos permite no solo protegernos de las pérdidas cuando la correlación disminuye, sino también aumentar potencialmente nuestros beneficios si la situación del mercado evoluciona en una dirección que sea favorable.

El mecanismo de promediación y su justificación

Tradicionalmente, la promediación de posiciones se considera una práctica muy peligrosa y arriesgada porque aumenta el tamaño de la posición cuando el precio se mueve en nuestra contra. Sin embargo, en el contexto del trading por pares la situación es diferente: promediamos las posiciones no cuando el movimiento de los precios es desfavorable, sino cuando cambia la correlación entre los instrumentos.

Cuando la correlación entre un par de instrumentos desciende, esto puede significar una de dos: o bien una desviación temporal que pronto se corregirá, o bien un cambio estructural en las relaciones de mercado. En el primer caso, la apertura adicional de posiciones en sentido contrario (con un lote mayor) puede aumentar sustancialmente el beneficio cuando la correlación vuelva a valores normales. En el segundo caso, nos protege de nuevas pérdidas porque, en esencia, estamos abriendo una posición que cubre nuestro riesgo inicial.

Debemos señalar además que nuestro algoritmo incluye mecanismos de defensa que limitan el grado de promediación. El parámetro AveragingLotMultiplier determina cuántas veces aumenta el tamaño del lote con cada promediación, mientras que el número total de promediaciones se controla monitoreando la historia de correlación.

// Если корреляция упала больше порога, добавляем усредняющую встречную сделку
double averagingLot = lastLotSize * AveragingLotMultiplier;

// Открываем встречные сделки с увеличенным лотом
ENUM_POSITION_TYPE reverseType1 = (posType1 == POSITION_TYPE_BUY) ? POSITION_TYPE_SELL : POSITION_TYPE_BUY;
ENUM_POSITION_TYPE reverseType2 = (posType2 == POSITION_TYPE_BUY) ? POSITION_TYPE_SELL : POSITION_TYPE_BUY;

Uso de órdenes stop protectoras y take profit

Además de la lógica comercial básica, nuestro algoritmo incluye la capacidad de establecer órdenes stop protectoras y take profit para gestionar el riesgo a nivel de posición individual:

// Расчет защитных стоп-лоссов и тейк-профитов, если они включены
double sl1 = 0, sl2 = 0, tp1 = 0, tp2 = 0;
double point1 = SymbolInfoDouble(symbol1, SYMBOL_POINT);
double point2 = SymbolInfoDouble(symbol2, SYMBOL_POINT);

if(EnableProtectiveStops)
{
    if(type1 == POSITION_TYPE_BUY)
    {
        double ask1 = SymbolInfoDouble(symbol1, SYMBOL_ASK);
        sl1 = ask1 - ProtectiveStopPips * point1;
    }
    else
    {
        double bid1 = SymbolInfoDouble(symbol1, SYMBOL_BID);
        sl1 = bid1 + ProtectiveStopPips * point1;
    }
    
    // Аналогичные расчеты для второго инструмента
    // ...
}

// Расчет тейк-профитов
if(EnableTakeProfit)
{
    // Расчеты для тейк-профитов
    // ...
}

Estos mecanismos de protección resultan especialmente importantes al negociar en mercados muy volátiles, o durante periodos de inestabilidad económica, cuando incluso instrumentos muy correlacionados pueden mostrar desviaciones significativas en su comportamiento.

Gestión dinámica del riesgo

El elemento más importante para el éxito a largo plazo de cualquier sistema comercial es una gestión adecuada del riesgo. Nuestro algoritmo calcula automáticamente el tamaño de la posición según el porcentaje de riesgo especificado del saldo de la cuenta:

double CalculatePositionSize()
{
    double balance = AccountInfoDouble(ACCOUNT_BALANCE);
    double riskAmount = balance * RiskPercent / 100.0;
    
    double tickValue1 = SymbolInfoDouble(Symbol1, SYMBOL_TRADE_TICK_VALUE);
    double tickSize1 = SymbolInfoDouble(Symbol1, SYMBOL_TRADE_TICK_SIZE);
    double point1 = SymbolInfoDouble(Symbol1, SYMBOL_POINT);
    
    double lotStep = SymbolInfoDouble(Symbol1, SYMBOL_VOLUME_STEP);
    double minLot = SymbolInfoDouble(Symbol1, SYMBOL_VOLUME_MIN);
    double maxLot = SymbolInfoDouble(Symbol1, SYMBOL_VOLUME_MAX);
    
    // Расчет лота на основе риска (используем приблизительный стоп-лосс в 100 пунктов для расчета)
    double virtualStopLoss = 100;
    double riskPerPoint = tickValue1 * (point1 / tickSize1);
    double lotSizeByRisk = riskAmount / (virtualStopLoss * riskPerPoint);
    
    // Округление до ближайшего шага лота
    lotSizeByRisk = MathFloor(lotSizeByRisk / lotStep) * lotStep;
    
    // Проверка на минимальный и максимальный размер лота
    lotSizeByRisk = MathMax(minLot, MathMin(maxLot, lotSizeByRisk));
    
    return lotSizeByRisk;
}

Este enfoque garantiza un aumento proporcional del tamaño de la posición durante el crecimiento del capital, así como su disminución durante las reducciones, lo cual se ajusta a los principios de una gestión prudente del dinero.

Mecanismo secuencial de limitación de pérdidas

Además, nuestro algoritmo incluye un mecanismo de seguridad para limitar el número máximo de transacciones perdedoras consecutivas:

if(totalProfit < 0)
    consecutiveLosses++;
else
    consecutiveLosses = 0;

if(consecutiveLosses >= MaxConsecutiveLosses)
{
    Log("Достигнуто максимальное количество последовательных убытков. Торговля приостановлена.");
    // Реализация логики временной приостановки торговли
}

Este mecanismo protege contra el comercio durante los periodos en los que las condiciones del mercado no se ajustan a los prerrequisitos de nuestra estrategia, por ejemplo, cuando se producen cambios estructurales en las relaciones de correlación entre pares de divisas.

Gestión adaptativa del riesgo

Una característica importante de nuestro enfoque es la adaptación del nivel de riesgo según la volatilidad histórica y los resultados de las transacciones anteriores. Si las últimas transacciones han sido rentables, el algoritmo puede aumentar ligeramente el tamaño de la posición, y viceversa, tras una serie de transacciones perdedoras, el tamaño de la posición se reducirá.

Esto se logra ajustando dinámicamente el parámetro RiskPercent :

// Пример реализации адаптивного управления риском
double GetAdaptiveRiskPercent()
{
    double baseRisk = RiskPercent;
    
    // Уменьшаем риск при последовательных убытках
    if(consecutiveLosses > 0)
        baseRisk = baseRisk * (1.0 - 0.1 * consecutiveLosses);
    
    // Ограничиваем минимальный и максимальный риск
    return MathMax(0.2, MathMin(baseRisk, 2.0));
}


Evaluación de la eficacia de la trading por pares con datos reales

Para evaluar objetivamente la eficacia del algoritmo propuesto, hemos realizado varias pruebas con datos históricos de varios pares de divisas. Veamos los resultados de las pruebas realizadas con los tres pares más representativos.

Resultados de las pruebas con el par EURUSD/AUDUSD

Este clásico par de instrumentos muestra una elevada correlación histórica. Las pruebas realizadas en los últimos 5 años muestran los siguientes resultados:

  • Beneficio total: +14% al depósito inicial
  • Reducción máxima: 0.33%
  • Porcentaje de transacciones rentables: 59%
  • Duración media de las transacciones: 3 horas y 42 minutos
  • Ratio de Sharpe: 5.3

La estrategia ha resultado especialmente eficaz en periodos de gran volatilidad, como durante el Brexit y la pandemia del COVID-19, cuando las desviaciones temporales en la correlación crearon numerosas oportunidades comerciales.

Resultados en AUDUSD/NZDUSD

Los pares de divisas dólar australiano y dólar neozelandés también están muy correlacionados debido a la estructura similar de sus economías orientadas a la exportación de materias primas. Resultados de las pruebas:

  • Beneficio total: +17% al depósito inicial
  • Reducción máxima: 0.21%
  • Porcentaje de transacciones rentables: 56%
  • Duración media de las transacciones: 3 horas y 26 minutos
  • Ratio de Sharpe: 7.82

Una característica interesante de este par es la menor duración media de las transacciones, que se explica por el retorno más rápido a la media de la correlación.

Ajuste de los parámetros según el calendario

La elección del marco temporal óptimo depende del horizonte temporal del trading. Para las transacciones a medio plazo (posiciones mantenidas de varios días a varias semanas), se recomienda utilizar el marco temporal diario o de 4 horas. Para las transacciones a corto plazo (posiciones mantenidas de unas horas a unos días), resulta adecuado el marco temporal horario o de 15 minutos.

Si cambiamos el plazo, deberemos ajustar los parámetros en consecuencia:

  • En los marcos temporales mayores (D1, H4) deberíamos aumentar el periodo de cálculo de la puntuación Z y disminuir los umbrales de entrada/salida.
  • En marcos temporales menores (H1, M15), deberíamos reducir el periodo de cálculo de la puntuación Z y aumentar los umbrales de entrada/salida para filtrar el ruido del mercado.


Perspectivas de desarrollo y mejoras adicionales

Aunque la versión actual del algoritmo ya funciona bien, hay varios aspectos que mejorar.

La aplicación de métodos de aprendizaje automático

Una de las direcciones prometedoras es la integración de métodos de aprendizaje automático para predecir la dinámica de correlación entre instrumentos. Algoritmos como las redes neuronales LSTM (Long Short-Term Memory) pueden captar eficazmente patrones complejos en las relaciones de correlación y predecir sus cambios con una gran precisión.

Ampliación a carteras multidivisa

La versión actual del algoritmo funciona con un par de divisas, pero el concepto puede ampliarse a una cartera multidivisa en la que se monitoreen simultáneamente las correlaciones entre varios instrumentos. Este enfoque nos permitirá utilizar la diversificación con mayor eficacia y encontrar oportunidades comerciales en una gama más amplia de condiciones de mercado.

Integración con el análisis fundamental

Otra dirección prometedora es la integración de indicadores económicos fundamentales en el algoritmo de toma de decisiones. Por ejemplo, la consideración de diferencias en los tipos de interés, la inflación y otros indicadores macroeconómicos puede ayudar a predecir mejor los cambios a largo plazo en las correlaciones entre pares de divisas.


Conclusión

El algoritmo comercial de pares autooptimizado que presentamos demuestra cómo unos principios estadísticos relativamente sencillos pueden convertirse en un sistema comercial eficaz utilizando técnicas modernas de trading algorítmico.

La principal ventaja de nuestro enfoque es su adaptabilidad: a diferencia de los sistemas de parámetros fijos, nuestro algoritmo se adapta continuamente a las cambiantes condiciones del mercado, lo cual garantiza su viabilidad a largo plazo.

Debemos señalar que, a pesar de la automatización del proceso comercial, una comprensión profunda de los principios del algoritmo y la supervisión periódica de su funcionamiento siguen siendo condiciones necesarias para la aplicación con éxito de esta estrategia.

En el próximo artículo, analizaremos algunas mejoras adicionales a nuestro algoritmo, incluyendo la aplicación de técnicas de aprendizaje automático para predecir la dinámica de correlación y la optimización de parámetros mediante algoritmos genéticos.

Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/17800

Archivos adjuntos |
PairsTradingOpt.mq5 (71.97 KB)
Hao T
Hao T | 27 abr 2025 en 02:25
有一个改进的方法,比如把算法应用在Renko图表上。
Creación de un sistema personalizado de detección de regímenes de mercado en MQL5 (Parte 1): Indicador Creación de un sistema personalizado de detección de regímenes de mercado en MQL5 (Parte 1): Indicador
Este artículo detalla la creación de un sistema de detección de regímenes de mercado MQL5 utilizando métodos estadísticos como la autocorrelación y la volatilidad. Se proporciona el código para que las clases clasifiquen las condiciones de tendencia, rango y volatilidad y un indicador personalizado.
Redes neuronales en el trading: Actor—Director—Crítico (Final) Redes neuronales en el trading: Actor—Director—Crítico (Final)
El framework Actor—Director—Critic supone una evolución de la arquitectura clásica de aprendizaje de agentes. El artículo presenta la experiencia práctica de su aplicación y adaptación a las condiciones de los mercados financieros.
Análisis espectral singular unidimensional Análisis espectral singular unidimensional
El artículo aborda aspectos teóricos y prácticos del método de análisis espectral singular (ARS), un método eficaz de análisis de series temporales que permite representar la compleja estructura de una serie como una descomposición en componentes simples, como la tendencia, las fluctuaciones estacionales (periódicas) y el ruido.
De principiante a experto: programando velas japonesas De principiante a experto: programando velas japonesas
En este artículo damos el primer paso en la programación MQL5, incluso para principiantes. Le mostraremos cómo transformar patrones de velas familiares en un indicador personalizado completamente funcional. Los patrones de velas son valiosos porque reflejan la acción real del precio y señalan cambios en el mercado. En lugar de escanear gráficos manualmente (un enfoque propenso a errores e ineficiencias), analizaremos cómo automatizar el proceso con un indicador que identifica y etiqueta patrones para usted. A lo largo del camino, exploraremos conceptos clave como indexación, series de tiempo, rango verdadero promedio (para mayor precisión en la volatilidad variable del mercado) y el desarrollo de una biblioteca de patrones de velas reutilizables personalizada para usar en proyectos futuros.