Autoaprendizaje del lenguaje MQL5 desde cero - página 52

 

Preste atención al filtro por símbolo y por asistente en el bucle de posición. Si no hay ningún filtro, pero se rastrean todas las posiciones abiertas en todos los símbolos, y esto es malo.

Así que, a primera vista, todo parece estar bien.

Совершение сделок - Торговые операции - Справка по MetaTrader 5
Совершение сделок - Торговые операции - Справка по MetaTrader 5
  • www.metatrader5.com
Торговая деятельность в платформе связана с формированием и отсылкой рыночных и отложенных ордеров для исполнения брокером, а также с управлением текущими позициями путем их модификации или закрытия. Платформа позволяет удобно просматривать торговую историю на счете, настраивать оповещения о событиях на рынке и многое другое. Открытие позиций...
 
Andrei Novichkov:

Preste atención al filtro por símbolo y por asistente en el bucle de posición. Si no hay ningún filtro, pero se rastrean todas las posiciones abiertas en todos los símbolos, y esto es malo.

Así pues, todo parece estar bien a primera vista.

Muchas gracias, Andrew! Entiendo todo lo de Magic porque se pueden abrir varias posiciones para un símbolo, pero tengo otra pregunta. ¿El Asesor Experto recorrerá las posiciones abiertas de todos los símbolos a la vez, si no se apunta explícitamente al símbolo actual? ¿Y esto a pesar de que está fijado para un determinado par de divisas, por ejemplo, EURUSD? Sinceramente, no entiendo muy bien este punto.

Saludos, Vladimir.

 
MrBrooklin:

Muchas gracias, Andrey! Entiendo todo lo de Magic, porque se pueden abrir varias posiciones en un símbolo, pero tengo una contrapregunta. ¿Buscará el EA las posiciones abiertas de todos los símbolos a la vez, si no se apunta explícitamente al símbolo actual? ¿Y esto a pesar de que está fijado para un determinado par de divisas, por ejemplo, EURUSD? Sinceramente, no entiendo muy bien este punto.

Sinceramente, Vladimir.


Sí, se establece en todas las posiciones abiertas para todos los símbolos.
De forma constante para todos los puestos abiertos.
He aquí una simple búsqueda en el libro de texto.

https://book.mql4.com/ru/build/trading
 
MrBrooklin:

Así que, basándome en la literatura que leí, escribí un breve algoritmo para crear un Asesor Experto con la función trailing stop:

  1. Vamos acrear un Asesor Experto para automatizar el nivel de trailing stop Loss de una posición ya abierta con niveles especificados de Take Profit y Stop Loss.
  2. En el Asesor Experto, cree un bloque de parámetros de entrada con dos parámetros: establecer "nivel de arrastre" y establecer "paso de arrastre".
  3. Cuando llegan nuevas cotizaciones, las procesa con la función OnTick( ). El arrastre sólo funciona cuando se produce un nuevo tick para el símbolo actual.
  4. Vamos a crear y ejecutar un bucle para buscar todas las posiciones.
  5. Si de repente no encontramos posiciones abiertas, volvemos al bucle
  6. Refrescamos las cotizaciones.
  7. Si hay una posición abierta, continuamos.
  8. Definimos el tipo de posición abierta: Comprar o Vender.
  9. Si hay una posición de compraabierta , definimos dónde se encuentra el precio actual en relación con la posición abierta .
  10. Si el precio actual es superior al precio al que se abrió la posición, comprobamos a qué nivel ha subido.
  11. Si el precio actual ha alcanzado el "nivel de arrastre" definido en los parámetros de entrada, movemos elStop Loss al nivel sin pérdida que equivale al precio de apertura de la posición decompra. Si no, no hacemos nada.
  12. Si el precio actual excede el nivel de Trailing Stop por el valor igual al nivel de Trailing Stop, elStop Loss se mueve desde el nivel de precio de apertura de la posición decompra por el valor igual al nivel de Trailing Stop y así sucesivamente hasta que el precio alcance el nivel de Take Profit especificado para esta posición .
  13. Si el precio gira y alcanza el nivel deStop Lossya movido , la posición se cierra .
  14. Si la posición es deVenta, definimos dónde está el precio actual en relación con la posición abierta .
  15. Si el precio actual es inferior al precio de la posición abierta, comprobamos a qué nivel ha caído.
  16. Si el precio actual ha alcanzado el nivel de arrastre especificado en los parámetros de entrada, movemos el Stop Loss al nivel sin pérdida igual al precio de apertura de la posición deVenta. Si no, no hacemos nada.
  17. Si el precio actual ha superado el nivel de Trailing Stop por el valor igual al nivel de Trailing Stop, elStop Loss se desplaza desde el nivel de la posición de venta de apertura por el valor igual al nivel de Trailing Stop y así sucesivamente hasta que el precio alcance el nivel de Take Profit especificado para esa posición .
  18. Si el precio gira y alcanza el nivel deStop Loss, la posición se cierra .

