Eventos de teclado
Los programas MQL pueden recibir mensajes sobre pulsaciones de teclas desde el terminal mediante el procesamiento en la función OnChartEvent de los eventos CHARTEVENT_KEYDOWN.
Es importante tener en cuenta que los eventos sólo se generan en el gráfico activo, y sólo cuando éste tiene el foco de entrada.
En Windows, el foco es la selección lógica y visual de una ventana concreta con la que el usuario está interactuando en ese momento. Por regla general, el foco se desplaza mediante un clic del ratón o mediante atajos de teclado especiales (Tab, Ctrl+Tab), lo que hace que se resalte la ventana seleccionada. Por ejemplo, aparecerá un cursor de texto en el campo de entrada, la línea actual se coloreará en la lista con un color alternativo, etc.
Efectos visuales similares se aprecian en el terminal, en concreto cuando se pone el foco en alguna de las ventanas Market Watch o Data Window, o en el registro de Expertos. Sin embargo, la situación es algo diferente con las ventanas de gráficos. No siempre es posible distinguir por signos externos si el gráfico visible en primer plano tiene o no el foco de entrada. Se garantiza que pueda cambiar el foco, como ya se ha mencionado, haciendo clic en el gráfico deseado (en el gráfico, y no en el título de la ventana o en su marco) o utilizando teclas de acceso rápido:
- Alt+W abre una ventana con una lista de gráficos, en la que puede seleccionar uno.
- Ctrl+F6 cambia al gráfico siguiente (en la lista de ventanas, donde el orden corresponde, por regla general, al orden de las pestañas).
- Crtl+Mayús+F6 cambia al gráfico anterior.
La lista completa de las teclas de acceso rápido de MetaTrader 5 se encuentra en la documentación. Tenga en cuenta que algunas combinaciones no se ajustan a las recomendaciones generales de Microsoft (por ejemplo, F10 abre la ventana de cotizaciones, pero no activa el menú principal).
Los parámetros del evento CHARTEVENT_KEYDOWN contienen la siguiente información:
- lparam - código de la tecla pulsada
- dparam - el número de pulsaciones generadas durante el tiempo que se mantuvo pulsada
- sparam - una máscara de bits que describe el estado de las teclas del teclado, convertida en una cadena
| Bits | Descripción | 
|---|---|
| 07 | Código de escaneado de la tecla (depende del hardware, OEM) | 
| 8 | Atributo de tecla de teclado ampliado | 
| 912 | Para fines de servicio de Windows (no utilizar) | 
| 13 | Estado de la tecla Alt (1 - pulsada, 0 - liberada), no disponible (véase más abajo) | 
| 14 | Estado anterior de la tecla (1 - pulsada, 0 - liberada) | 
| 15 | Estado de tecla cambiado (1 si está liberada, 0 si está pulsada) | 
En realidad, el estado de la clave Alt no está disponible, ya que está interceptado por el terminal y este bit es siempre 0. El bit 15 también es siempre igual a 0 debido al contexto de activación de este evento: sólo las pulsaciones de teclas se pasan al programa MQL, no las liberaciones de teclas.
El atributo del teclado extendido (bit 8) se establece, por ejemplo, para las teclas del bloque numérico (en los portátiles suele activarse con Fn), teclas como NumLock, ScrollLock, Ctrl derecha (en contraposición a la izquierda, Ctrl principal), etc. Obtenga más información al respecto en la documentación de Windows.
La primera vez que se pulse cualquier tecla que no sea del sistema, el bit 14 será 0. Si mantiene pulsada la tecla, las siguientes repeticiones del evento generadas automáticamente tendrán 1 en este bit.
La siguiente estructura ayudará a garantizar que la descripción de los bits es correcta:
| struct KeyState
 | 
