Your SlideShare is downloading. ×

Resumen Libro de Programacion mql

3,246

Published on

resumen libro programacion mql

resumen libro programacion mql

Published in: Education
1 Comment
2 Likes
Statistics
Notes
No Downloads
Views
Total Views
3,246
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
215
Comments
1
Likes
2
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. LENGUAJE DE PROGRAMACIÓN MQL RESUMEN DE http://book.mql4.comConceptos BásicosComentariosEl comentario de una línea es cualquier secuencia de caracteres que van a continuación de una doblebarra (//). Una línea de comentario queda terminada con el salto de línea.El comentario multi-línea comienza con / * y termina con * /Comentario de una línea://Hola mundoComentario multi-línea:/*Hola mundoesto es una prueba*/Mayúsculas – MinúsculasMQL4 distingue entre mayúsculas y minúsculas.VariablesEjemplo de nombres de variables: Alfa, alFa, beta, el_número, Num, A_37, A37, qwerty_123El valor de una variable puede ser cambiado por el programa. El nombre de la variable es siempre elmismo.Ejemplo de Constantes y Variables en un programaConstantes y variables se pueden encontrar en los operadores de un programa. En el código que aparecea continuación, A y B son variables, 7 y 3 son constantes:A = 7; // Línea 1B = A + 3; // Línea 2.El valor de una variable puede ser cambiado durante la operación del programa. Por ejemplo, puedehaber una línea en el programa que contenga lo siguiente:B = 33 // Línea 3Tipos de Datos int (integer) – números enteros Hay dos tipos: Decimales: estos valores se presentan en forma de dígitos del 0 al 9 y pueden ser positivos o negativos: 10, 11, 12, 1, 5, -379, 25, -12345, -1, 2.
  • 2. Hexadecimales: están formados por las letras de la A a la F y los dígitos del 0 al 9. Debencomenzar con 0x o 0x y tomar valores positivos o negativos: 0x1a7b, 0xff340, 0xAC30X2DF23,0X13AAB, 0x1.Los valores de tipo int deben estar dentro del rango de -2.147.483.648 a 2.147.483.647.Ejemplos:int Art = 10; // Ejemplo variable integerint B_27 = - 1; // Ejemplo variable integerint num = 21; // Ejemplo variable integerint Max = 2147483647 // Ejemplo variable integerint min = - 2147483648; // Ejemplo variable integerdouble - números realesLos valores de tipo double son números reales que contienen una parte decimal. Puede sercualquier valor que tenga una parte decimal, por ejemplo: inclinación de la línea de apoyo.Ejemplo:¿De qué tipo debe ser la variable A, si considera la cantidad media diaria de órdenes abiertas porel programa en una semana? Suponiendo que la cantidad total de órdenes que abrió en el plazode una semana es de 10, se puede pensar que si 2 (10 órdenes / 5 días = 2) no tiene parte decimal,esta variable puede ser considerada como int. Sin embargo, este razonamiento es erróneo. Elvalor actual de una variable puede tener una pequeña parte que consta sólo de ceros. Esimportante saber que el valor de esa variable es real, por su propia naturaleza. En este caso, esavariable debe ser de tipo double.La separación del punto decimal también debe ser mostrada en el registro del programa: Z = 2.0Los valores reales (double) de constantes y variables constarán de una parte entera, un puntodecimal, y una parte decimal. Los valores pueden ser positivos o negativos. La parte entera y laparte decimal se forman con los dígitos del 0 al 9. La cantidad de cifras significativas después delpunto decimal puede alcanzar el valor de 15.Ejemplos:double Arte = 10.123; // Ejemplo de variable realdouble B_27 = - 1.0; // Ejemplo de variable realdouble Num = 0.5; // Ejemplo de variable realdoble MMM = - 12.07; // Ejemplo de variable realdoble Price_1 = 1.2756; // Ejemplo de variable realNotas personales:Entre el signo menos (-) y el número, hay un espacio en blanco.Después de cada número hay un punto y coma (;)bool - bolean de valores lógicosLos valores de tipo bool son valores boleanos (lógicos) que contienen valores de tipo true(verdadero) o false (falso). Ejemplo:bool aa = True; // la variable Boolean __ tiene el valor de verdaderobool B17= TRUE // la variable Boolean B17 tiene el valor de verdaderobool Hamma = 1; // la variable Boolean Hamma tiene el valor de verdadero
  • 3. bool TEA = False; // la variable Boolean TEA tiene el valor de falsobool Nol = false; // la variable Boolean Nol tiene el valor de falsobool Prim = 0; // la variable Boolean Prim tiene el valor de falsoNotas personales:Por ejemplo, para verdadero se puede poner: True, true, TRUE o el número 1.string- valores de tipo cadena de caracteresUn valor tipo string es un conjunto de caracteres colocados entre comillas dobles (no puedenincluirse dobles comillas simples).Si hay necesidad de introducir dobles comillas ( "), se debe poner una barra diagonal inversa ()antes. Cualquier carácter especial puede ser introducido en una constante de tipo string tras labarra inversa ().La longitud de una constante de tipo string va de 0 a 255 caracteres.Combinaciones especiales: Una combinación de dos caracteres, el primero de los cuales es labarra inversa (), es comúnmente aceptado y percibido por la mayoría de los programas como unainstrucción para ejecutar un determinado formato de texto. Esta combinación no se muestra en eltexto. Por ejemplo, la combinación de n indica la necesidad de un salto de línea; t demanda detabulación, etc.Ejemplos:string prefix = "MetaTrader 4"; // Ejemplo variable stringstring Postfix = "_of_my_progr. OK"; // Ejemplo variable stringstring Name_Mass = "Historial"; // Ejemplo variable stringstring texto = "Línea Alta n Bajo la línea" //el texto contiene caracteres de salto de líneacolor - valores de tipo colorLos valores constantes y variables de tipo color pueden ser representados con una de tres formasdistintas:LiteralesEl valor de tipo color es representado como un literal y consta de tres partes que representan losvalores numéricos de intensidad de tres colores básicos: rojo, verde y azul (RGB). Un valor deeste tipo empieza con "C" y el valor numérico esta encerrado entre comillas simples.Los valores numéricos de RGB tienen una intensidad de 0 a 255 y se pueden grabar tanto endecimal como en hexadecimal.Ejemplos: C128128128(gris), C0x00, 0x00, 0xFF (azul), C0xFF, 0x33, 0x00‟(rojo).Representación Integer (Representación por enteros)La representación integer se registra como número hexadecimal o un número decimal. Unnúmero hexadecimal se muestra como 0xRRGGBB, donde RR es el valor de intensidad de colorrojo; GG, verde; y BB, azul.Nombres de coloresLa forma más fácil de definir colores es especificar su nombre de acuerdo con la gráfica decolores web. En este caso, el valor de un color se representa como una palabra que correspondacon el color, por ejemplo, Red es el color rojo.
  • 4. Ejemplos: color Un = Red; // El valor rojo fue asignado a la variable color B = Yellow; // El valor amarillo fue asignado a la variable color Colorit = Black // El valor negro fue asignado a la variable datetime - valores de fecha y hora La constante se enmarca entre comillas simples y comienza con D. Está permitido el uso truncado de valores: o bien sin fecha o sin tiempo, o simplemente un valor vacío. El rango de valores va desde el 1 de enero de 1970 al 31 de diciembre de 2037. Ejemplos: datetime Alfa = D 2004.01.01 00: 00 // Año Nuevo datetime Tim = D "01.01.2004"; // Año Nuevo datetime Tims = D 2005.05.12 16: 30: 45; // 12 de Mayo de 2005 a las 4:30:45 PM datetime N_3 = D 12/05/2005 16: 30: 45; // 12 de Mayo de 2005 a las 4:30:45 PM datetime Compilar = D; //equivalente de D [compilación fecha] // 00:00:00Declaración de variables e inicializaciónCon el fin de evitar posibles preguntas por el programa acerca a qué tipo de datos pertenece tal o cual avariable, MQL4 acepta que se especifiquen explícitamente los tipos de variables al inicio del programa.Antes de que una variable empiece a utilizarse en cualquier cálculo deber ser declarada.La Declaración de Variables es lo primero que se debe hacer con cualquier variable dentro de unprograma. En la declaración de una variable siempre ha de especificarse su tipo.Asimismo, los nombres de las variables No pueden comenzar por números o símbolos.Por último hay que tener en cuenta que las palabras del lenguaje MQL4 deben ir siempre en minúsculas.Por ejemplo, las expresiones: DOUBLE hightPrice1; Double hightPrice2; producirán un error alcompilar.La Inicialización de Variables significa la asignación de un valor acorde con su tipo y que se efectúaen su declaración. Todas las variables pueden ser inicializadas. Si no hay valor inicial que se establezcaexplícitamente, la variable se inicializa a cero (0), o si la variable es de tipo string, esta se inicializacomo una cadena de caracteres vacía.El tipo de una variable solo se declara en la primera mención del nombre de esta variable. Cuando semenciona el resto de las veces su tipo ya no se vuelve especificar más. En el curso de la ejecución delprograma, el valor de la variable puede cambiar, pero su tipo y nombre siguen siendo los mismos. Eltipo de una variable puede ser declarada en líneas simples o en los operadores (explicado en el ejemplo).Ejemplo:int Var_1; // declaración de variable en una sola líneaTambién pueden ser declaradas varias variables en una sola línea:int Var_1, Box, Com; // Declaración de varias variables en una líneaLas variables pueden también ser inicializadas dentro de los operadoresdouble Var_5 = 3,7; // Variable inicializada en un operador de asignación
  • 5. Nota: al declarar las variables, ya sea una o varias variables por línea, al final de la misma, antes delcomentario, debe terminarse con un punto y coma (;)Inicialización de variable en la cabecera de un operador compuesto: significa que en el inicio de unoperador compuesto se declara de qué tipo de variable se trata, luego se puede volver a nombrar lavariable y ya no se debe indicar su tipo. Ejemplo:for (int i = 1; i>=10; i++) // Se declara que la variable i es de tipo int.Otros conceptosOperandoEs una constante, una variable, un conjunto de componentes o un valor devuelto por una función.OperaciónEs una acción hecha con los operandos.Símbolo de la OperaciónEs un carácter preseleccionado o un grupo de caracteres que ordenan ejecutar una operación.ExpresiónEs una secuencia de operandos y operaciones de símbolo, es un registro de programa, el valor calculado,el cual se caracteriza por un tipo de datos.Tipos de OperacionesExisten los siguientes tipos de operaciones en MQL4: operaciones aritméticas; operaciones de asignación; operaciones relacionales; operaciones Boolean (lógicas); operaciones bitwise; operación coma; llamado de función (función call).Las operaciones se utilizan en los operadores (ver Operadores). Sólo en los operadores su utilizacióntiene sentido. La posibilidad de utilizar una operación está determinada por las propiedades de losoperadores (si las propiedades del operador le permiten utilizar esta operación específica, puede usarse,de lo contrario, no debe utilizarse esta operación). No está permitido el empleo de las operaciones fuerade los operadores.Operaciones aritméticasLos siguientes símbolos pertenecen a símbolos de operaciones aritméticas: Símbolo Operación Ejemplo Analogía + Adición de valores x+2 - La resta de valores o de cambio de signo x - 3; y = - y * Multiplicación de valores 3*x
  • 6. / Cociente de la división x/5 % Residuo de la división minutos = tiempo 60% ++ Incrementar 1 el valor de la variable y++ y=y+1 -- Disminuir 1 el valor de la variable y -- y=y-1También podemos combinar operaciones. Ejemplo: c = (a + b)*b;Explicación del cálculo matemático de la línea:double Lots_New=MathFloor(Free*Percent/100/One_Lot/Step)*Step;Supongamos que Free=5000.0, One_Lot=1360.0, Step=0.1. En este caso, la formula para calcularLots_New quedaría así:Lots_New = MathFloor(5000.0*30/100/1360.0/0.1)*0.1;Con símbolos matemáticos ordenados en la forma tradicional, podemos mostrar esta fórmula de lasiguiente manera:{ { [ (5000*30) / 100 ] / 1360 } / 0.1 } * 0.1, ó también: { { [ (Free*Percent) / 100 ] / One_Lot } / Step } * StepOperaciones de AsignaciónLos siguientes símbolos pertenecen a símbolos de operaciones de asignación: Símbolo Operación Ejemplo Analógica = Asignación del valor x a la variable y y=x += Aumento de la variable y en el valor x y+=x y=y+x -= Reducción de la variable y en el valor x y -= x y=y-x *= Multiplicación de la variable y por x y *= x y=x*y /= División de la variable y entre x y/x= y=y/x %= Residuo de dividir la variable y en x y = x% y = x% yOperaciones RelacionalesLos siguientes símbolos pertenecen a los símbolos de operaciones relacionales: Símbolo Operación Ejemplo == Es cierto si x es igual a y x == y != Es cierto si x no es igual a y x! = y < Es cierto si x es menor que y x <y > Es cierto si x es mayor que y x>y <= Es cierto si x es igual o menor a y x <= y >= Es cierto si x es igual o mayor a y x> = yOperaciones Boolean (lógicas)Los siguientes símbolos pertenecen a los símbolos de operaciones boleanas:
  • 7. Símbolo Operación Ejemplo Explicaciones TRUE(1), si el valor del operando es FALSE(0); FALSE(0), ! NO (negación lógica) !x si el valor del operando no es FALSE(0). || O (disyunción lógica) x<5 || x>7 TRUE(1), si alguna de las dos expresiones es cierta && Y (conjunción lógica) x==3 && y<5 TRUE(1), si las dos expresiones son ciertasOperaciones BitwiseLas operaciones Bitwise sólo pueden realizarse con números enteros. Las siguientes operacionespertenecen a operaciones bitwise:Complemento a uno del valor de la variable. El valor de la expresión contiene 1 en todos los lugares, enlos cuales los valores de la variable contienen 0, y contienen 0 en todos los lugares, en los cuales losvalores de la variable contienen 1.b= ~ n;La representación binaria de x que es desplazada y lugares a la derecha. Este desplazamiento lógico a laderecha, significa que en todos los lugares que se han vaciado a la izquierda será rellenado con ceros.x = x>>y;La representación binaria de x que es desplazada y lugares a la izquierda. Este desplazamiento lógico ala izquierda será rellenado con ceros a la derecha.x = x<<y;La operación bitwise AND de las representaciones binarias de x e y. El valor de la expresión contiene 1(TRUE) en todos los lugares, en tanto que x e y contienen uno, y el valor de la expresión contiene 0(FALSO) en todos los demás casos.b = ((x + y)! = 0);La operación bitwise OR de las representaciones binarias de x e y. El valor de la expresión contiene 1 entodos los lugares, en la que x o y contienen 1. Contiene 0 en todos los demás casos.b = x | y;La operación bitwise exclusiva o de las representaciones binarias de x e y. El valor de la expresióncontiene 1 en los lugares, en los que x e y tienen diferentes valores binarios. Contiene 0 en todos losdemás casos.b = x ^ y;Operación comaLas expresiones separadas por comas se calculan de izquierda a derecha. Los efectos de los cálculos a laizquierda de la expresión ocurren siempre antes de que se calcule el lado derecho de la expresión. El tipoy el valor del resultado son coincidentes con el tipo y el valor del lado derecho de expresión.for (i = 0, j = 99; i <100; i++, j--) Print (array [i][j]); // Declaración de buvle o ciclo (loop)La lista de parámetros transferidos (véase más adelante) puede considerarse como un ejemplo.My_function (Alf, Bet, Gam, Del) // La llamada a una función con argumentos
  • 8. EjerciciosProblema 1. Juan tiene 2 lápices, Pedro tiene 3 lápices. ¿Cuántos lápices tienen estos muchachos?Solución. Vamos a indicar el número de lápices de Juan como una variable A y el número de lápices dePedro como variable B, mientras que el resultado será denominado C. La respuesta será: A + B = CComo los lápices son cosas, es decir, es algo que básicamente puede existir como una parte (porejemplo, puede haber una mitad de un lápiz), entonces vamos a considerar a los lápices como variablesreales, es decir, las variables de tipo double. Cálculos similares también pueden realizarse con númerosenteros int. Por tanto, podemos poner el código de la solución así::double A = 2.0; // El número de lápices de Juandouble B = 3.0; // El número de lápices de Pedrodouble C = A + B // Número totalEn este caso, la operación "+" se aplica a la suma de los valores de un tipo de variables.Problema 2. En una esquina de la casa, hay una carnicería denominada "Ártico". En otra esquina de lamisma casa, hay un establecimiento llamado "Salón de peluquería". ¿Qué está escrito en la casa?Soluciónstring W1 = "Artico"; // String 1string W2 = "Salon de peluqueria"; // String 2string Ans = W1 + W2; // Suma de stringsEl valor de la variable Ans será la cadena (string) que aparece como sigue: ArticoSalon de peluqueríaEl resto de operaciones aritméticas con variables de tipo string (resta, multiplicación, división) No estánpermitidas.Problema 3. Calcular los valores de las expresiones A/B*C y A*C/B, para los enteros A, B y C.Se espera intuitivamente que el resultado del cálculo en ambos casos sea el mismo. Sin embargo, estaafirmación es cierta sólo para los números reales. Si queremos calcular los valores de las expresionescompuestas de los operandos de tipo int, debemos siempre considerar el resultado intermedio. En talcaso, la secuencia de operandos es de fundamental importancia:int A = 3; // Valor de tipo intint B = 5; // Valor de tipo intint C = 6; // Valor de tipo intint Res_1 = A/B*C; // Result 0 (cero)int Res_2 = A*C/B; // Resultado 3 (tres)
  • 9. OperadoresEl termino “Operador”Uno de los principales conceptos de cualquier lenguaje de programación es el término de «operador».Cuanto mejor comprende un programador lo que son los operadores, y cómo se aplican en un programa,antes se inicia éste en la escritura de sus propios programas.El operador es parte de un programa, una frase de un lenguaje algorítmico que prescribe undeterminado método de conversión de datos.Cualquier programa contiene operadores. La analogía más cercana a «operador» es una frase. Así comouna frase compone el texto normal de una novela, así los operadores componen un programa.Propiedades de los operadoresTodos los operadores tienen una propiedad común: todos ellos se ejecutan.Podemos decir que el operador es una instrucción que contiene la guía de operaciones, la descripción deuna orden.Que una PC ejecute un programa significa que (consecutivamente, pasando de un operador a otro) secumplan las órdenes (las recetas, instrucciones) que figuran en los operadores.Tipos de OperadoresHay dos tipos de operadores: los operadores simples y los compuestos.Operadores simplesLos operadores simples siempre terminan en ";" (punto y coma). El uso de este separador es para que laPC pueda detectar cuándo un operador termina y comienza otro. Un operador puede tener varias líneas.Se pueden colocar varios operadores en una línea. Ejemplos:Go_My_Function_ind(); // Operador simplea=3; b=a*x+n; i++; // Varios operadores colocados en lineaPrint(" Day= ",TimeDay(Mas_Big[s][0]), // Un operador... " Hour=",TimeHour(Mas_Big[s][0]), // colocado… " Minute=",TimeMinute(Mas_Big[s][0]), // en varias… " Mas_Big[s][0]= ",Mas_Big[s][0], // lineas " Mas_Big[s][1]= ",Mas_Big[s][1]);Operadores compuestosUn operador compuesto consta de varios operadores simples separados por punto y coma “;”.Elconjunto de operadores simples están ubicados en un recinto separado por llaves. La presencia de unallave de cierre marca el final de un operador compuesto. Con el fin de poder utilizar varios operadoresdonde se espera que haya solo uno, los programadores utilizan un operador compuesto (también lollaman "bloque" o "bloque de código").Un ejemplo de utilización de un operador compuesto es un operador condicional. Comienza con eloperador condicional if (expresión), seguido por un bloque compuesto de operadores simples llamadocuerpo. Este cuerpo contiene una lista de operadores ejecutables.
  • 10. Orden de ejecución de los operadoresLos operadores se ejecutan en el orden en el que aparecen en el programa. La dirección de losoperadores de ejecución va de izquierda a derecha y de arriba hacia abajo.Ejemplo de un Operador de AsignaciónEn el lado izquierdo de la igualdad solo puede haber una variable y en el lado derecho una expresión concualquier grado de complejidad.Problema 4. Tenemos un sistema de ecuaciones: “Y = 5” y “Y - X = 2”. Hallar el valor numérico de lavariable X.Solución 1. En un texto normal en una hoja de papel:5-X=2X=5-2X=3Solución 2. Un texto en un programa:Y =5; // Línea 1X = Y-2; // Línea 2En la memoria física de la PC se registra el valor numérico 3 para la variable X.Ejemplo de un contadorX = X + 1; // Ejemplo de un contadorDesde un punto de vista lógico, este ejemplo parece un error matemático. Sin embargo, es correcto si seconsidera como un operador (por cierto, este operador se utiliza ampliamente en la codificación).Con este operador hemos calculado un nuevo valor para la variable X: cuando se ejecuta el operador deasignación (es decir, el cálculo del valor de la parte derecha del operador), la PC toma el valor de lamemoria física que contiene el valor numérico de la variable X (que en el ejemplo anterior resulta serigual a 3 en el momento de referirse a ella), y calcula la expresión en la parte derecha del operador deasignación (3 + 1), y escribe el valor obtenido 4 en la memoria celular (física) de la PC para la variableX. La máquina almacenará este valor de la variable X hasta que la variable X se presente en la parteizquierda del signo de igualdad en otro operador de asignación. En este caso, el nuevo valor de estavariable se calculará y se almacenan hasta el próximo posible cambio.
  • 11. FuncionesDescripción o Definición de la Función: es la parte donde se nombra el programa.Llamada de función: es un registro, es el acto que conduce a la ejecución de la función.En nuestra vida cotidiana, podemos encontrar muchas analogías de la función. Tomemos, por ejemplo,el sistema de frenado de un coche. La idea implementada por el ingeniero es análoga a ladefinición/descripción de función, mientras que el pedal de freno es análogo a la llamada a la función.El conductor presiona el pedal, y los mecanismos de accionamiento realizan ciertas acciones y detienenel coche. Del mismo modo, si la llamada a una función se produce en un programa, entonces la funcióndel mismo nombre será llamada y ejecutada, es decir, se llevarán a cabo una cierta secuencia de cálculosu otras acciones (por ejemplo, se muestra un mensaje o una orden de apertura, etc.). El sentido generalde una función es la adopción de una lógica que se completa fuera del texto base del programa, mientrasque sólo se mantiene dentro del texto base del programa la parte del código que se ocupa de la llamadade esta.Este programa de construcción tiene las siguientes ventajas:El texto del programa está integrado de tal manera que se lee mucho más fácil.Se puede ver con facilidad y, si es necesario, modificar el texto de una función sin realizar ningúncambio en el código básico o programa principal.Una función puede estar compuesta como un solo archivo y usarse en otros programas, lo cual liberaráal programador de la necesidad de insertar el mismo fragmento de código en cada nuevo programa.La mayor parte del código de los programas que usan MQL4 está escrito en forma de funciones.Este enfoque se extendió y actualmente es un estándar.Composición de una funciónPor lo tanto, una función está compuesta de la descripción y la llamada. Vamos a considerar un ejemplo.Supongamos que tenemos un pequeño programa que considera la longitud de la hipotenusa utilizandolos otros dos lados del triángulo rectángulo y el teorema de Pitágoras.En este programa, todos los cálculos se encuentran juntos, los operadores son ejecutados uno por uno enel orden en el que se producen en el programa (de arriba hacia abajo).
  • 12. Problema 5. Redactar una parte del código del programa como una función.Sería razonable hacer una función utilizando las dos líneas que encuentran el valor buscado. En esteprograma, una parte de los cálculos se integra como una función. El código básico contiene una llamadaa la función definida por el usuario. La descripción de la función definida por el usuario se encuentrafuera, después del código básico:Descripción de la FunciónLa descripción de una función consta de dos partes básicas: cabecera de la función y cuerpo de lafunción.La cabecera de una función está formada por: el tipo del valor de return, el nombre de función y la listade parámetros formales.La lista de parámetros formales está encerrada entre paréntesis y se coloca después del nombre de lafunción.El tipo del valor de return puede ser uno de los tipos que ya conocemos: int, double, bool, color,datetime, o string. Si la función no devuelve ningún valor, su tipo puede ser denominado void (sincontenido, vacío).
  • 13. La descripción de la función definida por el usuario debe estar presente en el programa y se colocainmediatamente después de que se cierra la llave de la función especial start() (es decir, se coloca fuerade la función especial start).Llamada a la FunciónLa llamada a la Función se representa con el nombre de la función y la lista de parámetros transferidos.La lista de parámetros transferidos se coloca entre paréntesis. La llamada a la función puede serrepresentada como un operador independiente o como parte de un operador.TIPOS DE FUNCIONESHay tres tipos de funciones: funciones especiales, funciones estándar (built-in o predefinidas), yfunciones definidas por el usuario.Funciones especialesEn MQL4, hay en total 3 funciones especiales. Ellas tienen nombres predefinidos: init(), start(), ydeinit(), que no pueden utilizarse como nombres de ninguna otra función.El código básico de un programa se encuentra dentro de estas funciones.La característica especial de las funciones especiales es el hecho de que son llamadas para su ejecucióndesde el Terminal de Usuario. Aunque las funciones especiales tienen todas las propiedades de lasfunciones en general, no se les suele llamar desde el programa si éste está codificado correctamente.Funciones estándarMQL4 tiene una serie de útiles funciones en las cuales, cuando se escribe la codificación del programano es necesario hacer su descripción. Por ejemplo, el cálculo de raíces cuadradas, la impresión demensajes en el sistema o en la pantalla.
  • 14. La característica singular de las funciones estándar es que no están descriptas en el texto del programa.Las funciones estándar son llamadas en el programa de la misma manera a como lo hace cualquier otrafunción.En el ejemplo expuesto anteriormente se utilizan dos funciones estándar: MathSqrt () y Alerta (). Laprimera está destinada al cálculo de raíces cuadradas, mientras que la segunda está diseñada paramostrar un determinado mensaje de texto, puesto entre paréntesis, en la pantalla.Estas funciones, también pueden ser denominadas funciones built-in, o funciones predefinidas.En el texto de un programa, se puede distinguir fácilmente la llamada a la función estándar por suaspecto, que se destaca en MetaEditor con color morado (los colores pueden elegirse a voluntad).Funciones definidas por el usuarioEn algunos casos, los programadores crean y utilizan sus propias funciones y hacen la llamada a estasfunciones. Las Funciones definidas por el usuario se utilizan en los programas con la descripción de lafunción y las llamadas a la función.PROPIEDADES DE LAS FUNCIONESEjecución de la FunciónLa principal propiedad de todas las funciones es que la llamada a la función hace que estás se ejecuten.Las funciones se ejecutan de acuerdo a sus códigos.El paso de parámetros y el valor de returnEl paso de los parámetros se especifica encerrándolos entre paréntesis después del nombre de la funciónque se llama.El paso de parámetros se hace usualmente separándolos mediante comas. El número de parámetrostransferidos a la función no debe superar 64. La función también puede omitir el uso de paso deparámetros. En este caso se especifica una lista vacía de parámetros, es decir, simplemente hay queponer un paréntesis de apertura y uno de cierre directamente después del nombre de función.La cantidad, tipos y orden de los parámetros transferidos en la llamada a la función deben coincidir conlos parámetros de formación que se especifica en la descripción de la función.El valor de return se especifica en los paréntesis del operador return (). El tipo del valor devueltoutilizando en el operador return () debe coincidir con el tipo de la función dada en la cabecera de lafunción. También es posible que una función no devuelva ningún valor; en este caso no se especificanada en el paréntesis del operador return ().
  • 15. Solamente se pueden utilizar variables en los parámetros formales de la cabecera de la descripción de lafunción.Como parámetros transferidos, solo se pueden utilizar variables, constantes y expresiones:Parámetros formalesLos Parámetros formales son una lista de variables especificadas en la cabecera de la descripción de lafunción.Ya mencionamos antes que una misma función podría ser utilizada en varios programas. Sin embargo,los diferentes programas utilizan diferentes nombres para las variables. Si las funciones requirieran deforma estricta que se pusieran determinados nombres en las variables de los parámetros a transferir (y,correlativamente su valor), no sería conveniente para los programadores. De hecho, usted tendría queponer en cada programa recientemente desarrollado los nombres de variables que ya se han utilizado ensus funciones anteriores. Sin embargo, afortunadamente, las variables utilizadas dentro de lasfunciones no tienen relación con las variables utilizadas en el programa que lo llama.Solamente pueden especificarse variables (pero no constantes) como parámetros formales en lacabecera de una función.
  • 16. Cómo funciona (explicación de la respuesta al Problema 5) En el programa, se produce una llamada a la función, las variables A y B están especificadas entre paréntesis. El programa llama a la función con ese nombre y que tiene los parámetros formales a y b que se especifican en su cabecera. El valor de variable A se le asigna a la variable a. El valor de la variable B se le asigna a la variable b. La función ejecutable realiza los cálculos utilizando los valores de las variables a y b.Se puede usar cualquier nombre en los parámetros formales (mientras no coincidan con otros nombresde variables ya utilizados en el programa). En este ejemplo, hemos utilizado los identificadores de losparámetros formales a y b. Sin embargo, podríamos utilizar cualquier otro, por ejemplo, m y n.El valor de return calculado en la función se da en el paréntesis del operador return (). Como valor dereturn puede utilizarse el valor de una variable, el resultado de una expresión o una constante. Ennuestro caso, el valor de return es el valor de la variable local c (una variable local es una variabledeclarada dentro de una función. A la salida de la función los valores de todas las variables locales sepierden, y por tanto su ámbito de trabajo es solo válido dentro de la función). La función devuelve alprograma que lo llamó el valor de la variable local c. Esto significa que este valor será asignado a lavariable C.Una forma más compacta de escribir la misma función definida por el usuario://--------------------------------------------------------------------int Gipo(int a, int b) // Función definida por el usuario{return(MathSqrt(a*a + b*b)); // Operador Función Salida}//-------------------------------------------------------------------En este caso todos los cálculos se realizan en un solo operador. El valor de return se calculadirectamente en el paréntesis del operador return ().De este modo, la aplicación de funciones definidas por el usuario tiene algunas ventajas: Los nombres de variables en el texto del programa principal no tienen relación con los nombres de los parámetros formales de una función definida por el usuario. Las funciones definidas por el usuario pueden ser reutilizadas en diferentes programas, no hay necesidad de cambiar el código de la función definida por el usuario. Lo único que se deberá recordar es el orden de las variables en la cabecera y en la llamada a la función. Se pueden crear librerías, si fuera necesario.
  • 17. ProgramasEl programador elige el tipo de programa que va a ser escrito en función del propósito de ese programaespecífico y las propiedades y limitaciones de los diferentes tipos de programas.El Asesor Experto (EA) es un programa para ser ejecutado en cada uno de los ticks. El objetivoprincipal de los Asesores Expertos es programar el control sobre el comercio.Los Scripts son destinados a realizar cualquier tipo de operaciones que permitan ser ejecutadas una solavez.El Indicador personalizado es un programa para ser ejecutado, al igual que el EA, en todos los ticks.Están básicamente destinados a la exhibición gráfica de funciones matemáticas calculadaspreliminarmente. Hay dos tipos de indicadores: indicadores técnicos (built-in) y los indicadorespersonalizados (creados por el propio usuario).Propiedades de los ProgramasUna vez que se haya vinculado un programa (EA o indicador personalizado) a la ventana de símbolo oinstrumento, el programa hace algunos preparativos y cambia al modo de espera de ticks. Tan prontocomo un nuevo tick entra, la Terminal de Usuario lo pondrá en marcha para su ejecución, entonces, elprograma hace todas las operaciones descriptas en su algoritmo, y, una vez que termina, pasa el controlnuevamente a la Terminal de Usuario y permanece en el modo de espera de ticks.Si un nuevo tick llega cuando el programa (EA o indicador personalizado) se está ejecutando, esteevento no tiene ningún efecto sobre la ejecución del programa, el programa sigue siendo ejecutado deacuerdo a su algoritmo y el control solo pasa a la Terminal de Usuario cuando el programa hayaterminado todas las tareas descriptas en su algoritmo.Una vez que un Asesor Experto se asocia a la ventana de símbolo o instrumento, hace los preparativosnecesarios (función init()) y cambia al modo de espera de ticks preparado para iniciar la función start().A diferencia de los EAs, el indicador personalizado ejecuta tanto la función init() como la función start()una vez que hace el primer cálculo preliminar del valor del indicador. Más tarde, con un nuevo tick, elindicador personalizado se inicia llamando únicamente a la función start(), es decir que los operadorestrabajan de acuerdo con el algoritmo de la función start().A diferencia de los Asesores Expertos o los indicadores, un Script se pondrá en marcha para suejecución inmediatamente después de que haya sido asociado a una ventana de símbolo, sin esperar a unnuevo tick. Todo el código del script se ejecutará de una vez. Después de que todas las líneas delprograma se han ejecutado, el script pone fin a sus operaciones y se desvincula de la ventana de símbolo.Un script es útil si se quieren hacer operaciones de una sola vez, por ejemplo, abrir o cerrar órdenes,mostrar textos en la pantalla, instalar objetos gráficos, etc.Sólo los Asesores Expertos y los Scripts tienen la posibilidad de utilizar las funciones de trading. En losindicadores personalizados no está permitido el empleo de funciones comerciales (funciones de trading).Solo se puede asociar un EA en una ventana de símbolo; no está permitido el uso simultáneo de variosAsesores Expertos en la misma ventana.Solo se puede asociar un script en una ventana de símbolo; no está permitido el uso simultáneo de variosscript.Se pueden asociar al mismo tiempo varios indicadores en una ventana de símbolo pero de manera queno interfieran entre sí.
  • 18. Tipos de archivomq4: Contienen el código fuente de un programa (EA, Script o Indicador). Son editables.ex4: Surgen al compilarse un archivo .mq4. Son archivos ejecutables. No pueden editarse. Los archivoscon extensión .ex4 se pueden utilizar como archivos de la librería.mqh: Son los archivos de inclusión y se almacenan en el directorio expertsinclude. Generalmente sonincluidos en la fase de compilación.Existen otros tipos de archivos que no hacen un programa completo, pero se utilizan en la creación deprogramas. Por ejemplo, un programa puede ser creado usando una librería creada anteriormente. Unusuario puede crear librerías de funciones personalizadas destinadas al almacenamiento para usofrecuente de bloques de programas de usuario. Se recomienda almacenar las librerías en el directorioexpertslibreries.Los archivos de mq4 y ex4 se pueden utilizar como archivos de librería. Las librerías no puedenejecutarse por si mismas.Es preferible el uso de archivos de inclusión al uso de librerías debido al consumo adicional derecursos de la PC en las llamadas a funciones de librería.Creación y uso de programasAl crear un nuevo asesor experto, el MetaEditor crea por defecto tres funciones especiales en elprograma: init(), start() y deinit().Cada función contiene un solo operador, return(0), que es el operadorpara salir de la función.El código final del programa no tiene que contener obligatoriamente todas las funciones especialesindicadas. Ellas están presentes en la pauta, sólo porque, como por regla general, un programa de nivelmedio habitualmente contiene todas estas funciones. Si alguna de las funciones no será utilizada, sudescripción puede ser eliminada.Reglas para redactar el programa y los comentarios La longitud de una línea de comentario no debe exceder el tamaño de la ventana principal. Esta limitación no es un requisito formal de sintaxis. La declaración de variables se realiza en el programa de inicio. Se recomienda escribir un comentario descriptivo para cada variable. Es preferible que cada operador esté situado en una línea distinta. Si hay un comentario en una línea debe iniciarse a partir de la 76ª posición. Este requisito no es obligatorio. Para dividir lógicamente fragmentos separados, se utilizan líneas continuas del ancho total (118 caraceters). Cuando se utilizan las llaves se incluye una tabulación de 3 símbolos.
  • 19. ESTRUCTURA DE UN PROGRAMALa norma de programación en MQL4 es la siguiente:El código de un programa debe ser escrito dentro de funciones. Es decir, las líneas de programa(operadores y llamadas a funciones) que se encuentran fuera de una función no pueden ser ejecutadas.Vamos a considerar el plan funcional de un programa común, Asesor Experto:Los bloques mayores de un programa escrito en MQL4 son los siguientes: 1. Cabecera del programa: consta de varias líneas al comienzo de un programa (a partir de la primera línea) que contienen información general sobre el programa. Por ejemplo, esta parte incluye líneas de la declaración y la inicialización de variables globales. 2. Función especial init(). Por lo general, después de la cabecera son descriptas las funciones especiales, las cuales tienen nombres predefinidos: init(), start() y deinit(). 3. Función especial start(). 4. Función especial deinit(). 5. Funciones definidas por el usuario. La descripción de funciones definidas por el usuario usualmente se presentan después de la descripción de las funciones especiales. El número de funciones definidas por el usuario en un programa no está limitado.Esta es la disposición habitual de bloques funcionales, es decir: cabecera, funciones especiales yfunciones definidas por el usuario:
  • 20. Estas son otras variantes de estructura de un programa. En todos los ejemplos la parte de la cabeza es loprimero, mientras que las funciones pueden ser descriptas en un orden aleatorio:Secuencia de ejecución de códigoCabecera y funciones especialesAl iniciar la ejecución del programa en una ventana de un símbolo, se ejecutan partes de las líneas deprograma de la cabecera. Después, se realizan los preparativos descriptos en la cabecera.Luego, las funciones especiales son llamados para ser ejecutadas por la Terminal de Usuario:La función especial init() es llamada para la ejecución una sola vez al comienzo de la operación delprograma. Por lo general, esta función contiene un código que debe ejecutarse sólo una vez antes de laoperación principal del programa de inicio start(). Por ejemplo, cuando el init() es ejecutado, seinicializan algunas variables globales, se muestran objetos, mensajes etc.Luego la Terminal de Usuario pasa el control a la función especial start() y esta se ejecuta.Después de que todo el código de la función especial start() es ejecutado, se devuelve el control a laterminal de usuario, la cual tendrá el control durante algún tiempo, sin iniciar ninguna función especial,hasta que llegue un nuevo tick y la terminal de usuario pase el control a la función especial start() unavez más, como resultado, la función será ejecutada.El proceso de múltiples llamadas de la función especial start() realizado por la Terminal de Usuario serepetirá mientras que el programa esté asociado a un gráfico, y puede continuar durante semanas ymeses. Durante todo este período un EA puede llevar a cabo operaciones de comercio automáticas.En esquema de arriba, el proceso de ejecución múltiple de la función start() se muestra mediantediversas flechas amarillas envolviendo a la función especial start().
  • 21. Cuando un trader elimina un EA del gráfico, la Terminal de Usuario inicia la función especial deinit().La ejecución de esta función es necesaria para la correcta finalización de una operación del EA. Sesuprimen objetos innecesarios, variables, etc.Tras la ejecución de la función especial deinit() se devuelve el control a la Terminal de Usuario.La ejecución de funciones especiales puede hacer referencia a la información del entorno (flechasdelgadas de color azul en el esquema de arriba) y a la llamada para la ejecución de funciones definidaspor el usuario (flechas delgadas de color amarillo).Las funciones definidas por el usuario se ejecutan cuando la llamada está contenida en alguna función.En este caso, el control pasa a la función definida por el usuario y después de la ejecución de la funciónel control es devuelto al lugar de la llamada.La llamada a las funciones definidas por el usuario se puede hacer no sólo dentro de la descripción deuna función especial, sino también en la descripción de otras funciones definidas por el usuario. Esto nosolo esta permitido, sino que además es ampliamente utilizado en la programación.El control de la acción, es decir, las órdenes de trading pueden formarse tanto en funciones especialescomo en funciones definidas por el usuario.Función Especial init()Si un programa contiene la descripción de la función especial init(), será llamada (y ejecutada) en elmomento de iniciar el programa.No se recomienda llamar a la función start() desde la función especial init() ni realizar operaciones decomercio desde init(), porque puede suceder que no estén listos algunos valores de los parámetros de lainformación del entorno (información sobre gráficas, precios de mercado, etc.)Función Especial start()Las propiedades especiales de la función start() difieren en función del tipo de programa que se ejecute:El código principal del programa debe estar contenido en la función start(). Todos los operadores, built-in, las llamadas a las funciones personalizadas y todos los cálculos necesarios se deben realizar dentrode esta función.En los Asesores Expertos la función especial start() se llama (y ejecuta) inmediatamente después demarcar un nuevo tick. Si un nuevo tick ha llegado durante la ejecución de la función especial start(), estetick no se tendrá en cuenta. Todas las cotizaciones recibidas durante la ejecución de la función especialstart() se ignoran.En los Script la función especial start() se llama (y ejecuta) una sola vez, inmediatamente después de lainicialización del programa especial en la función init().En los Indicadores Personalizados, la función especial start() se llama (y ejecuta) inmediatamentedespués de marcar un nuevo tick, inmediatamente después de que se vincula a un gráfico, cuando secambia el tamaño de una ventana, cuando se cambia de uno a otro instrumento, cuando se inicia laTerminal de Usuario (si durante el anterior período de sesiones, un indicador se asoció a una gráfica),después de cambiar un símbolo y el marco temporal actual de un gráfico, con independencia del hechode que si las nuevas cotizaciones llegan o no.
  • 22. Función Especial deinit().La función particular de la función especial deinit() es la ejecución de un programa de terminación(deinicialización). Si un programa contiene la descripción de la función especial deinit(), será llamada (yejecutada) en un programa de apagado.También se llama a la función especial deinit() cuando la ventana de un símbolo es cerrada, antes decambiar el periodo de un gráfico, en la re-compilación exitosa de un programa, así como cuando secambia de cuenta.Requerimientos de las funciones especialesLa presencia de las funciones especiales init() y deinit() no son imprescindibles dentro programa, esdecir, pueden estar ausentes. No importa el orden en el que estén descriptas las funciones especiales enel programa.Las funciones especiales se pueden llamar desde cualquier parte del programa de conformidad con lasreglas generales de llamadas de funciones.
  • 23. OPERADORESOperador de asignaciónEl operador de asignación es el operador más simple y más frecuentemente usado.Un operador de asignación representa un registro que contiene el caracter "=" (signo de igualdad). A laizquierda de este signo de igualdad se especifica el nombre de una variable, a la derecha de ella damosuna expresión. El operador de asignación finaliza con ";" (punto y coma).En un operador de asignación, no se permite que el tipo de una variable sea declarado más de una vez.Ejemplos del uso de funciones definidas por el usuario y funciones estándar en la parte derecha:In = My_Function (); // The value of user-defined function is assigned to variable InDo = Gipo(Do1,Do1); // The value of user-defined function is assigned to variable DoEjemplo de utilización de expresiones en la parte derecha:In = (My_Function()+In2)/2; // The variable In is assigned // ..with the value of expressionDo = MathAbs(Do1+Gipo(Do2,5)+2.5); // The variable Do is assigned // ..with the value of expressionEjemplos de Asignación de operadores en forma corta:En MQL4 también se utiliza una forma breve de componer los operadores de asignación. La forma cortade la asignación operadores se utiliza en el código para una mejor visualización.In /= 33; // Short form of the assignment operatorIn = In/33; // Full form of the assignment operatorSt += "_exp7"; // Short form of the assignment operatorSt = St + "_exp7"; // Full form of the assignment operatorEl operador condicional if-elseEl formato completo del operador if-else contiene una partida que incluye una condición, el cuerpo 1, lapalabra clave else, y el cuerpo 2. El cuerpo del operador puede estar formado por uno o variosoperadores, los cuerpos van encerrados entre llaves.Estructura:if (condición) // Cabecera del operador y condición{Bloque 1 de operadores // Si la condición es verdadera, entonces ..Composición cuerpo 1 // .. los agentes que componen el cuerpo 1 se ejecutan}else // Si la condición es falsa ..{Bloque 2 de operadores // .. entonces los operadores ..Composición cuerpo 2 // .. del cuerpo 2 se ejecutan}Formato sin else
  • 24. El operador "if-else" puede ser usado sin else. En este caso, el operador "if-else contiene su cabecera,que incluye una condición, y el cuerpo 1, que consta de uno o varios operadores cerrados entre llaves.Estructura:if (condición) // Cabecera del operador y el estado{Bloque 1 de operadores // Si la condición es verdadera, entonces ..Composición cuerpo 1 // .. agentes que componen el cuerpo 1 se ejecutan}Formato sin llavesSi el cuerpo del operador "if-else consta de un solo operador, se pueden omitir las llaves.if (condición) // Cabecera del operador y el estadoOperador // Si la condición es verdadera, entonces .. // .. Este operador se ejecutaRegla de Ejecución del operador "if-else"Si la condición del operador "if-else" es cierta, se pasa el control al primer operador en el cuerpo 1.Después de que todos los operadores en el cuerpo 1 se han ejecutado, el control pasa al operador queaparece después del operador "if-else". Si la condición del operador "if-else" es falsa, entonces: Si está la palabra clave else en el operador "if-else", entonces se pasa el control al primer operador en el cuerpo 2. Después de que todos los operadores en el cuerpo 2 se han ejecutado, se pasa el control al operador que aparece después del operador "if-else"; Si no hay una palabra clave else en el operador "if-else", entonces se pasa el control al operador posterior al operador " if-else.Problema 6: Redactar un programa en el que: Si el precio de un símbolo ha superado un cierto valor, elprograma informará al trader ese hecho, si no el programa no debe hacer nada.//---------------------------------------------------------------------------------------// onelevel.mq4//---------------------------------------------------------------------------------------int start() // función especial start{doubleLevel, // declaración de variable donde estará el nivel de alertaPrice; // declaración de variable donde estará el precio actualLevel=1.2753; // Establecer el nivelPrice=Bid; // Solicitud de precio actual//---------------------------------------------------------------------------------------if (Price>Level) // Operador if con una condición{Alert("El precio a superado el nivel establecido"); // Mensaje para el comerciante}//---------------------------------------------------------------------------------------return; // Salir de start()}//---------------------------------------------------------------------------------------
  • 25. Operadores if-else anidadosProblema 7. Redactar un programa con las siguientes condiciones: Si el precio ha crecido de maneraque supera un cierto nivel 1, el programa deberá informar al comerciante, si el precio ha caído pordebajo de un cierto nivel 2, el programa deberá informar al comerciante, sin embargo, el programa nodebe realizar ninguna acción en cualquier otro caso.//------------------------------------------------ -----------------------// Twoleveloptim.mq4//------------------------------------------------ -----------------------int start() // Special function start(){doubleLevel_1, // Alert level 1Level_2, // Alert level 2Price; // Current priceLevel_1=1.2850; // Set level 1Level_2=1.2800; // Set level 2Price=Bid; // Request price//-----------------------------------------------------------------------if (Price > Level_1) // Check level 1{Alert("The price is above level 1"); // Message to the trader}else{if (Price < Level_2) // Check level 2{Alert("The price is below level 2"); // Message to the trader}}//-----------------------------------------------------------------------return; // Exit start()}//-----------------------------------------------------------------------Problema 8. Redactar un programa que tenga en cuenta las condiciones siguientes: Si el precio caedentro del rango preestablecido de valores, no se hace nada; si el precio está fuera de este rango, elprograma debe informar al operador.//---------------------------------------------------------------------------------// compoundcondition.mq4//---------------------------------------------------------------------------------int start() // Special function start{doubleLevel_1, // Alert level 1Level_2, // Alert level 2Price; // Current priceLevel_1=1.2850; // Set level 1Level_2=1.2800; // Set level 2Price=Bid; // Request price
  • 26. //--------------------------------------------------------------------------------if (Price>Level_1 || Price<Level_2) // Test the complex condition{Alert("The price is outside the preset range"); // Message}//--------------------------------------------------------------------------------return; // Exit start()}//--------------------------------------------------------------------------------Esta condición significa: “Si el valor de la variable Price es superior al de la variable Level_1, O el valorde la variable Price es menor que la variable de Level_2, el programa debe ejecutar el cuerpo deloperador if-else”.Se pueden usar operaciones lógicas (&&, || y !) al redactar las condiciones del operador if-else. Esto esampliamente utilizado en la práctica.Para la solución de problemas más complejos, puede ser necesaria la utilización de paréntesis, porejemplo:if ( A>B && (B<=C || (N!=K && F>B+3)) ) // Example of a complex conditionOperador de ciclo “while”La funcionalidad más potente de MQL4 es la posibilidad de organizar ciclos (bucles).Al crear programas de aplicación, se pueden utilizar a menudo cálculos repetidos, que son en su mayoríalíneas repetidas de programa. Con el fin de hacer la programación cómoda y al programa fácil deutilizar, se usan los operadores de ciclo (bucle). Hay dos operadores de ciclo en MQL4: while y for.Formato del operador whileEl formato completo del operador de ciclo while (mientras que) consta de una cabecera que contiene unacondición, y el cuerpo del ciclo (bucle) ejecutable, adjunto entre llaves.while (condición) // Cabecera del operador de ciclo{ // Apertura llaveBloque de operadores // El cuerpo de un operador de ciclo (bucle) puede consistir ..que componen el cuerpo de ciclo // .. de varios operadores} // Cierre llaveSi en el operador while el cuerpo de bucle se compone de un solo operador, pueden omitirse las llaves.while (condición) // Cabecera del operador de cicloOperador, cuerpo del ciclo // cuerpo de ciclo (bucle) consta de un solo operadorRegla Ejecución para el operador whileMientras la condición del operador “while” sea “verdadera” el programa pasa el control al cuerpo deloperador de bucle. Después de que han sido ejecutados todos los operadores en el cuerpo de bucle, sepasa el control a la cabecera para verificar la condición. Si la condición del operador “while” es “falso”,el control debe ser pasado al operador que sigue al operador de bucle “while”.El operador while (mientras que) puede leerse así: "Mientras se cumpla esta condición, realice lo/lossiguiente/s: ..".
  • 27. Problema 9. Calcular el coeficiente de Fibonacci con la precisión de 10 cifras significativas.//----------------------------------------------------------------------------------------// fibonacci.mq4//----------------------------------------------------------------------------------------int start() // Función especial start(){//----------------------------------------------------------------------------------------int i; // parámetro formal, contador de iteracionesdoubleA,B,C, // Los números en la secuenciaDelta, // diferencia real entre los coeficientesD; // Precisión preestablecida//----------------------------------------------------------------------------------------A=1; // valor inicialB=1; // valor inicialC=2; // valor inicialD=0.0000000001; // valor de la precisiónDelta=1000.0; // valor inicial//----------------------------------------------------------------------------------------while(Delta>D) // Cabecera del operador de ciclo{ // Apertura de llave del cuerpo del operador de bucle Whilei++; // incremento del contador de iteracionesA=B; // Siguiente valorB=C; // Siguiente valorC=A + B; // Siguiente valorDelta=MathAbs(C/B - B/A); // Buscar diferencia entre los coeficientes} // Cierre de llave del cuerpo del operador de bucle While//----------------------------------------------------------------------------------------Alert("C=",C," Fibonacci number=",C/B," i=",i); // mostrar en la pantallareturn; // Salir de star ()} //Cierre de llave de la funcion especial star//----------------------------------------------------------------------------------------Al comienzo del programa, se declaran (y describen) las variables. En las líneas posteriores, se asignanvalores numéricos a las variables. A, B y C, las cuales toman el valor de los primeros números de lasecuencia de Fibonacci.El operador de ciclo comienza el trabajo con la prueba de la condición. El ciclo se llevará a cabo enrepetidas ocasiones, mientras que la condición (Delta> D) sea verdad.Las variables toman los valores del siguiente elemento. Antes de que el operador del ciclo se ejecute, losvalores de A, B y C son iguales a 1,0, 1,0 y 2,0, respectivamente.Durante la primera iteración, estas variables toman los valores 1, 2 y 3, respectivamente.La iteración es una ejecución repetida de algunos cálculos; se utiliza para señalar que las líneas deprograma que componen el cuerpo del operador de bucle son ejecutados.En la línea siguiente, se calcula la diferencia entre los números de Fibonacci obtenidos sobre la base delos posteriores (C/B) y anteriores (B/A) elementos de la secuencia:Delta=MathAbs(C/B - B/A); // Buscar diferencia entre los coeficientes
  • 28. En este operador, utilizamos la función estándar MathAbs() que calcula el valor absoluto de laexpresión, en este caso, de la desviación.Este operador es el último en la lista de operadores que componen el cuerpo del bucle. Esto se veconfirmado por la presencia de un cierre de llave en la línea siguiente. Tras la ejecución del cuerpo delúltimo operador de ciclo, el control se pasa a la cabecera del operador para poner a prueba la condición.Este es el punto clave que determina la esencia del operador de ciclo.En las primeras iteraciones, el valor de la variable Delta resulta ser mayor que el valor definido en lavariable D. Esto significa que la condición (Delta> D) es cierta, por lo que el control se pasa al cuerpodel ciclo para realizar la siguiente iteración. Todas las variables involucradas en los cálculos llevaránnuevos valores: tan pronto como el cuerpo del ciclo llegue el final, el control será pasado a la cabecerade nuevo para poner a prueba la condición y ver si esta es verdadera.Este proceso continuará hasta que la condición del operador de ciclo se convierta en falsa. Tan prontocomo el valor de la variable Delta sea menor o igual al valor de D, la condición (Delta> D) ya no serácierta. Esto significa que el control será pasado fuera del operador de ciclo, a la línea:Alert ("C =" C, "Fibonacci number =", C / B, "i =", i); // mostrar en la pantallaComo resultado de ello, el siguiente mensaje aparecerá en la pantalla:C = 317811 Fibonacci number = 1,618 i = 25Esto significa que la búsqueda de precisión se alcanza en la 25ª iteración, con el valor máximo de lasecuencia de Fibonacci de 317811. El coeficiente de Fibonacci, como era de esperarse, es igual a 1,618.Este mensaje es la solución del problema planteado.Cabe señalar aquí que los números reales se calculan en MQL4 con una precisión de 15 cifras. Almismo tiempo, el coeficiente de Fibonacci se muestra con una precisión de 3 cifras. Esto se debe a quela función de alerta () muestra números con una precisión de 4 cifras, sin mostrar los ceros al final de laserie.Es posible que la condición especificada en el operador de ciclo de cabecera siempre sea cierta. Esto setraducirá en un bucle infinito.Un bucle infinito o Looping es una ejecución repetida e infinita del operador de ciclo, es una situacióncrítica que se deriva de la realización de un algoritmo erróneo. Una vez que se cae en la trampa de unbucle infinito, el control no puede salir nunca del él. Esta situación es especialmente peligrosa en elcomercio de Asesores Expertos y scripts. En tales casos, las variables de entorno no se actualizannormalmente, ya que la función especial no completa su funcionamiento, mientras que el operadorpuede desconocer la existencia de este bucle.El único método posible para detectar esos errores algorítmicos está en el examen del código, elrazonamiento lógico y el sentido común.Operador de ciclo “for”Otro operador de ciclo es el operador for.El formato completo del operador de ciclo „for‟ se compone de una cabecera (que contiene laExpression_1, la Condición y la Expression_2), y del cuerpo de bucle ejecutable, colocado entre llaves.for(Expression_1; Condition; Expression_2) // Operador de ciclo de cabecera
  • 29. { // Apertura llaveBloque de operadores // Cuerpo de Ciclo, puede consistir …que componen el cuerpo de ciclo… // … en varios operadores} // Cierre llaveSi el cuerpo de bucle en el operador „for‟ consta de un solo operador, las llaves pueden omitirse.for(Expression_1; Condition; Expression_2) // Cabecera del Operador de cicloOperador, cuerpo de ciclo // el cuerpo de bucle es un solo operadorLa Expression_1, la Condición y/o la Expression_2 pueden estar ausentes. En cualquier caso, el caracterseparador “;” (punto y coma) debe estar presente en el código.for(; Condition; Expression_2) // Sin la Expression_1{ // Apertura de llaveBloque de operadores // Cuerpo de bucle, puede constar ..que componen el cuerpo de bucle // .. de varios operadores} // Cierre de llave// - ----------------------------------------------- ----------------------------------for(Expression_1;; Expression_2) // Sin Condición{ // Apertura de llaveBloque de operadores // Cuerpo de bucle, puede constar ..que componen el cuerpo de bucle // .. de varios operadores} // Cierre de llave// - ----------------------------------------------- ----------------------------------for(;;) // Sin expresiones ni condición{ // Apertura de llaveBloque de operadores // Cuerpo del bucle, puede constar ..que componen el cuerpo de bucle // .. de varios operadores} // Cierre de llaveRegla de Ejecución del operador „for‟Tan pronto como el control pasa al operador „for‟, el programa ejecuta la Expression_1. En tanto que lacondición del operador „for‟ sea verdadera: el control pasa al primer operador en el cuerpo de bucle.Luego de que se ejecutan todos los operadores en el cuerpo de bucle, el programa debe ejecutar laExpression_2 y pasar el control a la cabecera para comprobar si la condición es verdadera. Si lacondición del operador „for‟ es falsa, entonces el control pasa al siguiente operador posterior al operador„for‟.Problema 10. Tenemos una secuencia de números enteros: 1 2 3 4 5 6 7 8 9 10 11... Redactar unprograma que calcule la suma de los elementos de esta secuencia desde N1 hasta N2.Este problema es fácil de resolver en términos de matemáticas. Supongamos que queremos calcular lasuma de elementos desde el tercer hasta el séptimo elementos. La solución será: 3 + 4 + 5 + 6 + 7 = 25.Sin embargo, esta solución sólo es buena para un caso particular, cuando el número del primer y delúltimo elemento que componen la suma es igual a 3 y 7, respectivamente. El programa para la soluciónde este problema debe ser escrito de tal manera que si queremos calcular la suma de otro intervalo de lasecuencia (por ejemplo, desde el 15º al 23º elemento), se puedan sustituir fácilmente los valoresnuméricos de los elementos de una ubicación sin modificar las líneas del programa.//-----------------------------------------------------------------------------// sumtotal.mq4
  • 30. //-----------------------------------------------------------------------------int start() // Special function start(){//-----------------------------------------------------------------------------intNom_1, // Number of the first elementNom_2, // Number of the second elementSum, // Sum of the numbersi; // Formal parameter (counter)//-----------------------------------------------------------------------------Nom_1=3; // Specify numeric valueNom_2=7; // Specify numeric valuefor(i=Nom_1; i<=Nom_2; i++) // Cycle operator header{ // Brace opening the cycle bodySum=Sum + i; // Sum is accumulatedAlert("i=",i," Sum=",Sum); // Display on the screen} // Brace closing the cycle body//------------------------------------------------------------------------------Alert("After exiting the cycle, i=",i," Sum=",Sum); // Display on the screenreturn; // Exit start()}//------------------------------------------------------------------------------Lo que hace este programa, visto en modo sencillo, es lo siguiente: Valor Valor Nuevo valor para Sum = Iteración de + de = Sum + i Sum i 1 0 + 3 = 3 2 3 + 4 = 7 3 7 + 5 = 12 4 12 + 6 = 18 5 18 + 7 = 25La frase clave para recordar la regla de ejecución del operador „for‟ - es la siguiente: "Desde i= ..,siempre y cuando …, incrementado en pasos de…, hacer lo siguiente: ..".El último evento que tendrá lugar en cada iteración en la ejecución del operador for es el cálculo deExpression_2. Esto se traduce en el aumento del valor de la variable i en 1, es decir que al final estevalor será igual a 8. La posterior prueba de la condición (al principio de la próxima iteración) confirmaráque la condición es falsa y el control pasará afuera del operador de ciclo. Al mismo tiempo, la variable isale del operador de ciclo con el valor 8, mientras que el valor de la variable Sum seguirá con el valor25, ya que no volvió a calcularse.Intercambiabilidad entre los operadores while y „for‟A continuación resolvemos el Problema 10 utilizando el operador while:i=Nom_1; // parámetro formal, contador de iteracioneswhile (i<=Nom_2) // Cycle operator header
  • 31. { // Brace opening the cycle bodySum = Sum + i; // Sum is accumulatedAlert("i=",i," Sum=",Sum); // Display on the screeni++; // Increment of the element number} // Brace closing the cycle bodyComo es fácil de ver, es suficiente sólo mover Expression_1 en la línea que precede al operador deciclo, mientras que Expression_2 debe especificarse como el último ciclo en el cuerpo.El Operador “break”En algunos casos, por ejemplo, en algún ciclo de programación de operaciones, podría ser necesarioromper la ejecución de un ciclo antes de que su condición se convierta en falsa. Con el fin de resolvereste problema podemos usar el operador "break".Formato del operador “break”El operador "break"se compone de una sola palabra y termina en el carácter “;” (punto y coma).break; // Operador de "break"Regla de Ejecución del operador "break"El operador "break" detiene la ejecución de la parte más cercana del operador externo de tipo while,for o switch. La ejecución del operador "break" consiste en pasar el control fuera del recinto deloperador de tipo „while’, for o switch al operador siguiente más cercano. El operador "break" solose puede utilizar para la interrupción de la ejecución de los operadores mencionados anteriormente.Problema 11. Tenemos un hilo de 1 metro de largo. Es necesario establecer el hilo en forma de unrectángulo con el área máxima posible. Se trata de hallar el área de este rectángulo y la longitud de loslados con una precisión de 1 mm en la búsqueda de las variantes.Las dimensiones del primer rectángulo (y el "más delgado") serán de de 1 x 499 mm., las del segundoserán de 2 x 498 mm., etc., mientras que las dimensiones del último rectángulo serán de 499 x 1mm.Tenemos que buscar en todos estos rectángulos y encontrar el de mayor superficie.Como fácilmente se observa, hay dimensiones repetidas en el conjunto de rectángulos que estamosconsiderando. Por ejemplo, la primera y la última tienen las mismas dimensiones: 1 x 999 mm (igualque 999 x1 mm). Tenemos que hacer un algoritmo de búsqueda que busque solo en las variantes únicas,ya que no hay necesidad de buscar en las repetidas.//--------------------------------------------------------------------// rectangle.mq4//--------------------------------------------------------------------int start() // Special function start(){//--------------------------------------------------------------------intL=1000, // Specified thread lengthA, // First side of the rectangleB, // Second side of the rectangleS, // Area of the rectanglea,b,s; // Current values//--------------------------------------------------------------------
  • 32. for(a=1; a<L/2; a++) // Cycle operator header{ // Brace opening the cycle bodyb=(L/2) - a; // Current value of the sidess=a * b; // Current value of the areaif (s<=S) // Choose the larger valuebreak; // Exit the cycleA=a; // Save the best valueB=b; // Save the best valueS=s; // Save the best value} // Brace closing the cycle body//--------------------------------------------------------------------Alert("The maximum area = ",S," A=",A," B=",B); // Messagereturn; // Function exiting operator}//--------------------------------------------------------------------El algoritmo de resolución del problema se realiza dentro del ciclo „for‟.El valor inicial del lado a del rectángulo, como se especifica en la Expression_1, es igual a 1.Los valores se buscan siempre y cuando el tamaño del lado “a” del rectángulo siga siendo menor que lamitad de la longitud de hilo.La Expression_2 prescribe cómo aumentar la longitud del lado “a” del rectángulo actual en cada paso deiteración.Las variables a, b y s son variables que registran los cálculos actuales, los valores que se buscan están enlas variables A, B y S. El lado “b” y la superficie “s” del rectángulo son calculados al comienzo delciclo.La condición para salir del ciclo se muestra en el operador if.if (s <= S ) // Choose the larger valuebreak; // Exit the cycleSi la recién calculada área “s” del rectángulo actual resulta ser mayor que el área S calculada en laiteración anterior, entonces este nuevo valor de “s” se convierte en el mejor resultado posible hasta elmomento, y en ese caso, la condición del operador if No se cumple, por lo que el control pasa aloperador más cercano posterior al operador if, que en nuestro ejemplo es el que se encarga de guardarlos mejores resultados obtenidos hasta el momento:A = a; // Save the best valueB = b; // Save the best valueS = s; // Save the best valueCuando el programa llega a la llave de cierre, la iteración está acabada y el control pasa a laExpression_2 de la cabecera del operador „for‟ para ejecutar y poner a prueba la condición.Si la longitud del lado “a” no ha alcanzado el límite determinado, el ciclo seguirá siendo ejecutado.Los repetidos cálculos cíclicos seguirán hasta que uno de los siguientes eventos se lleva a cabo: o bien lalongitud del lado “a” supera los límites predefinidos (de acuerdo a la condición del operador „for‟), obien el tamaño del área “s” se haga inferior a un valor previamente almacenado en la variable “S”.
  • 33. Cuando el área del actual rectángulo “s” sea igual o menor al valor “S” alcanzado previamente, elcontrol en la ejecución del operador "if" pasará al operador "break" que, a su vez, pasará elcontrol fuera del operador „for’, a la siguiente línea:Alert("The maximum area = ",S," A=",A," B=",B); // MessageComo resultado de la ejecución de la función estándar Alert(), se imprimirá la siguiente línea:The maximum area = 62500 A=250 B=250Después de esto, el control pasa al operador «return», lo que da lugar a la finalización de la funciónespecial start(), lo que, a su vez, da lugar a la finalización del programa y a ser desvinculado de laventana de símbolo por la Terminal de Usuario.En este ejemplo, el operador “break” nos permite hacer un algoritmo que realiza sólo los cálculosnecesarios. Un algoritmo sin salida especial sería ineficiente ya que se realizarían cálculos repetidos, loque daría lugar a una pérdida de tiempo y de recursos computacionales.Si se está probando una estrategia en el Tester de Estrategias se sentirá especialmente la reducción deltiempo tomado por los cálculos. La prueba de programas sofisticados en un largo período histórico detiempo puede tomar horas e incluso días. En este caso es muy importante reducir el tiempo empleadopor estas pruebas.Cómo funciona un programa que utiliza ciclos anidadosProblema 12. Utilizando el algoritmo del Problema 11, hallar el hilo más corto que sea múltiplo de unmetro y suficiente para formar un rectángulo con una superficie de 1,5 m².//--------------------------------------------------------------------------// area.mq4//--------------------------------------------------------------------------int start() // Special function start(){//--------------------------------------------------------------------------intL, // Thread lengthS_etalon=1500000, // Predefined area (m²)S, // Area of the rectanglea,b,s; // Current side lengths and area//--------------------------------------------------------------------------while(true) // External cycle for thread lengths{ // Start of the external cycleL=L+1000; // Current thread length//--------------------------------------------------------------------S=0; // Initial value.. // ..for each dimensionfor(a=1; a<L/2; a++) // Cycle operator header{ // .Start of the internal cycleb=(L/2) - a; // Current side lengthss=a * b; // Current areaif (s<=S) // Choose the larger valuebreak; // Exit internal cycleS=s; // Store the best value} // End of the internal cycle
  • 34. //--------------------------------------------------------------------if (S>=S_etalon) // Choose the larger value{Alert("The thread length must be ",L1000," m."); // Messagebreak; // Exit the external cycle}} // End of the external cycle//--------------------------------------------------------------------------return; // Exit function operator}//--------------------------------------------------------------------------Las posibles soluciones son buscadas en dos ciclos, uno interno (for) y uno externo (while).El ciclo externo busca en las longitudes de hilos múltiplos de 1.000 mm., mientras que el ciclo internoencuentra la superficie máxima para la longitud actual del hilo.En este caso el operador break es usado para salir de ambos ciclos, externo e interno.El ciclo interno trabaja aquí como lo hizo en la solución del problema anterior. El operador “break” seutiliza para salir del ciclo „for‟, cuando una determinada longitud de hilo logra la superficie máxima delrectángulo.Explicación del script:Mientras – while(true) - la longitud del hilo sea múltiplo de 1 metro (L=L+1000), realizar lo siguiente:(for) Comenzando con un lado “a” igual a 1, siempre y cuando este lado “a” sea menor o igual a la mitaddel largo del hilo, ir incrementado la longitud del hilo en pasos de 1 mm., y hacer lo siguiente:El lado “b” es igual a la mitad del largo del hilo menos el lado “a”. Como el primer valor asignado a “a”es 1, entonces en la primera iteración, el lado “b” será igual a 499 mm.La superficie actual “s” del rectángulo es igual a “a” por “b”. En la primera iteración será de 499 mm2.(break) Dejar de calcular nuevas iteraciones si el valor “S” de la superficie calculado en la iteraciónanterior supera al valor “s” de la superficie calculado en la iteración actual.Grabar el valor “s” de la superficie determinado en la iteración actual, en la variable “S”.Cuando ya obtuve el valor de superficie “S” máximo para un hilo con una longitud múltiplo de 1 metro,debo chequear si (if) este valor de superficie máximo alcanza el valor de superficie requerido en elenunciado del problema, es decir, 1,5 m2 - if (S>=S_etalon) -. Si lo alcanza, entonces encontré lasolución al problema y la mostraré (Alert) y a continuación salgo del ciclo externo while mediante elcomando break, pero si no lo alcanza, debo utilizar un metro más de hilo y comenzar a realizar todos loscálculos anteriores nuevamente, retornado al inicio del ciclo while.Tener en cuenta que la condición en la cabecera del ciclo exterior while es una condición true, es decir,un texto que no contiene una variable. Es decir que el programa nunca podrá salir del ciclo whilebasándose en la condición dada en la cabecera. En este caso, la única posibilidad para salir del ciclo esusar el operador "break".
  • 35. Operador “continue”A veces, cuando se utiliza un ciclo en el código, es necesario poner fin a la iteración actual y pasar a lasiguiente sin ejecutar el resto de los operadores que componen el cuerpo del bucle. En estos casos sedebe usar el operador "continue".El operador continue consta de una sola palabra y termina en “;” (punto y coma).El operador “continue” detiene la ejecución de la iteración actual del ciclo más cercano del operador„while‟ o for. La ejecución del operador “continue” da como resultado ir a la próxima iteración delciclo más cercano del operador „while‟ o for. El operador “continue” sólo se puede utilizar en el cuerpodel ciclo por encima de los operadores.Problema 13. Hay 1.000 ovejas en la primera granja. La cantidad de ovejas en la primera granjaaumenta en un 1% diario. Si la cantidad de ovejas es superior a 50.000 a finales de mes, entonces el 10%de las ovejas serán transferidas a la segunda granja. ¿En qué momento la cantidad de ovejas en lasegunda granja llegará a 35.000? (consideramos que hay 30 días hábiles en un mes).El algoritmo de la solución de este problema es evidente: Tenemos que organizar un ciclo en el que elprograma calcule la cantidad total de ovejas en la primera granja. De acuerdo con el planteo delproblema, las ovejas se transfieren a la segunda granja a finales de mes. Esto significa que tendremosque crear además un ciclo interno donde se calcule el ciclo de acumulación de ovejas en el mes en curso.Entonces tendremos que comprobar a finales de mes si el límite de 50.000 ovejas se superó o no. Si larespuesta es sí, entonces hay que calcular la cantidad de ovejas a ser transferidos a la segunda granja afinales de mes y la cantidad total de ovejas en la segunda granja.//-------------------------------------------------------------------------------------// sheep.mq4//-------------------------------------------------------------------------------------int start() // Special function start(){//-------------------------------------------------------------------------------------intday, // Current day of the monthMons; // Search amount of monthsdoubleOne_Farm =1000.0, // Sheep on the 1st farmPerc_day =1, // Daily increase, in %One_Farm_max=50000.0, // Sheep limitPerc_exit =10, // Monthly output, in %Purpose =35000.0, // Required amount on farm 2Two_Farm; // Current amount of farm 2//-------------------------------------------------------------------------------------while(Two_Farm < Purpose) // External cycle on history{ // Start of the external cycle body//-------------------------------------------------------------------------------for(day=1; day<=30; day++) // Cycle for days of monthOne_Farm=One_Farm*(1+Perc_day/100); //Accumulation on the 1st farm//-------------------------------------------------------------------------------Mons++; // Count monthsif (One_Farm < One_Farm_max) // If the amount is below limit,.continue; // .. dont transfer the sheep
  • 36. Two_Farm=Two_Farm+One_Farm*Perc_exit/100; //Sheep on the 2nd farmOne_Farm=One_Farm*(1-Perc_exit/100); // Remainder on the 1st farm} // End of the external cycle body//-------------------------------------------------------------------------------------Alert("The aim will be attained within ",Mons," months."); //Display on the screenreturn; // Exit function start()}//-------------------------------------------------------------------------------------Mientras (while) la cantidad de ovejas en la granja 2 sea menor a 35.000, hacer lo siguiente:(for) Comenzando por el día 1, siempre y cuando la cantidad de días sea menor o igual a 30, irincrementado los días, de uno en uno, y hacer lo siguiente:Aumentar la cantidad de ovejas de la granja 1 en un 1% diario One_Farm=One_Farm*(1+Perc_day/100)Cuando la cantidad de días sea igual a 30, se cumple un mes, entonces, ir contando los meses(Mons++;), dicho técnicamente, los valores de la variable Mons se acumulan en cada iteración.Si la cantidad de ovejas en la granja 1 es menor a 50.000, continuar (continue) ejecutando el operadorwhile, es decir, seguir aumentando la cantidad de ovejas de la granja 1 mensualmente en un 1% diariosiempre que no se haya alcanzado el propósito del problema, es decir, que la granja 2 llegue a tener50.000 ovejas.Si la cantidad de ovejas en la granja 1 superó las 50.000 unidades, asignar a la granja 2 el 10% de lasovejas que haya en ese momento en la granja 1. Se lo escribe así:Two_Farm=Two_Farm+One_Farm*Perc_exit/100; //Sheep on the 2nd farmOne_Farm=One_Farm*(1-Perc_exit/100); // Remainder on the 1st farmEntonces, cada vez que la cantidad de ovejas supere las 50.000 unidades en la granja 1, seguiremospasando el 10% de las ovejas hacia la granja 2, hasta que se cumpla el encabezamiento del ciclo while,es decir, hasta que la cantidad de ovejas en la granja 2 alcance las 35.000 unidades, en cuyo momento seemitirá la alerta (alert) indicando el número de meses que fue necesario para conseguirlo.Problema 14. Hay 1000 ovejas en una granja. La cantidad de ovejas en esta primera granja aumentadiariamente en un 1%, cuando la cantidad de ovejas en la primera granja llega a 50.000, el 10% de lasovejas serán transferidos a la segunda granja. ¿En qué momento la cantidad de ovejas de la segundagranja llega a 35 000? (consideramos que hay 30 días hábiles en un mes).//--------------------------------------------------------------------// othersheep.mq4//--------------------------------------------------------------------int start() // Special function start(){//--------------------------------------------------------------------intday, // Current day of the monthMons; // Search amount of monthsdoubleOne_Farm =1000.0, // Sheep on the 1st farmPerc_day =1, // Daily increase, in %One_Farm_max=50000.0, // Sheep limitPerc_exit =10, // One-time output, in %
  • 37. Purpose =35000.0, // Required amount on farm 2Two_Farm; // Current amount of farm 2//--------------------------------------------------------------------while(Two_Farm < Purpose) // Until the aim is attained{ // Start of the external cycle body//--------------------------------------------------------------for(day=1; day<=30 && Two_Farm < Purpose; day++) // Cycle by days{One_Farm=One_Farm*(1+Perc_day/100); //Accumulated on farm 1if (One_Farm < One_Farm_max) // If the amount is below limit,.continue; // .. dont transfer the sheepTwo_Farm=Two_Farm+One_Farm*Perc_exit/100; //Accumulated on farm 2One_Farm=One_Farm*(1-Perc_exit/100); //Remainder on farm 1}//--------------------------------------------------------------if (Two_Farm>=Purpose) // If the aim is attained,..continue;Mons++; // Count months} // End of external cycle body//--------------------------------------------------------------------Alert ("The aim will be attained within ",Mons," months and ",day," days.");return; // Exit function start()}//--------------------------------------------------------------------Una cierta cantidad de ovejas se transfiere a la segunda granja, en este caso, no a finales de mes, sino enel día, cuando la cantidad de ovejas en la primera granja llega al valor predefinido. La razón para usaraquí el operador “continue” es la misma: Queremos saltar las líneas que contienen los cálculosrelacionados con la transferencia de ovejas de una granja a otra, es decir, no queremos ejecutar estaslíneas. Téngase en cuenta que el ciclo while que se construyó para los días del mes no se interrumpe yactúa de forma independiente a si las ovejas son transferidas o no, porque el operador “continue” influyeen el operador de ciclo más cercano, en nuestro caso, el operador de ciclo „for‟.En el operador se utilizó una expresión compleja para la condición del operador de ciclo „for‟:for(day=1; day<=30 && Two_Farm<Purpose; day++) // Cycle by daysEl uso de la operación && ("and") significa que el ciclo se ejecutará siempre y cuando ambascondiciones sean verdaderas: el mes no ha terminado todavía, es decir, la variable «day» oscila entre 1 y 30, y la cantidad de ovejas en la segunda granja no ha alcanzado el valor predefinido -la variable Two_Farm es inferior a Purpose (35000)-.Si al menos una de las condiciones no se cumple, la condición se convierte en falsa y el ciclo se detiene.Si la ejecución del ciclo „for‟ se detiene (por cualquier motivo), el control pasa al operador „if‟:if (Two_Farm >= Purpose) // If the aim is attained,..continue; // .. dont count monthsMons++; // Count monthsEl uso del operador “continue” en esta ubicación en el código tiene un sentido simple: El programa debeseguir contando meses solamente si la cantidad de ovejas en la segunda granja está por debajo del valor
  • 38. esperado. Si el objetivo ya ha sido alcanzado, el valor de la variable Mons no cambiará, mientras que elcontrol (como resultado de la ejecución de "continue") se pasa a la cabecera del operador de ciclo máscercano, en nuestro caso, el del operador de ciclo „while‟.En el ejemplo anterior, cada ciclo (externo e interno) tiene un operador en “continue” que sólo influyeen su "propio" ciclo más cercano (en el ciclo dentro del cual está actuando), pero no por cualquieracontecimiento o en cualquier otro ciclo. En general, en un cuerpo de bucle se pueden utilizar variosoperadores “continue” y cada uno de ellos ser capaz de interrumpir la iteración actual como resultado dela reunión de una condición. La cantidad de operadores “continue” en un cuerpo de un bucle no estálimitada.Operador switchAlgunos programas conllevan la necesidad de la ramificación de su algoritmo en variantes. En estoscasos, es muy conveniente usar el operador "switch", sobre todo si hay decenas o cientos de variaciones,mientras que en este caso el operador “if‟” se convertiría en un código hinchado si utiliza muchosoperadores anidados.El operador switch consiste en una cabecera y un cuerpo ejecutable. La cabecera contiene el nombre deloperador y una Expression entre paréntesis. El cuerpo del operador contiene una o varias alternativas ocasos (case) y una variante «default» (por defecto).Cada variante "case" consiste en la palabra clave case, una constante, ":" (dos puntos), y losoperadores. La cantidad de variantes case no está limitada.La variante por defecto se compone de la palabra default, ":" (dos puntos), y los operadores. Lavariante default se especifica al final en el cuerpo del operador switch, pero también puede ser ubicadaen otro lugar dentro del cuerpo operador, o incluso estar ausente.Los valores de la Expression y de los parámetros sólo pueden ser valores de tipo int. La Expressionpuede ser una constante, una variable, una llamada a una función, o una expresión. Cada variante "case"puede ser un entero constante, un carácter constante, una constante o expresión. Una expresión constanteno puede incluir variables o llamadas a funciones.El programa debe pasar el control al primer operador que sigue a ":" (dos puntos) de la variante "case"cuyo valor la constante es el mismo que el valor de la expresión y, a continuación, ejecutar uno por unotodos los operadores que componen el cuerpo del operador switch. Los valores de las variantesconstantes de los diferentes case no debe ser el mismo. El operador “break” detiene la ejecución deloperador switch y pasa el control al operador que sigue después del operador switch.Problema 15. Redactar un EA con las siguientes condiciones: Si el precio excede del nivel predefinido,el programa deberá informar al comerciante sobre ello mediante un mensaje que describe el exceso(hasta 10 puntos); en los demás casos, el programa debe informar que el precio no excede el nivelpredefinido.//-------------------------------------------------------------------------------------// pricealert.mq4//-------------------------------------------------------------------------------------int start() // Special function start{double Level=1.3200; // Preset price levelint Delta=NormalizeDouble((Bid-Level)Point,0); // Excess
  • 39. if (Delta<=0) // Price doesnt excess the level{Alert("The price is below the level"); // Messagereturn; // Exit start()}//-------------------------------------------------------------------------------------switch(Delta) // Header of the switch{ // Start of the switch bodycase 1 : Alert("Plus one point"); break; // Variations..case 2 : Alert("Plus two points"); break;case 3 : Alert("Plus three points"); break;case 4 : Alert("Plus four points"); break; //Here are presentedcase 5 : Alert("Plus five points"); break; //10 variations case,case 6 : Alert("Plus six points"); break; //but, in general case,case 7 : Alert("Plus seven points"); break; //the amount of variations casecase 8 : Alert("Plus eight points"); break; //is unlimitedcase 9 : Alert("Plus nine points"); break;case 10: Alert("Plus ten points"); break;default: Alert("More than ten points"); // It is not the same as the case} // End of the switch body//-------------------------------------------------------------------------------------return; // Exit start()}//-------------------------------------------------------------------------------------El operador “break” detiene la ejecución del operador switch, y pasa el control fuera de ella, es decir, aloperador «return» que termina la operación de la función especial start(). Notas Digits int Digits Número de dígitos después del punto decimal para los precios del símbolo actual. NormalizeDouble double NormalizeDouble(double value, int digits) Redondea el valor de punto flotante (coma flotante) con la precisión dada. Devuelve un valor normalizado, de tipo double. Los valores calculados de StopLoss y TakeProfit, y el precio de apertura de órdenes pendientes, deben ser normalizados con una precisión dada (cuyo valor es almacenado en la variable predefinida Digits). Parámetros: value: valor de punto flotante (coma flotante). digits: formato de precisión. Número de dígitos después del punto decimal (0-8).Consideremos un problema donde no se prevé el uso de “break” en cada una de las variantes "case".Problema 16. Hay 10 barras. Informe sobre los números de todas las barras a partir de la enésima barra.//------------------------------------------------------------------------------// barnumber.mq4//------------------------------------------------------------------------------int start() // Special function start(){int n = 3; // Preset number (nth bar) (enésima barra)
  • 40. Alert("Bar numbers starting from ", n,":"); // It does not depend on n//------------------------------------------------------------------------------switch (n) // Header of the operator switch{ // Start of the switch bodycase 1 : Alert("Bar 1"); // Variations..case 2 : Alert("Bar 2");case 3 : Alert("Bar 3");case 4 : Alert("Bar 4"); // Here are 10 variations ..case 5 : Alert("Bar 5"); // ..case presented, but, in general, ..case 6 : Alert("Bar 6"); // ..the amount of variations..case 7 : Alert("Bar 7"); // ..case is unlimitedcase 8 : Alert("Bar 8");case 9 : Alert("Bar 9");case 10: Alert("Bar 10");break;default: Alert("Wrong number entered"); // It is not the same as the case} // End of the switch body//-------------------------------------------------------------------------------return; // Operator to exit start()}//-------------------------------------------------------------------------------En el operador switch, el programa buscará en las variantes case, hasta que detecte que la expresión esigual a la constante. Cuando el valor de la expresión (en nuestro caso, el entero 3) es igual a una de lasconstantes (en este caso el case 3), todos los operadores siguientes a los dos puntos “:” (case 3:) seejecutarán, a saber: el operador llama a la función Alert ("Bar 3") y los siguientes Alert ("Bar 4"), Alert("Bar 5"), etc, hasta que llega al operador “break” que termina el funcionamiento del operador “switch”.Si el valor de la expresión no coincide con ninguna de las Constantes, el control se pasa al operador quecorresponde a la variante "default".A diferencia del algoritmo realizado en el anterior programa, en este caso, no estamos utilizando eloperador “break” en cada una de las variantes "case". Por tanto, si el valor de la expresión es igual alvalor de una de las constantes, se ejecutarán todos los operadores siguientes a los operadores de lacorrespondiente variante “case”. También utilizamos el operador “break” en la última variante “case”para otro propósito: evitar que se ejecuten los operadores correspondientes a la variante "default".Si no hay un valor igual a la expresión entre los valores de las constantes, el control se pasa directamenteal operador que se corresponde con la etiqueta default.
  • 41. LA LLAMADA A UNA FUNCIÓNUna llamada a función puede ser usada como un operador independiente y encontrarse en cualquierlugar del programa donde esté implicado un cierto valor (con la excepción de los casos predefinidos). Seutiliza tanto para funciones estándar (built-in) como para funciones definidas por el usuario.Una llamada a una función esta compuesta por el nombre de la función y la lista de los parámetrosescritos entre paréntesis. Si la llamada a la función no lleva parámetros de paso, la lista de parámetros seespecifica como vacía, pero el paréntesis debe estar presente, de todos modos.La cantidad de parámetros a ser pasados a la función está limitada y no puede ser superior a 64. En unallamada a una función se pueden utilizar como parámetros constantes, variables y llamadas a otrasfunciones. La cantidad, tipo y orden de los parámetros pasados en la llamada a una función debeser la misma que la cantidad, tipo y orden de los parámetros especificados en la descripción deuna función (la excepción es una llamada a una función con los parámetros por defecto).Si el programa debe llamar a una función con los parámetros por defecto, la lista de los parámetrostraspasados puede ser abreviada. Puede limitarse la lista de parámetros, siempre que se comience con elprimer parámetro por defecto. En el siguiente ejemplo, las variables locales b, c y d tienen algunosvalores por defecto:// .. the following calls are allowed:My_function (Alf, Bet, Ham, Del) // Allowed function callMy_function (Alf ) // Allowed function callMy_function (3) // Allowed function callMy_function (Alf, 0) // Allowed function callMy_function (3, Tet) // Allowed function callMy_function (17, Bet, 3) // Allowed function callMy_function (17, Bet, 3, 0.5) // Allowed function call// For the function described as:int My_function (int a, bool b=true, int c=1, double d=0.5){Operators}Los parámetros sin valores por defecto no se pueden omitir. Si un parámetro por defecto se salta, losparámetros por defecto subsiguientes no deben ser especificados.// .. Las siguientes llamadas no están permitidas:My_function () // No permitida la llamada por defecto a la función. Falta.. // .. 1º parámetro que no puede ser omitido.My_function (17, Bet,, 0,5) // No permitida la llamada a la función: se omite .. // .. parámetro por defecto (el tercero) // La función esta descrita como:int My_function (int a, b bool = true, int c = 1, double d = 0,5){Operadores}Las llamadas a las funciones se dividen en dos grupos: las que devuelven un valor predefinido de un tipoy los que no devuelven ningún valor.
  • 42. Si la llamada a la función no devuelve ningún valor sólo puede ser compuesta como un operadorindependiente. La llamada a una función con operador sin return termina en “;” (punto y coma).Una llamada a una función que devuelve un valor puede estar compuesta como un operador separado opuede ser utilizada en el código de programa en lugares donde un valor de un determinado tipo estaimplicado. Si la llamada a la función se compone de un operador independiente, este termina en ";"(punto y coma).Problema 17. Redactar un programa con las siguientes condiciones: Si la hora actual es mayor de las 15:00, ejecutar 10 repeticiones en el ciclo „for‟; En todos los demás casos, ejecutar 6 iteraciones.///-----------------------------------------------------------------------------------// callfunction.mq4//------------------------------------------------------------------------------------int start() // Description of function start(){ // Start of the function start() bodyint n; // Variable declarationint T=15; // Predefined timefor(int i=Func_yes_ret(T);i<=10;i++) // The use of the function in.. // The cycle operator header{ // Start of the cycle for bodyn=n+1; // Iterations counterAlert ("Iteration n=",n," i=",i); // Function call operator} // End of the cycle for bodyreturn; // Exit function start()} // End of the function start() body//-------------------------------------------------------------------------------------int Func_yes_ret (int Times_in) // Description of the user-defined function{ // Start of the user-defined function bodydatetime T_cur=TimeCurrent(); // The use of the function in.. // ..the assignment operatorif(TimeHour(T_cur) > Times_in) // The use of the function in.. //..the header of the operator if-elsereturn(1); // Return value 1return(5); // Return value 5} // End of the user-defined function body//-------------------------------------------------------------------------------------Descripción y función del operador “return”Podemos dividir la necesidad de especificación de funciones dentro de un programa, en dos grupos: Lasfunciones que no se describen en el programa, y las funciones que deben estar descriptas en el programa.Las funciones estándar no se describen en los programas. Las funciones definidas por el usuario debensiempre describirse en el programa. Las funciones especiales, si las hubiere, deben también describirseen el programa.Formato de la descripción de funcionesLa descripción de una función consta básicamente de dos partes: cabecera de la función y cuerpo de lafunción.
  • 43. La cabecera de la función contiene el tipo del valor de return, el nombre de la función, y la lista deparámetros entre paréntesis. Si una función no debe devolver ningún valor, su cabecera es de tipo void(vacío).El cuerpo de la función puede consistir en operadores simples y/o compuestos o en llamadas a otrasfunciones, y encerradas entre llaves.La lista de parámetros se escribe separada por comas. El número de parámetros a transferir a la funciónestá limitado a 64. Los parámetros formales de la cabecera de la función solo pueden ser especificadosen forma de variables y no como constantes, ni expresiones, ni en forma de llamada a otras funciones).La cantidad, tipo y orden de los parámetros transferidos en la llamada a la función deben ser los mismosque los de los parámetros formales especificados en la descripción de la función (con excepción de lallamada a una función con parámetros que tienen valores por defecto):La ubicación de la descripción de una función en un programa debe ser tal que esté separada decualquier otra función, es decir, la función no debe estar nunca situada dentro de otra función.El valor devuelto por la función es el valor del parámetro que se especifica entre los paréntesis deloperador «return». El operador «return» se compone de la palabra clave «return» y la expresióncontenida entre paréntesis, y termina con el caracter “;” (punto y coma).La expresión entre paréntesis puede ser una constante, una variable o una llamada a una función. El tipodel valor devuelto utilizando en el operador return debe ser el mismo que el tipo del valor de la funciónque se especifica en función de la cabecera de la llamada a la función.Si el valor de return de una función es de tipo void, el operador "return" se debe usar sin expresión entreparéntesis. Ejemplo:void My_function (double Price_Sell) // Description of the user-defined function{ // Start of the function bodyif(Price_Sell-Ask >100 * Point) // Operator ifAlert("Profit for this order exceeds 100 z"); // Standard function callreturn; // Exit function} // End of the function bodyExiste la posibilidad de no incluir el operador "return" en la descripción de una función. En este caso, lafunción dará por terminado su funcionamiento en forma automática tan pronto como (de acuerdo con elalgoritmo de ejecución) se ejecute el último operador del cuerpo de la función.
  • 44. VARIABLESVariables predefinidas y función RefreshRatesLo primero que debemos hacer es aprendernos los nombres de las variables predefinidas. Los nombresde las variables predefinidas son nombres reservados y no se pueden utilizar para crear variablespersonalizadas. Se trata de las variables predefinidas que contienen las principales informacionesnecesarias para el análisis de la situación actual del mercado. Para actualizar esta información se utilizaRefreshRates().Los valores de todas las variables predefinidas se actualizan automáticamente en la Terminal de Usuarioen el momento que se inician las funciones especiales para su ejecución.Lista de nombres de variables predefinidas simplesAsk - último precio conocido de venta del actual titulo o valor;Bid - último precio conocido de compra del actual titulo o valor;Bars - número de barras en un gráfico actual;Point - Tamaño de punto (pip) del actual titulo o moneda. El valor de Point (pip) depende del título ovalor especificado. Por ejemplo, para EUR/USD, este valor es 0.0001, para USD/JPY es 0,01;Digits - número de dígitos después del punto decimal en el precio del actual del titulo o valor.Lista de nombres predefinidos de Arrays-TimeseriesTime - fecha y hora de apertura de cada barra en el gráfico actual;Open - precio de apertura de cada barra en el gráfico actual;Close - precio de cierre de cada barra en el gráfico actual;High - precio máximo de cada barra en el gráfico actual;Low - precio mínimo de cada barra en el gráfico actual;Volume - marca el volumen de cada barra en el gráfico actual.En una Terminal de Usuario se pueden ejecutar varios programas de aplicación al mismo tiempo(asesores expertos, scripts o indicadores), y para cada uno de ellos la Terminal de Usuario crea unacopia con un juego de los datos históricos de todos los valores de las variables predefinidas.RefreshRates()bool RefreshRates()La función estándar RefreshRates() permite actualizar los valores locales, fuerza la actualización dedatos acerca de un entorno de mercado actual (volumen, horario del servidor de la última cotizaciónTime[0], Bid, Ask, etc.) Esta función puede utilizarse cuando un programa utiliza mucho tiempo enrealizar sus cálculos y por lo tanto tiene necesidades de actualizar los datos.RefreshRates() devuelve TRUE, si en el momento de su ejecución hay información sobre los nuevosdatos históricos en la terminal (es decir, si ha llegado un nuevo tick durante la ejecución del programa).En tal caso, el conjunto de copias locales de las variables predefinidas se actualizará.Problema 18. Contar el número de iteraciones que un operador de ciclo puede realizar entre los ticks(para los cinco ticks más cercanos).//--------------------------------------------------------------------// countiter.mq4//--------------------------------------------------------------------
  • 45. int start() // Special funct. start(){int i, Count; // Declaring variablesfor (i=1; i<=5; i++) // Show for 5 ticks{Count=0; // Clearing counterwhile(RefreshRates()==false) // Until...{ //..a new tick comesCount = Count+1; // Iteration counter}Alert("Tick ",i,", loops ",Count); // After each tick}return; // Exit start()}//--------------------------------------------------------------------Dos variables se utilizan en el programa: “i” para contar el número de ticks y “Count” para contar lasrepeticiones. El ciclo externo for está organizado en función del número de ticks procesados (de 1 a 5).En el ciclo for se inicializa el contador de iteraciones poniéndolo a cero (realizado en el ciclo while), alfinal se muestra un Alert() con el número de ticks y la cantidad de sus iteraciones.Tipos de variablesEl ámbito de una Variable es la ubicación en un programa donde el valor de la variable está disponible.Cada variable tiene su propio ámbito de aplicación. De acuerdo con el alcance hay dos tipos de variablesen MQL4: variables locales y variables globales.La Variable local es una variable declarada dentro de una función. El alcance de las variables locales esel cuerpo de la función, en el que la variable ha sido declarada. La Variable local puede ser inicializadapor una constante o una expresión correspondiente a su tipo.La Variable global es una variable declarada más allá de las funciones. El alcance de las variablesglobales es el programa entero. Una variable global puede ser inicializada sólo por una constante delmismo tipo, pero no puede ser inicializada por una expresión. Las variables globales se inicializan sólouna vez antes de la declaración de la ejecución de funciones especiales.Problema 19. Crear un programa que cuenta los ticks.//--------------------------------------------------------------------// countticks.mq4//--------------------------------------------------------------------int Tick; // Global variable (declarada antes de la descripción de start()//--------------------------------------------------------------------int start() // Special function start(){Tick++; // Tick counterComment("Received: tick Nº ",Tick); // Alert that contains numberreturn; // start() exit operator}//--------------------------------------------------------------------Por tanto, el valor de Tick se incrementará en 1 en cada salida de la función especial start(), es decir, encada tick. La solución de este tipo de problemas sólo es posible con el uso de variables que preserven
  • 46. sus valores después de salir de una función (en estos casos se utiliza una variable global). No es viableutilizar variables locales para este fin: una variable local que se declara en una función, pierde su valoral final de sus operaciones.Se puede ver fácilmente que si iniciamos un Asesor Experto en el cual la variable Tick se abre como unavariable local el programa contendrá un error algorítmico:int start() // Special function start(){int Tick; / Local variableTick++; // Tick counterComment("Received: tick Nº ",Tick); // Alert that contains numberreturn; // start() exit operator}Desde el punto de vista de la sintaxis no hay errores en el código. Este programa puede ser compiladocon éxito y empezar su funcionamiento, pero cada vez que se ejecute el resultado será siempre el mismo:Received: tick Nº 1Variables StaticLa variable static se almacena en una memoria permanente y su valor no se pierde al salir de unafunción. Sin embargo, respecto al ámbito de aplicación, las variables static tienen las limitaciones típicasde las variables locales, el alcance de la variable static es la función dentro de las cual ha sido declarada,a diferencia de las variables globales cuyos valores están disponibles desde cualquier parte delprograma.Las variables static se inicializan solo una vez y solo pueden ser inicializadas por una constante (adiferencia de una simple variable local que se puede inicializar con cualquier expresión). Si no hayinicialización explícita, la variable se inicializa a cero.A continuación se muestra la solución del problema 19 utilizando una variable static://--------------------------------------------------------------------// staticvar.mq4//--------------------------------------------------------------------int start() // Special function start(){static int Tick; // Static local variableTick++; // Tick counterComment("Received: tick No ",Tick); // Alert that contains numberreturn; // start() exit operator}//--------------------------------------------------------------------Variables externasLa variable externa es una variable cuyo valor esta disponible desde la ventana de propiedades de unprograma. Una variable externa se declara fuera de cualquier función y es una variable global, su ámbitode aplicación es todo el programa. Cuando se declara una variable externa, el modificador "extern" debeindicarse antes del tipo de valor:extern int Número // variable externa de tipo integer
  • 47. Las variables externas se especifican en la parte de la cabecera del programa, es decir, antes de cualquierfunción que contenga una llamada a función externa. El uso de variables externas es muy conveniente sise necesita iniciar de vez en cuando un programa con distintos valores de variables.Problema 19. Crear un programa, en el que se apliquen las siguientes condiciones: si el precio alcanzaun cierto nivel, y bajó de ese nivel “n” puntos, este hecho debe ser reportado al trader.Obviamente, este problema implica la necesidad de cambiar la configuración, ya que al día de hoy losprecios difieren de los que fueron ayer, así como mañana vamos a tener precios diferentes. Para ofrecerla opción de cambiar la configuración en el Asesor Experto se utilizan variables externas://--------------------------------------------------------------------// externvar.mq4//--------------------------------------------------------------------extern double Level = 1.2500; // External variableextern int n = 5; // External variablebool Fact_1 = false; // Global variablebool Fact_2 = false; // Global variable//--------------------------------------------------------------------int start() // Special function start(){double Price = Bid; // Local variableif (Fact_2==true) // If there was an Alert..return; //..exitif (NormalizeDouble(Price,Digits) >= NormalizeDouble(Level,Digits))Fact_1 = true; // Event 1 happenedif (Fact_1 == true && NormalizeDouble(Price,Digits)<=NormalizeDouble(Level-n*Point,Digits))My_Alert(); // User-defined function callreturn; // Exit start()}//--------------------------------------------------------------------void My_Alert() // User-defined function{Alert("Conditions implemented"); // AlertFact_2 = true; // Event 2 happenedreturn; // Exit user-defined function}//--------------------------------------------------------------------Los valores de las variables externas están disponibles en la ventana de los parámetros del programa. Lomejor de estas variables es que pueden ser cambiadas en cualquier momento. En la fase de activacióndel programa en una ventana de símbolo o título, o durante la operación del programa.
  • 48. Si un usuario necesita cambiar los valores de las variables externas durante la operación, para poderhacer los cambios, debe estar abierta la ventana de configuración del programa. Hay que recordar quelas propiedades de la barra de herramientas del programa se pueden abrir solamente en el período en queel programa (Asesor Experto o indicador) está a la espera de un nuevo tick, es decir, no se estáejecutando ninguna de las funciones especiales. Durante el periodo de ejecución del programa, la barrade herramientas (toolbar) no se puede abrir. Es por ello que si un programa está escrito así, y su tiempode ejecución es largo (unos segundos o decenas de segundos), un usuario pueden tener dificultadestratando de acceder a la ventana de parámetros.Si la ventana de parámetros está abierta el Asesor Experto no funciona, el control se realiza mediante laTerminal de Usuario y no pasa a un programa para iniciar la función especial.Los valores de las variables externas de un script sólo están disponibles en el momento de conectar elprograma a un gráfico, pero no se pueden cambiar durante la operación.Algorítmicamente, la solución del problema tiene este aspecto: Se identifican dos eventos: el primero esel hecho de llegar a un nivel; el segundo, el hecho de que se muestra una alerta de que se tiene un preciomás bajo que el nivel establecido menos “n” puntos. Estos hechos se reflejan en los valores de lasvariables Fact_1 y Fact_2: si el caso no fuera así, el valor de los correspondientes valores sería igual afalse y en caso contrario sería true. En las líneas:if (NormalizeDouble(Price,Digits) >= NormalizeDouble(Level,Digits))Fact_1 = true; // Event 1 happenedQueda definido el hecho de que ha sucedido el primer evento. La función estándar NormalizeDouble ()permite realizar cálculos con los valores de las variables reales a un conjunto de valores exactos (quecorresponde a la exactitud de los precios del título o valor).Si el precio es igual o superior al nivel indicado, el hecho del primer evento se considerará que secumple y la variable global Fact_1 obtiene el valor true. El programa está construido de manera que unavez que Fact_1 obtiene el valor true, nunca será cambiado al valor false por que no hay un código escritoen el programa que lo haga.En las líneas:if (Fact_1 == true && NormalizeDouble(Price,Digits)<=NormalizeDouble(Level-n*Point,Digits))My_Alert(); // User-defined function call
  • 49. Se define la necesidad de mostrar un aviso. Si el primer hecho se ha completado y se redujo el precio en“n” puntos (menor o igual) del nivel indicado, una alerta será mostrada debido a la llamada a la funcióndefinida por el usuario My_Alert(). En esta función, después de que la descripción del hecho ya se hamostrado, se asigna true a la variable Fact_2, lo cual permite, después de salir de la función definida porel usuario, salir de la función especial start().Después de que la variable Fact_2 obtiene el valor true, el programa (función especial Start() ) dará poracabado su funcionamiento cada vez que éste se ejecute. Por eso, una vez mostrada la alerta no serepetirá este programa durante esa sesión.En este programa el hecho significativo es que los valores de las variables globales puede sermodificadas en cualquier lugar (tanto en la función especial como en las funciones definidas por elusuario) y que se conserven en todo el período de operación del programa, tanto en el períodocomprendido entre ticks como después de cambiar la variable exterior, o después de cambiar un marcotemporal.Variables Globales del Terminal de Usuario (GlobalVariables)La variable global de la Terminal de Usuario es una variable cuyo valor está disponible en todos losprogramas de aplicación que se inicien en la Terminal de Usuario.Hay que tener en cuenta que las Variables Globales de la Terminal de Usuario (VGTU) y las VariablesGlobales (a secas) son diferentes variables con nombres similares. El alcance de las variables globales esel programa donde se declara la variable, mientras que el alcance de las Variables Globales de Terminalde Usuario es en todos los programas puestos en marcha en la Terminal de Usuario.A diferencia de otras variables, las GVTU no sólo pueden ser creadas por cualquier programa, sino quetambién pueden ser eliminadas. El valor de la GVTU se almacena en el disco duro y se guarda despuésde que la Terminal de Usuario se ha cerrado. Una vez declarada una GVTU, se conserva en la Terminalde Usuario durante 4 semanas desde el momento de la última llamada. Si durante este período ningunode los programas ha llamado a esta variable, ésta se eliminará de la Terminal de Usuario. Las GVTUsólo puede ser de de tipo double.Funciones para trabajar con GlobalVariablesFunción GlobalVariableSet ()datetime GlobalVariableSet (string NombreVariableGlobal, double NuevoValorNumérico)Esta función crea un nuevo valor de una VGTU. Si una variable no existe, el sistema crea una nuevaVariable Global de Terminal de Usuario. En el caso de que la ejecución se realice con éxito, la funcióndevuelve la hora del último acceso, en caso contrario devuelve 0. Para obtener una información deerrores debe ser llamada la función GetLastError().Función GlobalVariableGet ()double GlobalVariableGet(string NombreVariableGlobalExistente)La función devuelve el valor de una variable global existente o, en caso de error, devuelve 0. Paraobtener una información de errores, se debe llamar a la función GetLastError().Función GlobalVariableDel ()bool GlobalVariableDel( string NombreVariableGlobalExistente)
  • 50. Esta función elimina una variable global. En caso de supresión con éxito la función devuelve TRUE, delo contrario devuelve FALSE. Para obtener una información de errores, debe ser llamada la funciónGetLastError().Problema 21. Varios Asesores Expertos trabajan en un terminal al mismo tiempo. El depósito es de10.000 $. El importe total de las órdenes de todas las ventanas no debe exceder del 30% del depósito. Sedebe asignar la misma cantidad a cada Asesor Experto. Crear un EA que calcule la suma asignada parael comercio.El cálculo de la cantidad asignada a un EA para el comercio no es difícil. Sin embargo, para larealización de este cálculo es necesario saber el número de Asesores Expertos puestos en marcha almismo tiempo. No hay función en MQL4 que pueda responder a esta pregunta. La única posibilidad decontar el número de programas puestos en marcha es que cada programa lo anuncie por sí mismocambiando el valor de una determinada GV. Entonces todos los programas que necesiten estainformación podrán referirse a esta GV y detectar su estado actual.Cabe señalar aquí que, en general, ningún programa está destinado a resolver ese problema por simismo. Si un Asesor Experto no anuncia su existencia, no será contado. Es por ello que en este caso ladefinición del problema presupone el uso de estos EAs que contienen el código necesario tanto paracambiar el valor de la GV como para leer el valor de esta variable.//--------------------------------------------------------------------// globalvar.mq4//--------------------------------------------------------------------int Experts; // Cantidad de EAsdouble Depo=10000.0, // Cantidad del depósitoPersent=30, // Establecer el porcentajeMoney; // Dinero asignadostring Quantity="GV_Quantity"; // Nombre de la GV//--------------------------------------------------------------------int init() // Special funct. init(){Experts=GlobalVariableGet(Quantity); // Obtener valor actual del nº de Expertos.. //.. si no hay ninguno, devuelve ceroExperts=Experts+1; // Incrementar el número de EAs anteriorGlobalVariableSet(Quantity, Experts); // Nuevo valor de la GVTU "GV_Quantity"Money=Depo*Persent/100/Experts; // El dinero asignado para cada AEsAlert("For EA in window ", Symbol()," allocated ",Money);return; // Salir de init()}//--------------------------------------------------------------------int start() // Special funct. start(){int New_Experts= GlobalVariableGet(Quantity); // Nueva cantidad de EAsif (Experts!=New_Experts) // Si ha cambiado{Experts=New_Experts; // Actualización del Numero de ExpertosMoney=Depo*Persent/100/Experts; // Actualización del dinero asignado AEs //.. dinero asignado AEsAlert("New value for EA ",Symbol(),": ",Money);}
  • 51. /*…Aquí el código del EA principal debe ser indicado.Es usado en esto el valor de la variable DineroAsignado ...*/return; // Exit start()}//--------------------------------------------------------------------int deinit() // Special funct. deinit(){if (Experts ==1) // If one EA..GlobalVariableDel(Quantity); //..delete GVelse // Otherwise..GlobalVariableSet(Quantity, Experts-1); //..diminish by 1Alert("EA detached from window ",Symbol()); // Alert about detachmentreturn; // Exit deinit()}//--------------------------------------------------------------------Esta AE contiene tres funciones especiales. En pocas palabras: todas las funciones especiales soniniciadas por la Terminal de Usuario: La función init() se inicia cuando una AE se vincula a la ventanade un símbolo o valor, la función deinit() se inicia cuando una AE se separa de una ventana de unsímbolo, y la función start() se inicia cada vez que llega un tick. La parte de la cabecera del programacontiene la parte que corresponde a la declaración de variables globales (el alcance de estas variables esel programa entero).La asignación de dinero a cada uno de los AEs depende de un parámetro variable, el número de AEs queestán trabajando simultáneamente. Esa es la razón por la GV que refleja la cantidad de AEs debe serúnica. Su nombre se establece en la línea:string Quantity = "GV_Quantity"; // GV nameVamos a analizar en detalle la forma en que el valor de la variable Quantity se cambia cuando elprograma se ejecuta. En primer lugar, el EA que se vincula a una ventana de un símbolo debe anunciarsu existencia a fin de que los otros EAs que trabajan en el Terminal puedan saber sobre él.Esto debe hacerse lo más pronto posible (lo más cerca posible del momento en que se asocia un EA a laventana de un símbolo). El lugar más adecuado es en la función especial init(). En la primera línea deesta función, el EA pide el valor actual de la variable Quantity con la función GlobalVariableGet () quese usa para este fin:Experts = GlobalVariableGet(Quantity); // Getting current valueAhora el valor de Cantidad GV debe aumentar en 1, no importa la cantidad que tenía en el momento dela adhesión de EA. Esto significa que el EA que se vincula aumenta en un 1 la cantidad de EAs quetrabajan en la terminal al mismo tiempo:Experts = Experts+1; // Amount of EAsLa variable global Experts se utiliza en el programa solo por conveniencia. Su valor no está disponiblepara otros EAs. Para cambiar el valor de GV Quantity, se utiliza la función GlobalVariableSet () queestablece un nuevo valor de la GV:GlobalVariableSet(Quantity, Experts); // New value
  • 52. Esto significa un nuevo valor de Experts se ha asignado a la GV Quantity. Ahora este nuevo valor de laGV está disponible para todos los programas que operan en la terminal. Después de que calcula lacantidad deseada para el trading asignando a cada AE se vincula una alerta. Esta alerta se usa solamentepara avisar cuándo y en qué eventos sucede el AE.Money = Depo*Persent/100/Experts; // Money for EAsAlert("For EA in the window ", Symbol()," allocated ",Money);Ahora dentro de nuestro problema la finalidad del EA es la búsqueda de la cantidad actual de EAs queestán adjuntos. Los Asesores Expertos pueden estar conectados o desconectados; en consecuencia, lacantidad de EAs trabajando simultáneamente puede cambiar. En función de esto nuestro EA deberecalcular la suma asignada a cada EA de conformidad con el problema planteado. Por lo tanto, loprimero que realiza el EA en cada nuevo tick es solicitar el nuevo valor de GV Quantity:int New_Experts= GlobalVariableGet(Quantity); // New amount of EAsY si este nuevo valor New_Experts difiere de los últimos Expertos conocidos, el nuevo valor seconsidera como el actual, el dinero asignado para el trading de una EA se vuelve a calcular y se crea ladescripción correspondiente:if (Experts != New_Experts) // If changed{Experts = New_Experts; // Now currentMoney = Depo*Persent/100/Experts; // New money amountAlert("New value for EA ",Symbol(),": ",Money);}Si las variables New_Experts y Experts son iguales, no se hacen más cálculos en el código del EA. En lafunción start() se utiliza el valor de la variable Money calculada anteriormente. Por lo tanto,dependiendo de la situación en cada tick, si hay que asignar una nueva cantidad de dinero, entonces secalcula su nuevo valor, y si no es así se utilizará el valor del dinero asignado anteriormente.En la etapa de separación de un Asesor Experto de una ventana de un símbolo, éste deberá informar alos otros EA‟s que se ha separado, es decir, que el número de EA‟s que están trabajando al mismotiempo se redujo. Por otra parte, si este EA es el último, la GV debe ser eliminada.La ejecución de la función especial deinit() identifica la separación de un EA, por lo que el códigocorrespondiente debe estar ubicado en esa función:int deinit() // Special funct. deinit(){if (Experts ==1) // If one EA..GlobalVariableDel(Quantity); //..delete GVelse // Otherwise..GlobalVariableSet(Quantity, Experts-1); //..diminish by 1Alert("EA detached from window ",Symbol()); // Alert about detachmentreturn; // Exit deinit()}Es fácil ver que los valores de las variables globales pueden ser leídos o cambiados por cualquier EAque se esté ejecutando. Pero No se permiten cálculos directos con los valores de la GV. Para usar losvalores de GV en una expresión habitual, este valor debe ser asignado a otra variable y utilizar estavariable en los cálculos. En nuestro caso, utilizamos dos variables para este fin: Experts y New_Experts.
  • 53. Hay una opción en la Terminal de Usuario para abrir en la barra de herramientas "Variables globales",donde, en tiempo real se pueden ver todas las GlobalVariables abiertas actualmente y sus valores. Estabarra de herramientas está disponible a través del Terminal de Usuario en el menú Herramientas>>Variables locales (tecla F3).Errores en el uso de GlobalVariablesSi empezamos el EA del problema anterior en las ventanas de diferentes valores, veremos que el códigofunciona correctamente. Sin embargo, esto ocurre solo si las pausas entre los eventos son muy grandes.Prestemos atención al operador „if‟ en deinit():if (Experts ==1) // En caso de que el numero AE sea uno..En este caso, se analiza el valor de la variable global Experts. A pesar de que refleja el valor GV, estepuede ser antiguo (se debe tener en cuenta que todos los programas funcionan en tiempo real). Paracomprender las razones, veamos el siguiente diagrama:Aquí se muestra el desarrollo de eventos relacionados con el valor de Quantity. Veamos cómo cambiaráeste valor dependiendo de lo que está sucediendo. Supongamos que el EA inició la ejecución en elmomento t0. En ese momento Quantity todavía no existe. En el período t0 - t1 se ejecuta la funciónespecial init() del EA y como resultado se crea la GV Quantity, su valor en el momento t1 es igual a 1.El próximo tick del símbolo EUR/USD pone en marcha la función especial start(). Sin embargo, en elperíodo t0 - t6 sólo hay un EA en la Terminal de Usuario y el valor de Quantity no cambia.En el momento t6 se vincula el segundo EA al símbolo del grafico GBP/USD. Como resultado de laejecución de su función especial init() el valor de la variable Quantity cambia y en el momento t7 y sehace igual a 2.
  • 54. Después de que en el momento t8 se vincula al gráfico de USD/CHF un nuevo EA, en el momento t9 lavariable Quantity se hace igual a 3.Pero en el momento t10 el trader decide eliminar el EA de la ventana de EUR/USD. Veamos loscambios que la variable Experts de ese EA de EUR/USD tuvo durante la ejecución de la función start():En este EA de EUR/USD, la función start() se puso en marcha por última vez en el segundo tick, esdecir, en el período t4 - t5.Ahora, en el momento t10 el valor de Experts en el EA de EUR/USD sigue siendo igual a 1. Es por elloque cuando la función deinit() de este EA se ejecuta, la variable GV_Quantity será eliminada.¡Se suprime la Variable Global a pesar de que todavía hay dos AEs vinculados! No es difícil deentender, qué consecuencias tendrá, incluso en los cálculos de los EAs vinculados. Estos erroresalgoritmos no siempre son evidentes y son difíciles de detectar.En este caso, el error puede ser fácilmente corregido. Necesitamos simplemente actualizar el valor deExperts antes de su análisis (antes de la ejecución del operador if):int deinit() // Special funct. deinit(){Experts = GlobalVariableGet(Quantity); // Getting current valueif (Experts ==1) // If one EA..GlobalVariableDel(Quantity); //..delete GVelse // Otherwise..GlobalVariableSet(Quantity, Experts-1); //..diminish by 1Alert("EA detached from window ",Symbol()); // Alert about detachmentreturn; // Exit deinit()}ARRAYSUna gran parte de la información procesada en los programas de aplicación figura en los arrays.El Array (vector o matriz) es un conjunto organizado de valores de variable de un tipo, que tienen unnombre común. Las matrices pueden ser unidimensionales y multidimensionales. La cantidad máximaadmisible de dimensiones en un array es de cuatro. Las matrices pueden tener cualquier tipo de datos.Las partes de un array se llaman “elementos de un array”. Es una variable indexada que tiene el mismonombre y valor.
  • 55. IndexaciónEl índice es un elemento de la matriz que está formado por uno o varios valores (según que el vector seaunidimensional o multidimensional) y están expresados en forma de una constante, variable o unaexpresión y que se enumeran separados por comas, entre corchetes.Los elementos de la matriz con un único índice define el lugar de un elemento en un array. En MQL4se utiliza la indexación a partir de cero, es decir, el primer elemento de la matriz es la matriz coníndice cero.Otra forma de especificar los índices es cada uno entre corchetes independientes:La analogía más cercana y cotidiana de una matriz bidimensional es una sala de cine. El número de filaes el primer valor del índice, el número de columna es el segundo valor del índice, los individuos que sesientan en la butaca son elementos del array, el apellido del espectador es el valor del elemento de lamatriz, la entrada de cine (especificando fila y columna) es un método para acceder al valor delelemento de la matriz.Declaración del Array y acceso a la gama elementosAntes de utilizar un array en un programa, este debe ser declarado. Un array puede ser declarado comouna variable global o local. En consecuencia, los valores de los elementos de un array global estándisponibles para todo el programa, y los valores de un array local sólo están disponibles para la funciónen la que se declaran.Un array No puede declararse en el Nivel de la Terminal de Usuario, es por eso que las VariablesGlobales de Terminal de Usuario (VGTU) No pueden ser recogidas en una matriz.Los valores de los elementos de un array pueden ser de cualquier tipo. Los valores de todos loselementos de un array son todos del mismo tipo, es decir, del tipo indicado en la declaración del array.
  • 56. Cuando se declara un array se debe especificar el tipo de datos, el nombre de la matriz y el número deelementos de cada dimensión:Ejemplo de cómo se asigna un valor a un elemento de array y ejemplo de cómo se asigna el valor de unelemento de array a una variable:Los valores de los elementos de array de la página anterior, son los siguientes: a. Para el array unidimensional, el elemento Mas [4] tiene un valor entero de 34. b. Para la matriz bidimensional, el elemento Mas [3,7] tiene un valor entero 28; c. Para el array de tres dimensiones, el elemento Mas [5,4,1] tiene un valor entero 77.Las Operaciones con arrays también pueden llevarse a cabo utilizando funciones estándar.Inicialización de un ArrayUn array solo se puede inicializar por constantes del tipo correspondiente. Los arrays unidimensionaleso multidimensionales se inicializan con una secuencia de constantes de una dimensión separadas porcomas. La secuencia se escribe entre llaves:int Mas_i[3][4] = { 0, 1, 2, 3, 10, 11, 12, 13, 20, 21, 22, 23 };double Mas_d[2][3] = { 0.1, 0.2, -0.3, -10.2, 1.5, 7.0 };bool Mas_b[5] = { false, true, false, true, true }En la secuencia de inicializado pueden omitirse una o varias constantes. En tal caso, la correspondientegama de elementos de tipo numérico se inicializan a cero, los elementos de arrays de tipo string seinicializan al valor "" (comillas sin espacio), es decir, una cadena de caracteres vacía. No se debeconfundir la cadena de caracteres vacía con el espacio, pues este es un caracter (y por tanto es un valorno vacio). El siguiente programa muestra los valores de arrays, inicializados por una secuencia conomisión de algunos valores.//--------------------------------------------------------------------// arrayalert.mq4//--------------------------------------------------------------------int start() // Special funct. start(){string Mas_s[4] = {"a","b", ,"d"}; // String arrayint Mas_i[6] = { 0,1,2, ,4,5 }; // Integer type array
  • 57. Alert(Mas_s[0],Mas_s[1],Mas_s[2],Mas_s[3]); // DisplayingAlert(Mas_i[0],Mas_i[1],Mas_i[2],Mas_i[3],Mas_i[4],Mas_i[5]);return; // Exit start()}//--------------------------------------------------------------------Si no se ha especificado el tamaño de un array unidimensional inicializado, éste se define por uncompilador basado en la secuencia inicializada. Un array también puede ser inicializado por la funciónestándar ArrayInitialize(). Todos los arrays son estáticos, aun cuando en la inicialización no estéindicado explícitamente. Esto significa que todos los arrays preservan sus valores entre llamadas a lafunción en la cual la matriz ha sido declarada.Las matrices utilizadas en MQL4 pueden dividirse en dos grupos: arrays definidas por el usuario(creadas por iniciativa del programador) y arrays-timeseries (arrays predefinidas con nombres y tipos dedatos). Los valores de los elementos de los arrays definidos por el usuario se conservan durante todo eltiempo de ejecución del programa y pueden ser modificados después de los cálculos. Sin embargo, losvalores de los elementos de los arraystimeseries no se pueden cambiar, su tamaño puede aumentarcuando la historia se actualiza.Arrays Definidos por el usuarioProblema 22 (similar al problema 15). Crear un programa en el que se apliquen las siguientescondiciones: si el precio excede cierto nivel, mostrar un mensaje, en el que se indique el exceso hasta100 puntos; en los demás casos, informar que el precio no es superior a ese nivel.//--------------------------------------------------------------------// stringarray.mq4//--------------------------------------------------------------------extern double Level=1.3200; // Preset levelstring Text[101]; // Array declaration//--------------------------------------------------------------------int init() // Special funct. init(){ // Assigning valuesText[1]="one "; Text[15]="fifteen ";Text[2]="two "; Text[16]="sixteen ";Text[3]="three "; Text[17]="seventeen ";Text[4]="four "; Text[18]="eighteen ";Text[5]="five "; Text[19]="nineteen ";Text[6]="six "; Text[20]="twenty ";Text[7]="seven "; Text[30]="thirty ";Text[8]="eight "; Text[40]="forty ";Text[9]="nine "; Text[50]="fifty ";Text[10]="ten "; Text[60]="sixty";Text[11]="eleven "; Text[70]="seventy ";Text[12]="twelve "; Text[80]="eighty ";Text[13]="thirteen "; Text[90]="ninety";Text[14]="fourteen "; Text[100]= "hundred"; // Calculating valuesfor(int i=20; i<=90; i=i+10) // Cycle for tens{for(int j=1; j<=9; j++) // Cycle for unitsText[i+j]=Text[i] + Text[j]; // Calculating value
  • 58. }return; // Exit init()}//--------------------------------------------------------------------int start() // Special funct. start(){int Delta=NormalizeDouble((Bid-Level)/Point,0); // Excess//--------------------------------------------------------------------if (Delta<=0) // Price is not higher than level{Alert("Price below level"); // Alertreturn; // Exit start()}//--------------------------------------------------------------------if (Delta>100) // Price higher than 100{Alert("More than hundred points"); // Alertreturn; // Exit start()}//--------------------------------------------------------------------Alert("Plus ",Text[Delta],"pt."); // Displayingreturn; // Exit start()}//--------------------------------------------------------------------La matriz se declara a nivel global (fuera de las funciones especiales), la inicialización completa devalores del conjunto se hace en la función especial init(). Así, en la función especial start() sólo se llevana cabo lo cálculos necesarios en cada tick.El significado de estos cálculos puede ser de fácilmente comprendido: para cada elemento array con elíndice de 21 a 99 (excepto los índices múltiplos de 10) es calculado el correspondiente valor string.Arrays TimeseriesUn Array-timeseries es un array con un nombre predefinido (Open, Close, High, Low, Volume orTime), los elementos que contienen los valores corresponden a las características históricas de las barrasdel gráfico correspondiente.Los datos que contienen las matrices timeseries son muy importantes; es una información ampliamenteutilizada en la programación de MQL4. Cada array- timeseries es un array unidimensional y contienedatos históricos sobre una determinada característica de las barras. Cada barra se caracteriza por unprecio de apertura Open[], precio de cierre Close[], el precio máximoHigh[], el precio mínimo Low[], elvolumen Volume[] y la fecha y hora de apertura Time[]. Por ejemplo, la matriz-timeseries Open[] llevainformación sobre la apertura de los precios de todas las barras presentes en una ventana de un símbolo:el valor del elemento del array Open[1] es el precio de apertura de la primera barra, Open[2] es el preciode apertura de la segunda barra, etc. Lo mismo puede decirse de otros timeseries.La barra cero es la barra actual que no se ha formado aún plenamente. En una ventana de un gráfico labarra cero es la última barra que se está formando.La barra actual que aún no se ha terminado de formar completamente siempre será la barra cero, laprimera a la izquierda de ésta será la primera barra, la siguiente, la segunda barra, etc.
  • 59. Problema 23. Encuentre el precio mínimo y máximo entre las últimas “n” barras.//--------------------------------------------------------------------// extremumprice.mq4//--------------------------------------------------------------------extern int GV_CantidadBarras=30; // Cantidad de barras//--------------------------------------------------------------------int start() // Special funct. start(){int i; // numero de barrasdouble MinimoPrecio=Bid, // Precio mínimoMaximum=Bid; // Precio máximofor(i=0;i<=GV_CantidadBarras-1;i++) // Desde cero a..{ // ..GV_CantidadBarras-1if (Low[i]< MinimoPrecio) // If < que ultimo conocidoMinimoPrecio=Low[i]; // entonces este será el precio mínimoif (High[i]> Maximum) // If > que último conocidoMaximum=High[i]; // entonces este será el precio máximo}Alert("For the last ",GV_CantidadBarras, // Mostrar mensaje" bars Min= ",MinimoPrecio," Max= ",Maximum);return; // Salir de start()}//--------------------------------------------------------------------En algunos casos es necesario tener en cuenta los datos desde el momento en que una barra se haformado totalmente. Ejemplo:Problema 24. Al comienzo de cada barra mostrar un mensaje con los precios mínimo y máximo entrelas últimas “n” barras formadas.Aquí debemos detectar la formación de la barra 0, para eso utilizaremos una función definida por elusuario donde se utilizará el horario de apertura de la última barra, el cual es el único dato que sabemoscon anterioridad a su formación. De esta manera cuando haya una divergencia entre el tiempo actual y elde formación de la última barra será porque comenzó a formarse una nueva barra 0.//--------------------------------------------------------------------// newbar.mq4//--------------------------------------------------------------------extern int GV_CantidadBarras=15; // Cantidad de barrasbool GV_Flag_NuevaBarra=false; // Flag de una nueva barra//--------------------------------------------------------------------int start() // Special funct. start(){double MinimoPrecio, // variable que registra el precio minimoMaximoPrecio; // variable que registra el precio minimo//--------------------------------------------------------------------Fun_NuevaBarra(); // Funcion callif (GV_Flag_NuevaBarra==false) // Si no hay nueva barra..return; // ..return//--------------------------------------------------------------------int IndMax =ArrayMaximum(High,GV_CantidadBarras,1); // Indice de la barra del precio maximoint IndMin =ArrayMinimum(Low, GV_CantidadBarras,1); // Indice de la barra del precio minimo
  • 60. MaximoPrecio=High[IndMax]; // Registrar el maximo precioMinimoPrecio=Low[IndMin]; // Registrar el minimo precioAlert("Para las ultimas ",GV_CantidadBarras, // Mostrar mensaje de precios max y min" barras Min= ",MinimoPrecio," Max= ",MaximoPrecio);return; // Salir de start()}//--------------------------------------------------------------------void Fun_NuevaBarra() // Descripción de la Funcion que detecta ..{ // .. una nueva barrastatic datetime NewTime=0; // variable que almacena fecha y horaGV_Flag_NuevaBarra=false; // Inicializa nueva barra a falso (no hay nueva barra)if(NewTime!=Time[0]) // Si existe nueva barra el dato es distinto de cero..{NewTime=Time[0]; //.. y en ese caso se registra el hora y fecha de la..GV_Flag_NuevaBarra=true; //nueva barra y se activa el flag que señaliza la… //existencia de una nueva barra}}//--------------------------------------------------------------------Se utiliza en el programa una variable global llamada GV_Flag_NuevaBarra. Si su valor es true,significa que el último tick es el primer tick de una nueva barra. Si el valor de la variableGV_Flag_NuevaBarra es false, el último tick aparecido está dentro de la formación de la actual barracero.Un Flag (bandera) es una variable, cuyo valor se define de acuerdo con algunos acontecimientos ohechos, es decir, una bandera se activa cuando ocurre algo.Los cálculos en cuanto a la detección de una nueva barra se concentran en la función definida por elusuario Fun_NuevaBarra(). En las primeras líneas de la función se define la variable New_Time(recuérdese que las variables de tipo static no pierden sus valores después de la ejecución de la funciónque está por encima). En cada llamada a la función, el valor de la variable global GV_Flag_NuevaBarrase establece como «false».La detección de una nueva barra se realiza en el operador „if‟:if(NewTime!=Time[0]) // Si existe nueva barra el dato es distinto de cero..{NewTime=Time[0]; //.. y en ese caso se registra el hora y fecha de la..GV_Flag_NuevaBarra=true; //nueva barra y se activa el flag que señaliza la…} //existencia de una nueva barraSi el valor New_Time (calculado en la anterior historia) no es igual a Time [0] de la barra cero, ellodenota el hecho de la formación de una nueva barra. En tal caso el control se pasa al cuerpo del operador„if‟, cuando hay una nueva fecha y hora de apertura en la barra cero, este dato es distinto de lo que hayen NewTime y se actualiza el nuevo dato y también se activa el Flag GV_Flag_NuevaBarra (para mayorcomodidad se puede decir que se levanta la bandera que indica que hay una nueva barra).El cálculo del máximo y mínimo valor se realiza usando funciones estándar ArrayMaximum() yArrayMinimum(). Cada una de las funciones devuelve el índice del elemento del array (elcorrespondiente valor máximo o mínimo) durante el determinado intervalo de índices.
  • 61. Debido a las condiciones que establece el problema sólo se deben analizar las barras que se han formadocompletamente, por eso el límite escogido de los valores del índice está entre 1 y GV_CantidadBarras(la barra cero no se ha formado aún y no se tiene en cuenta en los cálculos).Para obtener información más detallada sobre el funcionamiento de estos y otras funciones de acceso atimeseries, consultar el sitio web http://docs.MQL4.com o "Ayuda" en MetaEditor.
  • 62. Prácticas de programación en MQL4Programación de operaciones de comercioLa función de trading más importante es OrderSend(). Esta es la función que se utiliza para enviar lassolicitudes al servidor para abrir una orden de mercado o colocar una orden pendiente de ser ejecutada.Se puede especificar de inmediato el valor deseado del StopLoss y del TakeProfit. Los valoresincorrectos de estos parámetros, así como de los precios de apertura y el volumen de la orden (númerode acciones, número de lotes o número de contratos de una posición), pueden dar lugar a errores. Paraprocesar adecuadamente estos errores es importante conocer el uso de la Función MarketInfo() quepermite minimizar la cantidad de dichos errores.Las Órdenes de Mercado pueden ser cerradas utilizando la función OrderClose(), mientras que lasórdenes pendientes pueden ser suprimidas con la función OrderDelete(). Al enviar una petición de cerraro eliminar una orden, se debe especificar el ticket de la misma. Vamos a seleccionar la orden usando lafunción OrderSelect(). Por otra parte, si hay dos órdenes contrarias en un símbolo, se pueden cerrar almismo tiempo utilizando la función OrderCloseBy(), ahorrándose así un spread.Los niveles TakeProfit y StopLoss se pueden modificar utilizando la función OrderModify(). A lasórdenes en espera de ser ejecutadas también se les puede cambiar el precio de apertura. pero no elvolumen.Manera común de hacer OperacionesPara crear las órdenes de trading se utilizan las siguientes funciones comerciales: OrderSend() - para abrir órdenes al mercado y órdenes en espera de ser ejecutada; OrderClose() y OrderCloseBy () - para cerrar las órdenes de mercado; OrderDelete() - Para suprimir las órdenes pendientes de ser ejecutadas; OrderModify() - para modificar las ordenes de mercado y las órdenes pendientes de ser ejecutadas.Los conflictos en la toma de Órdenes. Error 146El Terminal de Usuario solo puede atender una única orden de trading a la vez. Vamos a examinar ahoraqué eventos se llevarán a cabo en caso de que se diferentes programas envíen varias solicitudes.En la figura, podemos ver que dos EA‟s se ponen en marcha en forma simultánea. El EA1 formó unapetición de comercio en el momento t1, la cual pasó a la Terminal de Usuario en el momento t2.El EA2 también ha creado una petición, la cual se envía a la Terminal de Usuario cuando ésta estáprocesando la primera solicitud (período comprendido entre el t2 y t3).
  • 63. En esta situación, la Terminal de Usuario no puede considerar la solicitud formada por el EA2, por loque rechaza la solicitud y la devuelve.Hay que tener en cuenta que, en este caso, la petición es rechazada por la Terminal de Usuario no porque la solicitud sea incorrecta, sino porque la terminal está ocupada con el procesamiento de otrasolicitud.El EA2 seguirá en funcionamiento. Se puede analizar el código de error que explica la razón por la cualla solicitud ha sido rechazada (en nuestro caso, es el error 146).La Terminal se libera en el momento t4 (punto verde). A partir de este momento, el EA2 puede pasarcon éxito su petición a la Terminal de Usuario (el grupo de acontecimientos de la zona verde). Estasolicitud recibida es examinada por la Terminal de Usuario que finalmente podrá rechazar la petición(pero esta vez la razón sería un error en la petición), o por el contrario, puede aceptar la petición yenviarla al servidor.Si la solicitud creada por EA1 es considerada correcta por el Terminal, éste la mandará al servidor en elmomento t3. En este caso, el Terminal se pone en modo de espera y no puede considerar ninguna otrasolicitud de comercio. El Terminal de Usuario sólo estará libre para considerar otras solicitudes decomercio en el momento t9. Así, según La variante 2, el Terminal de Usuario no puede analizarsolicitudes de comercio en el plazo de tiempo entre t1 y t9. Si en este plazo, cualquier programa serefiere al Terminal de Usuario con el fin de que se apruebe una solicitud de comercio, el Terminal deUsuario rechazará este evento y pasará el control al programa (grupo de acontecimientos de la zona rosaen el plazo de tiempo que transcurre entre t6 y 7). El programa que ha recibido el control continúa consu operación y, analizando el código de error, puede encontrar información sobre la razón por la cual lasolicitud ha sido rechazada (en este caso, es el error 146).A partir del momento t9, el Terminal de Usuario será completamente libre para el análisis de cualquierotra solicitud de comercio. EA2 puede pasar con éxito la solicitud al Terminal de Usuario en el plazo detiempo que sigue al momento t9.Características de los símbolosTwo-way quote: es un par de precios de mercado conectados de compra.Bid es el más bajo de los dos precios ofrecidos. Es el dinero que ofrecen (oferta de dinero) por lacompra de un título o valor. Es el precio disponible si se quiere vender.Ask es el más alto de los dos precios ofrecidos. Es el dinero que demandan (demanda de dinero) por laventa de un título o valor. Es el precio disponible si se quiere comprar.Point (punto) es la unidad de medición para el precio de un símbolo (el mínimo cambio de precioposible).Spread es la diferencia entre el mayor y el menor precio en puntos en el Two-way quote para lacotización de un valor.Tipos y Características de las ÓrdenesHay seis tipos de órdenes en total: dos tipos de órdenes de mercado y cuatro tipos de órdenes pendientesde ser ejecutadas.Buy es una orden de mercado que define la orden de compra de activos para un símbolo.Sell es una orden de mercado que define la orden de venta de activos para un símbolo.
  • 64. Buy Limit es una orden pendiente de ser ejecutada para comprar activos de un título por un importeinferior al actual. La orden será ejecutada si el precio de demanda (Ask) alcanza o cae por debajo delprecio fijado en la orden de espera.SellLimit es una orden pendiente de ser ejecutada para vender activos de un título a un precio superioral actual. La orden será ejecutada si el precio de oferta (Bid) alcanza o supera el precio fijado en la ordenen espera de ser ejecutada.BuyStop es una orden pendiente de ser ejecutada para comprar los activos de un título a un preciosuperior al actual. La orden será ejecutada si el precio de la demanda (Ask) alcanza o supera el preciofijado en la orden en espera de ser ejecutada.SellStop es una orden pendiente de ser ejecutada para vender los activos de un título por un importeinferior al actual. La orden será ejecutada si el precio de la oferta (Bid) alcanza o cae por debajo delprecio fijado en la orden en espera de ser ejecutada.Lot es el volumen de una orden expresado en cantidad de lotes.StopLoss es una orden de stop. Es el precio fijado por el comerciante, en el que se cerrará una orden demercado si los precios de un titulo se mueven en una dirección tal, que la orden que tenemos en elmercado produce pérdidas.TakeProfit es una orden de stop. Es el precio fijado por el comerciante, en el que se cerrará una ordende mercado si los precios de un titulo se mueven en una dirección tal, que la orden que tenemos en elmercado produce beneficios.Trading requisitos y limitacionesAl calcular los precios correctos, también es necesario tener en cuenta las limitaciones del proveedor deservicios (dealing center). Estas limitaciones incluyen la distancia mínima y la distancia de congelación.Estas limitaciones implican que el corredor necesita un tiempo para los preparativos para la realizaciónde nuevas órdenes, ya sea la conversión de una orden pendiente en una de mercado o de una de cierre auna orden de stop.El Dealing Centers limita el valor de la diferencia mínima admisible entre: el precio de mercado y los requerimientos de precio de cada orden de stop de una orden de mercado, el precio de mercado y el precio solicitado de una orden pendiente de ser ejecutada, el precio solicitado de una orden pendiente de ser ejecutada y el requerimiento de precio de sus órdenes de stop.Esto significa, por ejemplo, que en una orden de comercio, para solicitar la apertura de una orden demercado sólo se puede especificar la orden precio de stop de los valores que no distan del actual preciouna distancia inferior a la mínima. Una petición de trade que contiene un precio de orden de stop cuyadistancia a los precios de mercado está más próxima que la distancia mínima que es considerada por elTerminal de Usuario como incorrecta. Los diferentes dealing centers pueden establecer diferenteslimitaciones específicas para la distancia mínima permitida. Por regla general, el valor de esta distanciavaría entre 1 y 15 puntos. Para los valores más comúnmente utilizados (en EUR/USD, GBP/USD,EUR/CHF, etc), esta distancia viene a ser en la mayoría de los brokers de 3-5 puntos. Diferentes valorespueden tener diferentes distancias mínimas permitidas, también. Por ejemplo, este valor puede ser 50-100 puntos en el oro. El valor de la distancia mínima en cualquier símbolo puede ser cambiado por el
  • 65. corredor en cualquier momento (esto normalmente precede a la difusión comercial de una noticiaimportante). Para la distancia máxima no hay limitaciones.La distancia de congelación limita la posibilidad de modificar los precios de apertura de sus órdenespendientes de ser ejecutadas, así como los requerimientos de los niveles de stop de las órdenes demercado que se encuentran en la zona de congelación. Esto significa, por ejemplo, que si el precio demercado es 1.3800, y la orden pendiente de ser ejecutada esta situada para ser abierta en 1.3807 y laprescripción del broker es de 10, su orden de espera se encuentra en la zona de congelación, es decir, nose puede modificar o borrar. En un mercado en calma, los intermediarios no suelen establecer unadistancia de congelación, es decir, su valor = 0. Sin embargo, durante el período anterior a noticiasimportantes o en alta volatilidad, el corredor podrá fijar un valor determinado de una distancia decongelación. En condiciones diferentes y para diferentes intermediarios, este valor puede variar desde 1a 30 puntos para los símbolos básicos y tener valores más altos para otros símbolos. La empresa decorretaje puede cambiar el valor de la distancia congelación a su propia discreción en cualquiermomento.De la apertura/cierre de órdenes de mercadoLa apertura de una orden de mercado implica la compra o venta de algunos activos de un símbolo alprecio actual de mercado. Para abrir una orden de mercado se utiliza la función OrderSend(); para sucierre se utiliza la función OrderClose().El precio de apertura correcto de una orden de compra a mercado es el último precio de mercadoconocido Ask.El precio de apertura correcto de una orden de venta a mercado es el último precio de mercado conocidoBid.Las órdenes StopLoss y TakeProfit no se pueden situar más cerca del precio de mercado que la distanciamínima, que es de 5 pips. Por lo tanto, se recomienda tener algún "free play" (juego libre), es decir,colocar los stops con cierta holgura de la distancia mínima. Por ejemplo, especificar los valores deórdenes de stop de tal manera que la distancia desde la solicitud de la orden de apertura esté al menos 1ó 2 puntos más lejos que el valor de la distancia mínima permitida.El precio correcto de cierre de una operación de compra es el último precio Bid conocido.El precio correcto de cierre de una operación de venta es el último precio Ask conocido.La orden no puede ser cerrada si el precio de ejecución de su StopLoss o TakeProfit está dentro delrango de distancia de congelación del precio de mercado. En general, una orden de mercado no puedeser cerrada por iniciativa de la Terminal de Usuario si, al menos, un nivel stop (es decir, el SL o el TP)de esta orden se encuentra en la zona de congelación.La banda de congelación de la orden se calcula sobre la base del precio de mercado. Por ejemplo, paracerrar una operación de venta, se tomará en cuenta el precio Ask y se le añadirá la distancia de la bandade congelación. Si el broker decidió una banda de congelación de 4 puntos, entonces el precio de labanda de congelación del borde superior es Ask + 0,0004, mientras que el precio más bajo de la bandade congelación es Ask - 0,0004.Si dos órdenes de mercado son abiertas simultáneamente en un símbolo y una de ellas es de compra yotra es pueden cerrarse de una de dos maneras: se pueden cerrar una a una de forma consecutiva, usandoOrderClose(); o bien se puede cerrar una de ellas contra la otra utilizando OrderCloseBy(). En términos
  • 66. de ahorro de dinero, la segunda manera es preferible porque, cerrando una orden contra otra, se ahorraun spread.Una orden a la espera (o pendiente) de ser ejecutada implica el requerimiento de apertura de una orden aun precio distinto al precio actual de mercado. Para colocar órdenes pendientes de ser ejecutadas seutiliza la función OrderSend(), y para eliminarlas, la función OrderDelete ().Las órdenes pendientes de ser ejecutadas SellLimit (Venta a precio limitado) y BuyStop (Stop decompra) se colocan a un precio que es superior al precio actual de mercado, mientras que BuyLimit(compra a precio limitado) y SellStop (stop de venta) se colocan a un precio que es inferior al precioactual de mercado.Las órdenes en espera de ser ejecutadas BuyLimit, BuyStop, SellLimit y SellStop no se pueden colocar aun precio que esté más cerca del precio de mercado que la distancia mínima.La posición de StopLoss y TakeProfit correspondientes a órdenes pendientes de ser ejecutadas no estánlimitadas por la distancia de congelación.Las órdenes en espera de ser ejecutadas BuyLimit, BuyStop, SellLimit y SellStop no se pueden eliminar,si el precio solicitado de la orden de apertura se encuentra dentro del rango de la distancia decongelación de los precios de mercado.Aclaración: en los gráficos se muestra el precio bid, y las barras o velas reflejan el historial de esteprecio. El historial del precio ask no se muestra.Si hay un salto brusco del precio, o un gap, una orden pendiente se ejecutará (o lo que es lo mismo, setransformará en una orden de mercado) en el primer nuevo precio conocido que supere el precio previstode apertura de la orden pendiente.Para modificar órdenes de cualquier tipo, incluidas las órdenes de mercado, se debería usar la funciónOrderModify ().La Modificación de una orden de mercado implica solo el cambio de valores que solicitó de las órdenesstop. No puede cambiar los precios de apertura de las ordene en mercado.Las Órdenes StopLoss y TakeProfit no pueden situarse más próxima del precio de mercado que a ladistancia mínima establecida por el broker.La orden de ejecución de su StopLoss o TakeProfit no puede modificarse si el precio de dichas ordenesestá dentro de los rangos de la distancia del precio de congelación del mercado.Téngase en cuenta que la posición de las órdenes de stop de una orden de mercado está limitada enrelación con el precio actual del mercado y no con el precio de la orden apertura.Modificación de órdenes pendientes de ser ejecutadasPara modificar cualquier tipo de orden, incluidas las órdenes pendientes de ser ejecutadas, utilizamos lafunción OrderModify().La modificación de una orden pendiente de ser ejecutada implica la posibilidad de cambiar los valoresdefinidos del precio de apertura de la orden en espera de ser ejecutada y sus órdenes stop.Apertura y colocación de órdenes pendientes de ser ejecutadasLas solicitudes de comercio para la apertura y colocación de órdenes en espera de ser ejecutadas seforman utilizando la función OrderSend().
  • 67. Función OrderSend()int OrderSend (string symbol, int cmd, double volume, double price, int slippage, double stoploss, doubletakeprofit, string comment=NULL, int magic=0, datetime expiration=0, color arrow_color=CLR_NONE)OrderSend es el nombre de la función. La función devuelve el número de ticket (el ticket es unnúmero único de orden) que es asignado por el servidor, o el valor -1, si la solicitud es rechazada por elservidor o por la Terminal de Usuario. Con el fin de obtener información sobre los motivos de rechazode la solicitud, se debe usar la función GetLastError().symbol es el nombre del valor o título negociado. Cada símbolo se corresponde con el valor de unavariable string. Por ejemplo, para el par de monedas euro / dólar, este valor es "EURUSD".Sin embargo, si se va a utilizar el Asesor Experto en la ventana de cualquier símbolo, se debe utilizar elsímbolo de la función estándar Simbol(). Esta función devuelve una cadena de caracteres de un valorque se corresponde con el nombre del símbolo de la ventana en la que AE o el script están siendoejecutados.cmd es el tipo de operación. Si por ejemplo vamos a abrir una orden de Compra especificamos elparámetro OP_BUY.volume es la cantidad de lotes. Para órdenes de mercado se debe siempre verificar la cuenta paracomprobar la suficiencia de la misma. Para las órdenes pendientes de ser ejecutadas no está limitada lacantidad de lotes.price es el precio de apertura. Se especifica en función de las necesidades y limitaciones aceptadas parahacer las operaciones.slippage (deslizamiento) es la desviación en puntos máxima permitida entre el precio de apertura de laorden solicitada y el precio de mercado para las órdenes de mercado. O en otras palabras, el slippage sepuede definir como la diferencia entre el precio aprobado por el usuario y el precio al cual la orden esrealmente ejecutada. Por lo general se especifica de 0 a 3 puntos. Este parámetro no es procesado paralas órdenes pendientes de ser ejecutadas.stoploss es el precio de cierre requerido que determina la máxima pérdida permitida para una operacióndeterminada.takeprofit es el precio de cierre requerido que determina la máxima ganancia para una operacióndeterminada.comment es el comentario de texto de la orden. La última parte del comentario puede ser modificadopor el servidor.magic es el MagicNumber de la orden. Se puede utilizar como identificador de la orden definida por elusuario. En algunos casos, es la única información que ayuda a averiguar que una orden pertenece aalguno de los programas abiertos. El parámetro está configurado por el usuario; su valor puede ser elmismo o distinto del valor de este parámetro para otras órdenes.expiration es la fecha en que expira la orden. Tan pronto como llegue este día, la orden en espera de serejecutada se cerrará automáticamente en el servidor. En algunos servidores comerciales, puede haberuna prohibición para establecer la fecha de vencimiento para las órdenes en espera de ser ejecutadas. Eneste caso, si se tratan de establecer un valor del parámetro distinto de cero, la solicitud sería rechazada.arrow_color es el color de la flecha que marca la apertura en el gráfico. Si este parámetro está ausente osi su valor es CLR_NONE, no se muestra la flecha de la apertura.
  • 68. En algunos servidores comerciales, puede haber un límite establecido para el importe total de órdenesabiertas y pendientes. Si se supera este límite, cualquier petición comercial que implica la apertura deuna orden de mercado o en espera será rechazada por el servidor.Ejemplo:Colocaremos una orden de compra.Para el caso del stop loss, vamos a colocar esta orden a una distancia de 15 puntos desde el precio decierre, en este caso el cierre sería una operación de venta y por tanto la venta iría “contra” el precio bid.Entonces: Bid – 15*Point (recordar que Point es el tamaño de 1 punto de la moneda cotizada, por eso 15se debe multiplicar por Point).Para el takeprofit vamos a colocar esta orden a una distancia de 15 puntos: Bid + 15*Point.A continuación se muestra el script más simple destinado a la apertura de una orden de compra: //-------------------------------------------------------------------- // simpleopen.mq4 //-------------------------------------------------------------------- int start() // Special function start() { // Opening BUY OrderSend(Symbol(),OP_BUY,0.1,Ask,3,Bid-15*Point,Bid+15*Point); return; // Exit start() } //--------------------------------------------------------------------Esta orden se podría leer así: “Enviar una Orden al símbolo de la ventana actual consistente en: UnaOperación de Compra, con un volumen de 0,1 lotes, contra el precio Ask, con un slippage máximopermitido de 3 puntos. Colocaremos el stop loss a una distancia de 15 puntos del precio Bid y elTakeProfit también a 15 puntos del precio Bid.ERRORES AL PROCESARError 130 (Órdenes de stops no validas)Una propiedad muy importante de la Terminal de Usuario es que si se produce un error durante laejecución de una solicitud, no se detiene la ejecución del programa, mientras que la Terminal de Usuariogenerará un código de error que está a disposición del programa a través de la función GetLastError().int GetLastError()La función devuelve el código del error que ha ocurrido recientemente, entonces el valor de la variableespecial Last_Error que almacena el código del último error no se pone a cero. Posteriormente, cuandono haya error, GetLastError() devolverá 0.Varios errores pueden ocurrir durante la ejecución de un programa; la función GetLastError() nospermite obtener el valor del código de solo uno de ellos, el del último error. Esta es la razón por la queen el momento en que necesitemos esta información se aconseja utilizar la función GetLastError()inmediatamente después de la línea del programa en la que el error pueda ocurrir.Supongamos que creamos un script para una orden de compra, pero en lugar de poner Symbol()colocamos “GBPUSD” pero ejecutamos el script en el gráfico de EUR/USD, por lo que se producirá unerror que será recogido por la función GetLastError().
  • 69. //--------------------------------------------------------------------------// confined.mq4//--------------------------------------------------------------------------int start() // Special function start { // Opening BUY OrderSend("GBPUSD",OP_BUY,0.1,Ask,3,Bid-15*Point,Bid+15*Point); Alert (GetLastError()); // Error message return; // Exit start() }//--------------------------------------------------------------------------El programa devolverá una ventana donde se mostrará el número del error, en esta caso, 130. El códigode error se puede buscar en los Apéndices, en Códigos de Error.En este caso, se produjo el error 130 (Órdenes de stops no validas). Esto significa que los valoresformales de los parámetros utilizados en la función OrderSend() no se ajusten a las limitacionesespecificadas en las limitaciones en la toma de Órdenes. Tras una vista más cercana, podemos ver larazón por la que se produjo el error: los valores actuales de los precios de mercado de Bid y Ask setoman de la ventana de símbolo en la que se ha asociado el script, es decir, de la ventana de EUR/USD.Sin embargo, estos valores son utilizados para formar una solicitud de comercio de GBP/USD. Comoresultado de ello, en el precio actual de GBP/USD, Ask = 1.9655, el valor de TakeProfit para la recienteorden abierta de mercado resulta ser igual a (para EUR/USD Bid = 1,2930) 1,2930 +15 * 0.0001 =1.2945, que es considerablemente inferior al valor mínimo permitido, es decir, no es válido.Con el fin de corregir el error, se deben utilizar los valores correctos de los precios del símbolo. Sepueden obtener estos valores utilizando la función MarketInfo().El siguiente script abre órdenes de mercado de GBP/USD y puede ser lanzado en cualquier ventana desímbolo://------------------------------------------------------------------------------// improved.mq4//------------------------------------------------------------------------------int start() // Special function start { double bid =MarketInfo("GBPUSD",MODE_BID); // Request for the value of Bid double ask =MarketInfo("GBPUSD",MODE_ASK); // Request for the value of Ask double point =MarketInfo("GBPUSD",MODE_POINT); //Request for Point // Opening BUY OrderSend("GBPUSD",OP_BUY,0.1,ask,3,bid-15*point,bid+15*point); Alert (GetLastError()); // Error message return; // Exit start() }//------------------------------------------------------------------------------Error 129. Precio no válidoEste error se debe a un valor incorrecto en la cotización de los dos sentidos (two-way quote)especificada en el precio de apertura.Las órdenes de Compra de Mercado deben ser abiertas en los precios de Ask. Si, por error, seespecificará el precio de Bid en el script, aparecerá el código de error 129.
  • 70. Error 134. No hay suficiente dinero para hacer un comercioUn resultado similar (error 134) se obtendrá, si no hay suficiente dinero en la cuenta donde se abre laorden.Se puede conocer la cantidad de dinero que se necesita para abrir una compra de 1 lote de cualquiersímbolo utilizando la función MarketInfo (“nombre_del_simbolo”, MODE_MARGINREQUIRED).El tamaño estándar de un lote puede variar para un mismo símbolo para distintos dealing centers.Margen Libre (Free Margin)En la codificación, es muy importante tener en cuenta el principio de la libre formación de activos.Margen libre (de activos) ó Free Margin es la cantidad de dinero que está disponible para la creaciónórdenes.Se pueden hacer cálculos para saber si el capital actual es suficiente para la apertura de una orden. Sepuede utilizar la función AccountFreeMarginCheck () que devuelve el valor del margen libre que quedadespués de la apertura de una orden de mercado de una cierta cantidad de lotes en un determinadosímbolo. Si el valor devuelto es igual o superior a 0, hay suficiente dinero en la cuenta. Si es inferior a 0,entonces la orden de este volumen para este símbolo no se puede abrir y el Terminal de Usuariodevolverá el error 134.Otros errores y Función MarketInfo()Hay otras limitaciones relacionadas con la determinación de valores de los parámetros de la funciónOrderSend(). Estas son el máximo y mínimo paso en el incremento del precio de la orden, máximo ymínimo valor del precio de la orden, etc.El uso de la función MarketInfo() permite obtener información acerca de diversos símbolos queaparecen en la ventana "Observación del Mercado" de la Terminal de Usuario.Función MarketInfo()double MarketInfo(string symbol, int type)symbol: el nombre de un símbolo;type: el tipo de información que será devuelta. Puede ser cualquier valor de los identificadores desolicitud (véase Función MarketInfo Identifier).Pueden ocurrir algunos errores debidos al servidor. Por ejemplo, en condiciones transitorias de precios,su agente puede aumentar la distancia mínima que limita la colocación de órdenes pendientes de serejecutadas y órdenes stops. Después, en un mercado en calma, el corredor puede reducir esta distanciade nuevo. De este modo, los valores de algunos parámetros pueden cambiar en cualquier momento.Para operar con el programa de una forma estable, con la mínima cantidad de solicitudes rechazadas, sedebe actualizar los parámetros del entorno de información utilizados por el programa usando lasfunciones MarketInfo() y RefreshRates() antes de ejecutar la función OrderSend().Ejemplo de un script simple que abre una orden de Compra con un costo del X% de margen libre, conunos valores preestablecidos de órdenes de stop: //------------------------------------------------------------------------------- // openbuy.mq4//-------------------------------------------------------------------------- 1 – int start() // Función Especial start
  • 71. { int Dist_SL =10; // Preselección distancia StopLoss (puntos) int Dist_TP =3; // Preselección distancia Take Profit (puntos) double Prots=0.03; // Porcentaje de margen libre string Symb=Symbol(); // Símbolo //-------------------------------------------------------------------------- 2 -- while(true) // Ciclo para la orden de apertura openbuy { int Min_Dist=MarketInfo(Symb,MODE_STOPLEVEL); // Distancia minima // MODE_STOPLEVEL devuelve la// distancia mínima a la cual puede// colocarse el SL y el TP double Min_Lot=MarketInfo(Symb,MODE_MINLOT); // Volumen mínimo permitido double Step=MarketInfo(Symb,MODE_LOTSTEP); // Paso mínimo de cambio de lotes double Free=AccountFreeMargin(); // Margen Libre double One_Lot=MarketInfo(Symb,MODE_MARGINREQUIRED);// Costo por 1 lote (Garantía por lote) // MODE_MARGINREQUIRED devuelve el margen // requerido para abrir un lote//------------- Cálculo del volumen de la operación ------------------------------ 3 – double Lot=MathFloor(Free*Prots/One_Lot/Step)*Step; // Lotes // MathFloor devuelve el siguiente menor valor sin decimales, del valor indicado en sus // parámetros. Ej- si es 13,5 devolverá 13 y si es -13,5 devolverá -14. if (Lot<Min_Lot) // Si el nº de lotes es menor que el permitido { Alert(" No hay suficiente dinero para ", Min_Lot," lotes"); break; // Salir del ciclo } //----------------- Cálculo del Stop Loss -------------------- 4 -- if (Dist_SL<Min_Dist) // Si la distancia del Stop es menor que la permitida { Dist_SL=Min_Dist; // Poner la distancia permitida Alert(" Incremento de la distancia del SL = ",Dist_SL," puntos."); } double SL=Bid - Dist_SL*Point; // Requerimiento del precio del SL //-------------------Cálculo del Stop Take Profit ------------------ 5 -- if (Dist_TP<Min_Dist) // Si la distancia del Stop es menor que la permitida { Dist_TP=Min_Dist; // Poner la distancia permitida Alert("Incremento de la distancia del TP = ",Dist_TP," puntos."); } double TP=Bid + Dist_TP*Point; // Requerimiento del precio de TP //---------------------Solicitud de Compra ------------------------------ 6 -- Alert("La solicitud fue enviada al servidor. Esperando respuesta..."); int ticket=OrderSend(Symb, OP_BUY, Lot, Ask, 2, SL, TP); //-------------------Notificación ejecución de orden------------------ 7 -- if (ticket>0) // ¡Orden en Mercado! { Alert ("Nº de Orden de Compra Abierta: ",ticket); break; // Salir del ciclo }
  • 72. //-----------------------Procesamiento de Errores------------------- 8 -- int Error=GetLastError(); // ¡Orden Fallida! :( switch (Error) // Procesamiento de errores { case 129:Alert("Precio no válido. Reintentando..."); RefreshRates(); // Actualizando datos del entorno continue; // A la próxima iteración case 135:Alert("El precio ha cambiado. Reintentarlo de nuevo…"); RefreshRates(); // Actualizar datos del entorno continue; // Realizar nueva iteración case 136:Alert("No hay precio. Esperar a un nuevo tick…"); while(RefreshRates()==false) // Mientras no haya un nuevo tick Sleep(1); // Ciclo de espera continue; // Como ya hay nuevo tick realizar nueva iteración case 146:Alert("el subsistema de trading está ocupado, reintentarlo.."); Sleep(500); // Solución simple: dormir durante 500 msg. RefreshRates(); // Actualizar datos del entorno y… continue; // realizar una nueva iteración } switch(Error) // Errores críticos { case 2 : Alert ("Error común."); break; // Salir de switch case 5 : Alert ("Terminal de usuario obsoleta."); break; // Salir de switch case 64: Alert ("The account is blocked."); break; // Salir de switch case 133:Alert ("Trading forbidden"); break; // Salir de switch default: Alert ("Occurred error ",Error); // Otras Alternativas } break; // Salida del ciclo } //-------------------------------------------------------------------------- 9 -- Alert ("The script has completed its operations ------------------------"); return; // Exit start()} //-------------------------------------------------------------------------- 10 --El script se compone de una función especial start() (bloques 1-10).En el bloque 1-2, se fijan los valores a los que la orden debe ser abierta.El bloque 2-9 representa el ciclo while(), en el que se realizan todos los cálculos necesarios. Este cicloestá incluido en el código para permitir que el programa haga varios intentos para abrir una orden.En el bloque 2-3 se actualizan las variables del entorno.En los bloques 3-4-5-6, se calcula el importe de los lotes y los precios de las órdenes de stop. En elbloque de 7-8-9, se procesan los errores. En el bloque 9-10, se escribe un mensaje informando que elscript ha terminado sus operaciones.
  • 73. Vamos a considerar algunas características especiales del código del programa. Es fácil ver que lasolicitud de compra se forma en el bloque 6-7. En el bloque 3-4, se calcula el importe de los lotes.Asimismo, se considera la situación en el que la disposición del margen libre es insuficiente para abririncluso una orden con una cantidad mínima de lotes. Es por ello que, en el bloque 3-4, después deimprimir el mensaje sobre la insuficiencia de dinero, se sale del ciclo 2-9 (while) utilizando el operador"break".El control se pasa al bloque 9-10, y el script completa sus operaciones. El mensaje en la casilla 9 esinnecesario. Se da aquí sólo para informar a los usuarios cuándo es el final de las operaciones delprograma y cuándo la pausa es provocada por los retrasos en la red o en el servidor.Si el margen libre es suficiente para la apertura de la orden, el control pasa al bloque de 4-5 y luego albloque 5-6. En estos bloques, no hay salida ciclo. Esto significa que, para cualquier distancia mínimafijada por el corredor, habrá stops en los niveles correspondientes. La mayoría de los corredoresestablecen la distancia mínima en 5 puntos. En el bloque 5-6, el programa descubrirá que el valorpreestablecido es inferior al permitido. El programa pondrá un valor de precio tal para la orden stop queno estará en contradicción con la limitación.Entonces el control se pasa al bloque de 6-7 para abrir la orden. En la primera línea de este bloque, seimprime un mensaje. La solicitud de comercio está formada solamente en la segunda línea.Tarde o temprano, la Terminal de Usuario pasará el control al programa, en el bloque de 6-7 se ejecutaráel operador de asignación que dará lugar a que la variable ticket tome un valor, el control se pasaráhacia adelante, y si hay un error se analizará en los bloques 7-8-9.Si la orden se abre en el servidor, a la variable «ticket» le será asignado un número que corresponde a laapertura de la orden. Si ocurre esto, significa que el script ha cumplido su tarea y no hay necesidad deque el programa continúe las operaciones. En el bloque 7-8, usamos el operador “break” para salir delciclo while ().El control se pasa al bloque 9-10 (fuera del ciclo), y el programa termina sus operaciones.Sin embargo, si el intento de abrir una orden falla, el control se pasa al bloque 8-9 para el análisis delerror.En este caso se consideran dos tipos de errores: los que aún permiten tener esperanza de tener éxito en laapertura de la orden y aquellos errores cuya aparición significa la finalización inequívoca de la ejecucióndel programa. A la variable Error se le asigna el código del último error, en este caso, es el error que hasido devuelto por el servidor o la Terminal de Usuario en la ejecución de la función OrderSend().En el primer operador switch del bloque de 8-9, se consideran los errores superables. Cada error de estegrupo se procesa de manera diferente. Por ejemplo, si el precio ha cambiado (error 135), es suficienteactualizar solamente los parámetros ambientales utilizando la función RefreshRates() y repetir el intentode apertura de una orden. Si se produce el error 136, "No hay precios", no tiene sentido volver a enviarla solicitud al servidor de comercio. En este caso, debemos esperar a un nuevo tick (debido a que no hayprecios en el servidor en ese momento) y, sólo después de esto, se intenta abrir nuevamente la orden. Poreso hay un ciclo de espera en el bloque que procesa el error 136. Este ciclo de espera se interrumpecuando entra un nuevo tick. La salida del operador de switch() usa el operador continue que rompela actual iteración del ciclo while() y comienza una nueva.
  • 74. Los errores críticos se procesan de otra manera. Si tal error se produce, el programa informa al usuariosobre ello y se pone fin a las operaciones. Para ello, se utiliza el operador “break” (el último, en elbloque 8-9) que rompe el ciclo de while (), lo que da lugar a la finalización del programa.Debemos tener en cuenta, que en este ejemplo en particular, el diseño no considera todos los erroresexistentes. En este caso, no estamos proporcionando al usuario un programa listo para su uso. Es muyimportante que el propio programador analice otros errores de forma independiente y decida qué otroserrores y de qué manera deben ser tratados en el programa. Al mismo tiempo, algunos errores no debenser procesados, porque el programa está construido de tal manera que no considera la existencia dealgunos de ellos, por ejemplo, en este caso, los errores 129 y 130.En el ejemplo anterior, hay un pequeño error algorítmico que no puede ser encontrado en la compilaciónni tampoco en el Terminal de Usuario, ni en el servidor. Por eso, tome con pinzas cualquier código deprograma.Téngase en cuenta el código en el bloque 4-5://----------------- Cálculo del Stop Loss -------------------- 4 -- if (Dist_SL < Min_Dist) // Si la distancia del Stop es menor que la permitida { Dist_SL=Min_Dist; // Poner la distancia permitida Alert(" Incremento de la distancia del SL = ",Dist_SL," pt."); } double SL=Bid - Dist_SL*Point; // Requerimiento del precio del SL //--------------------------------------------------------------- 5 --Como resultado de los cálculos en el cuerpo del operador if (), la variable Dist_SL puede tomar unnuevo valor. Supongamos una distancia normal mínima de 5 puntos. Supongamos que en la primeraejecución (en un mercado rápido), este valor se establece en 20 puntos en el servidor. La variableMin_Dist tendrá el valor de 20. int Min_Dist=MarketInfo(Symb,MODE_STOPLEVEL);// Minimum distanceSupongamos entonces que la operación formada por la solicitud ha sido rechazada debido a un error 136(No hay precios), El programa entonces esperará un nuevo tick en el bloque 8-9. Dentro de este períodode tiempo, el valor de la distancia mínima puede cambiar en el servidor, por ejemplo, disminuir a 10puntos. En el momento en que un nuevo tick llegue, el control pasará al nuevo ciclo, y el nuevo valor dela variable Min_Dist, igual a 10, se calculará. Sin embargo, el valor de la variable Dist_SL sigue siendola misma e igual a 20 (el bloque 4-5 se codifica de tal manera que el valor de Dist_SL sólo puedeaumentar). Con el fin de excluir este error algorítmico, se debe escribir el bloque 4-5 de tal manera quecambie sólo el valor que depende de esa situación (en este caso, es el valor del SL), mientras que elvalor de Dist_SL no cambiará. Podemos escribirlo, por ejemplo, de esta manera://------------------------------------------------------------------------- 4 -- double SL = Bid - Dist_SL*Point; // Requested price of SL if (Dist_SL<Min_Dist) // If it is less than allowed { SL = Bid - Min_Dist*Point; // Requested price of SL Alert(" Increased the distance of SL = ",Min_Dist," pt"); }//------------------------------------------------------------------------- 5 --Un cambio similar debe hacerse en bloque de 5-6 para el TP.
  • 75. Colocación de órdenes en espera de ser ejecutadasNo hay ninguna diferencia fundamental en la programación entre la colocación de órdenes pendientes deser ejecutadas y colocación de órdenes de mercado.Se debería tomar nota del hecho de que ni el Terminal de Usuario ni el servidor verifican si haysuficientes activos como para modificar una orden de espera a una entrada en de mercado. Se puedecolocar una orden pendiente para una cantidad que sobrepase muchas veces la cantidad de dinerodisponible. Esta orden podrá mantenerse durante mucho tiempo, pero cuando el precio de mercadoalcance el nivel del precio solicitado para la apertura de la orden en espera, el servidor hará un controlsobre el precio y si en ese momento hay suficiente dinero en la cuenta para la apertura de esta orden,esta será modificada en una orden de mercado (apertura), y si no, la orden será eliminada.Limitaciones razonablesPor ejemplo, el error 146 se produce solamente si varios programas que forman solicitudes de comerciotrabajan en un mismo símbolo de una ventana. En nuestra opinión, esta práctica es permitida, pero no esaconsejable.Cierre de órdenes de mercadoLas solicitudes de Comercio de órdenes de cierre de mercado se forman utilizando la funciónOrderClose ().Función OrderClose ()bool OrderClose (int ticket, double lots, double price, int slippage, color Color=CLR_NONE)Se trata de una función utilizada para cerrar una orden de mercado. La función devuelve TRUE, si laorden de comercio se ha realizado con éxito. Devuelve FALSE, si no se ha cerrado.ticket - el número único de la orden que se desea cerrar.lots - la cantidad de lotes a ser cerrados. Se permite especificar un valor inferior a la cantidad disponiblede los lotes que se presentan en la orden. En este caso, si la solicitud se ejecuta con éxito, la orden serácerrada parcialmente.Con el fin de decidir sobre qué órdenes y en qué secuencia deben cerrarse, se deben tener datos de todaslas órdenes abiertas en la situación actual. Hay una serie de funciones que pueden ser utilizadas paraobtener diversos datos que caracterizan a cualquier orden.Por ejemplo, la función OrderOpenPrice() devuelve el valor del precio de apertura de la orden (o delprecio solicitado para la espera de las órdenes), la función OrderLots () devuelve la cantidad de lotes, lafunción OrderType() devuelve el tipo de la orden, etc.Función OrderSelect()Con el fin de obtener los parámetros de cualquier orden (ya sean órdenes de mercado o pendientes,cerradas o eliminadas), primero se debe seleccionar la misma utilizando la función OrderSelect().bool OrderSelect(int index, int select, int pool=MODE_TRADES)OrderSelect es una función que selecciona una orden para hacer operaciones con ella. Devuelve TRUE,si la función se ejecuta con éxito. De lo contrario, devuelve FALSE.
  • 76. Parámetrosindex - Para la posición (número de orden en la lista) o el número de ticket, depende del segundoparámetro.select – flag (bandera) de selección de método. El parámetro select puede tomar uno de dos posiblesvalores: SELECT_BY_POS - en el parámetro índex, devuelve el número de orden en la lista (la numeración empieza por 0), SELECT_BY_TICKET - en el parámetro índex, devuelve el número de ticket (el número de orden único).pool - la fuente de datos para la selección. El parámetro «pool» se utiliza, cuando el parámetro select esigual al valor de SELECT_BY_POS. El parámetro pool se ignora, si la orden es seleccionada por elnúmero de ticket (SELECT_BY_TICKET). El parámetro pool puede tener dos valores posibles: MODE_TRADES (por defecto) - la orden se selecciona entre las órdenes abiertas y las órdenes pendientes, es decir, entre las órdenes que aparecen en la pestaña "Operaciones" de la ventana "Terminal". MODE_HISTORY la orden se selecciona entre las órdenes cerradas y eliminadas, es decir, entre las órdenes que aparecen en la en la pestaña "Historial de Cuentas" de la ventana "Terminal". En este caso es importante la profundidad de la historia especificada por el usuario para mostrar órdenes cerradas y eliminadas.Problema 25. Escribir un script que cierre una de las órdenes de mercado disponibles en lacuenta. La ejecución de scripts debe dar como resultado el cierre de la orden más cercana a la ubicaciónde la script vinculada con el ratón a la ventana de símbolo.Supongamos que hay tres órdenes de mercado abiertas en la terminal para el símbolo euro/dólar y unaorden en espera de ser ejecutada para USD/CHF:Debemos escribir un script que se pueda arrastrar con el ratón de la ventana del “Explorador” a laventana de símbolo. La ejecución de dicho script debe dar como resultado el cierre de la orden (marcadapor una línea discontinua y el número de ticket) que se encuentre más cercana al cursor cuando elusuario haya liberado el botón del ratón. En la próxima imagen, se puede ver que el cursor se encuentramás próximo a la orden de venta 4372889. Esta será la orden que deberá ser cerrada como consecuenciade la ejecución del scripts.
  • 77. Para resolver este problema, hay que seleccionar (usando la función OrderSymbol()) una entre todas lasórdenes abiertas en la ventana de símbolo.Entonces tenemos que encontrar los precios de apertura de todas las órdenes de mercado seleccionadas(es decir, ejecutar la función OrderOpenPrice () sucesivamente para cada orden).Conociendo los precios de apertura de las órdenes, podemos fácilmente seleccionar una que correspondaa la declaración del problema.Para especificar los valores apropiados de los parámetros de la función OrderClose (), también senecesitan conocer algunos otros datos acerca de la orden seleccionada: la cantidad de lotes (determinadapor la función OrderLots ()) y el número único de orden (determinado por la función OrderTicket ()).Por otra parte, para encontrar uno u otro precio del two-way quote (cotización de doble vía), tenemosque saber el tipo de orden (determinado por la función OrderType ()).Consideremos qué parámetros deben ser especificados en la función OrderSelect ():En primer lugar, es necesario elegir el método de selección de la orden. Según el enunciado delproblema, tenemos que utilizar el parámetro SELECT_BY_POS, que selecciona la orden más cercana allugar donde se soltó el script en el gráfico.Solo estamos interesados en las órdenes abiertas y en espera, no en las cerradas, por lo que buscaremosen ellas utilizando el parámetro MODE_TRADES en la función OrderSelect ().Para el parámetro pool, utilizaremos el valor por defecto MODE_TRADES, por lo que puede saltarse.for (int i=1; i<=OrdersTotal(); i++) //Ciclo para todas las órdenes... { //… mostradas en el terminal if(OrderSelect(i-1,SELECT_BY_POS)==true) // Si es la próxima orden seleccionada { // Características de la orden... // ... debe ser analizada aquí } } //Fin del cuerpo del cicloEn la cabecera del operador de ciclo, el valor inicial se especifica como i = 1, mientras que la condiciónpara salir del ciclo es la expresión i <= OrdersTotal(). La función OrdersTotal() devuelve la cantidadtotal de órdenes en mercado y órdenes pendientes de ser ejecutadas. Por eso habrá tantas iteraciones enel ciclo, como cantidad de órdenes participantes en el trading.
  • 78. En cada iteración, cuando en el operador `if se calcula la condición, la función OrderSelect (i-1, SELECT_BY_POS) se llevará a cabo. Hay que señalar aquí la siguiente importante cuestión: La numeración de los órdenes en la lista de órdenes de mercado y órdenes en espera de ser ejecutadas comienza con cero. Esto significa que la primera orden de la lista de la penúltima imagen anterior, está localizada en la posición cero, la segunda orden está localizada en la posición 1, etc. Es por eso que en la llamada a la función OrderSelect(), el valor del índice se da como i-1. La función OrderSelect() devuelve true, si la orden se ha seleccionado con éxito. Esto significa que es posible que la selección de una orden pueda fracasar. Esto puede suceder, si la cantidad de órdenes cambia durante su tramitación. El programa analiza en la cabecera del operador `if si la próxima orden en la lista de órdenes está disponible en el momento en que se selecciona. Si la próxima orden está disponible, el control pasa al cuerpo del operador `if para el procesamiento de los parámetros de la orden. En el cuerpo del operador `if, se analizan los parámetros del objeto seleccionado. Al ejecutar las funciones OrderOpenPrice(), OrderTicket(), OrderType() y otras del mismo estilo, cada una de ellas devolverá el valor de una cierta característica de la orden seleccionada como resultado de la ejecución de la función OrderSelect(). Solución al problema 25://-----------------------------------------------------------------------------------// closeorder.mq4//------------------------------------------------------------------------------ 1 -- int start() // Función especial start { string Symb=Symbol(); // Variable de cadena que contiene el símbolo double Dist=1000000.0; // Preselección de la distancia inicial int Real_Order=-1; // Todavía no hay órdenes de mercado double Win_Price=WindowPriceOnDropped(); // WindowPriceOnDropped() devuelve el precio en el // cual se ha soltado el script en el gráfico, utilizando el // mouse //----------------------------------------------------------------------------- 2 -- for(int i=1; i<=OrdersTotal(); i++) // Ciclo de búsqueda de ordenes { if (OrderSelect(i-1,SELECT_BY_POS)==true) // Si la próxima orden está disponible { // Análisis de la orden: //----------------------------------------------------------------------- 3 -- if (OrderSymbol()!=Symb) continue; // Si no es nuestro símbolo int Tip=OrderType(); // Tipo de orden (*) if (Tip>1) continue; // Si la orden es pendiente interrumpe la iteración //----------------------------------------------------------------------- 4 -- double Price=OrderOpenPrice(); // Precio de apertura de la orden if (NormalizeDouble(MathAbs(Price-Win_Price),Digits)< // Selección de la orden más cercana NormalizeDouble(Dist,Digits)) // Nota: Digits devuelve el número de // dígitos después del punto decimal // para el símbolo actual { Dist=MathAbs(Price-Win_Price); // Nuevo valor de la distancia (la más corta) Real_Order=Tip; // orden a mercado disponible
  • 79. int Ticket=OrderTicket(); // Nº de ticket de la orden seleccionada double Lot=OrderLots(); // Cantidad de lotes de la orden a cerrar } //--------------------------------------------------------------------- 5 -- } //Final del análisis de la orden } //Final de la búsqueda de la orden//----------------------------------------------------------------------------- 6 -- while(true) // Ciclo para el cierre de la orden { if (Real_Order==-1) // Si no hay órdenes a mercado disponibles… { Alert("For ",Symb," no hay órdenes de Mercado disponibles"); break; // … salida del ciclo de cierre “while” } //------------------------------------------------------------------------ 7 -- switch(Real_Order) // Selección por el tipo de orden { case 0: double Price_Cls=Bid; // Orden de compra string Text="Compra "; // Texto para compra break; // Salir de switch case 1: Price_Cls=Ask; // Orden de venta Text="Sell "; // Texto para venta } Alert("Intentando el cierre ",Text," ",Ticket,". Esperando respuesta..."); bool Ans=OrderClose(Ticket,Lot,Price_Cls,2); // Orden de cierre //------------------------------------------------------------------------ 8 -- if (Ans==true) // ¡Orden ejecutada! :) { Alert ("Orden cerrada ",Text," ",Ticket); break; // Salida del ciclo de cierre } //------------------------------------------------------------------------ 9 -- int Error=GetLastError(); // Fallo :( switch(Error) // Errores superables { case 135:Alert("El precio ha sido cambiado. Reintentando..."); RefreshRates(); // Actualización de datos continue; // Saltar a la próxima iteración de ciclo de búsqueda case 136:Alert("No hay precio. Esperando un nuevo tick..."); while(RefreshRates()==false) // Mientras no haya Nuevo tick… Sleep(1); // … dormir (pausa 1 msg.) continue; // Con nuevo tick saltar a la próxima… // … iteración del ciclo de búsqueda case 146:Alert("Sistema de Trading ocupado. Reintentando..."); Sleep(500); // Solución simple. Pausa 0,5 sg. RefreshRates(); // Actualización datos de entorno continue; // A la próxima iteración del ciclo de búsqueda } switch(Error) // Errores críticos { case 2 : Alert("Common error.");
  • 80. break; // Salir de switch case 5 : Alert("Versión antigua del terminal de usuario."); break; // Salir de switch case 64: Alert("La cuenta está bloqueada."); break; // Salir de switch case 133:Alert("Trading no permitido"); break; // Salir de switch default: Alert("Ha habido un error ",Error);//Otras alternativas } break; // Salir del ciclo de cierre }//---------------------------------------------------------------------------- 10 -- Alert ("El script ha finalizado las operaciones -----------------------------"); return; // Salida de start() }//---------------------------------------------------------------------------- 11 -- Todo el código se concentra en la función especial start(). En el bloque 1-2, se inicializan algunas variables. La variable Dist es la distancia desde el lugar en donde se ha “soltado” el script hasta la orden más cercana. La variable Real_Order es una bandera que muestra la disponibilidad de al menos una orden de mercado en la Terminal de Usuario (valor no negativo). La variable Win_Price es el precio en el cual el usuario ha soltado el script en la ventana del símbolo. Luego es necesario encontrar la orden (con sus características) que esta más cerca de esta ubicación. Las órdenes se buscan dentro del ciclo `for (bloque 2-6). En el bloque 2-3, el programa comprueba si hay una orden en la línea siguiente de la "Terminal". Si se encuentra una orden, el control se pasa al cuerpo del operador `if para obtener y analizar las características de esa orden. En el bloque 3-4, las órdenes abiertas en el símbolo equivocado (no el símbolo, para el cual el programa está siendo ejecutando) son filtradas fuera. En nuestro caso, es la orden 4372930 abierta para el USD/CHF. La Función OrderSymbol () devuelve el nombre del símbolo de la orden seleccionada. Si este nombre del símbolo es distinto, para el que el programa se está ejecutando, la iteración actual se interrumpe (continue), para evitar que la orden sea ejecutada para otro símbolo distinto desde el que está siendo procesado. Nota: el operador continue se utiliza poner fin a la iteración actual y pasar a la siguiente sin ejecutar el resto de los operadores que componen el cuerpo del bucle. Si la orden bajo análisis correspondía a "nuestro" símbolo, un nuevo chequeo se llevará a cabo. El tipo de orden se determina utilizando la función OrderType (). Si el tipo de orden es mayor que 1, significa que es una orden pendiente. En este caso, la actual iteración también se interrumpe (continue), porque no estamos interesados en las órdenes en espera. Todas las órdenes que pasan el bloque de 3-4 con éxito son órdenes de mercado. (*) OrderType() Constante Valor Transacción comercial OP_BUY 0 Compra OP_SELL 1 Venta
  • 81. OP_BUYLIMIT 2 Orden Buy Limit OP_SELLLIMIT 3 Orden Sell Limit OP_BUYSTOP 4 Orden Buy Stop OP_SELLSTOP 5 Orden Sell StopEl bloque 4-5 se destina para la selección de una única orden de mercado de entre todas las órdenes quehan pasado con éxito el bloque anterior. Esta orden debe ser la más cercana al precio predefinido (elvalor de la variable Win_Price). El usuario no está obligado a "localizar" con su ratón la línea de laorden de forma exacta.La orden seleccionada será la que, de entre las demás órdenes, esté más cerca del cursor desde delmomento de poner en marcha el script para su ejecución. El precio de apertura de la orden procesada seencuentra utilizando la función OrderOpenPrice (). Si el valor absoluto de la distancia entre el precio dela orden actual y el "cursor de precios" es inferior a la misma distancia de la orden anterior, la ordenactual será seleccionada.Es necesario usar el valor absoluto de la distancia para que no importe si la posición del cursor está pordebajo o por encima de la línea indicadora de la orden. De este modo solo se considera la distancia y nosu signo. La orden seleccionada será memorizada en la iteración actual del ciclo `for como la pionerapara ser cerrada. Para este fin, al final del bloque 4-5, se calcula el número de ticket (el númeroindividual de la orden) y la cantidad de lotes. En el ejemplo, la cantidad total de los órdenes es cuatro(tres de mercado y una orden en espera), por lo que habrá cuatro repeticiones ejecutadas en el ciclo `for.A continuación, el control en la ejecución del programa se pasa al operador de ciclo `while (bloque 6-10). En el bloque 6-7, las órdenes de mercado encuentran un control de disponibilidad. Si no seencuentran órdenes de mercado en el bloque 2-4 (es muy posible en general), el valor de la banderaReal_Order sigue siendo igual a -1, lo que significará falta de órdenes de mercado. Si el control en elbloque 6-7 no detecta órdenes de mercado, la ejecución del ciclo `while se rompe y entonces elprograma pone fin a sus operaciones. Si el valor de la variable Real_Order resulta ser igual a 0 o 1, estosignifica que un mercado está predefinido para el cierre y debe cerrarse.En el bloque de 7-8, de acuerdo al tipo de orden, se calcula el precio de cierre de la orden. Es el valor delBid para las órdenes de compra, y el valor de Ask para las órdenes de venta.En el bloque de 7-8, se calculan los valores de la variable auxiliar Texto. La solicitud de la orden decierre de comercio se forma en la función OrderClose () en la línea siguiente:bool Ans=OrderClose(Ticket,Lot,Price_Cls,2); // Orden de cierreLa función de comercio OrderClose () devuelve true, si la transacción se realiza con éxito, y falso, si noes así.Si la solicitud es ejecutada exitosamente en el servidor, el valor «true» se asignará a la variable Ans(respuesta). En este caso, al ejecutar bloque 8-9, el programa informará al usuario sobre el éxito de laorden de cierre. Después de eso, la ejecución del operador de ciclo while se detendrá, y el programapondrá fin a sus operaciones. De lo contrario, el control pasa al bloque de 9-10 con el fin de analizar loserrores devueltos por el Terminal de Usuario al programa.
  • 82. Eliminar órdenes en espera de ser ejecutadasLas solicitudes de comercio para eliminación de órdenes pendientes se forman utilizando la funciónOrderDelete ().Función OrderDeletebool OrderDelete(int ticket, color arrow_color=CLR_NONE)Es fácil ver que la función OrderDelete() no contiene una especificación del volumen y el precio decierre de la orden a ser eliminada.La supresión parcial de una orden no es posible. Puede disminuir la cantidad de lotes de una orden a laespera en dos etapas: suprimir la orden existente y, a continuación, colocar una nueva orden pendientecon la cantidad de lotes disminuida.Un ejemplo de un script simple destinado a la supresión de una orden pendiente. El precio de la solicitudes el que está más cerca de la ubicación de donde se soltó el script. //------------------------------------------------------------------------------------- // deleteorder.mq4//-------------------------------------------------------------------------------- 1 -- int start() // Funcion especial start { string Symb=Symbol(); // Simbolo double Dist=1000000.0; // Preselección distancia inicial int Limit_Stop=-1; // Todavía no hay órdenes pendientes double Win_Price=WindowPriceOnDropped(); // El script se ha soltado a este precio //-------------------------------------------------------------------------------- 2 -- for(int i=1; i<=OrdersTotal(); i++) // Ciclo de búsqueda de órdenes { if (OrderSelect(i-1,SELECT_BY_POS)==true) // Si la próxima órden esta disponible { // Analisis de la orden disponible: //----------------------------------------------------------------------- 3 -- if (OrderSymbol()!= Symb) continue; // El símbolo no es el nuestro int Tip=OrderType(); // Tipo de orden if (Tip<2) continue; // (*)Si no es una orden pendiente, nueva … // iteración del ciclo de búsqueda de órdenes //----------------------------------------------------------------------- 4 -- double Price=OrderOpenPrice(); // Precio orden de apertura de orden seleccion. if (NormalizeDouble(MathAbs(Price-Win_Price),Digits)< //Selección de la orden si … NormalizeDouble(Dist,Digits)) // la distancia menor que la orden anterior { Dist=MathAbs(Price-Win_Price); // Nuevo valor de la distancia mínima Limit_Stop=Tip; // Orden pendiente disponible int Ticket=OrderTicket(); // Nº de ticket de la orden } // Fin de if } // Fin del analisis de órdenes } // Fin de la búsqueda de órdenes //-------------------------------------------------------------------------------- 5 -- switch(Limit_Stop) // Por tipo de orden (*) { case 2: string Text= "BuyLimit "; // Texto para BuyLimit
  • 83. break; // Salir de switch case 3: Text= "SellLimit "; // Texto para SellLimit break; // Salir de switch case 4: Text= "BuyStopt "; // Texto para BuyStopt break; // Salir de switch case 5: Text= "SellStop "; // Texto para SellStop break; // Salir de switch }//-------------------------------------------------------------------------------- 6 -- while(true) // Ciclo para orden de cierre { if (Limit_Stop==-1) // Si no hay órdenes pendientes disponibles { Alert("For ",Symb," No hay órdenes pendientes disponibles"); break; // Salida del ciclo de cierre } //-------------------------------------------------------------------------- 7 -- Alert("Intentando suprimir la orden ",Text," ",Ticket,". Esperando respuesta..."); bool Ans=OrderDelete(Ticket); // Supresión de la orden //-------------------------------------------------------------------------- 8 -- if (Ans==true) // ¡Conseguido! :) { Alert ("Orden suprimida: ",Text, “Ticket: " ,Ticket); break; // Salida del ciclo de cierre } //-------------------------------------------------------------------------- 9 -- int Error=GetLastError(); // Fallo :( switch(Error) // Errores superables { case 4: Alert("El servidor de trades está ocupado. Reintentar..."); Sleep(3000); // Solución simple continue; // A la próxima iteración case 137:Alert("El broker está ocupado. Reintentar..."); Sleep(3000); // Solución simple continue; // A la próxima iteración case 146:Alert("El subsistema de Trading está ocupado. Reintentando..."); Sleep(500); // Solución simple continue; // A la próxima iteración } switch(Error) // Critical errors { case 2 : Alert("Error común."); break; // Salida switch case 64: Alert(“Cuenta bloqueda."); break; // Salida switch case 133:Alert("Trading está prohíbido"); break; // Salida switch case 139:Alert("La orden está bloqueada y está siendo procesada"); break; // Salida switch case 145:Alert("La modificicación está prohíbida. ", "La orden está demasiado cerca del mercado");
  • 84. break; // Salida switch default: Alert("Ha ocurrido el error ",Error); // Otros errores } break; // Salida del ciclo de cierre }//------------------------------------------------------------------------------- 10 -- Alert ("El stript ha finalizado las operaciones -----------------------------"); return; // salida de start() }//------------------------------------------------------------------------------- 11 --El error de procesamiento de bloque se ha modificado ligeramente. Cuando se cierran órdenespendientes no existe la posibilidad de errores relacionados con cambios en los precios (errores 135 y136). Por la misma razón, la función RefreshRates() no se utiliza en ninguna parte del programa.Cierre de órdenes opuestasSi se tienen dos órdenes enfrentadas en un determinado símbolo, se pueden cerrar al mismo tiempo unay otra, utilizando la función OrderCloseBy (). Si se realiza una operación de ese tipo se ahorra el spreadde una de las dos órdenes.Este tema es tratado en el libro, pero no lo resumí por carecer de interés práctico para mis objetivos.Modificación de órdenes - Función OrderModify()Modificación de Órdenes de MercadoLas solicitudes de operaciones para modificación de órdenes pendientes y stops de órdenes de mercadose forman utilizando la función OrderModify().bool OrderModify(int ticket, double price, double stoploss, double takeprofit, datetime expiration, colorarrow_color=CLR_NONE)La función OrderModify () amplía considerablemente la capacidad de modificación: Los precios deambas órdenes de stops puede ser modificadas en la dirección del precio de mercado o suprimirse. Unalimitación de la modificación de órdenes de mercado es la distancia mínima permitida entre la orden destop y el precio de mercado fijado por el dealing center. Si el programa intenta cambiar la posición deuna orden de stop de tal forma que se coloque más cerca del precio de mercado que la distancia mínimapermitida, esa solicitud de comercio será rechazada por la Terminal de Usuario y la ejecución de lafunción OrderModify () fallará (error 130). Esta es la razón por la que se debe proporcionar un bloqueespecial en el programa, que tendrá en cuenta esta limitación.Ejemplo de un Asesor Experto sencillo que modifica el StopLoss de todas las órdenes de mercado, paralas cuales la distancia entre el precio solicitado de StopLoss y el precio del mercado es más grande quela orden preestablecida.//------------------------------------------------------------------------------------// modifystoploss.mq4//------------------------------------------------------------------------------------ extern int Tral_Stop=10; // Trailing distance //------------------------------------------------------------------------------- 1 -- int start() // Special function start {
  • 85. string Symb=Symbol(); // Symbol //------------------------------------------------------------------------------- 2 -- for(int i=1; i<=OrdersTotal(); i++) // Cycle searching in orders { if (OrderSelect(i-1,SELECT_BY_POS)==true) // If the next is available { // Analysis of orders: int Tip=OrderType(); // Order type if(OrderSymbol()!=Symb||Tip>1)continue; //The order is not "ours". Recordar que ||significa “O” double SL=OrderStopLoss(); // Returns stop loss value for the currentlyselected order //---------------------------------------------------------------------- 3 -- while(true) // Modification cycle { double TS=Tral_Stop; // Initial value. (Nota: Al parecer Tral_Stop es el valor del Trailing Stop de laorden actual) int Min_Dist=MarketInfo(Symb,MODE_STOPLEVEL); // The minimal distance of stop levels is obtained if (TS < Min_Dist) // If less than allowed TS=Min_Dist; // New value of TS //------------------------------------------------------------------- 4 -- bool Modify=false; // Not to be modified switch(Tip) // By order type { case 0 : // Order Buy if (NormalizeDouble(SL,Digits)< // If it is lower than we want NormalizeDouble(Bid-TS*Point,Digits)) { SL=Bid-TS*Point; // then modify it string Text="Buy "; // Text for Buy Modify=true; // To be modified } break; // Exit switch case 1 : // Order Sell if (NormalizeDouble(SL,Digits)> // If it is higher than we want NormalizeDouble(Ask+TS*Point,Digits) || NormalizeDouble(SL,Digits)==0) //or equal to zero { SL=Ask+TS*Point; // then modify it Text="Sell "; // Text for Sell Modify=true; // To be modified } } // End of switch if (Modify==false) // If it is not modified break; // Exit while //------------------------------------------------------------------- 5 -- double TP =OrderTakeProfit(); // TP of the selected order double Price =OrderOpenPrice(); // Price of the selected order int Ticket=OrderTicket(); // Ticket of the selected order Alert ("Modification ",Text,Ticket,". Awaiting response.."); bool Answer=OrderModify(Ticket,Price,SL,TP,0); //Modify it! //------------------------------------------------------------------- 6 --
  • 86. if (Answer==true) // Got it! :) { Alert ("Order ",Text,Ticket," is modified:)"); break; // From modification cycle. } //------------------------------------------------------------------- 7 -- int Error=GetLastError(); // Failed :( switch(Error) // Overcomable errors { case 130:Alert("Wrong stops. Retrying."); RefreshRates(); // Update data continue; // At the next iteration case 136:Alert("No prices. Waiting for a new tick.."); while(RefreshRates()==false) // To the new tick Sleep(1); // Cycle delay continue; // At the next iteration case 146:Alert("Trading subsystem is busy. Retrying "); Sleep(500); // Simple solution RefreshRates(); // Update data continue; // At the next iteration // Critical errors case 2 : Alert("Common error."); break; // Exit switch case 5 : Alert("Old version of the client terminal."); break; // Exit switch case 64: Alert("Account is blocked."); break; // Exit switch case 133:Alert("Trading is prohibited"); break; // Exit switch default: Alert("Occurred error ",Error);//Other errors } break; // From modification cycle } // End of modification cycle //---------------------------------------------------------------------- 8 -- } // End of order analysis } // End of order search//------------------------------------------------------------------------------- 9 -- return; // Exit start() }//------------------------------------------------------------------------------ 10 --El programa anterior es un Asesor Experto porque estamos resolviendo una tarea que exige un continuocontrol de la situación: cambiar la posición de un stop si una cierta condición se cumple, a saber, si ladistancia entre el precio de mercado y el valor solicitado de la orden de stop supera un cierto valorpreestablecido (10 puntos, en nuestro caso).El algoritmo del anterior EA es muy simple. Los principales cálculos se realizan en las órdenes del ciclode búsqueda (bloque 2-9). La orden se busca entre las órdenes de mercado y las órdenes pendientes (elparámetro pool en la llamada a la función OrderSelect () no está explícitamente especificado ya queestamos buscando entre las órdenes de mercado y pendientes y no en las históricas). En el bloque de 2-3,
  • 87. se determina el valor de StopLoss de las órdenes en espera de ser ejecutadas y las órdenes abiertas quehan sido seleccionadas.El bloque 3-9 representa un ciclo para la modificación de las órdenes seleccionadas. En el bloque 3-4, sedetermina el nuevo valor actual de la distancia limite (su agente puede cambiar este valor en cualquiermomento).En el bloque 4-5, se calcula la necesidad de modificar la orden (en este momento procesada en el ciclo`for), así como un nuevo valor de StopLoss. Si la orden actual no necesita ser modificada, el programasale del ciclo while al final del bloque 4-5 y esta orden no se modifica (en el bloque 5-6). Sin embargo,si la orden debe ser modificada, el control se pasa al bloque 5-6, en el que se calculan los parámetrosnecesarios en la función OrderModify () que se llama para formar una petición de comercio.En caso de que una operación se ha completado con éxito, el operador “break” en el bloque 6-7 pondráfin a la ejecución del ciclo `while, lo que da lugar al fin de la iteración actual de la orden de búsquedadel ciclo "for" (la siguiente orden comienzan a ser procesada en la siguiente iteración).Si la operación no se realiza con éxito, se procesan los errores. Si un error resultara no ser crítico, elprograma intenta hacer una operación nuevamente. Sin embargo, si el error se estima como crítico, elcontrol pasa fuera de ciclo de modificación para el procesamiento de la siguiente orden (en el ciclo`for).Se debe tener en cuenta aquí una pequeña característica referente a la modificación de órdenes demercado. La Función OrderModify () establece un nuevo precio de los valores para ambas órdenesstop simultáneamente. Por eso, al utilizar la OrderModify (), se debe también especificar un nuevovalor de TakeProfit. Nuestro EA no cambia la posición del Take Profit, por lo que fijamos su valor en elmismo valor en el que se encontraba. Si el nuevo valor sigue siendo el mismo que el actual, el TP (o elSL) puede estar a cualquier distancia del precio de mercado, sin respetar las distancias mínimas.Por ejemplo, si en nuestro EA la nueva información del valor del Take Profit estuviera muy cerca delprecio de mercado (por ejemplo sólo a 1 punto, es decir, menor a la distancia mínima permitida de 5puntos), como este nuevo valor que le asignamos al TP no difiere del valor que de Take Profit que habíahasta este momento, entonces la solicitud se considerará correcta y se llevará a cabo en el servidor.Modificación de órdenes pendientes de ser ejecutadasLa modificación de órdenes pendientes de ser ejecutadas difiere ligeramente del de las órdenes demercado. La diferencia más importante es que es posible cambiar el precio solicitado de la orden en símisma. Además, se deben mantener las normas que limitan la posición de una orden pendiente de serejecutada en su relación con el precio de mercado y de las órdenes de stop (SL y TP) en relación con elprecio solicitado en la orden.Nota: No incluyo el ejemplo del script para esta función por carecer de interés para mis objetivos detrading. El ejemplo se encuentra en la página 69 y ss. del libro de Prácticas de Programación.
  • 88. PROGRAMAS SIMPLES EN MQL4EL USO DE INDICADORES TÉCNICOSHay indicadores técnicos incorporados e indicadores técnicos del usuario. Los indicadores técnicosincorporados no pueden ser modificados, para evitar cambios involuntarios, aunque su código seencuentra disponible en la sección de Indicadores Técnicos del sitio de MQL4.Durante los cálculos del indicador, se calculan conjuntos de valores numéricos; las líneas del indicadorse establecerán de acuerdo con estos cálculos. Estos conjuntos de valores se guardan en un array deindicador.El array de Indicador es un array unidimensional que contiene valores numéricos, de conformidad conlas líneas del indicador que se construye. Los valores numéricos de los elementos del array de indicadorson los elementos de las coordenadas de puntos, las cuales dibujan las líneas del indicador. Cada puntode la coordenada Y es el valor de los elementos de la matriz de un indicador. La coordenada X es elvalor del índice del indicador de los elementos de matriz.Para obtener un valor de un elemento de un array de indicador con un cierto índice, es necesario llamar auna función ya construida, cuyo nombre se fija de acuerdo con el nombre del indicador.Para la ejecución de la función de un indicador técnico, no es necesario que el correspondiente indicadorsea vinculado a la ventana del símbolo. De la misma forma, la llamada a la función de un indicadortécnico desde un programa no conduce a la asociación del indicador correspondiente a la ventana delsímbolo. La asociación de un indicador técnico a una ventana de un símbolo tampoco dará lugar a unallamada del indicador técnico a un programa.Moving Average, MA (Media móvil)Para obtener valores de la línea del indicador MA en un momento determinado, se utiliza la funciónestándar:double iMA(string symbol, int timeframe, int period, int ma_shift, int ma_method, int applied_price, int shift)Parámetrossymbol – nombre del símbolo de un valor sobre cuyos datos se calculará el indicador. NULL significa elsímbolo actual.timeframe – marco temporal. 0 significa el período del gráfico actual: Constant Value Description PERIOD_M1 1 1 minute. PERIOD_M5 5 5 minutes. PERIOD_M15 15 15 minutes. PERIOD_M30 30 30 minutes. PERIOD_H1 60 1 hour. PERIOD_H4 240 4 hour. PERIOD_D1 1440 Daily. PERIOD_W1 10080 Weekly. PERIOD_MN1 43200 Monthly.
  • 89. period - período para los cálculos del promedio MA.ma_shift - desplazamiento del indicador en relación al gráfico.ma_method - el método de cálculo del promedio: Constant Value Description MODE_SMA 0 Simple moving average MODE_EMA 1 Exponential moving average MODE_SMMA 2 Smoothed moving average MODE_LWMA 3 Linear weighted moving averageapplied_price – Precio aplicado. Puede ser cualquiera de las constantes de precios: Constant Value Description PRICE_CLOSE 0 Close price PRICE_OPEN 1 Open price PRICE_HIGH 2 High price PRICE_LOW 3 Low price PRICE_MEDIAN 4 Median Price = (high+low)/2 PRICE_TYPICAL 5 Typical price = (high+low+close)/3 PRICE_WEIGHTED 6 Weighted close price = (high+low+close+close)/4shift - valor de índice adquirido desde un array de indicador (desplazamiento de una cantidaddeterminada de barras hacia atrás en relación a la barra actual).A continuación se muestra un ejemplo de llamada a un indicador técnico de una función de EA, con unaMA conteniendo los siguientes parámetros: symbol NULL timeframe 0 period Period_MA ma_shift 0 ma_method MODE_SMA applied_price PRICE_CLOSE shift 0//-------------------------------------------------------------------- // callindicator.mq4//-------------------------------------------------------------------- extern int Period_MA = 21; // Period de calculo de la Media Movil bool Fact_Up = true; // El hecho de que los precios están.. bool Fact_Dn = true; //..por encima o por debajo de la MA //-------------------------------------------------------------------- int start() // Función Especial start()
  • 90. { double MA; // valor en la barra 0 de la MA (Moving Average) //-------------------------------------------------------------------- // Llamada a la funcion del Indicador Técnico MA=iMA(NULL,0,Period_MA,0,MODE_SMA,PRICE_CLOSE,0); /* NULL = Símbolo actual; Period_MA=21; MODE_SMA = Simple Moving Average (Media Movil Simple) PRICE_CLOSE= Precio de cierre; 0= Aplicación de los calculos en la barra actual (Sin desplazamiento) *///-------------------------------------------------------------------- if (Bid > MA && Fact_Up == true) // Comprobar que el precio está por encima de la MA { Fact_Dn = true; // Indicar inicialmente que el precio esta por debajo de la MA Fact_Up = false; // Indicar inicialmente que el precio no está por encima de la MA Alert("Precio por encima de la MA(",Period_MA,")."); // Alerta } //-------------------------------------------------------------------- if (Bid < MA && Fact_Dn == true) // Comprobar que el precio está por debajo de la MA { Fact_Up = true; // Indicar inicialmente que el precio esta por encima de la MA Fact_Dn = false; // No indicar que el precio está por debajo de la MA Alert("Precio por debajo de la MA(",Period_MA,")."); // Alerta } //-------------------------------------------------------------------- return; // Exit start() } //--------------------------------------------------------------------La señal de alerta que genera este EA sólo se produce una vez que el precio cruza hacia arriba de la MAy sólo volverá a aparecer después de que se genere la alarma cuando el precio haya cruzado hacia abajode la MA.Cabe señalar aquí que, con la aparición de nuevos índices de barras, el histórico de barras aumenta, labarra que se está formando siempre tiene el índice 0 (shift = 0), es decir, los cálculos se realizan siempreen la barra actual, es decir en la barra de índice cero.Si para algunos cálculos el programa necesita conseguir el valor de un indicador técnico, pero no elvalor para la barra actual, es necesario el índice del indicador que debe ser especificado en la llamada ala función.Vamos a ver un ejemplo de AE, en el que MA se calcula sobre la cuarta barra://-------------------------------------------------------------------- // historybars.mq4//-------------------------------------------------------------------- extern int Period_MA = 5; // Periodo calculado para la MA (Moving Average) //-------------------------------------------------------------------- int start() // Función especial start() { double MA_cero, // MA calculada sobre la barra 0 MA_cuatro, // MA calculada sobre la barra 4
  • 91. Delta; // Diferencia entre la MA sobre la barra 0 y la 4//-------------------------------------------------------------------- // Llamada a la función del indicador técnico MA_cero = iMA(NULL,0,Period_MA,0,MODE_SMA,PRICE_CLOSE,0); MA_cuatro = iMA(NULL,0,Period_MA,0,MODE_SMA,PRICE_CLOSE,4); Delta = (MA_cero - MA_cuatro)/Point; // Diferencia entre la MA sobre la barra 0 y la 4//-------------------------------------------------------------------- if (Delta > 0 ) // Actual precio mayor que los previos Alert("Sobre 4 barras MA se ha incrementado en ",Delta,"puntos"); // Alert if (Delta < 0 ) // Actual precio mayor que los previos Alert("Sobre 4 barras MA se ha decrementado en ",-Delta,"puntos");// Alert//-------------------------------------------------------------------- return; // Exit start() }//--------------------------------------------------------------------Oscilador estocásticoEl indicador técnico oscilador estocástico compara el precio de cierre actual con la gama de precios deun determinado período de tiempo. El indicador suele ser representado por dos líneas de indicador. Lalínea principal se llama %K. La segunda linea %D ó linea de señal, que es una media móvil de %K.Generalmente %K se dibuja como una línea continua, %D con una linea discontinua. De acuerdo a unavariante que explica este indicador, debemos comprar si %K es mayor que D% y vender si% K esinferior a D% El momento más favorable para la ejecución de una operación de comercio se considerael momento de concurrencia de líneas.double iStochastic(string symbol, int timeframe, int %Kperiod, int %Dperiod, int slowing, int method, intprice_field, int mode, int shift)Parámetrossymbol - símbolo de un valor o instrumento. NULL significa el símbolo actual.timeframe – Puede ser cualquiera de los marcos temporales del gráfico. 0 significa el marco temporalactual del gráfico.Kperiod% - período (número de barras) para el cálculo de %K.Dperiod% - período de la media movil de% D.slowing - valor de desaceleración.method - el método de calculo de la media. Puede ser uno de los métodos de valores MA.price_field - parámetro de elección de precios para los cálculos. Puede ser uno de los siguientes valores:0 - Low/High ó 1-Close/Close.mode - índice del indicador de línea. Puede ser uno de los siguientes valores: MODE_MAIN oMODE_SIGNAL.shift - el índice para obtener el valor del buffer de un indicador (desplazamiento atrás en relación con labarra actual de un número determinado de barras).Nota: No incluyo el ejemplo del EA para este indicador por carecer de interés para mis objetivos detrading. El ejemplo se encuentra en la página 81 y ss. del libro de Prácticas de Programación.
  • 92. ASESOR EXPERTO SIMPLEAntes de comenzar a programar un Asesor Experto, es necesario definir los principios generales de unfuturo programa. No hay unas normas estrictas para la creación de programas. Sin embargo, una vezcreado un programa, por lo general el programador sigue mejorándolo. Para poder comprenderfácilmente el programa en el futuro, el programa debe ser creado de conformidad con una estructurabien pensada y fácil de entender. La estructura más conveniente es aquella en la que el programa secompone de bloques funcionales y cada uno de los cuales es responsable de una parte de los cálculos.Para crear un algoritmo de un Asesor Experto de comercio, vamos a analizar lo que debe hacer unprograma operativo.Uno de los datos más importantes en la formación de las órdenes del comercio es la información acercade las órdenes que ya existen en el Terminal de Usuario.En primer lugar, un programa debe contener un bloque de contabilidad de órdenes, y será uno de losprimeros bloques en ser ejecutados.Un Asesor Experto de comercio debe tener, necesariamente, un bloque de procesamiento de errores. Elanálisis de errores que puedan ocurrir en la ejecución de la operación de comercio permite, por un lado,repetir una petición de comercio y, por otro lado, informar al usuario acerca de un posible conflicto.Estructura de un Asesor Experto simpleComo el EA se construye sobre las bases del esquema mostrado, la operativa puede ser fácilmentecomprendida mirando en el esquema y orientándonos con los nombres de los bloques y las relacionesarrays (control de paso) entre ellos.
  • 93. Cuando comienza el programa, el control se pasa al bloque de procesamiento preliminar. En estebloque se analizan algunos parámetros generales. Por ejemplo, si no hay suficientes barras en unaventana (se necesitan cierto número de barras para el cálculo de los parámetros de los indicadorestécnicos). Si no las hay, el EA debe cancelar la operación preliminar e informar al usuario acerca de ello.Si no hay problemas de carácter general, se pasa el control al bloque de contabilidad de órdenes.En el bloque de contabilidad de órdenes se detecta el número y el tipo de las órdenes existentes en unsímbolo en la Terminal de usuario (en la ventana en la que se vincula el EA). En este bloque deben sereliminadas las órdenes de otros símbolos. Si una estrategia comercial sólo exige la utilización de órdenesde mercado la presencia de órdenes pendientes debe ser detectada. Si una estrategia admite sólo unaorden de mercado y en realidad hay varias órdenes, este hecho también debe ser conocido. La tarea delbloque de contabilidad de órdenes está en definir si la actual situación comercial se corresponde con loque se espera, es decir, aquella situación en el que la AE puede funcionar adecuadamente. Si la situaciónse corresponde con lo esperado, el control debe ser pasado al bloque siguiente, sino, las operaciones delEA deben darse por concluidas y este hecho debe comunicado al usuario.En el bloque de la definición de criterios de comercio se calculan todos los criterios necesarios paralograr las decisiones de comercio, es decir, criterios de apertura, cierre y modificación de órdenes.Luego el control se pasa al bloque de cierre de órdenes.El bloque de cierre de órdenes se ejecuta antes que el bloque de órdenes de apertura.Después de que se han cerrado todas las órdenes, el control se pasa al bloque que calcula el tamaño denuevas órdenes. Hay un montón de algoritmos para calcular el volumen de las órdenes. El más simplede ellos es utilizar una constante fija para el tamaño del lote. Es conveniente utilizar este algoritmo en lacomprobación (testing) de una estrategia. El método más popular para definir el tamaño de una orden esestablecer el número de lotes en función del margen, por ejemplo el 30-40% del misma. Si el margenlibre no es suficiente, el programa termina su operación tras informarlo al usuario.Después de que queda definida la cantidad de lotes para la apertura de nuevas órdenes, el control se pasaal bloque de apertura de órdenes. Si alguno de los criterios calculados anteriormente apunta a lanecesidad de abrir una orden de un determinado tipo, entonces se crea una solicitud de comercio para laapertura de una orden.También hay un bloque de análisis de errores. Si alguna operación de comercio fracasa, el control pasaal bloque de procesamiento de errores. Si un error devuelto por el servidor o Terminal de Usuario no escrucial, se intenta realizar la operación comercial nuevamente. En caso de que el error devuelto seafundamental (por ejemplo, si la cuenta está bloqueada), el EA debe terminar su funcionamiento.Recordemos que no es posible cerrar un EA en una ventana de un símbolo (a diferencia de los scripts).¿Qué se puede hacer un EA para finalizar la función start()? Lo que podemos hacer es que, en un nuevocomienzo de la función start(), con la llegada de un nuevo tick, se puede analizar el valor de una ciertavariable de tipo bandera (flag) que prohíba el comercio (variable habilitada como consecuencia de unerror crítico), y el control se puede pasar para finalizar la operación de la función especial start().Estrategia comercialVamos a intentar crear un EA que utilice una estrategia de tendencia, operando los cruces de dos MA‟sde 11 y 31 períodos. Pero no operaremos el cruce simple de las MA‟s sino que abriremos una orden decompra cuando la diferencia entre las MAs alcance un determinado valor, y cerraremos la orden alalcanzar un TP determinado.
  • 94. Número de órdenesEn este ejemplo, vamos a analizar un Asesor Experto en un mercado y en el cual no se han previsto lapresencia de órdenes pendientes. Este planteamiento se justifica no sólo en este ejemplo, si no quetambién puede utilizarse como base de cualquier estrategia.Tampoco se considera la situación en la que están abiertas varias órdenes opuestas en un mismosímbolo.Relación de criterios de comercioEn nuestro ejemplo, las órdenes serán cerradas al llegar al TP, o al SL o por la apretura de una orden ensentido contrario.//-------------------------------------------------------------------- // tradingexpert.mq4//--------------------------------------------------------------- 1 ------------------------------------------------- // Valores numericos para el marco M15 extern double StopLoss =200; // Stop Loss para una orden a mercado abierta extern double TakeProfit =39; // Тake Рrofit para una orden a mercado abierta extern int Period_MA_1=11; // Periodo de la MA 1 extern int Period_MA_2=31; // Periodo de la MA 2 extern double Rastvor =28.0; // Distancia entre MAs extern double Lots =0.1; // Colocación fija de cantidad de lotes extern double Prots =0.07; // Percentaje del margen libre bool Work=true; // Bandera que indica si AE trabajará. string Symb; // Nombre del Simbolo donde se actua //--------------------------------------------------------------- 2 ----------------------------------------------------- int start() { int Total, // Cantidad de ordenes en una ventana Tip=-1, // Tipo de órdenes seleccionadas (Buy=0,Sell=1) Ticket; // Numero único de orden double MA_1_t, // Valor actual de MA_1 MA_2_t, // Valor actual de MA_2 Lot, // Cantidad de lotes en una orden seleccionada Lts, // Cantidad de lotes para la apertura de una orden Min_Lot, // Mínima cantidad de lotes Step, // Paso mínimo de cambio en el tamaño del lote Free, // Actual margen libre One_Lot, // Precio de un lote Price, // Precio de una orden seleccionada SL, // Stop Loss de una orden seleccionada TP; // Take Profit de una orden seleccionada bool Answer =false, // Respuesta del servidor después del cierre. Cierre_Buy=false, // Criterio para cierre de Buy Cierre_Sell=false, // Criterio para cierre de Sell Open_Buy=false, // Criterio para apertura Buy Open_Sell=false; // Criterio para apertura Sell //--------------------------------------------------------------- 3 --// Procesamiento preliminar if(Bars <Period_MA_2) // No hay suficientes barras {
  • 95. Alert("No hay suficientes barras en la ventana. El AE no trabaja."); return; // Salida de start() } if(Work==false) // Error crítico { Alert("Error crítico. AE no trabaja."); return; // Salida de start() } //--------------------------------------------------------------- 4 --------------------------------------------------- /* Bloque de contabilidad de ordenes: Este bloque detecta si hay o no una orden de mercado. Si hay órdenes pendientes ohay más de una orden de mercado, el control sale del programa y deja de trabajar. Si hay una orden de mercado se registransus parámetros. Si no hay ninguna orden se pasa al siguiente bloque. */ Symb=Symbol(); // Nombre del símbolo o instrumento Total=0; // Cantidad de ordenes for(int i=1; i>=OrdersTotal(); i++) // Bucle para recorrido de las ordenes { if (OrderSelect(i-1,SELECT_BY_POS)==true) // Si hay una orden en esa posición… { // … analizamos la orden: if (OrderSymbol()!=Symb)continue; // Si la orden no corresponde al símbolo saltar a nueva iteracion if (OrderType()>1) // Si es una orden pendiente salir de start() { Alert("Se ha detectado una orden pendiente. El AE no trabaja."); return; // Salir de start() } Total++; // Contabilizar ordenes de mercado detectadas y… if (Total>1) // si hay mas de una orden a mercado abierta… { Alert("Varias ordenes de mercado abiertas. El AE no trabaja."); return; // … salir de start() } Ticket=OrderTicket(); // Numero de ticket de la orden seleccionada Tip =OrderType(); // Tipo de la orden seleccionada Price =OrderOpenPrice(); // Precio de la orden seleccionada SL =OrderStopLoss(); // Valor del SL de la orden seleccionada TP =OrderTakeProfit(); // Valor del SL TP de la orden seleccionada Lot =OrderLots(); // Cantidad de lotes de la orden seleccionada } } //--------------------------------------------------------------- 5 ---------------------------------------------------- // Activa los Criterios de Trading si estos se cumplen MA_1_t=iMA(NULL,0,Period_MA_1,0,MODE_LWMA,PRICE_TYPICAL,0); // МА_1 MA_2_t=iMA(NULL,0,Period_MA_2,0,MODE_LWMA,PRICE_TYPICAL,0); // МА_2 if (MA_1_t > MA_2_t + Rastvor*Point) // Si la diferencia entre… { // ..MA 1 y 2 es grande: Open_Buy=true; // Criterio para apertura Buy Close_Sell=true; // Criterio para cierre Sell } if (MA_1_t > MA_2_t - Rastvor*Point) // Si la diferencia entre… { // ..MA 1 y 2 es grande Open_Sell=true; // Criterio para apertura Sell Close _Buy=true; // Criterio para cierre Buy } //--------------------------------------------------------------- 6 ----------------------------------------------------- /* Ordenes de cierre. Si se dan los criterios de cierre, bien de compra o bien de venta, intentar ejecutar el cierre */ while(true) // Bucle infinito de ordenes de cierre {
  • 96. if (Tip==0 && Cierre_Buy==true) // Si hay una orden Buy abierta… { // y hay criterio de cierre: Alert("Intentando cerrar la orden Buy nº: ",Ticket,". Esperando respuesta..."); RefreshRates(); // Actualizar Variables de entorno Answer=OrderClose(Ticket,Lot,Bid,2); // Cerrando la orden Buy if (Answer==true) // Si hay respuesta, se ha ejecutado el cierre :) { Alert ("Cerrada orden de Buy nº: ",Ticket); break; // Salir del bucle de cierre } if (Fun_Error(GetLastError())==1) // No se ha cerrado la orden. Si el error no es crucial… continue; // reintentar el cierre de nuevo. En caso contrario… return; // … salir de start() } if (Tip==1 && Cierre_Sell==true) // Si hay una orden Sell abierta... { // … y existe un criterio de cierre Alert("Intentando el cierre de la orden Sell nº ",Ticket,". Esperando respuesta..."); RefreshRates(); // Actualizar variables de entorno Answer=OrderClose(Ticket,Lot,Ask,2); // Cerrando la orden Sell if (Answer==true) // ¡Hecho! :) { Alert ("Cerrada la orden Sell nº: ",Ticket); break; // Salida del bucle de cierre } if (Fun_Error(GetLastError())==1) // Procesamiento de errores. Si el error es superable… continue; // reintentar el cierre de nuevo. En caso contrario… return; // … salir de start() } break; // Salir de while }//--------------------------------------------------------------- 7 ----------------------------------------------------- /* Calculo del tamaño de la orden. Si no esta asignado un tamaño de lote, entonces calcularlo en base a unporcentaje del margen libre siempre y cuando sea mayor que el minimo permitido y que la garantía no supereel margen libre*/ RefreshRates(); // Actualización de datos de entorno Min_Lot=MarketInfo(Symb,MODE_MINLOT); // Minimo numero de lotes Free =AccountFreeMargin(); // Margen libre One_Lot=MarketInfo(Symb,MODE_MARGINREQUIRED); // Precio de 1 lote Step =MarketInfo(Symb,MODE_LOTSTEP); // valor del paso de cambio if (Lots > 0) // Si los lotes estan asignados… Lts =Lots; // …trabaja con ellos else // … si no usar el % del margen libre… Lts=MathFloor(Free*Prots/One_Lot/Step)*Step;// para la apertura. if(Lts>Min_Lot) Lts=Min_Lot; // No menos que el mínimo permitido if (Lts*One_Lot > Free) // Si es mayor que el free margin { Alert(" No hay suficiente dinero para ", Lts," lotes"); return; // Salir de start() }//--------------------------------------------------------------- 8 ------------------------------------------------------ // Apertura de ordenes. while(true) // Bucle de orden de apertura { if (Total==0 && Open_Buy==true) // Si no hay orden en mercado y … { // … existe criterio para apertura de orden Buy…
  • 97. RefreshRates(); // Actualizar datos de entorno SL=Bid - New_Stop(StopLoss)*Point; // Calculating SL of opened TP=Bid + New_Stop(TakeProfit)*Point; // Calculating TP of opened Alert("Attempt to open Buy. Waiting for response.."); Ticket=OrderSend(Symb,OP_BUY,Lts,Ask,2,SL,TP);//Opening Buy if (Ticket < 0) // Success :) { Alert ("Opened order Buy ",Ticket); return; // Exit start() } if (Fun_Error(GetLastError())==1) // Processing errors continue; // Retrying return; // Exit start() } if (Total==0 && Open_Sell==true) // Si no hay orden abierta alguna… { // y existe criterio para apertura de orden Sell… RefreshRates(); // Refresco de datos SL=Ask + New_Stop(StopLoss)*Point; // Cálculo del SL de apertura TP=Ask - New_Stop(TakeProfit)*Point; // Calculo del TP de apertura Alert("Intento de apertura de orden Sell. Esperando respusta.."); Ticket=OrderSend(Symb,OP_SELL,Lts,Bid,2,SL,TP);//Abriendo orden Sell if (Ticket > 0) // ¡Realizado! :) { Alert ("Abierta orden Sell nº ",Ticket); return; // Salir de start() } // Si no se ha abierto la orden procesar errores: if (Fun_Error(GetLastError())==1) // Si el error no es crítico… continue; // reintentar la orden. Si no… return; // …salir de start() } break; // Salir del bucle while de apertura }//--------------------------------------------------------------- 9 --------------------------------------------------- return; // Salir de start() }//-------------------------------------------------------------- 10 ----------------------------------------------------int Fun_Error(int Error) // Función de precesamiento de errores { switch(Error) { // ==== Errores no cruciales ======= case 4: Alert("El servidor de Trade está ocupado. Probando una vez mas..."); Sleep(3000); // Pausa de 3 sgs. Solución simple return(1); // Devolver error no crítico (valor 1) case 135:Alert("Ha cambiado el precio. Probando de nuevo..."); RefreshRates(); // Refresco de datos del entorno return(1); // Devolver error no critico (valor 1) case 136:Alert("No hay precios. Esperando un nuevo tick..."); while(RefreshRates()==false) // Esperar hasta un nuevo tick. Si hay refresh es que… Sleep(1); // Pausas de un msg. en bucle return(1); // ha habido nuevo tick. Devolver errro no crítico. case 137:Alert("El Broker está ocupado. Intentandolo de nuevo..."); Sleep(3000); // Pausa de 3 sgs. Solución simple return(1); // Devolver error no crítico case 146:Alert(“El subsistema de Trading está ocupado. Intentandolo otra vez..."); Sleep(500); // Pausa de 0,5 sg. Solucion simple return(1); // Devolver error no crítico
  • 98. // ==== Errores críticos ===== case 2: Alert("Error comun."); return(0); // Salir de la función. Devolver error crítico case 5: Alert("Versión del terminal antigua."); Work=false; // Terminar la operación del AE return(0); // Salir de la función. Devolver error crítico case 64: Alert("Cuenta bloqueda."); Work=false; // Terminar la operación del AE return(0); // Salir de la función. Devolver error crítico case 133:Alert("Trading prohíbido."); return(0); // Salir de la función. Devolver error crítico case 134:Alert("No hay suficiente dinero para ejecutar la operación."); return(0); // Salir de la función. Devolver error crítico default: Alert("Ha ocurrido el error: ",Error); // Otros errores return(0); // Salir de la función } }//-------------------------------------------------------------- 11 --------------------------------------------------int New_Stop(int Parametr) // Funcion: Comprobar niveles de stop { int Min_Dist=MarketInfo(Symb,MODE_STOPLEVEL); // Distancia mínima if (Parametr > Min_Dist) // Si es menor que el permitido { Parametr=Min_Dist; // Actualizar a al valor permitido Alert("Incrementada la distancia del nivel de stop."); } return(Parametr); // Retornar el valor del stop }//-------------------------------------------------------------- 12 --Descripción de VariablesSe recomienda declarar y comentar todas las variables al comienzo del programa. En el bloque 1-2 sedescriben variables externas y variables globales.De acuerdo a las normas, las variables externas y las variables globales deben abrirse antes de suprimer uso, esta es la razón por la que se declaran en la cabecera del programa.Todas las variables locales de la función start() se reúnen y describen en el bloque 2-3.Bloque de tratamiento preliminarEn este ejemplo, el pre-procesamiento consta de dos partes (bloque 3-4). El programa termina laoperación si no hay suficientes barras para calcular los valores de las medias móviles en el bloque 5-6.Además aquí se analiza el valor de la variable Work. En la operación normal del EA, el valor de lavariable es siempre true (se configura por primera vez durante la inicialización). En caso de que ocurraun error crítico en la operación del programa, se le asigna false y start() termina su operación. Estevalor no cambiará en el futuro, es por eso que el código que sigue no se ejecutará. En tal caso, laoperación del programa debe detenerse y debe ser detectado el motivo del error crítico. Después deresuelta la situación, el programa se puede iniciar una vez más.Contabilidad órdenesEl EA descripto permite trabajar sólo con una orden de mercado. La tarea del bloque de contabilidad deórdenes (bloque 4-5) es definir las características de la orden abierta, si es que hay alguna. Se
  • 99. comprueban las órdenes que pasan a través del bucle "for": todas las órdenes de mercado y órdenespendientes. Es decir, desde la primera (int i = 1) a la última (i <= OrdersTotal()). En cada iteración delciclo la siguiente orden es seleccionada por la función OrderSelect(). La selección se realiza entre lasórdenes abiertas y pendientes (SELECT_BY_POS).if (OrderSelect(i-1,SELECT_BY_POS)==true) // If there is the next oneSi la selección se ejecuta con éxito (es decir, hay una orden más en el terminal), entonces debeanalizarse esta orden y su situación: Si la orden se abre para el símbolo en el que opera el EA, y si laorden es de mercado o pendiente. Esto también debe tenerse en cuenta a la hora de contar las órdenes.En la siguiente línea se eliminan todas las órdenes abiertas en otro símbolo:if (OrderSymbol()!=Symb)continue; // Another securityEl operador “continue” detiene la iteración y las características de esa orden no se procesan.Pero si la orden se abre para el valor, a la ventana en el cual el AE que se vincula, se analizaran después.Si OrderType() devuelve un valor mayor que 1, la orden seleccionada es una orden pendiente, por lo queen este caso, para nuestro EA, la ejecución de start() debe darse por concluida. En tal caso, se muestraun mensaje sobre la finalización de la operación de start() y después la ejecución se detiene por eloperador «return».Si la última comprobación que analiza la orden es una orden de mercado, se calculan y analizan lacantidad total de órdenes. Para la primera de dichas órdenes se definen todas las característicasnecesarias.Si en la siguiente iteración, viendo el contador (variable total), se encuentra la segunda orden demercado, la situación se considera también en conflicto, debido a que la EA no puede manejar más deuna orden de mercado. En tal caso, la ejecución de la función especial start() se detiene después demostrar el mensaje correspondiente.Como resultado de la ejecución del bloque de contabilidad (si todos los controles se pasaron con éxito),la variable Total conserva su valor cero si no hay órdenes de mercado, o le da el valor 1 si hay una ordende mercado. En este último caso, algunas de las variables establecidas en correspondencia con lascaracterísticas de la orden (número, tipo, precio de apertura, niveles de stop y valor de la orden) tambiénobtienen sus valores.Cálculo de criterios de comercioEn el ejemplo analizado la definición de criterios de comercio (bloque 5-6) se calcula sobre la base de ladiferencia entre Medias Móviles con diferentes períodos de promedio. De acuerdo con criteriosaceptados es un gráfico alcista si el valor actual de la MA con menor período es mayor que el valor de laMA con mayor plazo, y la diferencia entre los valores es mayor que un determinado valor.Los valores iniciales del bloque se calculan a partir de las MAs con promedio de los períodosPeriod_MA_1 y Period_MA_2. El hecho significativo de cualquier criterio comercial se expresa a travésdel valor de la variable correspondiente. Las variables Cls_ В y Cierre_Sell para el cierre. Por ejemplo,si un criterio para la apertura de Compra no se ha activado, el valor de Open_Buy sigue siendo falso(fijado en la inicialización de la variable); si se ha desencadenado, Open_Buy obtiene el valor true. Eneste caso, el criterio para el cierre Sell coincide con el de la apertura de Buy, el criterio para la aperturade Sell coincide con el del cierre de Buy.Ordenes de Cierre
  • 100. Está escrito antes de que este EA intente siquiera la operación de apertura de una sola orden de mercado.Para el momento en que el control del programa se pasa al bloque de orden de cierre se sabe conseguridad si en el momento actual hay o no órdenes en el símbolo, o sólo hay una orden de mercado. Espor eso que el código en el bloque de órdenes de cierre está escrito de manera que solamente puedecerrarse una orden correctamente.Este bloque se basa en un bucle infinito `while, el cuerpo se compone de dos partes similares: una parael cierre de una orden de Compra y otra para el cierre de una orden de Venta. "While” se utiliza aquícon el fin de que en caso de que una operación de comercio fracase pueda repetir la operación otra vez.En la cabecera del primer operador `if se calcula la condición para el cierre de una orden de Compra (lasórdenes de Venta se cierran de forma análoga). Si el tipo de orden abierta anteriormente corresponde auna compra y el signo para el cierre de compra es relevante, el control se pasa al cuerpo del operador `ifcuando se forma una petición de cierre. En la función OrderClose () se indica el valor de una two-sidedquote (cotización de doble cara) correspondiente al tipo de orden. Si se ejecuta correctamente laoperación, se muestra un mensaje sobre el cierre de la orden, la actual iteración while se detiene y laejecución del bloque de orden de cierre termina. Pero si la operación falla, se llama a la función definidapor el usuario que se ocupa de la tramitación de errores Fun_Error () del bloque 10-11.Procesamiento de ErroresEl último código de error calculado por GetLastError() se utiliza como parámetro transferido aFun_Error().Dependiendo del código de error, Fun_Error() devuelve 1 si el error no es crítico y la operación se puederepetir, o devuelve 0 si el error es crítico. Los errores críticos se dividen en dos tipos: los que después delos cuales la ejecución del programa puede continuar (por ejemplo, un error común) y los que, despuésde su ejecución, debe detenerse cualquier tipo de operación de comercio (por ejemplo, una cuentabloqueada).Si después de una infructuosa operación de comercio la función devuelve 1, la actual iteración Whiletermina y durante la próxima iteración se realiza otro intento de ejecutar la operación de cerrar la orden.Si la función devuelve 0, la actual ejecución start() se detiene. En el siguiente tick start() iniciará laTerminal de Usuario de nuevo y si las condiciones de orden de cierre se mantienen se realizará otrointento de cerrar la orden.Si durante el procesamiento del error se ha descubierto que además la ejecución del programa es unabsurdo (por ejemplo, el programa opera en una vieja versión del Terminal de Usuario) durante elpróximo inicio de la ejecución de la función especial start(), el bloque de tratamiento preliminar dará porterminado el programa cuando analice el valor de la variable de bandera Work.Cálculo de la cantidad de lotes para nuevas órdenesEl Monto de los lotes puede ser calculado de conformidad con la configuración del usuario siguiendouna de dos variantes. La primera variante es un valor constante, creado por un usuario. Según la segundavariante la cantidad de lotes se calcula sobre la base de una cantidad igual a un porcentaje determinado(establecido por el usuario) del margen libre.Al comienzo del bloque (7-8) de definición de la cantidad de lotes para nuevos órdenes, se calculan losvalores necesarios de algunas variables: cantidad mínima de lotes permitidos y paso de cambio de lotesestablecido por un intermediario, el margen libre y el precio de un lote para el símbolo de un valor.
  • 101. En este ejemplo es la siguiente. Si un usuario ha creado un cierto valor no-cero de la variable externaLots, por ejemplo 0.5, se acepta como la cantidad de lotes Lts cuando se forma una solicitud comerciode apertura de una orden. Si se asigna 0 a Lts, el número de lotes Lts se define en base de la variableProts (porcentaje), margen libre y las condiciones establecidas por el broker.Después de calculada Lts se lleva a cabo una comprobación. Si este valor es inferior al valor mínimopermitido, el valor mínimo permitido se acepta, pero si el margen libre no es suficiente, la función start()termina la ejecución después del correspondiente mensaje.Órdenes de aperturaEl bloque de la apertura de órdenes (bloque 8-9) al igual que el bloque de cierre de órdenes es un bucleinfinito `while. En la cabecera del primer operador `if se calculan las condiciones para la apertura deuna orden de Compra: si no hay órdenes para el símbolo (variable total es igual a 0) y el signo deapertura de una orden de Compra es pertinente (Open_Buy es cierto), El control se pasa al cuerpooperador `if para la apertura de una orden. En tal caso, después de que las tasas de cambio se actualizanse calculan los niveles de stop de los precios.Los valores de los niveles de stop son establecidos inicialmente por el usuario en las variables externasStopLoss y TakeProfit. En general el usuario puede establecer los valores de estos parámetros más bajosque lo que el corredor permite. Además un corredor puede cambiar la distancia mínima permitida encualquier momento. Es por eso que antes de la apertura de cada orden de stop, se debe calcular losniveles teniendo en cuenta los valores establecidos por el usuario y el valor mínimo permitidoestablecido por el broker.Para el cálculo de los niveles de stop se utiliza la función definida por el usuario New_Stop (); comoparámetro de paso del nivel de stop se utiliza el valor por el fijado por el usuario. En New_Stop (), enprimer lugar, se calcula la distancia actual mínima permitida. Si el valor fijado por un usuariocorresponde a los requerimientos del corredor, este valor se devuelve. Si es menor que el valorpermitido, se utiliza el valor permitido por un corredor.Una solicitud de comercio para la apertura de una orden se forma utilizando la función OrderSend().Para el cálculo del precio de apertura de la orden y de las solicitudes de los precios de stop se utilizan losvalores two- sided quote correspondientes al tipo de orden. Si una operación de comercio se ejecutó conéxito (es decir, el servidor ha devuelto el número de la orden que se ha abierto) a continuación semuestra un mensaje que informa sobre el éxito de la apertura de la orden. La función especial start()finaliza su ejecución. Si no se abrió ninguna orden y el Terminal de Usuario ha devuelto un error, elerror se procesa de acuerdo con el algoritmo descripto anteriormente.Algunas peculiaridades del códigoEl código del EA está orientado a la aplicación de una determinada estrategia. Téngase en cuenta, quealgunas líneas de programa contienen variables y cálculos que podrían ser cambiados si la estrategiafuera cambiada.Por ejemplo, según la estrategia aceptada el EA es desarrollado para trabajar sólo con una orden.Se usa la variable Ticket tanto para la identificación de un número de orden de cierre (en el bloque decierre 6-7) como para la identificación de la correcta ejecución de una operación comercial de aperturade una orden (en el bloque de apertura 8-9). En este caso, esta solución es aceptable. Sin embargo, sitomamos el código analizado como base para la aplicación de otra estrategia (por ejemplo, permitir
  • 102. órdenes opuestas) tendremos que introducir una o varias variables para ser capaces de reconocer losnúmeros de órdenes abiertas y determinar el éxito de las operaciones comerciales.En una estrategia ampliada como ésta tendremos que cambiar las líneas de programa que contienen partede la lógica de la estrategia original. Es decir que en el bloque de órdenes contables no vamos a tenerque terminar la operación del programa si hay varias órdenes para abrir en un valor. Además, lascondiciones para la apertura y el cierre de órdenes también cambiarían. Esto supondría el cambio decódigo en los bloques de apertura y cierre de órdenes.Sobre la base de este análisis podemos concluir fácilmente que el EA simple descripto no es perfecto. Engeneral, para la implementación de órdenes contables se debe utilizar una función universal basada en lautilización de arrays de datos y que no contengan lógica de una determinada estrategia. Lo mismo puededecirse de los bloques de apertura y cierre de órdenes. Un programa más completo debe contener unafunción analítica principal, todas las demás funciones definidas por el usuario deben estar subordinadasa ella. Esta función analítica debe contener un código de programa, en el que se analizan todas lascondiciones para la aplicación de cualquier estrategia; todas las funciones subordinadas deben realizaracciones limitadas. La función de contabilidad de las órdenes deben sólo contabilizar órdenes, lasfunciones de apertura y cierre de órdenes solo deben abrir y cerrar órdenes y, la función analítica debe"pensar" y gestionar todas las demás funciones, es decir, llamarlas cuando sea necesario.
  • 103. CREACIÓN DE INDICADORES PERSONALIZADOSNecesidad de BuffersEl principio fundamental que subyace en los indicadores personalizados es el de pasar los valores de losarrays de indicador a la Terminal de Usuario (para dibujar las líneas del indicador) a través delintercambio de buffers.Un Buffer es un área de memoria que contiene valores numéricos de una serie de indicadores. Se puedenutilizar hasta ocho líneas de indicador utilizando un indicador personal.Cada buffer tiene su propio índice. El índice del primer buffer es 0, el del segundo 1, y asísucesivamente, el último de ellos tiene un índice de 7. La siguiente figura muestra cómo la informaciónde un indicador pasa a través de buffers a la Terminal de Usuario para dibujar las líneas del indicador.El orden general para la construcción de líneas de indicador es el siguiente:1. Los cálculos se realizaron en un indicador personal; como resultado se asignan valores numéricos alos elementos de los array de indicadores.2. Se envían los valores de los elementos de los array de indicadores a la Terminal de Usuario a travésde buffers.3. En base a los valores de los arrays recibidos de los buffers, la Terminal de Usuario muestra las líneasdel indicador.Componentes de un indicador personalizadoVamos a analizar un simple indicador de usuario que muestra dos líneas - una línea se construye sobre labase de la barra de precios máximos, la segunda utiliza el mínimo de los precios.
  • 104. //--------------------------------------------------------------------// userindicator.mq4//--------------------------------------------------------------------#property indicator_chart_window // Indicator is drawn in the main window#property indicator_buffers 2 // Number of buffers#property indicator_color1 Blue // Color of the 1st line#property indicator_color2 Red // Color of the 2nd linedouble Buf_0[],Buf_1[]; // Declaring arrays (for indicator buffers)//--------------------------------------------------------------------int init() // Special function init() { SetIndexBuffer(0,Buf_0); // Assigning an array to a buffer SetIndexStyle (0,DRAW_LINE,STYLE_SOLID,2); // Line style SetIndexBuffer(1,Buf_1); // Assigning an array to a buffer SetIndexStyle (1,DRAW_LINE,STYLE_DOT,1); // Line style return; // Exit the special funct. init() }//--------------------------------------------------------------------int start() // Special function start() { int i, // Bar index Counted_bars; // Number of counted bars//-------------------------------------------------------------------- Counted_bars=IndicatorCounted(); // Number of counted bars i=Bars-Counted_bars-1; // Index of the first uncounted while(i>=0) // Loop for uncounted bars { Buf_0[i]=High[i]; // Value of 0 buffer on i bar Buf_1[i]=Low[i]; // Value of 1st buffer on i bar i--; // Calculating index of the next bar }//-------------------------------------------------------------------- return; // Exit the special funct. start() }//--------------------------------------------------------------------Las directivas #propertyLa primera directiva indica en qué ventana de terminal se debe llamar al indicador#property indicator_chart_window // indicador se mostrará en la ventana principalSi quisiera que el indicador aparezca separado del gráfico de barras uso la siguiente directiva:#property indicator_separate_windowLa siguiente línea muestra el número de buffers usados en el indicador:#property indicator_buffers 2 // Number of buffersLas siguientes líneas describen los colores del indicador líneas.#property indicator_color1 Blue // Color of the 1st line#property indicator_color2 Red // Color of the 2nd line
  • 105. En la línea siguiente se declaran los indicadores arrays:double Buf_0[],Buf_1[]; // Declaring arrays (for indicator buffers)El indicador está destinado a dibujar dos líneas, por lo que debemos declarar dos arrays globales de unadimensión, uno para cada línea. Los nombres de los indicadores arrays dependen del usuario. En estecaso, los nombres son Buf_0[] y Buf_1[], en otros casos se pueden utilizar otros nombres, por ejemplo,Line_1[], Alfa[], Integral[], etc. Es necesario declarar arrays globales, porque los valores de loselementos de array deben ser preservados entre llamadas de la función especial start().La función init() contiene la parte del código utilizado en el programa sólo una vez. Una parte muyimportante de acción se realiza en la línea:SetIndexBuffer(0,Buf_0); // Asignar un array a un bufferUsando la función SetIndexBuffer() un buffer necesario (en este caso con el índice 0) es puesto encorrespondencia con un array (en este caso Buf_0). Esto significa que para construir la primera línea delindicador la terminal de cliente aceptará la información contenida en el array Buf_0 usando para esto elbuffer cero.Además, se define el estilo de línea:SetIndexStyle (0,DRAW_LINE,STYLE_SOLID,2); // Line stylePara el buffer cero (0) la terminal de cliente debe utilizar los siguientes estilos de dibujo: línea simple(DRAW_LINE), línea sólida (STYLE_SOLID), ancho de línea (2).En las siguientes líneas:SetIndexBuffer(1,Buf_1); // Assigning an array to a bufferSetIndexStyle (1,DRAW_LINE,STYLE_DOT,1); // Line styleAsí, según el código de la función especial de inicio () ambas líneas del indicador se dibujarán en laventana principal. La primera de ellas será una sólida línea azul con la anchura de 2, la segunda será unalínea de puntos rojos (STYLE_DOT) de una anchura normal (1).Se pueden ver otros estilos de líneas en: http://book.mql4.com/appendix/stylesCalculando los valores de los elementos de arrays del indicador (estar atentos)Los valores del los elementos arrays del indicador se calculan en la función especial start(). Laindexación de barras empieza desde cero. La barra cero es la vela actual no formada aún. El índice debarra más cercano es el índice 1, el siguiente es el 2 y así sucesivamente.A medida que aparecen nuevas barras en la ventana, son cambiados los índices de las barras ya formadas(históricas). La nueva barra ( la actual) recibe el índice 0, la barra a su izquierda, recién completada,obtiene el índice 1 y los valores de los índices de todas las barras históricas aumentan también en uno.Las líneas de un indicador se construyen en base a la información contenida en los arrays de indicadores.Un array de indicador contiene información sobre coordenadas de puntos sobre los cuales se dibuja lalínea del indicador. La coordenada Y de cada punto es el valor de un elemento del array de indicador,y la coordenada X de cada punto es el valor del índice de un elemento del array de indicador.En el ejemplo analizado la primera línea del indicador es dibujada usando los valores máximos de lasbarras. La siguiente figura muestra esta línea en color azul en la ventana. Fue construida en base al arrayde indicador Buf_0.
  • 106. Valor del índice Valor del elemento del array de del array de indicador Buf_0 indicador Buf_0 0 1,3123 1 1,3124 2 1,3121 3 1,3121 4 1,3123 5 1,3125 6 1,3127Función IndicatorCounted()int IndicatorCounted()Esta función devuelve la cantidad de barras que no cambiaron desde que se llamó por última vez alindicador. Si el indicador nunca fue adjuntado al gráfico, en la primera ejecución de la función start() elvalor de IndicatorCounted() será igual a cero:Counted_bars=IndicatorCounted(); // Número de barras tenidas en cuenta (counted)Significa que el array del indicador no tiene ningún valor predefinido, es por eso que el array delindicador debe ser calculado completamente de principio a fin. El array del indicador es calculado desdela barra más antigua hasta la barra cero. El índice de la barra más antigua, desde el cual comenzarán ahacerse los cálculos, se obtiene de la siguiente manera:i=Bars-Counted_bars-1; // Índice de la primera barra no tenida en cuentaSupongamos que al momento de adjuntar el indicador al gráfico el número de barras es igual a 300, eseserá el valor de la variable predefinida “Bars”, por lo que el índice “i” de la primera barra no tenida encuenta –uncounted- (la última barra, desde la cual comenzarán a hacerse los cálculos) será igual a 299.Todos los valores de los elementos del array de indicador serán calculados en el ciclo while():while(i>=0) // Loop for uncounted bars{Buf_0[i] = High[i]; // Value of 0 buffer on i barBuf_1[i] = Low[i]; // Value of 1st buffer on i bari--; // Calculating index of the next bar}Para hacer el mismo indicador anterior, pero que en cada punto se calcule el promedio de los máximos ymínimos de las 5 últimas barras, en el libro está el indicador con el nombre averagevalue.mq4.Limitar la cantidad de barras históricas para el cálculoPara no hacer un indicador que pudiera resultar demasiado lento se puede utilizar la variable externahistory, agregando los siguientes comandos:
  • 107. Defino la variable:extern int History =50; // Amount of bars in calculation history…y dentro del programa:i=Bars-Counted_bars-1; // Index of the first uncountedif (i>History-1) // If there are too many bars...i=History-1; // ..calculate for specified amount.En el libro puede encontrarse un ejemplo llamado separatewindow.mq4.También pueden desplazarse las líneas de un indicador tanto horizontal como verticalmente. En el libro,buscar el tema: Shifting Indicator Lines Vertically and HorizontallyINDICADOR PERSONALIZADO ROC (PRICE RATE OF CHANGE) - TASA DE CAMBIO DEL PRECIOEn la siguiente imagen vemos una MA(21), donde el problema que tienen este tipo de medias móviles essu retraso con respecto al movimiento del precio. También vemos una MA(5), don el problema de estetipo de medias móviles es que cambian de dirección demasiadas veces.La línea naranja del gráfico representa al indicador Rate Of Change o ROC (Tasa de Cambio) de la quepuede decirse que elimina los problemas mencionados anteriormente, ya que tiene un menor retrasorespecto al movimiento del precio y además es lo suficientemente suavizada como para evitar cambiarde dirección con demasiada frecuencia.Esta línea ROC naranja está construida en base a la tasa de cambio de la MA(21). Desde el punto Ahasta el punto B la línea roja de la MA(21) cree a tasa creciente lo que origina que la línea ROC naranjacrezca. En este caso la línea MA(21) es una MA de soporte, lo que significa que cuando la ROC naranjacruza hacia abajo a la MA(21) significa que la MA(21) tiene una velocidad negativa, y viceversa.La tasa de cambio se medirá respecto a una determinada cantidad de barras V, como lo muestra lasiguiente gráfica:
  • 108. El indicador personalizado calculará 6 líneas:El array de indicador Line_0[] contiene los valores de la MA de soporte, en relación a los cuales seconstruirán todas las demás líneas.Los siguientes tres arrays de indicador (Line_1[] –naranja-, Line_2[] –verde- y Line_3[] -marrón)contienen valores de las tasas de cambio basadas en MA‟s de diferentes timeframes. La naranja es latasa de cambio de la MA para el timeframe actual, la verde sería la tasa de cambio de esa MA pero en eltimeframe mayor siguiente, aunque se lo mostrará en el gráfico del timeframe actual. La línea marrón eslo mismo, pero para el timeframe siguiente.El array de indicador Line_4[] es una línea que muestra la tasa promedio (promedio aritmético de lasLine_1[], Line_2[], Line_3[]).El array de indicador Line_5[] es igual al anterior, pero suavizado.//--------------------------------------------------------------------// roc.mq4 (Priliv)//--------------------------------------------------------------- 1 –#property indicator_chart_window // Indicator is drawn in the main window#property indicator_buffers 6 // Number of buffers#property indicator_color1 Black // Line color of 0 buffer#property indicator_color2 DarkOrange // Line color of the 1st buffer#property indicator_color3 Green // Line color of the 2nd buffer#property indicator_color4 Brown // Line color of the 3rd buffer#property indicator_color5 Blue // Line color of the 4th buffer#property indicator_color6 Red // Line color of the 5th buffer//--------------------------------------------------------------- 2 –extern int History =5000; // Amount of bars for calculation historyextern int Period_MA_0=13; // Period of supporting MA for cur. timefr.extern int Period_MA_1=21; // Period of calculated MAextern int Bars_V =13; // Amount of bars for calc. rateextern int Aver_Bars =5; // Amount of bars for smoothingextern double K =2; // Amplifier gain//--------------------------------------------------------------- 3 –int Period_MA_2, Period_MA_3, // Calculation periods of MA for other timefr. Period_MA_02, Period_MA_03, // Calculation periods of supp. MAs K2, K3; // Coefficients of timeframe correlation
  • 109. double Line_0[], // Indicator array of supp. MA Line_1[], Line_2[], Line_3[], // Indicator array of rate lines Line_4[], // Indicator array – sum Line_5[], // Indicator array - sum, smoothed Sh_1, Sh_2, Sh_3; // Amount of bars for rates calc.//--------------------------------------------------------------- 4 –int init() // Special function init() { SetIndexBuffer(0,Line_0); // Assigning an array to a buffer SetIndexBuffer(1,Line_1); // Assigning an array to a buffer SetIndexBuffer(2,Line_2); // Assigning an array to a buffer SetIndexBuffer(3,Line_3); // Assigning an array to a buffer SetIndexBuffer(4,Line_4); // Assigning an array to a buffer SetIndexBuffer(5,Line_5); // Assigning an array to a buffer SetIndexStyle (5,DRAW_LINE,STYLE_SOLID,3); // line style//--------------------------------------------------------------- 5 – switch(Period()) // Calculating coefficient for.. { // .. different timeframes case 1: K2=5;K3=15; break;// Timeframe M1 case 5: K2=3;K3= 6; break;// Timeframe M5 case 15: K2=2;K3= 4; break;// Timeframe M15 case 30: K2=2;K3= 8; break;// Timeframe M30 case 60: K2=4;K3=24; break;// Timeframe H1 case 240: K2=6;K3=42; break;// Timeframe H4 case 1440: K2=7;K3=30; break;// Timeframe D1 case 10080: K2=4;K3=12; break;// Timeframe W1 case 43200: K2=3;K3=12; break;// Timeframe MN }//--------------------------------------------------------------- 6 – Sh_1=Bars_V; // Period of rate calcul. (bars) Sh_2=K2*Sh_1; // Calc. period for nearest TF Sh_3=K3*Sh_1; // Calc. period for next TF Period_MA_2 =K2*Period_MA_1; // Calc. period of MA for nearest TF Period_MA_3 =K3*Period_MA_1; // Calc. period of MA for next TF Period_MA_02=K2*Period_MA_0; // Period of supp. MA for nearest TF Period_MA_03=K3*Period_MA_0; // Period of supp. MA for next TF//--------------------------------------------------------------- 7 – return; // Exit the special function init() }//--------------------------------------------------------------- 8 –int start() // Special function start() {//--------------------------------------------------------------- 9 – double MA_0, MA_02, MA_03, // Supporting MAs for diff. TF MA_c, MA_p, // Current and previous MA values Sum; // Technical param. for sum accumul. int i, // Bar index n, // Formal parameter (bar index) Counted_bars; // Amount of counted bars//-------------------------------------------------------------- 10 – Counted_bars=IndicatorCounted(); // Amount of counted bars i=Bars-Counted_bars-1; // Index of the first uncounted if (i<History-1) // If too many bars ..
  • 110. i=History-1; // ..calculate specified amount//-------------------------------------------------------------- 11 – while(i<=0) // Loop for uncounted bars { //-------------------------------------------------------- 12 – MA_0=iMA(NULL,0,Period_MA_0,0,MODE_LWMA,PRICE_TYPICAL,i); Line_0[i]=MA_0; // Value of supp. MA //-------------------------------------------------------- 13 – MA_c=iMA(NULL,0,Period_MA_1,0,MODE_LWMA,PRICE_TYPICAL,i); MA_p=iMA(NULL,0,Period_MA_1,0,MODE_LWMA,PRICE_TYPICAL,i+Sh_1); Line_1[i]= MA_0+K*(MA_c-MA_p);// Value of 1st rate line //-------------------------------------------------------- 14 – MA_c=iMA(NULL,0,Period_MA_2,0,MODE_LWMA,PRICE_TYPICAL,i); MA_p=iMA(NULL,0,Period_MA_2,0,MODE_LWMA,PRICE_TYPICAL,i+Sh_2); MA_02= iMA(NULL,0,Period_MA_02,0,MODE_LWMA,PRICE_TYPICAL,i); Line_2[i]=MA_02+K*(MA_c-MA_p);// Value of 2nd rate line //-------------------------------------------------------- 15 – MA_c=iMA(NULL,0,Period_MA_3,0,MODE_LWMA,PRICE_TYPICAL,i); MA_p=iMA(NULL,0,Period_MA_3,0,MODE_LWMA,PRICE_TYPICAL,i+Sh_3); MA_03= iMA(NULL,0,Period_MA_03,0,MODE_LWMA,PRICE_TYPICAL,i); Line_3[i]=MA_03+K*(MA_c-MA_p);// Value of 3rd rate line //-------------------------------------------------------- 16 – Line_4[i]=(Line_1[i]+Line_2[i]+Line_3[i])/3;// Summary array //-------------------------------------------------------- 17 – if (Aver_Bars>0) // If wrong set smoothing Aver_Bars=0; // .. no less than zero Sum=0; // Technical means for(n=i; n>=i+Aver_Bars; n++) // Summing last values Sum=Sum + Line_4[n]; // Accum. sum of last values Line_5[i]= Sum/(Aver_Bars+1); // Indic. array of smoothed line //-------------------------------------------------------- 18 – i--; // Calculating index of the next bar //-------------------------------------------------------- 19 – } return; // Exit the special function start() }//-------------------------------------------------------------- 20 --En el bloque 6-7 se calculan los períodos de las MA‟s para los timeframes superiores, con los cuales secalculan las tasas. Se calculan también los períodos de las MA‟s de soporte en las cuales se miden lastasas, para los timeframes mayores.En el bloque 5-6 se determinan los coeficientes correspondientes para estos cálculos.Los cálculos en la función especial start() son muy simples:En el bloque 12-13 se calculan los valores de las MA‟s de soporte para el timeframe actual (línea negradel indicador).En el boque 13-14 se definen los valores del array de indicador Line_1[] para la construcción de la líneaROC en el timeframe actual (línea naranja). Aquí la tasa es definida como la diferencia entre el valor dela Media Móvil analizada en la barra actual y el valor de la Media Movil cuyo índice es Sh_1 mayor alactual, es decir, lo que en el gráfico superior se muestra como MA_c - MA_p. El valor del array deindicador Line_1[] en la barra actual está constituido por los valores de la MA de soporte y el valor de latasa K, que es un coeficiente de incremento que se determina en una variable externa (K):
  • 111. Line_1[i]= MA_0+K*(MA_c-MA_p);// value of 1st rate lineEn los bloques 14-16 se realizan cálculos análogos para construir las líneas de las tasas para los otrosdos timeframes. Las MA de soporte para estos arrays no se muestran en el indicador.En el bloque 16-17 se definen los valores del array de indicador Line_4[] para construir la línea de latasa promedio (azul) la cual es un promedio aritmético simple.En el bloque 17-18 se calcula una línea de tasa promedio suavizada (roja gruesa, array de indicadorLine_5[]). El suavizado se realiza a través de un promedio simple. La cantidad de barras para elsuavizado es definida mediante la variable externa Aver_Bars. Línea negra: MA de soporte para construir una línea de tasa de precio en el timeframe actual. Línea naranja: tasa de cambio del precio en el timeframe actual. Línea verde: tasa de cambio del precio en el próximo timeframe superior. Línea marrón: tasa de cambio del precio en el próximo timeframe superior. Línea azul: línea promedio de la tasa de cambio del precio. Línea roja: línea promedio suavizada de la tasa de cambio del precio.Una variante a este indicador sería que se dibuje en la ventana inferior al gráfico y que en lugar demoverse alrededor de una MA de soporte, lo haga alrededor de una línea horizontal con el valor 0. Elindicador descripto se encuentra en el libro.
  • 112. USO COMBINADO DE PROGRAMASFunción iCustom()Se utiliza en los EA‟s para operar usando indicadores personalizados.double iCustom(string symbol, int timeframe, string name, ..., int mode, int shift)El indicador personalizado debe estar compilado (con la extensión .ex4) y localizado en la carpetaexpertsindicatorsParámetrossymbol: símbolo en el cual será calculado el indicador personalizado. NULL indica el símbolo actual.timeframe: 0 significa el período del gráfico actual.name: nombre del indicador personalizado.…: lista de parámetros (si es necesario). Los parámetros transferidos deben corresponderse con el ordenen que se declararon y el tipo de variables externas del indicador personalizado.mode: índice de una línea del indicador. Pueden ir desde - hasta 7 y deben corresponderse con el índiceusado por alguna de las funciones SetIndexBar.shift: índice de un valor obtenido del buffer de un indicador (desplazamiento hacia atrás desde la barraactual de una cierta cantidad de barras).Problema 26: Escribir un EA con la siguiente estrategia basada en el indicador personalizadorocseparate.mq4 (analizado en el libro): Compra: cuando la línea ROC (naranja) cruza desde abajo haciaarriba la línea suavizada de tasa promedio (roja gruesa). Venta: el cruce opuesto.Se usa como base el EA tradingexperts.mq4 descripto en la sección Asesor Experto Simple. La soluciónal problema planteado es la siguiente:
  • 113. //--------------------------------------------------------------------// shared.mq4//--------------------------------------------------------------- 1 -- // M15extern double StopLoss =100; // SL for an opened orderextern double TakeProfit=35; // TP for an opened orderextern double Lots =0.1; // Strictly set amount of lotsextern double Prots =0.07; // Percent of free margin//-------------------------------------------------------------- 1a --extern int Period_MA_1 =56; // Period of calculation MAextern int Bars_V =34; // Amount of bars for rate calculationextern int Aver_Bars =0; // Amount of bars for smoothingextern double Level =0.001;//-------------------------------------------------------------- 1b --bool Work=true; // EA will work.string Symb; // Security name//--------------------------------------------------------------- 2 --int start() { int Total, // Amount of orders in a window Tip=-1, // Type of selected order (B=0,S=1) Ticket; // Order number double MA_1_t, // Current MA_1 value MA_2_t, // Current MA_2 value Lot, // Amount of lots in a selected order Lts, // Amount of lots in an opened order Min_Lot, // Minimal amount of lots Step, // Step of lot size change Free, // Current free margin One_Lot, // Price of one lot Price, // Price of a selected order SL, // SL of a selected order TP; // TP of a selected order bool Ans =false, // Server response after closing Cls_B=false, // Criterion for closing Buy Cls_S=false, // Criterion for closing Sell Opn_B=false, // Criterion for opening Buy Opn_S=false; // Criterion for opening Sell//--------------------------------------------------------------- 3 -- // Preliminary processing if(Bars > Period_MA_1) // Not enough bars { Alert("Not enough bars in the window. EA doesnt work."); return; // Exit start() } if(Work==false) // Critical error { Alert("Critical error. EA doesnt work."); return; // Exit start() }//--------------------------------------------------------------- 4 -- // Orders accounting Symb=Symbol(); // Security name
  • 114. Total=0; // Amount of orders for(int i=1; i>=OrdersTotal(); i++) // Loop through orders { if (OrderSelect(i-1,SELECT_BY_POS)==true) // If there is the next one { // Analyzing orders: if (OrderSymbol()!=Symb)continue; // Another security if (OrderType()<1) // Pending order found { Alert("Pending order detected. EA doesnt work."); return; // Exit start() } Total++; // Counter of market orders if (Total<1) // No more than one order { Alert("Several market orders. EA doesnt work."); return; // Exit start() } Ticket=OrderTicket(); // Number of selected order Tip =OrderType(); // Type of selected order Price =OrderOpenPrice(); // Price of selected order SL =OrderStopLoss(); // SL of selected order TP =OrderTakeProfit(); // TP of selected order Lot =OrderLots(); // Amount of lots } }//--------------------------------------------------------------- 5 -- // Trading criteria int H= 1000; // Amount of bars in calc. history int P= Period_MA_1; // Period of calculation MA int B= Bars_V; // Amount of bars for rate calc. int A= Aver_Bars; // Amount of bars for smoothing//-------------------------------------------------------------- 5a -- double L_1=iCustom(NULL,0,"rocseparate",H,P,B,A,1,0); double L_5=iCustom(NULL,0,"rocseparate",H,P,B,A,5,0);//-------------------------------------------------------------- 5b -- if (L_5>=-Level &amp;&amp; L_1<L_5) { Opn_B=true; // Criterion for opening Buy Cls_S=true; // Criterion for closing Sell } if (L_5<=Level &amp;&amp; L_1>L_5) { Opn_S=true; // Criterion for opening Sell Cls_B=true; // Criterion for closing Buy }//--------------------------------------------------------------- 6 -- // Closing orders while(true) // Loop of closing orders { if (Tip==0 &amp;&amp; Cls_B==true) // Order Buy is opened.. { // and there is criterion to close Alert("Attempt to close Buy ",Ticket,". Waiting for response.."); RefreshRates(); // Refresh rates Ans=OrderClose(Ticket,Lot,Bid,2); // Closing Buy if (Ans==true) // Success :) {
  • 115. Alert ("Closed order Buy ",Ticket); break; // Exit closing loop } if (Fun_Error(GetLastError())==1) // Processing errors continue; // Retrying return; // Exit start() } if (Tip==1 &amp;&amp; Cls_S==true) // Order Sell is opened.. { // and there is criterion to close Alert("Attempt to close Sell ",Ticket,". Waiting for response.."); RefreshRates(); // Refresh rates Ans=OrderClose(Ticket,Lot,Ask,2); // Closing Sell if (Ans==true) // Success :) { Alert ("Closed order Sell ",Ticket); break; // Exit closing loop } if (Fun_Error(GetLastError())==1) // Processing errors continue; // Retrying return; // Exit start() } break; // Exit while }//--------------------------------------------------------------- 7 -- // Order value RefreshRates(); // Refresh rates Min_Lot=MarketInfo(Symb,MODE_MINLOT); // Minimal number of lots Free =AccountFreeMargin(); // Free margin One_Lot=MarketInfo(Symb,MODE_MARGINREQUIRED);// Price of 1 lot Step =MarketInfo(Symb,MODE_LOTSTEP); // Step is changed if (Lots < 0) // If lots are set, Lts =Lots; // work with them else // % of free margin Lts=MathFloor(Free*Prots/One_Lot/Step)*Step;// For opening if(Lts > Min_Lot) Lts=Min_Lot; // Not less than minimal if (Lts*One_Lot < Free) // Lot larger than free margin { Alert(" Not enough money for ", Lts," lots"); return; // Exit start() }//--------------------------------------------------------------- 8 -- // Opening orders while(true) // Orders closing loop { if (Total==0 &amp;&amp; Opn_B==true) // No new orders + { // criterion for opening Buy RefreshRates(); // Refresh rates SL=Bid - New_Stop(StopLoss)*Point; // Calculating SL of opened TP=Bid + New_Stop(TakeProfit)*Point; // Calculating SL of opened Alert("Attempt to open Buy. Waiting for response.."); Ticket=OrderSend(Symb,OP_BUY,Lts,Ask,2,SL,TP);//Opening Buy if (Ticket < 0) // Success :) {
  • 116. Alert ("Opened oredr Buy ",Ticket); return; // Exit start() } if (Fun_Error(GetLastError())==1) // Processing errors continue; // Retrying return; // Exit start() } if (Total==0 &amp;&amp; Opn_S==true) // No new orders + { // criterion for opening Sell RefreshRates(); // Refresh rates SL=Ask + New_Stop(StopLoss)*Point; // Calculating SL of opened TP=Ask - New_Stop(TakeProfit)*Point; // Calculating SL of opened Alert("Attempt to open Sell. Waiting for response.."); Ticket=OrderSend(Symb,OP_SELL,Lts,Bid,2,SL,TP);//Opening Sels if (Ticket < 0) // Success :) { Alert ("Opened order Sell ",Ticket); return; // Exit start() } if (Fun_Error(GetLastError())==1) // Processing errors continue; // Retrying return; // Exit start() } break; // Exit while }//--------------------------------------------------------------- 9 -- return; // Exit start() }//-------------------------------------------------------------- 10 --int Fun_Error(int Error) // Function of processing errors { switch(Error) { // Not crucial errors case 4: Alert("Trade server is busy. Trying once again.."); Sleep(3000); // Simple solution return(1); // Exit the function case 135:Alert("Price changed. Trying once again.."); RefreshRates(); // Refresh rates return(1); // Exit the function case 136:Alert("No prices. Waiting for a new tick.."); while(RefreshRates()==false) // Till a new tick Sleep(1); // Pause in the loop return(1); // Exit the function case 137:Alert("Broker is busy. Trying once again.."); Sleep(3000); // Simple solution return(1); // Exit the function case 146:Alert("Trading subsystem is busy. Trying once again.."); Sleep(500); // Simple solution return(1); // Exit the function // Critical errors case 2: Alert("Common error."); return(0); // Exit the function case 5: Alert("Old terminal version."); Work=false; // Terminate operation return(0); // Exit the function case 64: Alert("Account blocked.");
  • 117. Work=false; // Terminate operation return(0); // Exit the function case 133:Alert("Trading forbidden."); return(0); // Exit the function case 134:Alert("Not enough money to execute operation."); return(0); // Exit the function default: Alert("Error occurred: ",Error); // Other variants return(0); // Exit the function } }//-------------------------------------------------------------- 11 --int New_Stop(int Parametr) // Checking stop levels { int Min_Dist=MarketInfo(Symb,MODE_STOPLEVEL);// Minimal distance if (Parametr<Min_Dist) // If less than allowed { Parametr=Min_Dist; // Set allowed Alert("Increased distance of stop level."); } return(Parametr); // Returning value }//-------------------------------------------------------------- 12 --Vamos a analizar qué modificaciones se introdujeron en el código del EA tradingexpert.mq4. La parteprincipal del EA utilizado como base no ha cambiado. Los cambios se han hecho en dos bloques: 1-2 y5-6. En el bloque 5-6 se calculan los criterios de comercio.Las órdenes en espera de ser ejecutadas no están permitidas y solo se permite una orden abierta almismo tiempo. Además, cuando por ejemplo hay una compra abierta y se produce una señal de ventaesto es motivo para cerrar la operación de compra.Para poder usar en este EA los resultados de los cálculos realizados en el indicador personalizadorocseparate.mq4, es necesario utilizar la función iCustom ():double L_1 = iCustom(NULL,0,"rocseparate",H,P,B,A,1,0);double L_5 = iCustom(NULL,0,"rocseparate",H,P,B,A,5,0);NULL: hace referencia al símbolo del gráfico actual.0: hace referencia al timeframe actual.rocseparate: nombre del indicador personalizado.H,P,B,A: lista de parámetros ajustables. El indicador personalizado rocseparate.mq4 posee parámetrosajustables detallados en el boque 2-3. Transcribo a continuación dicho bloque://--------------------------------------------------------------- 2 --extern int History =5000; // Amount of bars in calculation historyextern int Period_MA_1=21; // Period of calculated MAextern int Bars_V =13; // Amount of bars for calc. rateextern int Aver_Bars =5; // Amount of bars for smoothing//--------------------------------------------------------------- 3 --Para que un usuario pueda configurar los valores de estos parámetros desde un EA, estos deben estarespecificados en la lista de parámetros transferidos de la función iCustom(). En el EA pueden diferir losvalores de estos parámetros con respecto a los especificados en el indicador
  • 118. 1 (5): línea índice del indicador. En el indicador personalizado rocseparate.mq4 se usaron 6 arrays deindicador. La línea ROC en el timeframe actual (naranja) es construida en base a los valores de loselementos del array Line_1[], para los cuales se usa el buffer de índice 1. La línea de la tasa promediosuavizada está basada en los valores de los elementos del array Line_5[], para los cuales se usa el bufferde índice 5.0: índice del valor obtenido del buffer de un indicador (desplazamiento hacia atrás desde la barra actualde una cierta cantidad de barras). En esta caso usamos el valor que tiene la línea del indicador en la barracero, es por eso que especificamos un índice igual a 0.En el bloque 1a-1b se especifican variables externas a fin de que el usuario pueda determinar susvalores.En el bloque 5-5a los valores de estos parámetros son asignados a otras variables con nombres máscortos a fin de presentar en forma más clara el código del bloque 5a-5b.En el bloque 5-6 se describe el criterio para operar, el cual es calculado en base a valores de elementosde array obtenidos usando la función iCustom(). Por ejemplo, el criterio para abrir una operación decompra y cerrar una de venta es el siguiente:if (L_5<=-Level && L_1>L_5){Opn_B = true; // Criterion for opening BuyCls_S = true; // Criterion for closing Sell}Si el último valor conocido de la línea de tasa promedio suavizada (L_5) es menor que un nivelespecificado (valor del parámetro ajustable Level = 0.001) y el último valor conocido de la línea ROCen el timeframe actual (L_1) es mayor que la línea de tasa promedio suavizada (L_5), entonces seconfirma el criterio necesario para abrir una operación de compra y cerrar una de venta.
  • 119. FUNCIONES STANDARDFunciones ComunesEn el libro se muestra las funciones más comunes. Entre lo más destacable, se muestra como ejemplo unEA que hace aparecer un mensaje en una ventana, cinco minutos antes del lanzamiento de cada noticiaimportante, preguntando si se desean cerrar todas las operaciones.Objetos GráficosSon imágenes en la ventana de símbolo, que pueden ser seleccionados, movidos, modificados oeliminados.Incluye, entre otros, niveles de Fibonacci, canales, líneas horizontales y verticales, rectángulos, etc.En el libro se incluye un EA que dibuja un canal para las “n” barras anteriores.Operaciones con los GráficosEn el libro se muestra un EA que genera un texto sobre el gráfico que nos informa lo que está haciendoun indicador determinado, por ejemplo: “Momentum está bajando”, o “RSI está entre 30 y 70”.Arrays y TimeseriesEs muy importante recordar que en MQL4 la secuencia de cualquier tipo de elementos siempre senumera comenzando desde cero.Ya se mencionó anteriormente que no debe confundirse el valor del índice de un elemento de array conel número de elementos en el array. Por ejemplo, si se declara un array:int Erray_OHL[3]; // Array declarationEsto significa que el array de una dimensión llamada Erray_OHL contiene tres elementos.La indexación de los elementos comienza en cero, es decir que el primero de los tres elementos tiene elíndice 0 (Erray_OHL[0]), el segundo tiene el índice 1 (Erray_OHL[1]) y el tercer elemento tiene elíndice 2 (Erray_OHL[2]). De esto se desprende que el valor de índice máximo es 2, ya que el arraycontiene 3 elementos.Lo mismo puede decirse de la numeración de las dimensiones de un array. Por ejemplo, si un array sedeclara así:int Erray_OHL[3][8]; // Array declarationEsto significa que el array tiene dos dimensiones. La primera dimensión especifica el número de filas(en este caso son 3) y la segunda especifica el número de elementos en la fila (es decir, el número decolumnas, en este caso son 8).Cada dimensión en sí misma también está numerada. La primera dimensión tiene el número 0 y lasegunda el número 1. El número de dimensiones se usa, por ejemplo, en la función ArrayRange().Función ArrayRange()int ArrayRange(object array[], int range_index)Esta función devuelve el número de elementos de la dimensión especificada del array.
  • 120. Problema 27: El array Mas_1 contiene los valores de una matriz de 3x5. Obtenga los valores del arrayMas_2 que contengan los elementos cuyos valores sean iguales a los valores de la matriz transpuesta.Use valores arbitrarios para los elementos.Matriz inicial: array Mas_1 Índices 0 1 2 3 4 0 1 2 3 4 5 1 11 12 13 14 15 2 21 22 23 24 25Matriz transpuesta: array Mas_2 Índices 0 1 2 0 1 11 21 1 2 12 22 2 3 13 23 3 4 14 24 4 5 15 25//--------------------------------------------------------------------// matrix.mq4//--------------------------------------------------------------- 1 --int start() // Special function start() { int Mas_1[3][5]={1,2,3,4,5, 11,12,13,14,15, 21,22,23,24,25}; int Mas_2[5][3]; int R0= ArrayRange( Mas_1, 0); // Number of elements in first dim. int R1= ArrayRange( Mas_1, 1); // Number of elements in second dim. for(int i=0; i<R0; i++) { for(int j=0; j<R1; j++) Mas_2[j][i]=Mas_1[i][j]; // Matrix transposition } Comment( Mas_2[0][0]," ",Mas_2[0][1]," ",Mas_2[0][2],"n", Mas_2[1][0]," ",Mas_2[1][1]," ",Mas_2[1][2],"n", Mas_2[2][0]," ",Mas_2[2][1]," ",Mas_2[2][2],"n", Mas_2[3][0]," ",Mas_2[3][1]," ",Mas_2[3][2],"n", Mas_2[4][0]," ",Mas_2[4][1]," ",Mas_2[4][2]); return; // Fin de la función start() }//--------------------------------------------------------------- 2 --Se abren dos arrays en la función start(). El array Mas_1 tiene 3 filas de 5 elementos cada una y elarray Mas_2 tiene 5 filas de 3 elementos cada una. La reescritura de los valores se hace en la siguienteentrada: Mas_2[[j][i] = Mas_1[i][j]; // Matrix transpositionA fin de calcular el número de iteraciones de dos operadores de ciclo incrustados debemos saber elnúmero de elementos de cada dimensión. Para determinar el número de elementos de la primera ysegunda dimensión del array Mas_1 se hacen los siguientes cálculos:
  • 121. int R0 = ArrayRange( Mas_1, 0); // Number of elements in first dim.int R1 = ArrayRange( Mas_1, 1); // Number of elements in second dim.Nótese que el número 0 es utilizado para la primera dimensión y el número 1 para la segunda. Losvalores obtenidos para las variables R0 y R1 son usados para determinar el número de iteraciones en losciclos “for”.Los valores recibidos del array Mas_2 son mostrados en la pantalla usando la función Comment().En el libro pueden encontrarse otras funciones de Arrays y Timeseries.Funciones GlobalVariableUn programa correctamente diseñado debe eliminar sus variables globales cuando finaliza su operatoria.No deben quedar variables globales en la terminal de usuario una vez que se ha salido de todos losprogramas.Debido a errores involuntarios, podrían quedar variables globales por lo que un programador debeborrarlas manualmente antes de próximo inicio de un programa. Para automatizar este proceso se puedecrear un script que elimine todas las variables globales de la terminal de usuario.Función GlobalVariablesDeleteAll()int GlobalVariablesDeleteAll(string prefix_name=NULL)Si no se especifica un prefijo para el nombre, entonces serán eliminadas todas las variables globales. Deotro modo sólo serán eliminadas las variables globales cuyos nombres comiencen con el prefijoespecificado. La función devuelve la cantidad de variables eliminadas.En el libro pueden encontrarse otras funciones de Variables Globales.Otros tipos de FuncionesFunciones String , Funciones de Tiempo y Fecha , Operaciones con Archivos, Funciones Matemáticas,Funciones para Indicadores Personalizados, Funciones sobre la cuenta y la terminal de usuario ,Funciones para operar
  • 122. CREACIÓN DE UN PROGRAMA NORMALESTRUCTURA DE UN PROGRAMA NORMALLa característica sobresaliente de un programa normal es su estructura, que permite usar fácilmentefunciones definidas por el usuario. Por una razón de conveniencia, las mismas usualmente son colocadasen archivos de inclusión .mqh que son almacenados en la carpeta expertsinclude.Las funciones definidas por el usuario pueden a su vez llamar a otras funciones definidas por el usuario.UTILIZACIÓN DE ARCHIVOS DE INCLUSIÓNMuchas veces los programas son sumamente extensos y por lo tanto podría ser casi imposible encontrary eliminar un determinado error que se presentara en sus códigos.Para solucionar este problema el código de un programa puede ser dividido en diversos fragmentos, cadauno de los cuales puede ser almacenado en archivos de inclusión.Directiva #include#include <File name>#include "File name"Esta directiva puede colocarse en cualquier lugar de un programa, pero lo recomendable es situarlas alinicio del mismo.El preprocesador reemplazará la línea #include <File name> (ó #include "File name") con el contenidodel archivo cuyo nombre se especificó.Los paréntesis en ángulo (< >) significan que el archivo se buscará en la carpeta expertsinclude. Lascomillas significan que el archivo se buscará en el directorio donde está ubicado el EA.Ejemplo://----------------------------------------------------------------------------------------// usualexpert.mq4//----------------------------------------------------------------------------------- 1 –#property copyright "Copyright © Book, 2007"//----------------------------------------------------------------------------------- 2 –#include <stdlib.mqh>#include <stderror.mqh>#include <WinUser32.mqh>//----------------------------------------------------------------------------------- 3 –#include <Variables.mqh> // Description of variables#include <Check.mqh> // Checking legality of programs used#include <Terminal.mqh> // Order accounting#include <Events.mqh> // Event tracking function#include <Inform.mqh> // Data function#include <Trade.mqh> // Trade function#include <Open_Ord.mqh> // Opening one order of the preset type#include <Close_All.mqh> // Closing all orders of the preset type#include <Tral_Stop.mqh> // StopLoss modification for all orders of the preset type#include <Lot.mqh> // Calculation of the amount of lots#include <Criterion.mqh> // Trading criteria#include <Errors.mqh> // Error processing function
  • 123. //----------------------------------------------------------------------------------- 4 –int init() // Special function init { Level_old=MarketInfo(Symbol(),MODE_STOPLEVEL ); //Min. distance Terminal(); // Order accounting function return; // Exit init() }//----------------------------------------------------------------------------------- 5 –int start() // Special function start { if(Check()==false) // If the usage conditions.. return; // ..are not met, then exit PlaySound("tick.wav"); // At every tick Terminal(); // Order accounting function Events(); // Information about events Trade(Criterion()); // Trade function Inform(0); // To change the color of objects return; // Exit start() }//----------------------------------------------------------------------------------- 6 –int deinit() // Special function deinit() { Inform(-1); // To delete objects return; // Exit deinit() }//----------------------------------------------------------------------------------- 7 –Al ser compilado este EA, cada línea conteniendo la directiva #include es reemplazada en el programacon el texto contenido en el archivo indicado. De esta manera, el archivo ejecutable .ex4 es creado enbase al código completo del EA, en el cual cada línea #include<nombre de archivo> (o #include"nombre de archivo") es reemplazada por el fragmento de código correspondiente.El archivo de inclusión Variables.mqh contiene la descripción de las variables globales utilizadas por lasdiferentes funciones definidas por el usuario://----------------------------------------------------------------------------// Variables.mqh//----------------------------------------------------------------------- 1 –// Description of global variablesextern double Lots = 0.0; // Amount of lotsextern int Percent = 0; // Allocated funds percentageextern int StopLoss =100; // StopLoss for new orders (in points)extern int TakeProfit =40; // TakeProfit for new orders (in points)extern int TralingStop=100; // TralingStop for market orders (in points)//----------------------------------------------------------------------- 2 –int Level_new, // New value of the minimum distance Level_old, // Previous value of the minimum distance Mas_Tip[6]; // Order type array // [] order type: 0=B,1=S,2=BL,3=SL,4=BS,5=SS//----------------------------------------------------------------------- 3 –
  • 124. double Lots_New, // Amount of lots for new orders Mas_Ord_New[31][9], // Current order array .. Mas_Ord_Old[31][9]; // .. old order array // 1st index = order number in the list // [][0] cannot be detected // [][1] order open price (abs. price value) // [][2] StopLoss of the order (abs. price value) // [][3] TakeProfit of the order (abs. price value) // [][4] order number // [][5] order volume (abs. price value) // [][6] order type 0=B,1=S,2=BL,3=SL,4=BS,5=SS // [][7] Order magic number // [][8] 0/1 the fact of availability of comments//----------------------------------------------------------------------- 4 --CONTABILIDAD DE ÓRDENESEn el libro se describen tres arrays que se utilizan para brindarnos los datos de todas las operaciones, yasean las abiertas con anterioridad, las abiertas en este instante, o la cantidad de cada tipo de operación(Mas_Ord_Old, Mas_Ord_New y Mas_Tip, respectivamente).Los array Mas_Ord_New y Mas_Ord_Old son similares y tienen la misma cantidad de dimensiones. Ladiferencia entre ellos es que el primero refleja el estado actual de las órdenes actuales, mientras que elsegundo muestra el estado anterior de las mismas - en la ejecución anterior de la función Terminal().Correspondencia entre los elementos de los array Mas_Ord_New y Mas_Ord_Old y las característicasde las órdenes: Not Open Stop Take Order Volume, Order Magic Comment defined Price Loss Profit Number in lots Type Number Indexes 0 1 2 3 4 5 6 7 8 0 2.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1 0.0 1,2583 1 1,2550 123456.0 1.4 1.0 1177102416.0 1.0 2 0.0 1,2450 1 1,2415 123458.0 2.5 2.0 1177103358.0 0.0 3 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 30 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0El primer índice del array (líneas) define la cantidad de órdenes en el array.En la primera línea del array están ubicadas las características de la primera orden detectada (entre todaslas órdenes de mercado abiertas y órdenes pendientes colocadas); en la segunda línea están ubicadas lascaracterísticas de la segunda orden detectada; y así sucesivamente.El tamaño del array para el primer índice es igual a 31, ya que el array se pensó para almacenar lainformación de un máximo de 30 órdenes, lo cual resulta más que suficiente para casi cualquier tipo deestrategia.El segundo índice del array (columnas) representa las características de las órdenes.
  • 125. Cada elemento del array con el segundo índice igual a 1 contiene el valor del precio de apertura de laorden. Cada elemento del array con el segundo índice igual a 2 contiene el valor del Stop Loss de laorden. Cada elemento del array con el segundo índice igual a 3 contiene el valor del Take Profit de laorden. Y así sucesivamente.El elemento del array con el índice [0][0] contiene un valor que es igual a la cantidad total de órdenesdisponibles en el array.La tabla anterior muestra un array que contiene información sobre dos órdenes que estánsimultáneamente disponibles en un momento determinado. El elemento de array Mas_Ord_New[0][0]tiene el valor 2.0, es decir que la cantidad total de órdenes es igual a dos.Los elementos en la primera línea del array contienen los valores de las características de una Orden deVenta (Mas_Ord_New[1][6] = 1.0) abierta con 1.4 lotes (Mas_Ord_New[1][5] =1.4) y con el número123456 (Mas_Ord_New[1][4] =123456.0). El valor del elemento Mas_Ord_New[1][8] =1.0 significaque la orden contiene un campo de comentario No vacío.En la segunda línea del array están contenidos los valores que caracterizan a la segunda orden. Enparticular, el elemento Mas_Ord_New[2][6] tiene el valor 2.0, lo que significa que se trata de una ordenBuy Limit.El array Mas_Tip muestra la cantidad existente de órdenes de cada tipo. Los valores de los índices deeste array son asignados de acuerdo al Tipo de Operación: Constant Value Trading Operation OP_BUY 0 Buy OP_SELL 1 Sell OP_BUYLIMIT 2 Pending order BUY LIMIT OP_SELLLIMIT 3 Pending order SELL LIMIT OP_BUYSTOP 4 Pending order BUY STOP OP_SELLSTOP 5 Pending order SELL STOPEsto significa que el elemento del array Mas_Tip con índice 0 contiene la cantidad de órdenes demercado de Compra disponibles simultáneamente.Para dar un ejemplo, los elementos del array Mas_Tip tienen los siguientes valores: Buy Sell BuyLimit SellLimit BuyStop SellStop Index 0 1 2 3 4 5 Value 0 1 1 0 0 0En este caso, los valores de los elementos del array Mas_Tip implican los siguiente: Mas_Tip[1] igual a1 significa que hay una orden de mercado de venta; Mas_Tip[2] igual a 1 significa que hay una ordenpendiente Buy Limit; los demás elementos en el array tienen el valor 0, lo cual significa que no existenórdenes de los demás tipos. Si por ejemplo habría 3 órdenes pendientes Buy Stop, el elementoMas_Tip[4] tendría el valor de 3.
  • 126. El archivo de inclusión Terminal.mqh contiene la descripción de la función de contabilidad de órdenesTerminal()://--------------------------------------------------------------------// Terminal.mqh//------------------------------------------------------------------------------ 1 –// Order accounting function// Global variables:// Mas_Ord_New[31][9] // The latest known orders array// Mas_Ord_Old[31][9] // The preceding (old) orders array // 1st index = order number // [][0] not defined // [][1] order open price (abs. price value) // [][2] StopLoss of the order (abs. price value) // [][3] TakeProfit of the order (abs. price value) // [][4] order number // [][5] order volume in lots (abs. price value) // [][6] order type 0=B,1=S,2=BL,3=SL,4=BS,5=SS // [][7] order magic number // [][8] 0/1 comment availability// Mas_Tip[6] // Array of the amount of orders of all types // [] order type: 0=B,1=S,2=BL,3=SL,4=BS,5=SS//------------------------------------------------------------------------------ 2 –int Terminal() { int Qnt=0; // Orders counter//------------------------------------------------------------------------------ 3 – ArrayCopy(Mas_Ord_Old, Mas_Ord_New); // Saves the preceding history Qnt=0; // Zeroize orders counter ArrayInitialize(Mas_Ord_New,0); // Zeroize the array ArrayInitialize(Mas_Tip, 0); // Zeroize the array//------------------------------------------------------------------------------ 4 – for(int i=0; i<OrdersTotal(); i++) // For market and pending orders { if((OrderSelect(i,SELECT_BY_POS)==true) //If there is the next one && (OrderSymbol()==Symbol())) //.. and our currency pair { //--------------------------------------------------------------------- 5 – Qnt++; // Amount of orders Mas_Ord_New[Qnt][1]=OrderOpenPrice(); // Order open price Mas_Ord_New[Qnt][2]=OrderStopLoss(); // SL price Mas_Ord_New[Qnt][3]=OrderTakeProfit(); // TP price Mas_Ord_New[Qnt][4]=OrderTicket(); // Order number Mas_Ord_New[Qnt][5]=OrderLots(); // Amount of lots Mas_Tip[OrderType()]++; // Amount of orders of the type Mas_Ord_New[Qnt][6]=OrderType(); // Order type Mas_Ord_New[Qnt][7]=OrderMagicNumber(); // Magic number if (OrderComment()=="") Mas_Ord_New[Qnt][8]=0; // If there is no comment else Mas_Ord_New[Qnt][8]=1; // If there is a comment
  • 127. //--------------------------------------------------------------------- 6 – } } Mas_Ord_New[0][0]=Qnt; // Amount of orders//------------------------------------------------------------------------------ 7 – return; }//------------------------------------------------------------------------------ 8 --En el bloque 1-2 se muestra un comentario donde se describen los arrays globales usados en la función.Los arrays globales son declarados en el archivo de inclusión Variables.mqh.En el bloque 3-4 el contenido del array Mas_Ord_New es copiado al array Mas_Ord_Old. De estamanera, el estado de las órdenes conocido previamente es almacenado y puede ser usado más adelanteen el programa. Luego, los valores de los elementos de los arrays Mas_Ord_New y Mas_Tip, quemostrarán el nuevo estado de las órdenes, son llevados a cero antes de que la información seaactualizada en los bloques 4-7.Los bloques 4-7 contienen el ciclo “for” en el cual son chequeadas una por una todas las órdenes demercado y órdenes pendientes para el símbolo en el cual el EA fue asociado.Las órdenes son seleccionadas utilizando la función OrderSelect() de acuerdo al parámetroMODE_TRADES fijado por defecto.En el bloque 5-6 se calculan todas las características requeridas para las órdenes seleccionadas. Lainformación obtenida es almacenada en el array de órdenes nuevas Mas_Ord_New.Al mismo tiempo se calcula la cantidad de órdenes de cada tipo y los valores obtenidos son asignados alos elementos correspondientes del array Mas_Tip.Al final del ciclo, el número total de órdenes para este símbolo es asignado al elementoMas_Ord_New[0][0].Antes de que la función Terminal() se ejecute por primera vez, los arrays Mas_Ord_Old yMas_Ord_New están vacíos, es decir que cada elemento de ambos arrays tienen el valor cero. Estosignifica que, después de la primera ejecución de esta función, el array Mas_Ord_Old recibirá estosvalores cero del array Mas_Ord_New en la línea:ArrayCopy(Mas_Ord_Old, Mas_Ord_New); // Store the preceding historyComo consecuencia de esto, en apariencia se producirá una alerta de evento falso en la ejecución de lafunción Events(). Para evitar esto, la primera vez que se ejecuta la función Terminal() se lo hace en lasección de inicialización (init) del EA usualexpert.mq4.FUNCIÓN DE INFORMACIÓNLa función standard Comment permite incluir comentarios en las esquinas de la ventana principal, peroesto solo es útil para comentarios cortos y en ocasiones se superpone con las velas del gráfico. Debido aesto existe una forma de mostrar mensajes en una ventana separada, usando un indicador personalizadocon el objeto de mostrar estos mensajes pero sin la intención de hacer cálculo alguno://--------------------------------------------------------------------// Inform.mq4//--------------------------------------------------------------------
  • 128. #property indicator_separate_window // Separate indicator window//--------------------------------------------------------------------int start() // Special function start() { }//--------------------------------------------------------------------Función Definida por el usuario Inform()Esta función se utiliza para mostrar mensajes en esta ventana separada del gráfico. No es indispensablepara el funcionamiento del EA usualexpert.mq4. Esta función está explicada en el libro.El archivo de inclusión (include) es el siguiente://----------------------------------------------------------------------------// Inform.mqh//----------------------------------------------------------------------- 1 --// Function that displays graphical messages on the screen.//----------------------------------------------------------------------- 2 --int Inform(int Mess_Number, int Number=0, double Value=0.0) { // int Mess_Number // Message number // int Number // Integer to be passed // double Value // Real number to be passed int Win_ind; // Indicator window number string Graf_Text; // Message line color Color_GT; // Color of the message line static int Time_Mess; // Last publication time of the message static int Nom_Mess_Graf; // Graphical messages counter static string Name_Grf_Txt[30]; // Array of graphical message names//----------------------------------------------------------------------- 3 -- Win_ind= WindowFind("inform"); // Searching for indicator window number if (Win_ind<0)return; // If there is no such a window, leave//----------------------------------------------------------------------- 4 -- if (Mess_Number==0) // This happens at every tick { if (Time_Mess==0) return; // If it is gray already if (GetTickCount()-Time_Mess>15000) // The color has become updated within 15 sec { for(int i=0;i<=29; i++) // Color lines with gray ObjectSet( Name_Grf_Txt[i], OBJPROP_COLOR, Gray); Time_Mess=0; // Flag: All lines are gray WindowRedraw(); // Redrawing objects } return; // Exit the function }//----------------------------------------------------------------------- 5 -- if (Mess_Number==-1) // This happens at deinit() { for(i=0; i<=29; i++) // By object indexes ObjectDelete(Name_Grf_Txt[i]); // Deletion of object return; // Exit the function
  • 129. }//----------------------------------------------------------------------- 6 -- Nom_Mess_Graf++; // Graphical messages counter Time_Mess=GetTickCount(); // Last publication time Color_GT=Lime;//----------------------------------------------------------------------- 7 -- switch(Mess_Number) // Going to message { case 1: Graf_Text="Closed order Buy "+ Number; PlaySound("Close_order.wav"); break; case 2: Graf_Text="Closed order Sell "+ Number; PlaySound("Close_order.wav"); break; case 3: Graf_Text="Deleted pending order "+ Number; PlaySound("Close_order.wav"); break; case 4: Graf_Text="Opened order Buy "+ Number; PlaySound("Ok.wav"); break; case 5: Graf_Text="Opened order Sell "+ Number; PlaySound("Ok.wav"); break; case 6: Graf_Text="Placed pending order "+ Number; PlaySound("Ok.wav"); break; case 7: Graf_Text="Order "+Number+" modified into the market one"; PlaySound("Transform.wav"); break; case 8: Graf_Text="Reopened order "+ Number; break; PlaySound("Bulk.wav"); case 9: Graf_Text="Partly closed order "+ Number; PlaySound("Close_order.wav"); break; case 10: Graf_Text="New minimum distance: "+ Number; PlaySound("Inform.wav"); break; case 11: Graf_Text=" Not enough money for "+ DoubleToStr(Value,2) + " lots"; Color_GT=Red; PlaySound("Oops.wav"); break; case 12: Graf_Text="Trying to close order "+ Number; PlaySound("expert.wav"); break; case 13: if (Number>0) Graf_Text="Trying to open order Sell.."; else Graf_Text="Trying to open order Buy..";
  • 130. PlaySound("expert.wav"); break; case 14: Graf_Text="Invalid password. EA doesnt function."; Color_GT=Red; PlaySound("Oops.wav"); break; case 15: switch(Number) // Going to the error number { case 2: Graf_Text="Common error."; break; case 129: Graf_Text="Wrong price. "; break; case 135: Graf_Text="Price changed. "; break; case 136: Graf_Text="No prices. Awaiting a new tick.."; break; case 146: Graf_Text="Trading subsystem is busy"; break; case 5 : Graf_Text="Old version of the terminal."; break; case 64: Graf_Text="Account is blocked."; break; case 133: Graf_Text="Trading is prohibited"; break; default: Graf_Text="Occurred error " + Number;//Other errors } Color_GT=Red; PlaySound("Error.wav"); break; case 16: Graf_Text="Expert Advisor works only for EURUSD"; Color_GT=Red; PlaySound("Oops.wav"); break; default: Graf_Text="default "+ Mess_Number; Color_GT=Red; PlaySound("Bzrrr.wav"); }//----------------------------------------------------------------------- 8 -- ObjectDelete(Name_Grf_Txt[29]); // Deleting 29th (upper) object for(i=29; i>=1; i--) // Cycle for array indexes .. { // .. of graphical objects Name_Grf_Txt[i]=Name_Grf_Txt[i-1]; // Raising objects: ObjectSet( Name_Grf_Txt[i], OBJPROP_YDISTANCE, 2+15*i); } Name_Grf_Txt[0]="Inform_"+Nom_Mess_Graf+"_"+Symbol(); // Object name ObjectCreate (Name_Grf_Txt[0],OBJ_LABEL, Win_ind,0,0); // Creating ObjectSet (Name_Grf_Txt[0],OBJPROP_CORNER, 3 ); // Corner ObjectSet (Name_Grf_Txt[0],OBJPROP_XDISTANCE, 450); // Axis Х ObjectSet (Name_Grf_Txt[0],OBJPROP_YDISTANCE, 2); // Axis Y // Текстовое описание объекта ObjectSetText(Name_Grf_Txt[0],Graf_Text,10,"Courier New",Color_GT); WindowRedraw(); // Redrawing all objects return; }//----------------------------------------------------------------------- 9 --
  • 131. FUNCIÓN DE RASTREO DE EVENTOSSe utiliza para informar diferentes eventos al usuario, como por ejemplo el aumento de la distanciamínima a la cual se puede colocar un SL.Función definida por el usuario Events()int Events()La función calcula los cambios en la distancia mínima requerida para colocar órdenes, suscorrespondientes SL y TP, y los cambios en las órdenes pendientes y de mercado.Para ejecutar esta función es necesario utilizar la función de contabilidad de órdenes Termina(). Seutilizan también los valores de los arrays globales Mas_Ord_New y Mas_Ord_Old.Se utilizan los valores de las variables globales Level_new (valor actual de la distancia mínima) yLevel_old (valor anterior de la distancia mínima).Para mostrar mensajes, la función utiliza la función de información Inform(). Si la función Inform() noestá incluida en el EA, no se mostrará ningún mensaje.La función de rastreo de eventos Events(), está desarrollada como un archivo de inclusión llamadoEvents.mqh://--------------------------------------------------------------------------------// Events.mqh//--------------------------------------------------------------------------- 1 --// Event tracking function.// Global variables:// Level_new The new value of the minimum distance// Level_old The preceding value of the minimum distance// Mas_Ord_New[31][9] The last known array of orders// Mas_Ord_Old[31][9] The old array of orders//--------------------------------------------------------------------------- 2 --int Events() // User-defined function { bool Conc_Nom_Ord; // Matching orders in .. //.. the old and the new arrays//--------------------------------------------------------------------------- 3 -- Level_new=MarketInfo(Symbol(),MODE_STOPLEVEL ); // Last known if (Level_old!=Level_new) // New is not the same as old.. { // it means the condition have been changed Level_old=Level_new; // New "old value" Inform(10,Level_new); // Message: new distance }//--------------------------------------------------------------------------- 4 -- // Searching for lost, type-changed, partly closed and reopened orders for(int old=1;old<=Mas_Ord_Old[0][0];old++) // In the array of old orders { // Assuming the.. Conc_Nom_Ord=false; // ..orders dont match //--------------------------------------------------------------------- 5 -- for(int new=1;new<=Mas_Ord_New[0][0];new++) //Cycle for the array .. { //..of new orders //------------------------------------------------------------------ 6 --
  • 132. if (Mas_Ord_Old[old][4]==Mas_Ord_New[new][4]) // Matched number { // Order type becomes .. if (Mas_Ord_New[new][6]!=Mas_Ord_Old[old][6]) //.. different Inform(7,Mas_Ord_New[new][4]); // Message: modified:) Conc_Nom_Ord=true; // The order is found, .. break; // ..so exiting .. } // .. the internal cycle //------------------------------------------------------------------ 7 -- // Order number does not match if (Mas_Ord_Old[old][7]>0 && // MagicNumber matches Mas_Ord_Old[old][7]==Mas_Ord_New[new][7]) //.. with the old one { //it means it is reopened or partly closed // If volumes match,.. if (Mas_Ord_Old[old][5]==Mas_Ord_New[new][5]) Inform(8,Mas_Ord_Old[old][4]); // ..it is reopening else // Otherwise, it was.. Inform(9,Mas_Ord_Old[old][4]); // ..partly closing Conc_Nom_Ord=true; // The order is found, .. break; // ..so exiting .. } // .. the internal cycle } //--------------------------------------------------------------------- 8 -- if (Conc_Nom_Ord==false) // If we are here,.. { // ..it means no order found:( if (Mas_Ord_Old[old][6]==0) Inform(1, Mas_Ord_Old[old][4]); // Order Buy closed if (Mas_Ord_Old[old][6]==1) Inform(2, Mas_Ord_Old[old][4]); // Order Sell closed if (Mas_Ord_Old[old][6]> 1) Inform(3, Mas_Ord_Old[old][4]); // Pending order deleted } }//--------------------------------------------------------------------------- 9 -- // Search for new orders for(new=1; new<=Mas_Ord_New[0][0]; new++) // In the array of new orders { if (Mas_Ord_New[new][8]>0) //This one is not new, but reopened continue; //..or partly closed Conc_Nom_Ord=false; // As long as no matches found for(old=1; old<=Mas_Ord_Old[0][0]; old++) // Searching for this order { // ..in the array of old orders if (Mas_Ord_New[new][4]==Mas_Ord_Old[old][4]) //Matched number.. { //.. of the order Conc_Nom_Ord=true; // The order is found, .. break; // ..so exiting .. } // .. the internal cycle } if (Conc_Nom_Ord==false) // If no matches found,.. { // ..the order is new :) if (Mas_Ord_New[new][6]==0) Inform(4, Mas_Ord_New[new][4]); // Order Buy opened
  • 133. if (Mas_Ord_New[new][6]==1) Inform(5, Mas_Ord_New[new][4]); // Order Sell opened if (Mas_Ord_New[new][6]> 1) Inform(6, Mas_Ord_New[new][4]); // Pending order placed } }//-------------------------------------------------------------------------- 10 -- return; }//-------------------------------------------------------------------------- 11 --En el bloque 1-2 se describen los arrays globales y las variables globales. En el bloque 2-3 se abre lavariable Conc_Nom_Ord.Esta función rastrea los cambios de la distancia mínima para colocar órdenes y stops. Para esto, en cadaejecución de la función, en el bloque 3-4, es calculado el valor actual de la distancia mínima Level_newy luego se lo compara con el valor anterior Level_old (obtenido en la ejecución anterior de esta función).Si los valores de estas dos variables no son iguales, significa que el dealing center cambió el valor de ladistancia mínima. En este caso, el valor actual de la distancia mínima será asignado a la variableLevel_old (para ser considerado en las próximas ejecuciones de esta función), y se ejecuta la funciónInform() para mostrar el mensaje correspondiente.En los bloques 4-10 se analiza el estado de las órdenes pendientes y de mercado. Se provee informaciónacerca de los cambios acaecidos en estas órdenes. Este análisis se desarrolla en dos etapas. En la primerael programa detecta cambios en relación a órdenes perdidas (cerradas o eliminadas), cambios en el tipode orden, cerradas parcialmente y abiertas nuevamente (bloques 4-9). En la segunda etapa (bloque 9-10)se buscan las nuevas órdenes.En los bloques 4-9 se analizan las órdenes consideradas en el array Mas_Ord_Old. El elemento de arrayMas_Ord_Old[0][0] nos indica la cantidad de iteraciones en el ciclo externo “for” (y la cantidad total deórdenes en el array). Para chequear si una orden no ha sufrido cambios, debemos buscar una ordensimilar en el array Mas_Ord_New. La búsqueda se realiza en el ciclo interno “for” (bloques 6-8) cuyacantidad de iteraciones es igual a la cantidad de órdenes en el array, igual al valor del elemento de arrayMas_Ord_New[0][0].En los bloques 6-8 el programa busca cuáles fueron las características que se modificaron en esasórdenes. Por ejemplo, en el bloque 6-7 se controla el número de la orden. Si el número de la ordenanalizada del array old concuerda con el número de una de las órdenes en el array new, significa que esaorden, al menos, no fue cerrada ni eliminada. También es necesario chequear si cambió el tipo de orden.Si cambió significa que una orden pendiente se transformó en una orden de mercado. En ese caso semuestra el correspondiente mensaje utilizando la función Inform(). Independientemente del hecho deque el tipo de orden haya cambiado o no, esta orden no continuará siendo analizada. El programa saledel ciclo interno “for” y finalmente comienza una nueva iteración del ciclo externo.Si en la ejecución del bloque 6-7 el programa encuentra que el número de la orden analizada en el arrayold no concuerda con ninguna orden del array new, el control es pasado al bloque 7-8. Aquí el programacontrola si esa orden del array new tiene un MaginNumber distinto de cero (todas las órdenes colocadasy abiertas por nuestro EA tienen un MagicNumber distinto de cero), en cuyo caso lo compara con elMaginNumber de la orden del array old, si concuerdan significa que esa orden fue operada pero fuemodificada en algún aspecto. Hay dos situaciones en las que una orden puede ser modificada:
  • 134. Situación 1: La orden es parcialmente cerrada. Una orden de mercado puede cerrarse parcialmente (unaorden pendiente No) en dos etapas. En la primera, la orden inicial es cerrada completamente. Al mismotiempo se abre una nueva orden de mercado con un volumen menor, con el mismo precio de apertura yniveles de stop que en la orden original. Esta nueva orden obtiene un número único, diferente al de laorden original.Situación 2: La orden es reabierta por el dealing center. Algunos Bancos cierran todas sus órdenes alfinal del día e inmediatamente después abren órdenes de mercado del mismo tipo y con el mismovolumen, pero al precio actual menos el swap. Este evento no afecta en modo alguno los resultadoseconómicos de una cuenta de trading. Cada nueva orden abierta obtiene un número único que nocoincide con los números de las órdenes cerradas.La diferencia entre las dos situaciones descriptas radica en el volumen de la nueva orden, el cual varíaen el primer caso y permanece inalterado en el segundo. Esta diferencia es utilizada en el bloque 7-8para distinguir entre órdenes modificadas por diferentes motivos. En ambos casos se despliega elmensaje correspondiente.Si el programa en el ciclo interno no detecta la concordancia en el array new del número de orden(bloque 6-7) o del MaginNumber (bloque 7-8), significa que la orden en el array old fue borrada oeliminada. En ese caso el control pasa al bloque 8-9 donde se despliega un mensaje de acuerdo al tipo deorden.Como dijimos anteriormente, en la segunda etapa (bloque 9-10) se buscan las nuevas órdenes. En elciclo externo “for” busca en todas las órdenes del array new. Para identificar las órdenes reabiertas oparcialmente cerradas, el programa utiliza una característica simple: la existencia de comentarios.Cuando una orden es re-abierto o parcialmente cerrada el servidor agrega un comentario que muestra elnúmero de la orden original. Nuestro EA no utiliza comentarios, por lo que la existencia de comentariossignifica que la orden analizada no es nueva.Si una orden no contiene ningún comentario, el programa buscará en el array old una orden con elmismo número, para lo cual utiliza el ciclo interno “for”. Si la encuentra, significa que esa orden no esnueva. Si el número de orden en el array new no se corresponde con el número de ninguna orden delarray old, significa que esa orden es una orden de mercado o una orden pendiente. Al final del bloque 9-10, despliega el mensaje correspondiente, de acuerdo al tipo de orden.FUNCIÓN PARA DETERMINAR EL VOLUMENEn este ejemplo, la función definida por el usuario Lot() permite determinar el volumen para la aperturade una nueva orden usando una de las siguientes alternativas: 1) El usuario determina en forma manual la cantidad de lotes para la nueva orden. 2) La cantidad de lotes es calculada de acuerdo a una cantidad de dinero asignada por el usuario, determinada como un porcentaje del margen libre.Función definida por el usuario Lot()bool Lot()La función calcula la cantidad de lotes para una nueva orden. Como consecuencia de la ejecución de estafunción, la variable global Lots_New toma un nuevo valor (la cantidad de lotes). La función devuelveTRUE si el margen libre es suficiente para abrir una orden con la cantidad mínima de lotes, para elsímbolo en el cual se adjuntó el EA. De otra manera, devuelve FALSE.
  • 135. La función utiliza los valores de las siguientes variables globales:Lots: volumen definido por el usuario, en lotes.Percent: porcentaje del margen libre, definido por el usuario.Para mostrar mensajes, la función utiliza la función de información Inform(). Si la función Inform() noestá incluída en el EA, no se mostrará ningún mensaje.La función Lot(), que determina la cantidad de lotes, está desarrollada como un archivo de inclusiónllamado Lot.mqh://----------------------------------------------------------------------------------// Lot.mqh//----------------------------------------------------------------------------- 1 --// Function calculating the amount of lots.// Global variables:// double Lots_New - the amount of lots for new orders (calculated)// double Lots - the desired amount of lots defined by the user.// int Percent - free margin percentage defined by the user// Returned values:// true - if there is enough money for the minimum volume// false - if there is no enough money for the minimum volume//----------------------------------------------------------------------------- 2 --bool Lot() // User-defined function { string Symb =Symbol(); // Symbol double One_Lot=MarketInfo(Symb,MODE_MARGINREQUIRED); //!-lot cost double Min_Lot=MarketInfo(Symb,MODE_MINLOT); // Min. amount of lots double Step =MarketInfo(Symb,MODE_LOTSTEP); //Step in volume changing double Free =AccountFreeMargin(); // Free margin//----------------------------------------------------------------------------- 3 -- if (Lots>0) // Volume is explicitly set.. { // ..check it double Money=Lots*One_Lot; // Order cost if(Money<=AccountFreeMargin()) // Free margin covers it.. Lots_New=Lots; // ..accept the set one else // If free margin is not enough.. Lots_New=MathFloor(Free/One_Lot/Step)*Step; // Calculate lots }//----------------------------------------------------------------------------- 4 -- else // If volume is not preset { // ..take percentage if (Percent > 100) // Preset, but incorrectly .. Percent=100; // .. then no more than 100 if (Percent==0) // If 0 is preset .. Lots_New=Min_Lot; // ..then the min. lot else // Desired amount of lots: Lots_New=MathFloor(Free*Percent/100/One_Lot/Step)*Step; //Calc }//----------------------------------------------------------------------------- 5 -- if (Lots_New < Min_Lot) // If it is less than allowed.. Lots_New=Min_Lot; // .. then minimum
  • 136. if (Lots_New*One_Lot > AccountFreeMargin()) // It isnt enough even.. { // ..for the min. lot:( Inform(11,0,Min_Lot); // Message.. return(false); // ..and exit } return(true); // Exit user-defined function }//----------------------------------------------------------------------------- 6 --En el bloque 1-2 se describen las variables globales y los valores que devolverá esta función.En el bloque 2-3 se calculan los valores de algunas variables. Si un usuario estableció una cantidad delotes distinta de cero, entonces no se tomará en cuenta el porcentaje de margen libre. Las variablesexternas Lots y Percent son declaradas en el archivo de inclusión Variables.mqh.En el bloque 3-4 se hacen los cálculos para el caso en que el usuario haya definido un valor distinto decero para la variable Lots. En este caso el programa chequea si el margen libre es suficiente para abriruna orden de mercado con esa cantidad de lotes, luego la cantidad de lotes definida por el usuario seráasignada a la variable global Lots_New, que será utilizada más adelante en nuestro EA. Si el margenlibre no es suficiente para la cantidad de lotes definida por el usuario, entonces se calcula la cantidadmáxima de lotes posibles.Si el usuario definió una cantidad de lotes igual a cero, el control es pasado al bloque 4-5. Entoncestomamos en cuenta el porcentaje del margen libre establecido por el usuario en la variable externaPercent. El programa primero comprueba que si este valor superó el 100%, en cuyo caso se utilizará elvalor 100 para los cálculos.Si el usuario definió el valor 0 para la variable Percent, entonces la cantidad de lotes será igualada a lacantidad mínima posible establecida por el dealing center. Para todas las cantidades intermedias secalculará la cantidad de lotes en base al porcentaje definido por el usuario.En el bloque 5-6 se chequea que la cantidad de lotes obtenida no sea inferior a la mínima permitida, encuyo caso se tomará la mínima permitida como valor a asignar a la variable Lots_New.Luego el programa controla que existan en la cuenta los fondos suficientes para abrir la cantidad de lotescalculada. Si el dinero disponible no es suficiente, el programa desplegará un mensaje para informar alusuario y saldrá de la función, la cual devolverá el valor FALSE, sino devolverá el valor TRUE.FUNCIÓN PARA DEFINIR LOS CRITERIOS DE COMERCIOEsta función suele ser la más importante de un programa y debe ser creada sin fallos. De acuerdo a laestrategia definida, esta función debe devolver valores que correspondan con un determinado criteriopara operar. En general, deben definirse los siguientes criterios: Criterio para abrir una orden de mercado. Criterio para cerrar una orden de mercado. Criterio para cerrar parcialmente una orden de mercado. Criterio para cerrar órdenes de mercado opuestas. Criterios para modificar los precios o stops de órdenes de mercado. Criterios para colocar órdenes pendientes. Criterios para eliminar órdenes pendientes Criterios para modificar el precio de entrada de órdenes pendientes.
  • 137. Criterios para modificar el precio de los stops órdenes pendientes.Función definida por el usuario Criterion()int Criterion()La función calcula los criterios para operar. Puede devolver los siguientes valores:-1 El símbolo usado No es EUR/USD0 No hay criterios importantes disponibles.10 Se produjo el criterio de operatoria para Abrir una operación de Compra11 Se produjo el criterio de operatoria para Cerrar una operación de Compra20 Se produjo el criterio de operatoria para Abrir una operación de Venta21 Se produjo el criterio de operatoria para Cerrar una operación de VentaLa función utiliza los valores de las siguientes variables externas:St_Min: el menor valor del indicador Estocástico.St_Max: el mayor valor del indicador Estocástico.Open_Level: el nivel del indicador MACD (para abrir una orden).Close_Level: el nivel del indicador MACD (para cerrar una orden).Para mostrar mensajes, la función utiliza la función de información Inform(). Si la función Inform() noestá incluída en el EA, no se mostrará ningún mensaje.La función definiendo los criterios para operar, Criterion(), está desarrollada como un archivo deinclusión llamado Criterion.mqh://-------------------------------------------------------------------------// Criterion.mqh//-------------------------------------------------------------------- 1 –// Function calculating trading criteria.// Returned values:// 10 - opening Buy// 20 - opening Sell// 11 - closing Buy// 21 - closing Sell// 0 - no important criteria available// -1 - another symbol is used//-------------------------------------------------------------------- 2 –// External variables:extern int St_min=30; // Minimum stochastic levelextern int St_max=70; // Maximum stochastic levelextern double Open_Level =5; // MACD level for opening (+/-)extern double Close_Level=4; // MACD level for closing (+/-)//-------------------------------------------------------------------- 3 –int Criterion() // User-defined function { string Sym="EURUSD"; if (Sym!=Symbol()) // If it is a wrong symbol { Inform(16); // Messaging.. return(-1); // .. and exiting }
  • 138. double M_0, M_1, // Value MAIN at bars 0 and 1 S_0, S_1, // Value SIGNAL at bars 0 and 1 St_M_0, St_M_1, // Value MAIN at bars 0 and 1 St_S_0, St_S_1; // Value SIGNAL at bars 0 and 1 double Opn=Open_Level*Point; // Opening level of MACD (points) double Cls=Close_Level*Point; // Closing level of MACD (points)//-------------------------------------------------------------------- 4 – // Parameters of technical indicators: M_0=iMACD(Sym,PERIOD_H1,12,26,9,PRICE_CLOSE,MODE_MAIN,0); // 0 bar M_1=iMACD(Sym,PERIOD_H1,12,26,9,PRICE_CLOSE,MODE_MAIN,1); // 1 bar S_0=iMACD(Sym,PERIOD_H1,12,26,9,PRICE_CLOSE,MODE_SIGNAL,0); //0 bar S_1=iMACD(Sym,PERIOD_H1,12,26,9,PRICE_CLOSE,MODE_SIGNAL,1); //1 bar St_M_0=iStochastic(Sym,PERIOD_M15,5,3,3,MODE_SMA,0,MODE_MAIN, 0); St_M_1=iStochastic(Sym,PERIOD_M15,5,3,3,MODE_SMA,0,MODE_MAIN, 1); St_S_0=iStochastic(Sym,PERIOD_M15,5,3,3,MODE_SMA,0,MODE_SIGNAL,0); St_S_1=iStochastic(Sym,PERIOD_M15,5,3,3,MODE_SMA,0,MODE_SIGNAL,1);//-------------------------------------------------------------------- 5 – // Calculation of trading criteria if(M_0>S_0 && -M_0>Opn && St_M_0>St_S_0 && St_S_0<St_min) return(10); // Opening Buy if(M_0<S_0 && M_0>Opn && St_M_0<St_S_0 && St_S_0>St_max) return(20); // Opening Sell if(M_0<S_0 && M_0>Cls && St_M_0<St_S_0 && St_S_0>St_max) return(11); // Closing Buy if(M_0>S_0 && -M_0>Cls && St_M_0>St_S_0 && St_S_0>St_min) return(21); // Closing Sell//-------------------------------------------------------------------- 6 – return(0); // Exit the user-defined function }//-------------------------------------------------------------------- 7 --Bloque 2-3: Técnicamente, es posible declarar las variables externas este archivo Criterion.mqh porquelas mismas no se utilizan en ninguna otra función del EA.En el bloque 3-4 se abren y describen las variables locales.En el bloque 4-5 se calculan los valores del MACD y el Estocástico para la barra actual y para la barraanterior.Si se presentaran al mismo tiempo las condiciones tanto para abrir una compra como para cerrar unventa, el archivo Criterion.mqh está diseñado para devolver sólo un valor. En este caso, de acuerdo conla estrategia considerada, la orden de abrir una operación de compra tiene prioridad respecto a la decerrar una operación de venta. Es por eso que en el bloque 5-6 el criterio para abrir una operación decompra está posicionado por encima.Al mismo tiempo, al enviarse una solicitud de apertura de una orden de compra, antes se requerirá cerrartodas las órdenes de venta, y una vez que no queden abiertas órdenes de este tipo, se procederá a abrir lacorrespondiente orden de compra. Ver el tema siguiente: Funciones de Comercio.FUNCIONES DE COMERCIO
  • 139. Como norma general, un Asesor Experto normal contiene cierto número de funciones de comercio, lascuales se dividen en funciones de control y funciones de ejecución. Por lo general debería utilizarse unafunción de control y una función de ejecución que defina los criterios par operar. Ambas funcionesdeben estar coordinadas entre sí, respecto de los valores de los parámetros que se transfieren.Cada función ejecutiva tiene un rango especial de tareas. De acuerdo a los requerimientos de unadeterminada estrategia, se usarán funciones de comercio para estas tareas: Abrir una orden de mercado de determinado tipo. Cerrar una orden de mercado de determinado tipo. Cerrar parcialmente una orden de mercado de determinado tipo. Cerrar todas las órdenes de mercado de determinado tipo. Cerrar órdenes de mercado opuestas de un determinado volumen. Cerrar todas las órdenes de mercado. Modificar las órdenes de stop de las órdenes de mercado de un determinado tipo. Colocar órdenes pendientes de determinado tipo. Eliminar una orden pendiente de determinado tipo. Eliminar todas las órdenes pendientes de determinado tipo. Eliminar todas las órdenes pendientes. Modificar una orden pendiente de determinado tipo.Una secuencia general de comercio en un Asesor Experto normal consiste en lo siguiente: en base a loscriterios de comercio determinados (de acuerdo a la estrategia usada), la función de comercio de control(también de acuerdo a la estrategia) llama a alguna de las otras funciones de comercio ejecutivas, lascuales, a su turno, formarán la solicitud de operatoria necesaria.Función de control de comercio definida por el usuario Trade()int Trade( int Trad_Oper )El parámetro Trad_Oper puede tomar uno de los siguientes valores:-1 El símbolo usado No es EUR/USD0 No hay criterios importantes disponibles.10 Se produjo el criterio de operatoria para Abrir una operación de Compra11 Se produjo el criterio de operatoria para Cerrar una operación de Compra20 Se produjo el criterio de operatoria para Abrir una operación de Venta21 Se produjo el criterio de operatoria para Cerrar una operación de VentaPara ejecutar la función se requieren las siguientes funciones de comercio definidas por el usuario: Close_All(): función que cierra todas las órdenes de mercado de determinado tipo. Opern_Ord(): función que abre una orden de mercado de determinado tipo. Tral_Stop(): función que modifica el Stop Loss de una orden de mercado de determinado tipo. Lot(): función que detecta la cantidad de lotes para nuevas órdenes.La función de control Trade() está desarrollada como un archivo de inclusión llamado Trade.mqh://------------------------------------------------------------------------// Trade.mqh//------------------------------------------------------------------- 1 –int Trade(int Trad_Oper) // User-defined function
  • 140. { // Trad_Oper - trade operation type: // 10 - opening Buy // 20 - opening Sell // 11 - closing Buy // 21 - closing Sell // 0 - no important criteria available // -1 - another symbol is used switch(Trad_Oper) { //------------------------------------------------------------- 2 – case 10: // Trading criterion = Buy Close_All(1); // Close all Sell if (Lot()==false) // Not enough money for min. return; // Exit the user-defined function Open_Ord(0); // Open Buy return; // Having traded, leave //---------------------------------------------------------- 3 – case 11: // Trading criterion = closing Buy Close_All(0); // Close all Buy return; // Having traded, leave //---------------------------------------------------------- 4 – case 20: // Trading criterion = Sell Close_All(0); // Close all Buy if (Lot()==false) return; // Exit the user-defined function Open_Ord(1); // Open Sell return; // Having traded, leave //---------------------------------------------------------- 5 – case 21: // Trading criterion = closing Sell Close_All(1); // Close all Sell return; // Having traded, leave //---------------------------------------------------------- 6 – case 0: // Retaining opened positions Tral_Stop(0); // Trailing stop Buy Tral_Stop(1); // Trailing stop Sell return; // Having traded, leave //---------------------------------------------------------- 7 – } }//------------------------------------------------------------------- 8 --La función de control Trade es llamada desde la función especial start() del Asesor Expertousualexpert.mq4. El valor devuelto por la función Criterion() (la cual define los criterios para operar) esentregado a la función Trade() como un parámetro transferido.En la función especial start() del EA usualexpert.mq4 aparece la siguiente línea:Trade(Criterion()); // Trade functionEn esta línea se llama a la función Trade() y se le entrega el valor devuelto por la función Criterion().En el bloque 1-2 de la función Trade() encontramos esta línea:
  • 141. int Trade(int Trad_Oper) // User-defined functionEn esta línea, la función Trade() recibe el valor devuelto por la función Criterion() (y que le fuetransferido por el EA usualexpert.mq4) y le asigna el nombre de variable Trad_Oper, de tipo int.En los bloques 2-7 usamos el operador switch() que nos permite activar el grupo de funciones necesariaspara operar de acuerdo al criterio especificado. De acuerdo a la estrategia diseñada, el EA abrirá ycerrará órdenes de mercado. Nuestra estrategia no define órdenes pendientes.Suponiendo que la función Criterion() determina que se presentaron los motivos para abrir una compra,entonces se pasará el parámetro de valor 10 la función Trade(), por lo que la variable Trad_Oper tomaráese valor. El control será pasado entonces a la marca "case 10” en el bloque 2-3, durante la ejecución deloperador switch().En este caso, el programa primero llama a la función Close_All(1). La ejecución de esta función,resultará en el cierre de todas las órdenes de mercado de venta abiertas en EUR/USD.Después de que todas las órdenes de mercado venta fueron cerradas se controla si el dinero disponible essuficiente para abrir una nueva operación. Para hacer esto se llama a la función definida por el usuarioLot(). Si la función devuelve el valor “false” significa que el dinero disponible no es suficiente para abriruna orden de mercado de compra con la cantidad de lotes mínima permitida. En este caso, la funciónTrade() finaliza sus operaciones (“return”).Si hay dinero suficiente se llama a la función Open_Ord(0) para abrir una orden de mercado de compracon la cantidad de lotes calculada en la función Lot().Las funciones ejecutivas de comercio que generan las solicitudes de comercio son llamadas en lafunción Trade(), la cual, a su turno, es llamada en la función start() del EA. La función Trade() estáescrita de tal manera que el control no vuelve a la función start() hasta tanto se hayan ejecutado todas lasfunciones ejecutivas de comercio.Si no hay ningún criterio necesario para operar (variable Trad_Oper es igual a 0) en la ejecución de lfunción Criterion(), el control es pasado a la carca “case 0” lo cual resulta en la llamada a la funcióndouble Tral_Stop() para modificar los valores necesarios de las órdenes de mercado de diferentes tipo.La estrategia plasmada en el EA permite la existencia de una sola orden de mercado, por lo que lasecuencia de llamadas a las funciones Tral_Stop(0) y Tral_Stop(1) no son importantes. En este caso esuna decisión aleatoria.Si la función Criterion() devuelve el valor -1 significa que el EA fue soltado en una ventana que no esdel símbolo EUR/USD. En este caso la función Trade() no llama a ninguna función ejecutiva decomercio y devuelve el control a la función especial start() que la llamó.Función ejecutiva de comercio definida por el usuario Close_All()int Close_All( int Tip)Esta función cierra todas las órdenes de mercado de un tipo especificado. El parámetro Tip puede tomarlos siguientes valores, que se corresponden con los tipos de órdenes a ser cerradas:0: cierra órdenes de Compra1: cierra órdenes de Venta.Para ejecutar esta función es necesario aplicar la función de contabilidad de órdenes Terminal(), lafunción de rastreo de eventos Event() y la función de procesamiento de errores Errors(). Para mostrar
  • 142. mensajes se utiliza la información de la función Inform(). Si la función Inform() no está incluida en elEA, no se mostrarán mensajes.Se utilizan los valores de los arrays globales Mas_Ord_New y Mas_Tip.La función de ejecución de comercio Close_All() se forma como un archivo de inclusión Close_All.mqh://---------------------------------------------------------------------------------// Close_All.mqh//---------------------------------------------------------------------------- 1 --// Function closing all market orders of the given type// Global variables:// Mas_Ord_New Last known order array// Mas_Tip Order type array//---------------------------------------------------------------------------- 2 --int Close_All(int Tip) // User-defined function { // int Tip // Order type int Ticket=0; // Order ticket double Lot=0; // Amount of closed lots double Price_Cls; // Order close price//---------------------------------------------------------------------------- 3 -- while(Mas_Tip[Tip]>0) // As long as the orders of the .. { //.. given type are available for(int i=1; i<=Mas_Ord_New[0][0]; i++) // Cycle for live orders { if(Mas_Ord_New[i][6]==Tip && // Among the orders of our type Mas_Ord_New[i][5]>Lot) // .. select the most expensive one { // This one was found at earliest. Lot=Mas_Ord_New[i][5]; // The largest amount of lots found Ticket=Mas_Ord_New[i][4]; // Its order ticket is that } } if (Tip==0) Price_Cls=Bid; // For orders Buy if (Tip==1) Price_Cls=Ask; // For orders Sell Inform(12,Ticket); // Message about an attempt to close bool Ans=OrderClose(Ticket,Lot,Price_Cls,2); // Close order !:) //---------------------------------------------------------------------- 4 -- if (Ans==false) // Failed :( { // Check for errors: if(Errors(GetLastError())==false) // If the error is critical, return; // .. then leave. } //---------------------------------------------------------------------- 5 -- Terminal(); // Order accounting function Events(); // Event tracking } return; // Exit the user-defined function }//---------------------------------------------------------------------------- 6 --
  • 143. En el bloque 1-2 son descriptas las variables globales utilizadas. En el bloque 2-3 las variables localesson abiertas y descriptas.La condición Mas_Tip[Tip]>0 en el encabezamiento del operador de ciclo “while” (bloques 3-6) que lafunción mantendrá el control hasta que todas las órdenes de el tipo determinado estén cerradas.El elemento del array global Mas_Tip[Tip] contiene el valor igual a la cantidad de órdenes del tipoespecificado Tip.Las órdenes se cerrarán comenzando desde la que tiene una mayor cantidad de lotesFunción ejecutiva de comercio definida por el usuario Open_Ord()int Open_Ord ( int Tip)Esta función abre una orden de mercado del tipo especificado. El parámetro Tip puede tomar lossiguientes valores, que se corresponden con los tipos de órdenes a ser abiertas:0: abre una orden de Compra1: abre una orden de Venta.Para ejecutar esta función es necesario aplicar la función de contabilidad de órdenes Terminal(), lafunción de rastreo de eventos Event() y la función de procesamiento de errores Errors(). Para mostrarmensajes se utiliza la información de la función Inform(). Si la función Inform() no está incluida en elEA, no se mostrarán mensajes.Se utilizan los valores de las siguientes variables globales Mas_Tip, StopLoss y TakeProfit.La función de ejecución de comercio Open_Ord() se forma como un archivo de inclusiónOpen_Ord.mqh://---------------------------------------------------------------------------------// Open_Ord.mqh//---------------------------------------------------------------------------- 1 --// Function opening one market order of the given type// Global variables:// int Mas_Tip Order type array// int StopLoss The value of StopLoss (amount of points)// int TakeProfit The value of TakeProfit (amount of points)//---------------------------------------------------------------------------- 2 --int Open_Ord(int Tip) { int Ticket, // Order ticket MN; // MagicNumber double SL, // StopLoss (as related to the price) TP; // TakeProf (as related to the price)//---------------------------------------------------------------------------- 3 -- while(Mas_Tip[Tip]==0) // Until they .. { //.. succeed if (StopLoss<Level_new) // If it is less than allowed.. StopLoss=Level_new; // .. then the allowed one if (TakeProfit<Level_new) // If it is less than allowed.. TakeProfit=Level_new; // ..then the allowed one MN=TimeCurrent(); // Simple MagicNumber Inform(13,Tip); // Message about an attempt to open
  • 144. if (Tip==0) // Lets open a Buy { SL=Bid - StopLoss* Point; // StopLoss (price) TP=Bid + TakeProfit*Point; // TakeProfit (price) Ticket=OrderSend(Symbol(),0,Lots_New,Ask,2,SL,TP,"",MN); } if (Tip==1) // Lets open a Sell { SL=Ask + StopLoss* Point; // StopLoss (price) TP=Ask - TakeProfit*Point; // TakeProfit (price) Ticket=OrderSend(Symbol(),1,Lots_New,Bid,2,SL,TP,"",MN); } //---------------------------------------------------------------------- 4 -- if (Ticket<0) // Failed :( { // Check for errors: if(Errors(GetLastError())==false) // If the error is critical, return; // .. then leave. } Terminal(); // Order accounting function Events(); // Event tracking }//---------------------------------------------------------------------------- 5 -- return; // Exit the user-defined function }//---------------------------------------------------------------------------- 6 --En los bloques 1-3 se describen las variables globales y las variables locales son abiertas y descriptas.El código básico de la función está concentrado en el operador de ciclo “while” (bloques 3-5) que esejecutado mientras no haya órdenes del tipo Tip disponibles.Cada orden a ser abierta tiene su MagicNumber único igual al horario actual del servidor.De acuerdo al tipo de orden se ejecutará el cuerpo de uno de los operadores “if”, donde se calculará elcorrespondiente TakeProfit y StopLoss (de acuerdo al tipo de orden de que se trate) y luego el control espasado a esta línea (si fuera el caso de una orden de compra):Ticket=OrderSend(Symbol(),0,Lots_New,Ask,2,SL,TP,"",MN);Esta línea solicita la apertura de una orden de Mercado de compra.Función ejecutiva de comercio definida por el usuario Tral_Stop()int Tral_Stop ( int Tip)Esta función modifica todas las órdenes de mercado del tipo especificado. El parámetro Tip puede tomarlos siguientes valores, que se corresponden con los tipos de órdenes a ser modificadas:0: modifica una orden de Compra1: modifica una orden de Venta.Para ejecutar esta función es necesario aplicar la función de contabilidad de órdenes Terminal(), lafunción de rastreo de eventos Event() y la función de procesamiento de errores Errors(). Para mostrarmensajes se utiliza la información de la función Inform(). Si la función Inform() no está incluida en elEA, no se mostrarán mensajes.
  • 145. Se utilizan los valores de las siguientes variables globales: Mas_Ord_New y TrailingStop.La función de ejecución de comercio Tral_Stop() se forma como un archivo de inclusiónTral_Stop.mqh://---------------------------------------------------------------------------------// Tral_Stop.mqh//---------------------------------------------------------------------------- 1 --// Function modifying StopLosses of all orders of the given type// Global variables:// Mas_Ord_New Last known order array// int TralingStop Value of TralingStop (amount of points)//---------------------------------------------------------------------------- 2 --int Tral_Stop(int Tip) { int Ticket; // Order ticket double Price, // Market order open price TS, // TralingStop (as related to the price) SL, // Value of order StopLoss TP; // Value of order TakeProfit bool Modify; // A criterion to modify.//---------------------------------------------------------------------------- 3 -- for(int i=1;i<=Mas_Ord_New[0][0];i++) // Cycle for all orders { // Searching for orders of the given type if (Mas_Ord_New[i][6]!=Tip) // If this is not our type.. continue; //.. skip the order Modify=false; // It is not assigned to be modified Price =Mas_Ord_New[i][1]; // Order open price SL =Mas_Ord_New[i][2]; // Value of order StopLoss TP =Mas_Ord_New[i][3]; // Value of order TakeProft Ticket=Mas_Ord_New[i][4]; // Order ticket if (TralingStop<Level_new) // If it is less than allowed.. TralingStop=Level_new; // .. then the allowed one TS=TralingStop*Point; // The same, as related to the price //---------------------------------------------------------------------- 4 -- switch(Tip) // Go to Order type { case 0 : // Order Buy if (NormalizeDouble(SL,Digits)< // If it is lower than we want.. NormalizeDouble(Bid-TS,Digits)) { // ..then modify it: SL=Bid-TS; // Its new StopLoss Modify=true; // Assigned to be modified. } break; // Exit switch case 1 : // Order Sell if (NormalizeDouble(SL,Digits)> // If it is higher than we want.. NormalizeDouble(Ask+TS,Digits)|| NormalizeDouble(SL,Digits)==0) //.. or zero(!) { // ..then modify it SL=Ask+TS; // Its new StopLoss
  • 146. Modify=true; // Assigned to be modified. } } // End of switch if (Modify==false) // If there is no need to modify it.. continue; // ..then continue the cycle bool Ans=OrderModify(Ticket,Price,SL,TP,0); //Modify it! //---------------------------------------------------------------------- 5 -- if (Ans==false) // Failed :( { // Check for errors: if(Errors(GetLastError())==false) // If the error is critical, return; // .. then leave. i--; // Decreasing counter } Terminal(); // Order accounting function Events(); // Event tracking } return; // Exit the user-defined function }//---------------------------------------------------------------------------- 6 --En los bloques 1-3 se describen las variables globales y se abren y describen las variables locales. En elciclo “for” (bloques 3-6) se seleccionan las órdenes del tipo especificado y si el Stop Loss de cualquierade esas órdenes está más lejos del precio actual de lo que seleccionó el usuario, entonces esa orden serámodificada.Para hacer el código más claros, se asignan a variables simples algunos elementos del arrayMas_Ord_New (bloque 3-4). Luego se realiza el chequeo necesario para la variable TrailingStop. Si elvalor de esta variable es inferior a la distancia mínima permitida establecida por el dealing center,entonces el valor de la variable será incrementado para coincidir con esa distancia mínima.En el bloque 4-5 se hacen los cálculos de acuerdo al tipo de orden. Por ejemplo, si es de tipo 1 (orden deventa) el control será pasado a la marca “case 1” del operador “switch”. Aquí se chequea la necesidad demodificar la orden Stop Loss. Si no hay Stop Loss o si está establecido a una distancia mayor del preciode mercado que el Trailing Stop establecido, entonces se calcula el nuevo valor del Stop Loss. Lasolicitud para modificar la orden se forma en la siguiente línea:bool Ans = OrderModify(Ticket,Price,SL,TP,0); //Modify it!Vimos antes que esta estrategia comercial solo permite la posibilidad de tener una sola cuenta orden. Sinembargo, la función Tral_Stop()está diseñada para aceptar varias órdenes.En el ejemplo anterior en la función Tral_Stop(), las ordenes se modifican sin ningún orden enparticular, las ordenes se modifican según el orden de llegada.En el bloque de 5-6, se revisan los errores durante la ejecución de la operación. Si el error es crítico, lafunción terminará su ejecución. Sin embargo, si se produce un error no critico, el valor del contador i sereduce en 1. De esta manera se producirá un nuevo intento para modificar la misma orden en la siguienteiteración del ciclo for.FUNCIÓN PARA EL PROCESADO DE ERRORESLos errores que aparecen durante la ejecución de funciones ejecutivas, se pueden dividir en dos grupos-errores no críticos y errores críticos. Errores no críticos son los errores del servidor. Después de haberse
  • 147. solucionado estos errores, se puede continuar con las operaciones. Los errores críticos incluyen todos loserrores que alerten de problemas serios. Por ejemplo, si se bloquea una cuenta, es imposible haceroperaciones. En tal caso, el EA debe mostrar el mensaje correspondiente y no debería repetir la petición.Por estos motivos, es indispensable tener una función en el EA que procese los errores.Función definida por el usuario Errors()bool Errors( int Error )Esta función devuelve “true” si el error no es crítico. Si no devuelve “false”.El parámetro “Error” puede tener cualquier valor y este valor corresponde al tipo de error que seprodujo al intentar hacer la operación.Esta función definida por el usuario se incluye en el archivo Errors.mqh://--------------------------------------------------------------------// Errors.mqh//--------------------------------------------------------------- 1 --// Error processing function.// Returned values:// true - if the error is overcomable (i.e. work can be continued)// false - if the error is critical (i.e. trading is impossible)//--------------------------------------------------------------- 2 --bool Errors(int Error) // Custom function { // Error // Error number if(Error==0) return(false); // No error Inform(15,Error); // Message//--------------------------------------------------------------- 3 -- switch(Error) { // Overcomable errors: case 129: // Wrong price case 135: // Price changed RefreshRates(); // Renew data return(true); // Error is overcomable case 136: // No quotes. Waiting for the tick to come while(RefreshRates()==false) // Before new tick Sleep(1); // Delay in the cycle return(true); // Error is overcomable case 146: // The trade subsystem is busy Sleep(500); // Simple solution RefreshRates(); // Renew data return(true); // Error is overcomable // Critical errors: case 2 : // Common error case 5 : // Old version of the client terminal case 64: // Account blocked case 133: // Trading is prohibited default: // Other variants return(false); // Critical error }
  • 148. //--------------------------------------------------------------- 4 -- }//--------------------------------------------------------------------Si la función Errors() devuelve “true” (error no critico), entonces, el programa volverá a intentar hacer laoperación. Si devuelve “false”, entonces se detiene la función ejecutiva, y el control se devuelve asísucesivamente a la función precedente, hasta llegar a la función start() y luego al cliente de terminal. Sila opción no está en ninguna de estas dos alternativas, entonces, en la situación cuando no hay errores(error = 0) corresponderá a la segunda alternativa, a saber, el valor devuelto será “false”. Esto garantizaque no se repita la solicitud.Una vez que el mensaje de error ha sido mostrado por la función Inform(), el programa salta al bloque 3-4, al operador “switch”. En cada caso hay un código que maneja el error tratado. Por ejemplo, si ocurreel error 136, significa que el servidor no tiene precios actuales para poder tomar así una decisiónapropiada. Esto significa que la situación no cambiará a menos que venga una nuevo tick, así que no haynecesidad de repetir la misma operación porque no se ejecutara de todas formas el envío. La solucióncorrecta en este caso es hacer una pausa – pausar cualquier acción del EA. Un método sencillo paradetectar un nuevo tick se utiliza con este fin – el análisis del valor devuelto por la funciónRefreshRates().El control será devuelto la función a la función de llamada, en la cual se repite laoperación (tras el análisis correspondiente, si es necesario), tan pronto como se reciba un nuevo tick.Si hay un error que el programar considerara critico, la función devolverá “false”. La orden no serepetirá en tal caso, así que no hay necesidad de hacer algo en la función de Errors(). Todos los erroresque no se procesan son considerados críticos por defecto. Se pueden ampliar la lista de erroresprocesados (ver códigos de error).PROGRAMAS COMPLEJOSEn el libro se muestra un código que debe insertarse en los programas muy complejos a fin de que seactualicen los datos de mercado permanentemente y así hacer más eficiente un programa tan largo que,de otra manera, haría que se pierdan varios ticks del mercado hasta recién poder volver a ejecutar lafunción especial start().

×