- Crear y eliminar símbolos personalizados
- Propiedades de símbolos personalizados
- Fijación de coeficientes de margen
- Configurar sesiones de cotización y trading
- Añadir, sustituir y suprimir cotizaciones
- Añadir, sustituir y eliminar ticks
- Conversión de los cambios en el libro de órdenes
- Particularidades del trading con símbolos personalizados
Particularidades del trading con símbolos personalizados
El símbolo personalizado solo lo conoce el terminal cliente y no está disponible en el servidor de trading. Por lo tanto, si un símbolo personalizado se construye sobre la base de algún símbolo real, entonces cualquier Asesor Experto colocado en el gráfico de dicho símbolo personalizado debe generar órdenes de trading para el símbolo original.
Como solución más sencilla a este problema, puede colocar un Asesor Experto en el gráfico del símbolo original pero recibir señales (por ejemplo, de indicadores) del símbolo personalizado. Otro enfoque obvio es sustituir los nombres de los símbolos al realizar operaciones de trading. Para probar ambos enfoques, necesitamos un símbolo personalizado y un Asesor Experto.
Como ejemplo práctico interesante de símbolos personalizados, tomemos varios gráficos de equivolumen diferentes.
Un gráfico de equivolumen es un gráfico de barras construido sobre el principio de igualdad del volumen contenido en ellas. En un gráfico normal, cada nueva barra se forma con una frecuencia determinada, que coincide con el tamaño del marco temporal. En un gráfico de equivolumen, cada barra se considera formada cuando la suma de ticks o volúmenes reales alcanza un valor preestablecido. En este momento, el programa empieza a calcular el importe de la barra siguiente. Por supuesto, en el proceso de cálculo de los volúmenes se controlan los movimientos de los precios, y obtenemos los conjuntos habituales de precios en el gráfico: Open, High, Low y Close.
Las barras de igual rango se construyen de forma similar: en ellas se abre una nueva barra cuando el precio supera un número determinado de puntos en cualquier dirección.
Así, el Asesor Experto EqualVolumeBars.mq5 soportará tres modos, es decir, tres tipos de gráficos:
- EqualTickVolumes - barras de equivolumen por ticks
- EqualRealVolumes - barras de equivolumen por volúmenes reales (si se emiten)
- RangeBars - barras de rango iguales
Se seleccionan mediante el parámetro de entrada WorkMode.
El tamaño de la barra y la profundidad del historial para el cálculo se especifican en los parámetros TicksInBar y StartDate.
input int TicksInBar = 1000;
|
Dependiendo del modo, el símbolo personalizado recibirá el sufijo «_Eqv», «_Qrv» o «_Rng», respectivamente, con la adición del tamaño de la barra.
Aunque el eje horizontal de un gráfico Equivolume/Equal-Range sigue representando la cronología, las marcas de tiempo de cada barra son arbitrarias y dependen de la volatilidad (número o tamaño de las operaciones) en cada marco temporal. A este respecto, el marco temporal del gráfico de símbolos personalizado debe elegirse igual al M1 mínimo.
La limitación de la plataforma es que todas las barras tienen la misma duración nominal, pero en el caso de nuestros gráficos «artificiales» hay que recordar que la duración real de cada barra es diferente y puede superar de forma considerable 1 minuto o, por el contrario, ser inferior. Así, con un volumen dado suficientemente pequeño para una barra, puede darse la situación de que se formen nuevas barras con mucha más frecuencia que una vez por minuto, y entonces el tiempo virtual de las barras de símbolos personalizados se adelantará al tiempo real, hacia el futuro. Para evitar que esto ocurra, debe aumentar el volumen de la barra (el parámetro TicksInBar) o mover las barras antiguas hacia la izquierda.
La inicialización y otras tareas auxiliares para la gestión de símbolos personalizados (en particular, la puesta a cero de un historial existente y la apertura de un gráfico con un nuevo símbolo) se realizan de forma similar a otros ejemplos, por lo que las omitiremos. Pasemos a los aspectos específicos de carácter aplicado.
Leeremos el historial de ticks reales utilizando las funciones incorporadas CopyTicks/CopyTicksRange: la primera es para intercambiar el historial en lotes de 10 000 ticks, y la segunda es para solicitar nuevos ticks desde el procesamiento anterior. Toda esta funcionalidad está empaquetada en la clase TicksBuffer (se adjunta el código fuente completo).
class TicksBuffer
|
El método público fill está diseñado para llenar el array interno con la siguiente porción de ticks, comenzando desde el tiempo cursor (en milisegundos). Al mismo tiempo, el tiempo en cursor en cada llamada avanza en función del tiempo del último tick leído en el búfer (nótese que el parámetro se pasa por referencia).
El parámetro history determina si se utiliza CopyTicks o CopyTicksRange. Por regla general, en línea leeremos uno o más nuevos ticks del manejador OnTick.
El método read devuelve un tick del array interno y desplaza el puntero interno (tick) al siguiente tick. Si se alcanza el final del array durante la lectura, el método devolverá false, lo que significa que es el momento de llamar al método fill.
Utilizando estos métodos, el algoritmo de desvío del historial de ticks se implementa de la siguiente manera (este código se llama indirectamente desde OnInit a través del temporizador).
ulong cursor = StartDate * 1000;
|
En la función HandleTick se requiere tener en cuenta las propiedades del tick t en algunas variables globales que controlan el número de ticks, el volumen total de trading (real, si lo hay), así como la distancia de movimiento del precio. Según el modo de funcionamiento, estas variables deben analizarse de forma diferente para la condición de la formación de una nueva barra. Así que si en el modo de equivolumen, el número de ticks excede TicksInBar, deberíamos empezar una nueva barra reiniciando el contador a 1. En este caso, la hora de una nueva barra se toma como la hora del tick redondeada al minuto más cercano.
Este grupo de variables globales permite almacenar la hora virtual de la última barra («actual») de un símbolo personalizado (now_time), sus precios OHLC y volúmenes.
datetime now_time;
|
Las variables se actualizan constantemente, tanto durante la lectura del historial como después, cuando el Asesor Experto empieza a procesar los ticks en línea en tiempo real (volveremos sobre esto un poco más adelante).
De forma algo simplificada, el algoritmo de HandleTick es el siguiente:
void HandleTick(const MqlTick &t, const bool history = false)
|
El parámetro history determina si el cálculo se basa en el historial o ya en tiempo real (en los ticks en línea entrantes). Si se basa en la historia, es suficiente para formar cada barra una vez, mientras que en línea, la barra actual se actualiza con cada tick. Esto permite acelerar el procesamiento del historial.
La función de ayuda IsNewBar devuelve true cuando se cumple la condición para cerrar la siguiente barra según el modo.
bool IsNewBar()
|
La función WriteToChart crea una barra con las características dadas llamando a CustomRatesUpdate.
void WriteToChart(datetime t, double o, double l, double h, double c, long v, long m = 0)
|
El mencionado bucle de lectura y procesamiento de ticks se realiza durante el acceso inicial al historial, tras la creación o el recálculo completo de un símbolo de usuario ya existente. Cuando se trata de nuevos ticks, la función OnTick utiliza un código similar pero sin las banderas de «historicidad».
void OnTick()
|
La función RefreshWindow añade un tick de símbolo personalizado en Observación de Mercado.
Tenga en cuenta que el reenvío de ticks incrementa en 1 el contador de ticks de la barra, y por tanto, al escribir el contador de ticks en la barra 0, previamente hemos restado uno (véase la expresión now_volume - !history al llamar a WriteToChart).
La generación de ticks es importante porque activa el evento OnTick en los gráficos de instrumentos personalizados, lo que potencialmente permite operar a los Asesores Expertos colocados en dichos gráficos. Sin embargo, esta tecnología requiere algunos trucos adicionales, que estudiaremos más adelante.
void RefreshWindow(const datetime t)
|
Hacemos hincapié en que la hora del tick personalizado generado se establece siempre igual a la etiqueta de la barra actual, ya que no podemos dejar la hora del tick real: si se ha adelantado más de 1 minuto y enviamos dicho tick a Observación de Mercado, el terminal creará la siguiente barra M1, lo que violará nuestra estructura de «equivolumen», ya que nuestras barras no se forman por tiempo, sino por llenado de volumen (y nosotros mismos controlamos este proceso).
En teoría, podríamos añadir un milisegundo a cada tic, pero no tenemos ninguna garantía de que la barra no vaya a necesitar almacenar más de 60 000 tics (por ejemplo, si el usuario pide un gráfico con un determinado rango de precios que es impredecible en términos de cuántos tics serán necesarios para ese movimiento).
En los modos por volumen, es teóricamente posible interpolar los componentes de segundo y milisegundo de la hora del tick mediante fórmulas lineales:
- EqualTickVolumes (now_volume - 1) * 60000 / TicksInBar;
- EqualRealVolumes (now_real - 1) * 60000 / TicksInBar;
Sin embargo, esto no es más que un medio para identificar los ticks, y no un intento de hacer que la hora de los ticks «artificiales» se aproxime a la de los reales. No se trata sólo de la pérdida de irregularidad del flujo real de ticks, que de por sí ya provocará diferencias de precio entre el símbolo original y el símbolo personalizado generado sobre su base.
El principal problema es la necesidad de redondear el tiempo de los ticks a lo largo del borde de la barra M1 y «empaquetarlos» dentro de un minuto (véase la barra lateral sobre tipos especiales de gráficos). Por ejemplo, el siguiente tick con tiempo real 12:37:05'123 se convierte en el tick 1001 y debería formar una nueva barra de equivolumen. Sin embargo, la barra M1 sólo puede marcarse hasta el minuto, es decir, 12:37. Como resultado, el precio real del instrumento a las 12:37 no coincidirá con el precio del tick que proporcionó el precio Open para la barra de equivolumen 12:37. Además, si los siguientes 1000 ticks se extienden durante varios minutos, aún nos veremos obligados a «comprimir» su tiempo para no llegar a la marca de 12:38.
El problema es de naturaleza sistémica debido a la cuantificación del tiempo cuando los gráficos especiales son emulados por un gráfico estándar de marco temporal M1. Este problema no puede resolverse completamente en este tipo de gráficos, pero cuando se generan símbolos personalizados con ticks en tiempo continuo (por ejemplo, con cotizaciones sintéticas o basadas en datos de streaming de servicios externos), este problema no se produce.
Es importante tener en cuenta que el reenvío de ticks se realiza en línea solamente en esta versión del generador, mientras que los ticks personalizados no se generan en el historial. Esto se hace para acelerar la creación de presupuestos. Si necesita generar un historial de ticks a pesar de la lentitud del proceso, deberá adaptar el Asesor Experto EqualVolumeBars.mq5: excluya la función WriteToChart y realice toda la generación utilizando CustomTicksReplace/CustomTicksAdd. Al mismo tiempo, hay que recordar que el tiempo original de ticks debe ser sustituido por otro, dentro de una barra de minutos, a fin de no perturbar la estructura del gráfico de equivolumen formado.
Veamos cómo funciona EqualVolumeBars.mq5. He aquí el gráfico de trabajo de EURUSD M15 con el Asesor Experto ejecutándose en él. Tiene el gráfico de equivolumen, en el que se asignan 1000 ticks a cada barra.
Gráfico EURUSD de equivolumen con 1000 ticks por barra generado por el Asesor Experto EqualVolumeBars
Observe que los volúmenes de ticks en todas las barras son iguales, excepto en la última, que aún se está formando (el recuento de ticks continúa).
Las estadísticas se muestran en el registro.
Creating "EURUSD.c_Eqv1000"
|
Comprobemos otro modo de funcionamiento: igual rango. A continuación se muestra un gráfico en el que el rango de cada barra es de 250 puntos.
Gráfico de igual rango EURUSD range con barras de 250 pips generadas por EqualVolumeBars
Para los instrumentos bursátiles, el Asesor Experto permite utilizar el modo de volumen real, por ejemplo, de la siguiente manera:
Gráfico de equivolumen y de Ethereum con volumen real de 10000 por barra
El marco temporal del símbolo de trabajo al colocar el generador del Asesor Experto no es importante, ya que el historial de ticks siempre se utiliza para los cálculos.
Al mismo tiempo, el marco temporal del gráfico de símbolos personalizado debe ser igual a M1 (el más pequeño disponible en el terminal). Así, el tiempo de las barras, por regla general, se corresponde de la forma más cercana posible (en la medida de lo posible) con los momentos de su formación. No obstante, durante los movimientos fuertes del mercado, cuando el número de ticks o el tamaño de los volúmenes forman varias barras por minuto, la hora de las barras se adelantará a la real. Cuando el mercado se calme, la situación con las marcas temporales de las barras de equivolumen se normalizará. Esto no afecta al flujo de precios en línea, por lo que probablemente no sea especialmente crítico, ya que el objetivo de utilizar barras de igual volumen o igual rango es desvincularse del tiempo absoluto.
Lamentablemente, el nombre del símbolo original y el símbolo personalizado creado a partir de él no pueden vincularse de ninguna manera a través de la propia plataforma. Sería conveniente disponer de un campo de cadena «origen» (fuente) entre las propiedades del símbolo personalizado, en el que pudiéramos escribir el nombre de la herramienta de trabajo real. Por defecto, estaría vacío, pero si se rellenara, la plataforma podría sustituir el símbolo en todas las órdenes de operaciones y solicitudes de historial, y hacerlo de forma automática y transparente para el usuario. En teoría, entre las propiedades de los símbolos definidos por el usuario, hay un campo SYMBOL_BASIS que es adecuado en cuanto a su significado, pero como no podemos garantizar que los generadores arbitrarios de símbolos definidos por el usuario (cualquier programa MQL) lo rellenen correctamente o lo utilicen exactamente para este propósito, no podemos confiar en su uso.
Como este mecanismo no está en la plataforma, tendremos que implementarlo nosotros mismos. Tendrá que establecer la correspondencia entre los nombres de los símbolos de origen y de usuario mediante parámetros.
Para resolver el problema, hemos desarrollado la clase CustomOrder (véase el archivo adjunto CustomOrder.mqh). Contiene métodos de envoltorio para todas las funciones de la API de MQL relacionadas con el envío de órdenes de trading y la solicitud de historial, que tienen un parámetro de cadena con el nombre del símbolo. En estos métodos, el símbolo personalizado se sustituye por el de trabajo actual o viceversa. Otras funciones de la API no requieren «unión». A continuación se ofrece un fragmento:
class CustomOrder
|
Tenga en cuenta que el método de trabajo principal replaceRequest sustituye no sólo el símbolo, sino también los precios actuales Ask y Bid. Esto se debe al hecho de que muchas herramientas personalizadas, como nuestro gráfico de equivolumen, tienen una hora virtual que es diferente de la hora del símbolo del prototipo real. Por lo tanto, los precios del instrumento personalizado emulado por el probador no coinciden con los precios correspondientes del instrumento real.
Este artefacto sólo se produce en el probador. Al operar en línea, el gráfico de símbolos personalizado se actualizará (a precios) de forma sincronizada con el real, aunque las etiquetas de las barras diferirán (una barra M1 «artificial» tiene una duración real de más o menos un minuto, y su tiempo de cuenta atrás no es múltiplo de un minuto). Así, esta conversión de precios es más bien una precaución para evitar recotizaciones en el probador. Sin embargo, en el probador normalmente no necesitamos hacer la sustitución de símbolos, ya que este puede operar con un símbolo personalizado (a diferencia del servidor del bróker). Además, sólo por interés, compararemos los resultados de las pruebas realizadas con y sin sustitución de caracteres.
Para minimizar las modificaciones en el código fuente del cliente, se proporcionan funciones globales y macros de la siguiente forma (para todos los métodos de CustomOrder):
bool CustomOrderSend(const MqlTradeRequest &request, MqlTradeResult &result)
|
Permiten redirigir automáticamente todas las llamadas a funciones estándar de la API a los métodos de la clase CustomOrder. Para ello, basta con incluir CustomOrder.mqh en el Asesor Experto y establecer el símbolo de trabajo, por ejemplo, en el parámetro WorkSymbol:
#include <CustomOrder.mqh>
|
Es importante que la directiva #include<CustomOrder.mqh> fuera la primera, antes que las demás. Por lo tanto, afecta a todos los códigos fuente, incluidas las bibliotecas estándar de la distribución de MetaTrader 5. Si no se especifica ningún símbolo de sustitución, el CustomOrder.mqh conectado no tiene ningún efecto sobre el Asesor Experto y transfiere «transparentemente» el control a las funciones estándar de la API.
Ahora tenemos todo listo para probar la idea de operar con un símbolo personalizado, incluido el propio símbolo personalizado.
Aplicando la técnica mostrada anteriormente modificamos el ya conocido Asesor Experto BandOsMaPro, renombrándolo a BandOsMaCustom.mq5. Probémoslo en el gráfico de equivolume EURUSD con un tamaño de barra de 1000 ticks obtenido mediante EqualVolumeBars.mq5.
El modo de optimización o simulación se establece en los precios OHLC M1 (métodos más precisos no tienen sentido porque no generamos ticks y también porque esta versión negocia a los precios de las barras formadas). El intervalo de fechas es todo el año 2021 y el primer semestre de 2022. Se adjunta el archivo con la configuración BandOsMACustom.set.
En los ajustes del probador, no debe olvidar seleccionar el símbolo personalizado EURUSD_Eqv1000 y el marco temporal M1, ya que es en él donde se emulan las barras de equivolumen.
Cuando el parámetro WorkSymbol está vacío, el Asesor Experto negocia un símbolo personalizado. He aquí los resultados:
Informe del probador al operar en el gráfico de equivolumen EURUSD_Eqv1000
Si el parámetro WorkSymbol es igual a EURUSD, el Asesor Experto negocia el par EURUSD, a pesar de que trabaja en el gráfico EURUSD_Eqv1000. Los resultados difieren, pero no mucho.
Informe del probador al operar EURUSD desde el gráfico de equivolumen EURUSD_Eqv1000
Sin embargo, como ya se mencionó al principio de la sección, hay una manera más fácil para que los Asesores Expertos que operan con señales de indicadores admitan símbolos personalizados. Para ello, basta con crear indicadores en un símbolo personalizado y colocar el Asesor Experto en el gráfico de un símbolo de trabajo.
Podemos aplicar fácilmente esta opción. Llamémosla BandOsMACustomSignal.mq5.
El archivo de encabezado CustomOrder.mqh ya no es necesario. En lugar del parámetro de entrada WorkSymbol, añadimos dos nuevos:
input string SignalSymbol = "";
|
Deben pasarse al constructor de la clase BandOsMaSignal que gestiona los indicadores. Antes, _Symbol y _Period se utilizaban en todas partes.
interface TradingSignal
|
Dado que ahora el símbolo y el marco temporal de las señales pueden diferir del símbolo y el período del gráfico, hemos ampliado la interfaz TradingSignal añadiendo métodos de lectura. Los valores reales se pasan al constructor en OnInit.
int OnInit()
|
En la clase SimpleStrategy, el método trade comprueba ahora la aparición de una nueva barra no según el gráfico actual, sino según las propiedades de la señal.
virtual bool trade() override
|
Para un experimento comparativo con la misma configuración, el Asesor Experto BandOsMACustomSignal.mq5 debe lanzarse en EURUSD (puede utilizar M1 u otro marco de tiempo), y EURUSD_Eqv1000 debe especificarse en el parámetro SignalSymbol. SignalTimeframe debe dejarse igual a PERIOD_M1 de manera predeterminada. Como resultado, obtendremos un informe similar.
Informe del probador al operar en el gráfico EURUSD basado en las señales del símbolo de equivolumen EURUSD_Eqv1000
El número de barras y ticks es diferente aquí porque EURUSD se eligió como instrumento probado y no el EURUSD_Eqv1000 personalizado.
Los resultados de las tres pruebas son ligeramente diferentes. Esto se debe al «empaquetamiento» de las cotizaciones en barras de minutos y a una ligera desincronización de los movimientos de precio de los instrumentos originales y personalizados. ¿Cuál de los resultados es más exacto? Lo más probable es que esto dependa del sistema de trading concreto y de las características de su aplicación. En el caso de nuestro Asesor Experto BandOsMa con control sobre la apertura de barras, la versión con trading directo en EURUSD_Eqv1000 debería tener los resultados más realistas. En teoría, casi siempre se cumple la regla empírica según la cual, de varias comprobaciones alternativas, la más fiable es la menos rentable.
Así, hemos analizado un par de técnicas para adaptar los Asesores Expertos para operar con símbolos personalizados que tienen un prototipo entre los símbolos de trabajo del bróker. Sin embargo, esta situación no es obligatoria. En muchos casos, los símbolos personalizados se generan a partir de datos de sistemas externos, como las bolsas de criptomonedas. El trading en ellos debe hacerse utilizando su API pública con funciones de red de MQL5.
Emulación de tipos especiales de gráficos con símbolos personalizados
Muchos operadores utilizan tipos especiales de gráficos, en los que el tiempo real continuo se excluye de la consideración. Esto incluye no sólo barras de igual rango y equivolumen, sino también Renko, Point-And-Figure (PAF), Kagi, y otros. Los símbolos personalizados permiten emular este tipo de gráficos en MetaTrader 5 utilizando gráficos de marco temporal M1, pero deben tratarse con precaución cuando se trata de simular sistemas de trading en lugar de análisis técnicos.
En tipos especiales de gráficos, la hora real de apertura de la barra (con precisión de milisegundos) casi siempre no coincide exactamente con el minuto con el que se marcará la barra M1. Así, el precio de apertura de una barra personalizada difiere del precio de apertura de la barra M1 de un símbolo estándar.
Además, otros precios OHLC también diferirán porque la duración real de la formación de la barra M1 en un gráfico especial no es igual a un minuto. Por ejemplo, 1000 ticks para un gráfico de equivolumen pueden acumularse durante más de 5 minutos.
El precio de cierre de una barra personalizada tampoco se corresponde con la hora de cierre real porque una barra personalizada es, técnicamente, una barra M1, es decir, tiene una duración nominal de 1 minuto.
Se debe tener especial cuidado cuando se trabaja con este tipo de gráficos como el clásico Renko o PAF. El hecho es que sus barras de inversión tienen un precio de apertura con un gap desde el cierre de la barra anterior. Así, el precio de apertura se convierte en un indicador del movimiento futuro de los precios.
Se supone que el análisis de tales gráficos se realiza en función de las barras formadas, es decir, su precio característico es el precio de cierre, pero cuando se trabaja por barras, el probador proporciona sólo el precio de apertura de la barra actual (última) (no existe el modo por precios de cierre). Aunque tomemos las señales de los indicadores de las barras cerradas (normalmente de la 1ª), de todas formas las transacciones se realizan al precio actual de la barra 0. E incluso si pasamos a los modos de ticks, el probador siempre genera ticks según las reglas habituales, guiándose por puntos de referencia basados en la configuración de cada barra. El probador no tiene en cuenta la estructura y el comportamiento de los gráficos especiales, que intentamos emular visualmente con las barras M1.
Operar en el probador utilizando tales símbolos en cualquier modo (por precios de apertura, M1 OHLC, o por ticks) afecta a la precisión de los resultados: son demasiado optimistas y pueden servir como fuente de expectativas demasiado altas. A este respecto, es esencial comprobar el sistema de trading no en un gráfico Renko o PAF separado, sino junto con la ejecución de órdenes en un símbolo real.
Los símbolos personalizados también pueden utilizarse para segundos marcos temporales o gráficos de ticks. En este caso se genera también tiempo virtual para barras y ticks, desacoplado del tiempo real. Por lo tanto, estos gráficos son muy adecuados para el análisis operativo, pero requieren una atención adicional a la hora de desarrollar y simular estrategias de trading, especialmente las multisímbolo.
Una alternativa para cualquier símbolo personalizado es el cálculo independiente de arrays de barras y ticks dentro de un Asesor Experto o indicador. No obstante, la depuración y visualización de tales estructuras requiere un esfuerzo adicional.