Por favor, revisa el algoritmo y dame algunas pistas sobre los puntos que se han perdido.

Sinceramente, Vladimir.

La teoría no está mal, ahora vamos a centrarnos en la práctica. ¿Funcionará?

 
Aliaksandr Hryshyn:

Bien en la teoría, ahora en la práctica. ¿Puedes hacerlo?

Lo intentaré. Pero comprendes que esto requiere un nivel de conocimiento totalmente diferente, y yo aún no lo tengo.

Saludos, Vladimir.

 
Aleksey Masterov:

Sí. En todas las poses abiertas en todos los símbolos...
De forma consistente en todas las poses abiertas.
Aquí hay un simple rastreo ya en el libro de texto.

https://book.mql4.com/ru/build/trading

Sí, Alexey, ya he visto este código. Está en la forma de un archivo de inclusión. Para ser sincero, no he encontrado nada sobre el símbolo en él, aunque lo he visto varias veces. Tal vez he entendido algo mal o simplemente estoy buscando mal.

Sinceramente, Vladimir.

 

Por ahora, continuemos con las funciones.

Como he escrito antes, las funciones están en todas partes, hay que amarlas y saber escribirlas. Las funciones, son nuestros pequeños luchadores en la solución de los problemas globales. Si fuéramos generales de un ejército, ¿qué tipo de combatientes querríamos controlar? Esta es una lista aproximada:

  • Un combatiente debe ejecutar claramente una orden. El nivel medio de inteligencia de un soldado de infantería no es grande. Por lo tanto, es mejor establecer objetivos claros y sencillos para estos combatientes: "tomar un búnker", "conseguir una lengua", "minar un puente".
  • Si la tarea es difícil, no busques un luchador súper inteligente para llevarla a cabo. Es mejor dividir la tarea en varias subtareas y tomar dos o tres luchadores más estúpidos pero más eficientes. Deje que todos resuelvan sus subtareas sin ninguna pregunta, o mejor aún, deje que ignoren el concepto y la tarea en su conjunto. Entonces, si alguien es tomado prisionero no será un problema, no se revelará todo el plan.
  • Un soldado debe cumplir la orden independientemente del entorno: nieve, lluvia, París y las mujeres - si dicho entorno no tiene ningún efecto en la ejecución de la orden, entonces esas condiciones y el entorno externo deben ser ignorados.
  • Sucede que las tareas pueden ser difíciles. Requieren de muchos luchadores para resolverlos. No se puede asignar un general a cada combatiente. En cambio, debes asignar a un soldado más inteligente para que dirija a varios combatientes. Este grupo, a su vez, se une con el mismo en una compañía de soldados y le nombra un oficial superior.

Pero nos hemos desviado, pasemos de nuevo a las funciones.

Si una función resuelve demasiados problemas en general -siguiendo con la analogía-, se trata de un luchador muy inteligente que, si algo sale mal, podría arruinar toda la empresa. Si se pregunta qué hace esa función, la respuesta puede ser larga. Si el resultado de esta función deja de ser correcto de repente, será muy difícil averiguar cuál es la causa del error en ella (porque hay muchas tareas, mucho código, muchas llamadas a subprocedimientos y es difícil entender dónde está exactamente el error).