En un programa MQL, se puede utilizar así:
| void OnChartEvent(const int id,
 | 
A efectos prácticos, es más cómodo extraer atributos de bits de la máscara de claves mediante macros.
| #define KEY_SCANCODE(SPARAM) ((uchar)(((ushort)SPARAM) & 0xFF))
 | 
Puede ejecutar el indicador EventAll.mq5 de la sección Propiedades de gráficos relacionados con eventos en el gráfico y ver qué valores de los parámetros se mostrarán en el registro cuando se pulsen determinadas teclas.
Es importante tener en cuenta que el código que aparece en lparam es uno de los códigos de las teclas del teclado virtual. Su lista se puede ver en el archivo MQL5/Include/VirtualKeys.mqh, que viene con MetaTrader 5. Por ejemplo, he aquí algunas de ellas:
| #define VK_SPACE          0x20
 | 
Los códigos se denominan virtuales porque las teclas correspondientes pueden estar situadas de forma diferente en los distintos teclados, o incluso implementarse mediante la pulsación conjunta de teclas auxiliares (como Fn en portátiles). Además, la virtualidad tiene otra cara: una misma clave puede generar símbolos o acciones de control diferentes. Por ejemplo, la misma tecla puede denotar letras diferentes en disposiciones lingüísticas distintas. Además, cada una de las teclas de letras puede generar una letra mayúscula o minúscula, según el modo de CapsLock y el estado de las teclas Shift.
A este respecto, para obtener un carácter a partir de un código de tecla virtual, la API de MQL5 dispone de la función especial TranslateKey.
short TranslateKey(int key)
La función devuelve un carácter Unicode basado en el código de tecla virtual pasado, dado el idioma de entrada actual y el estado de las teclas de control.
En caso de error, se devolverá el valor -1. Puede producirse un error si el código no coincide con el carácter correcto; por ejemplo, al intentar obtener un carácter para la tecla Shift.
Recordemos que además del código recibido de la tecla pulsada, un programa MQL puede además Comprobar el estado del teclado en términos de modos y teclas de control. Por cierto: las constantes de la forma TERMINAL_KEYSTATE_XXX, pasadas como parámetro a la función TerminalInfoInteger, se basan en el principio de 1000 + código de clave virtual. Por ejemplo, TERMINAL_KEYSTATE_UP es 1038 porque VK_UP es 38 (0x26).
A la hora de planificar algoritmos que reaccionen a las pulsaciones de teclas, hay que tener en cuenta que el terminal puede interceptar muchas combinaciones de teclas, ya que están reservadas para realizar determinadas acciones (más arriba se ha facilitado el enlace a la documentación). En concreto, al pulsar la barra espaciadora se abre un campo para navegar rápidamente por el eje temporal. La API de MQL5 permite controlar en parte ese procesamiento de teclado integrado y desactivarlo si es necesario. Véase la sección sobre Control del ratón y del teclado.
El sencillo indicador sin búfer EventTranslateKey.mq5 sirve para demostrar esta función. En su manejador OnChartEvent para los eventos CHARTEVENT_KEYDOWN se llama a TranslateKey para obtener un caracter Unicode válido. Si tiene éxito, el símbolo se añade a la cadena de mensajes que se muestra en el comentario del trazado. Al pulsar Enter, se inserta una nueva línea en el texto, y al pulsar Backspace, se borra el último carácter del final.
| #include <VirtualKeys.mqh>
 | 
Puede probar a introducir caracteres en distintos casos y en distintos idiomas.
Tenga cuidado. La función devuelve el valor con signo short, principalmente para poder devolver un código de error de -1. Sin embargo, el tipo de un carácter «ancho» de dos bytes se considera un entero sin signo, ushort. Si la variable receptora se declara como ushort, una comprobación utilizando -1 (por ejemplo, c!=-1) emitirá una advertencia del compilador de «desacuerdo de signo» (se requiere una conversión de tipos explícita), mientras que la otra (c >= 0) suele ser errónea, ya que siempre es igual a true.
Para poder insertar espacios entre las palabras del mensaje, la navegación rápida activada por la barra espaciadora está predesactivada en el manejador OnInit.
| void OnInit()
 | 
Como ejemplo plenamente desarrollado del uso de eventos de teclado, considere la siguiente tarea de aplicación. Los usuarios de terminales saben que la escala de la ventana principal del gráfico puede modificarse de forma interactiva sin abrir el cuadro de diálogo de ajustes con el ratón: basta con pulsar el botón del ratón en la escala de precios y, sin soltarlo, desplazarse hacia arriba/abajo. Por desgracia, este método no funciona en las subventanas.
Las subventanas siempre se escalan automáticamente para ajustarse a todo el contenido, y para cambiar la escala hay que abrir un cuadro de diálogo e introducir los valores manualmente. A veces es necesario hacerlo si los indicadores de la subventana muestran «valores atípicos», es decir, lecturas individuales demasiado grandes que interfieren con el análisis del resto de los datos de tamaño normal (medio). Además, a veces conviene simplemente ampliar la imagen para abordar detalles más finos.
Para resolver este problema y permitir al usuario ajustar la escala de la subventana mediante pulsaciones de teclas, hemos implementado el indicador SubScalermq5. No tiene búferes y no muestra nada.
SubScaler debe ser el primer indicador de la subventana o, para decirlo en términos más estrictos, debe añadirse a la subventana antes de que se añada allí el indicador de trabajo que le interesa y cuya escala desea controlar. Para que SubScaler sea el primer indicador debe colocarse en el gráfico (en la ventana principal) y crear así una nueva subventana, en la que podrá añadir un indicador subordinado.
En el cuadro de diálogo de configuración del indicador de trabajo, es importante activar la opción Inherit scale (en la pestaña Scale).
Cuando ambos indicadores están funcionando en una subventana, puede utilizar las teclas de flecha Up/Down para acercar/alejar el zoom. Si se pulsa la tecla Shift, el rango de valores visible actualmente en el eje vertical se desplaza hacia arriba o hacia abajo.
Ampliar significa acercarse a los detalles («zoom de cámara»), por lo que algunos datos pueden quedar fuera de la ventana. Alejar significa que la imagen global se hace más pequeña («alejamiento de la cámara»).
Los parámetros de entrada establecidos son los siguientes:
- Máximo inicial - el límite superior de los datos durante la colocación inicial en el gráfico, +1000 por defecto.
- Mínimo inicial - el límite inferior de datos durante la colocación inicial en el gráfico, por defecto -1000.
- Factor de escala - paso con el que cambiará la escala al pulsar las teclas, valor en el rango [0.01 ... 0.5], por defecto 0.1.
Nos vemos obligados a pedirle al usuario el mínimo y el máximo porque SubScaler no puede conocer de antemano el rango de valores de trabajo de un indicador arbitrario de terceros, que se añadirá a continuación a la subventana.
Cuando se restaura el gráfico tras iniciar una nueva sesión de terminal o cuando se carga una plantilla tpl, SubScaler recoge la escala del estado anterior (guardado).
Veamos ahora la implementación de SubScaler.
Los ajustes anteriores se establecen en las variables de entrada correspondientes:
| input double FixedMaximum = 1000;  // Initial Maximum
 | 
Además, la variable Disabled permite desactivar temporalmente la respuesta del teclado para una instancia específica del indicador con el fin de configurar varias escalas diferentes en distintas subventanas (una por una).
Dado que las variables de entrada son de sólo lectura en MQL5, nos vemos obligados a declarar una variable más ScaleFactor para corregir el valor introducido dentro del rango permitido [0.01 ... 0.5].
| double ScaleFactor; | 
El número de la subventana actual (w) y el número de indicadores en la misma (n) se almacenan en variables globales: todas ellas se rellenan en el manejador OnInit.
| int w = -1, n = -1;
 | 
En la función OnChartEvent procesamos dos tipos de eventos: cambios en el gráfico y eventos de teclado. El evento CHARTEVENT_CHART_CHANGE es necesario para realizar un seguimiento de la adición del siguiente indicador a la subventana (indicador de trabajo que se va a escalar). Al mismo tiempo, solicitamos el rango actual de valores de la subventana (CHART_PRICE_MIN, CHART_PRICE_MAX) y determinamos si es degenerado, es decir, cuando tanto el máximo como el mínimo son iguales a cero. En este caso es necesario aplicar los límites iniciales especificados en los parámetros de entrada (FixedMinimum,FixedMaximum).
| void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
 | 
Cuando se recibe un evento de pulsación de teclado se llama a la función principal Scale, que recibe no sólo lparam, sino también el estado de la tecla Shift obtenido mediante referencia a TerminalInfoInteger(TERMINAL_KEYSTATE_SHIFT).
| void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
 | 
Dentro de la función Scale, lo primero que hacemos es obtener el rango actual de valores en las variables min y max.
| void Scale(const long cmd, const int shift)
 | 
A continuación, en función de si se pulsa la tecla Shift, se realiza el zoom o la panorámica, es decir, se desplaza el rango visible de valores hacia arriba o hacia abajo. En ambos casos, la modificación se lleva a cabo con un determinado paso (multiplicador) ScaleFactor, relativo a los límites min y max, y se asignan a las propiedades del indicador INDICATOR_MINIMUM e INDICATOR_MAXIMUM, respectivamente. Debido al hecho de que el indicador subordinado tiene el ajuste Heredar escala , éste se convierte en un ajuste de trabajo para él también.
|  if((shift &0x10000000) ==0)// Shift is not pressed - scalechange
 | 
Para cualquier cambio, se llama a ChartRedraw para actualizar el gráfico.
Veamos cómo funciona SubScaler con el indicador estándar de volúmenes (cualquier otro indicador, incluidos los personalizados, se controla de la misma manera).

Una escala diferente establecida por los indicadores SubScaler en dos subventanas
Aquí, en dos subventanas, dos instancias de SubScaler aplican diferentes escalas verticales a los volúmenes.