Trading por pares: Trading algorítmico con optimización automática en la diferencia de puntuación Z
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ónTradicionalmente, 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
Advertencia: todos los derechos de estos materiales pertenecen a MetaQuotes Ltd. Queda totalmente prohibido el copiado total o parcial.
Este artículo ha sido escrito por un usuario del sitio web y refleja su punto de vista personal. MetaQuotes Ltd. no se responsabiliza de la exactitud de la información ofrecida, ni de las posibles consecuencias del uso de las soluciones, estrategias o recomendaciones descritas.
Creación de un sistema personalizado de detección de regímenes de mercado en MQL5 (Parte 1): Indicador
Redes neuronales en el trading: Actor—Director—Crítico (Final)
Análisis espectral singular unidimensional
De principiante a experto: programando velas japonesas
- Aplicaciones de trading gratuitas
- 8 000+ señales para copiar
- Noticias económicas para analizar los mercados financieros
Usted acepta la política del sitio web y las condiciones de uso
文章配对交易:Z 分数差异自动优化的算法交易已经发表:
作者: Yevgeniy Koshtenko