Si una función calcula resultados correctos los lunes, miércoles y domingos y los días de descanso según nuestro "estado de ánimo", ¿podemos confiar en esta función? Imagina que la función OrderSend, digamos, abre posiciones sólo los jueves y si se define algún parámetro mágico con valor 13. Y esto no es una tontería ni una fantasía en absoluto. Este comportamiento se puede arreglar con un chasquido de dedos: basta con hacer que la función dependa de algunos parámetros del entorno externo.

Supongamos que la función

double sum(double a, double b)
{
   return a+b;
}

siempre devolverá la suma de dos valores, independientemente del entorno externo. Esto significa que incluso si copiamos esta función en otro script o Asesor Experto, funcionará perfectamente bien allí. Esta función puede escribirse una vez y utilizarse en muchos de nuestros programas con sólo copiarla de forma obtusa. Siempre podremos confiar en su resultado sabiendo que su funcionamiento no depende de nada. Estas funciones, cuyo resultado no depende de su entorno, se denominan funciones puras o sin efectos secundarios. Si nos esforzamos por escribir funciones puras, pronto obtendremos un montón de ellas. Esto significa que puede combinarlos en un archivo e incluirlos en sus nuevos proyectos. Esto se llama reutilización de código. No hacemos el trabajo dos veces. En su lugar, utilizamos funciones ya escritas que conocemos y cuya fiabilidad ha sido probada más de una vez.

Veamos ahora el antiejemplo:

double c = 0.0;
double sum(double a, double b)
{
   return a+b+c;
}

El resultado parece ser el mismo, porque c es siempre cero. ¿O no es siempre así? ¿Y si alguien cambia c en algún sitio? ¿Y entonces qué? ¿Qué pasa si alguien en algún lugar también utiliza la variable externa c, pero para sus propios fines, y tiene una variable c de un tipo diferente, digamos cadena? Combinar estas dos funciones ya no es posible (el compilador no permite declarar dos variables con el mismo nombre). Sus dependencias comunes también son difíciles de resolver. No sé qué hacer con él en absoluto. Por ejemplo, todavía no conozco una manera fiable y fácil de hacer que esas funciones funcionen juntas.

Incluso si no hay ninguna otra función y sólo una función lee una variable externa, no es tan fácil copiarla en otro lugar. Tenemos que copiar tanto esta función como su dependencia. ¿Pero qué pasa si copiamos estas funciones en un archivo común? Tenemos 50 o 100 de estas funciones allí. Y cada uno de ellos copia consigo un montón de sus propias variables dependientes. Obtenemos una maraña de variables relacionadas con funciones poco claras. Pero, ¿para qué sirve todo esto? ¿Qué problemas resuelve? ¿Por qué crear dependencias innecesarias cuando se puede prescindir de ellas en la gran mayoría de los casos?

Las funciones tienen otra característica sorprendente. Las funciones son autodescriptivas. En otras palabras, no hay que dibujar un esquema, sólo elegir buenos nombres y dividir el algoritmo general en funciones. He aquí un ejemplo:

void OnTick()
{
   if(SelectFirstPendingOrder(ORDER_TYPE_BUY))
       CancelSelectPendingOrder();
}

No sé qué hace este código, porque las funciones ni siquiera están escritas. Pero si lo leyera, probablemente significaría que si la primera <primera> orden pendiente con dirección ORDER_TYPE_BUY se selecciona con éxito, se cancelaría (la primera función selecciona, la segunda cancela). Como el código se ejecutaría cada tick, no importa cuántas órdenes pendientes hubiera, cada una se cancelaría tarde o temprano. Esto también significa que cualquier intento de colocar una orden de compra pendiente sería suprimido - la orden sería inmediatamente eliminada. Al mismo tiempo, las órdenes de venta se colocarán sin problemas.

Sólo hay dos líneas de código y dos funciones. Y el algoritmo no es trivial, y lo que es más importante, es fiable.

Al hablar de ACM, debemos mencionar un poco más las funciones puras. Al tratarse de un lenguaje de aplicación, es difícil escribir algo sin depender de los datos que proporciona el terminal. Al fin y al cabo, ésta es la tarea principal: interactuar adecuadamente con el entorno comercial. Formalmente, cualquier entorno de negociación es cambiante: precios, número de órdenes, cambios de saldo, etc., etc. Por lo tanto, cualquier función que interactúe con un entorno comercial tan cambiante no está clara. Porque el entorno comercial externo también puede considerarse como una variable global, que cambia constantemente. Pero cuando escribimos OrdersTotal(), no esperamos que esta función devuelva siempre el mismo valor. En cambio, esperamos que devuelva el número de órdenes pendientes que, naturalmente, variará. Por lo tanto, en MQL, consideraremos las funciones como limpias y reutilizables, incluso en el caso de que llamen a funciones de la API externa, como OrdersTotal(). Será nuestra indulgencia razonable.

 
Vasiliy Sokolov:

Sigamos con las funciones...

Muchas gracias, Vasily, por los impagables conocimientos que compartes no sólo conmigo, sino también con aquellos programadores noveles que lean o vayan a leer este tema.

Con el mismo gran respeto, Vladimir.

 

Sigo estudiando el lenguaje de programación MQL5. Mientras que no había observaciones serias sobre el algoritmo de escritura de código de Trailing_Stop Expert Advisor (recuerdo sobre el símbolo y la Magia, ¡lo añadiré al algoritmo más tarde!), creé parámetros de entrada para el EA y escribí el código del bucle que inicia la búsqueda de posiciones abiertas.

Cuando ejecuté el Asesor Experto vi un problema - en la pestaña "Expertos" del terminal de operaciones aparecen 2 mensajes idénticos "Se ha iniciado un bucle" en cada tick, a pesar de que el terminal de operaciones tiene sólo un gráfico del par de divisas EURUSD y sólo se abre una posición en él. Y estos mensajes tienen la misma hora exacta de salida.

Luché hasta la medianoche pero no pude ganar. No puedo entender cuál es el problema.


El código del Asesor Experto está escrito en inglés, mientras que los comentarios están en ruso para facilitar el proceso. En este EA, he tratado de describir todo, como prometí antes, de una manera comprensible para un estudiante de primer grado de una escuela de programación.

Saludos, Vladimir.

//+------------------------------------------------------------------+
//|                                                Trailing_Stop.mq5 |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"

input ushort TrailingLevel=100; //Уровень трейлинга (для включения)
input ushort TrailingStep=10;   //Шаг трейлинга (для перемещения)
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---

   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---

  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   /* Для цикла for создаем локальную переменную i и присваиваем ей значение "торговая функция PositionsTotal",
      которая возвращает нам количество открытых позиций*/
   int i=PositionsTotal();
   /* Разберемся, что такое оператор for.
      Оператор for состоит из трех Выражений и выполняемого Оператора:
      for(Выражение_1; Выражение_2; Выражение_3)
         Оператор;
      Выражение_1 описывает инициализацию цикла. За инициализацию цикла будет отвечать "Торговая функция PositionsTotal".
      Выражение_2 проверяет условия завершения цикла. Если оно истинно, то выполняется Оператор в теле цикла for.
      Все повторяется до тех пор, пока Выражение_2 не станет ложным. Если оно ложно, цикл заканчивается
      и управление передается следующему оператору.
      Выражение_З вычисляется после каждой итерации (т.е. после каждого повторения действия).
   */
   for(i; i>=0; i--) //запускаем цикл перебора открытых позиций (i) от максимума до нуля (i>=0) с шагом минус 1 (i--)
      Print("Запущен цикл");
  }
//+------------------------------------------------------------------+
//| Timer function                                                   |
//+------------------------------------------------------------------+
void OnTimer()
  {
//---

  }
//+------------------------------------------------------------------+
 
MrBrooklin:


i es igual al número de posiciones abiertas, por lo que muchos ciclos serán con impresión

Print("Запущен цикл");
es necesario eliminar el signo "=" en
   for(i; i>=0; i--)
por qué necesita pasar por el ciclo cuando el número de posiciones abiertas es 0. esta llamada a cero es de donde viene la segunda impresión