El mercado y la física de sus patrones globales

31 marzo 2021, 16:04
Evgeniy Ilin
0
515

Introducción

En este artículo, trataremos de averiguar cómo utilizar la física del mercado en el comercio automatizado. El lenguaje de las matemáticas supone una transición de la abstracción y la incertidumbre hacia los pronósticos, lo cual nos permite operar no con valores aproximados e inconcretos, sino con fórmulas o criterios claros para mejorar la calidad de los sistemas creados. No vamos a inventar teorías ni leyes: reflexionaremos únicamente sobre la base de hechos conocidos por todos, convirtiendo paulatinamente dichos hechos al lenguaje del análisis matemático. En sí misma, la física del mercado resulta imposible de concebir sin las matemáticas, porque las señales que generamos son sustancia matemática y no de otro tipo. Mucha gente trata de crear teorías o fórmulas sin analizar antes las estadísticas, o bien dichas estadísticas son de naturaleza muy limitada, insuficientes para sacar conclusiones tan audaces. Solo la práctica constituye el criterio de la verdad. Primero, intentaremos reflexionar un poco y luego, partiendo de dichas reflexiones, crearemos un asesor. Después de todo ello, pondremos al asesor a prueba.


El precio y qué nos aporta

Dentro de cualquier mercado, siempre hay un producto diferente: en el mercado de divisas, la divisa en sí es el producto. La divisa representa el derecho a poseer un determinado producto o información establecido como estándar en todo el mundo. Tomemos, por ejemplo, la pareja EURUSD y el valor actual de su gráfico. El valor actual del gráfico significará en este caso USD/EUR = CurrentPrice; también podemos expresarlo así: USD =  EUR*CurrentPrice, que representa el número de dólares contenido en un euro. En otras palabras, muestra la relación del peso de cada divisa; en este caso, además, asumimos que existe algún equivalente común de cambio para cada divisa, algún bien común u otra cosa de valor. El precio se forma en la profundidad de mercado, y la dinámica de esta determina el movimiento del precio. Al mismo tiempo, merece la pena recordar que nunca podremos considerar todos los factores implicados en la formación de los precios, por ejemplo, FORTS está vinculado a FÓREX y ambos mercados se influyen entre sí. No poseemos grandes conocimiento de esta variedad, pero aun así, podemos entender que todo está vinculado y que cuantos más canales de datos haya, mejor. Probablemente sea mejor no adentrarse en una jungla así: mejor concentrarse en las cosas simples que mueven el precio.

Cualquier dependencia se puede representar como una función de muchas variables, al igual que cualquier cotización. Inicialmente, el precio es:

  • P=P(t)

En otras palabras, el precio es una función de tiempo cuya forma no podemos establecer de forma fiable para cada pareja de divisas o cualquier otro instrumento, ya que requeriría una cantidad de tiempo infinita. Pero, en esencia, esta presentación no nos dará nada. La verdadera naturaleza del precio es dual, ya que implica tanto componentes predecibles como aleatorios. Por parte predecible ni siquiera podemos comprender una función en sí, sino su primera derivada en el tiempo. Carece de sentido representar esta función en forma de términos, ya que esto no nos proporcionará nada en absoluto a la hora de comerciar; no obstante, si consideramos su primera derivada, resulta que:

  • P'(t)=Pa'(t)+Pu'(t)

Aquí, el primer término muestra la parte que podemos analizar de alguna manera usando el análisis matemático; el segundo supone una parte impredecible. De esta fórmula, podemos deducir directamente que resulta imposible predecir la magnitud y la dirección del movimiento con una precisión del 100%. Podemos no pensar cuál será el último término, ya que no lo podremos determinar, pero el primero sí. Es posible suponer que este término se puede presentar de otra forma, considerando que la función del precio es discreta y no podemos aplicar operaciones diferenciales. Pero, en cambio, podemos tomar la derivada promedio sobre el intervalo de tiempo "st". Para el precio, esta representará la duración de la barra; para los ticks, será el tiempo mínimo entre dos ticks.

  • PaM(t)=(Pa(t)-Pa(t-st))/st - movimiento promedio de precio (derivada de tiempo) durante un periodo temporal fijo
  • Ma(t)=Ma(P(t),P(t-st) + ... + P(t-N*st), D(t),D(t-st) + ... + D(t-N*st),U[1](),U[2](t) + ... + U[N](t) )
  • P(t[i]) - valores de precio antiguos (datos de las barras o ticks)
  • D(t[i]) - valores de precio antiguos en otras parejas de divisas
  • U[i](t) - otras magnitudes conocidas o desconocidas que influyan en el mercado
  • Ma(t) - esperanza matemática de la magnitud PaM(t) en este punto temporal 

En otras palabras, asumiremos que la parte predecible del precio puede depender de las barras o ticks anteriores, así como de los datos de precio de otras parejas de divisas y de los datos de otras bolsas y eventos mundiales. En este caso, además, deberemos comprender que incluso esta parte del precio no se puede predecir al 100%: solo podemos calcular algunas de sus características. Esta característica solo puede suponer una cierta probabilidad o ciertos parámetros de una variable aleatoria, como la esperanza matemática, la varianza, la desviación estándar y otras magnitudes relacionadas con la teoría de la probabilidad. Para conseguir implementar un comercio rentable, solo necesitamos operar con esperanzas matemáticas. Después de tratar con todas estas conclusiones, si nos tomamos nuestro tiempo y lo pensamos detenidamente, podremos llegar a la conclusión de que resulta posible analizar el mercado, y no solo con la ayuda de dicha lógica. La cuestión es que la parte predecible del precio se desarrolla usando como base la actividad general de los jugadores. Así que podemos descartar esos parámetros del mercado, excepto los factores creados por los propios jugadores. Obviamente, todo esto hará que disminuya la fiabilidad de nuestro método de análisis, pero así simplificaremos el modelo para nosotros mismos. Solo necesitamos comprender que cuanto menor sea el valor "st", con mayor precisión describirán nuestras fórmulas el mercado.

  • VMa(t)=VMa(P(t),P(t-st) + ... + P(t-N*st))
  • VMa(t)=VBuy(t)-VSell(t)
  • VMa(t) - volúmenes totales
  • VBuy(t) - volúmenes de las órdenes de compra abiertas
  • VSell(t) - volúmenes de las órdenes de venta abiertas

La función que presentamos aquí describe la diferencia en los volúmenes de todas las posiciones de compra y venta abiertas ahora. El caso es que algunas de estas posiciones se anulan entre sí, mientras que el resto son independientes. Una vez las posiciones están abiertas, estas simbolizan la promesa de cerrar transcurrido un tiempo. Seguro que no es un secreto para nadie que las compras hacen subir el precio, mientras que las ventas lo hacen bajar. La única forma de saber hacia dónde irá el precio es evaluar los volúmenes de las posiciones abiertas y la dirección de estas posiciones; en este caso, además, aquí solo podremos considerar las órdenes de mercado abiertas.

La naturaleza ondulatoria del mercado realmente se relaciona con este hecho simple: es solo un caso especial de un proceso más general de fluctuaciones en el volumen de posiciones alcistas y bajistas, si lo desea.

Con respecto a las barras, también podemos considerar que hay 4 precios dentro de la barra, esto nos proporcionará mejores fórmulas. La presencia de más datos implica un análisis más preciso, por lo que resulta importante considerar todos los datos de precio. Lo único negativo es que no resulta agradable considerar cada tick, porque estos datos reducen en varias decenas de veces la velocidad de nuestros algoritmos, sin mencionar el hecho de que todos los brókeres tienen ticks diferentes. Lo principal es que los precios de apertura y cierre de las barras son casi idénticos para la mayoría de los brókeres. Vamos a reescribir la función de volumen para considerar todos los datos de precio:

  • VMa(t)=VMa(O(t),O(t-st) +...+ O(t-N*st) + C(t),C(t-st) + C(t-N*st),H(t),H(t-st)...H(t-N*st),L(t),L(t-st)...L(t-N*st))

A esta función también le podemos añadir variables relacionadas con la hora, los días de la semana, los meses y las semanas, pero en ese caso tendremos muchas fórmulas vinculadas a áreas específicas del mercado, y nosotros necesitamos comprender la física del mercado al completo. Sabremos que no podremos romper esta, y que seremos capaces de usarla mientras exista el mercado. Otra ventaja será su carácter multidivisa.

A efectos prácticos, el uso de este tipo de representación no tiene sentido, ya que necesitamos saber exactamente cómo construir esta función y en base a qué. No podemos simplemente escribir la forma de esta función y determinar sus dependencias, pero estas expresiones sí que pueden ayudarnos a alcanzar una comprensión inicial de cómo analizar todo esto y dar paso a los siguientes supuestos. En última instancia, podemos representar cualquier conjunto de condiciones lógicas como una función de este tipo, y al contrario: la función en sí puede convertirse en un conjunto de condiciones. No importa cómo la implementemos, lo importante es que nos resulte comprensible. Cualquier algoritmo se puede reducir a una sola fórmula. Lo único es que a veces resulta más sencillo describir las señales como condiciones (o como un sistema de condiciones) que aislar una función supercompleja. Otra cuestión interesante es cómo construir dicha función.

En un sistema de comercio real, no podemos analizar la historia completa a la vez, sino solo un periodo de tiempo fijo. En este caso, podemos adoptar 4 enfoques. Enunciaremos nuestros propios nombres y les explicaremos por qué los nombres son así exactamente:

  • Fórmulas (indicadores o sus funciones)
  • Simulación
  • Matemáticas generales
  • Tipos de aprendizaje de máquinas

La primera opción implica el uso de un determinado valor o conjunto de valores; podemos usar como ejemplo algunos indicadores o nuestras propias fórmulas. La ventaja de este enfoque reside en la disponibilidad de un amplio conjunto de herramientas en los terminales MetaTrader 4/5. Además, en internet y en el Mercado podremos encontrar muchos indicadores creados según las teorías más populares del mercado. La desventaja de este enfoque es que en la mayoría de los casos no entenderemos la base funcional del indicador, e incluso si lo hiciéramos, estas consideraciones no tendrían por qué poseer ninguna utilidad.

La segunda opción pasa por rechazar los datos que no entendamos, o los datos cuya utilidad está en duda. En cambio, podemos tratar de imitar las órdenes en el mercado y así saber que nuestro sistema, aunque sea en parte, será capaz de describir cuántas posiciones están abiertas en una dirección y en otra. Esta información nos proporcionará los pronósticos necesarios y nos permitirá describir el mercado desde un punto de vista global con la mayor calidad posible. Esta es la única alternativa al aprendizaje automático.

Entendemos por matemáticas la comprensión de ciertas leyes fundamentales o el conocimiento de ciertos principios matemáticos que nos permiten explotar cualquier cotización, independientemente de la situación actual del mercado. El hecho es que todas las funciones, incluidas las discretas, poseen ciertas características que podemos explotar. Obviamente, todo esto resultará así siempre y cuando la dependencia no sea caótica (en nuestro caso, el mercado fórex no es caótico, por lo que podemos explotar cualquier cotización). En el próximo artículo, analizaremos uno de esos principios que casi todo el mundo conoce. Pero "conocer" y "poder utilizar" son dos cosas distintas. La ventaja de este enfoque es que, en caso de construir un sistema exitoso, no tendremos que preocuparnos por su comportamiento futuro, ya que sabremos que no puede haber ningún motivo para el cambio. 

El cuarto enfoque es el más progresivo, ya que el aprendizaje automático puede aprovechar cualquier dato al máximo. Cuanto más poder computacional tengamos, mayor será la calidad del análisis. La desventaja de este enfoque es que no podremos comprender la física del mercado. La ventaja del enfoque reside en su simplicidad y en la máxima calidad de los resultados con un mínimo de tiempo invertido. Sin embargo, este enfoque no es aplicable en el marco del presente artículo.


Sobre los patrones

Existen muchos términos en la vida diaria de un tráder; algunos de ellos son muy importantes, mientras que otros lo son menos. Hay términos que usamos todo el tiempo, pero, por algún motivo, parece que solo un pequeño porcentaje de tráders entiende lo que estos términos realmente significan y lo que nos ofrecen. Uno de estos términos es "patrón". Vamos a intentar explicar qué es en el lenguaje de las matemáticas. El patrón siempre está vinculado a un periodo temporal, una pareja de divisas y un periodo del gráfico concretos. Existen patrones más fuertes. Dichos patrones pueden tener una naturaleza multidivisa o global, o ambas características. El patrón ideal se conoce como Grial. Hay algunas afirmaciones que son verdaderas para cualquier patrón:

  • La presencia de una fórmula o conjunto de condiciones que simboliza el patrón.
  • La presencia de indicadores de prueba mínimos en el simulador de estrategias o de indicadores de una cuenta demo o una cuenta real
  • La clasificación de patrones en términos de rendimiento para todas las parejas de divisas y periodos de los gráficos.
  • El periodo de la historia donde se encontró el patrón.
  • El intervalo de tiempo en el futuro en el que el patrón permanece operativo.
  • El segundo periodo temporal en el futuro después del primero en el que el patrón original mantiene los parámetros o los invierte y cómo usarlo

Si leemos detenidamente cada propiedad, podremos comprender que un patrón es la fórmula o el conjunto de condiciones que describen con la mayor precisión posible el movimiento del precio en los intervalos seleccionados. El patrón puede resultar en realidad una casualidad, especialmente si tenemos un intervalo demasiado pequeño e indicadores demasiado optimistas en el sistema. Es muy importante comprender que cuando ponemos a prueba el sistema en periodos breves, nuestras posibilidades de encontrar patrones globales tienden a cero. La cuestión reside en el tamaño de la muestra. Cuanto más pequeña sea la muestra, más resultados aleatorios obtendremos. 

Una vez que hayamos determinado qué es un patrón, tendremos que plantearnos cómo utilizar estos patrones de manera efectiva. Todo dependerá de cómo hayamos encontrado este patrón y de su calidad. Si nos abstraemos de los métodos de análisis que usan la potencia computacional de las máquinas, recurriremos la analítica. En nuestra opinión, la analítica no puede competir con ningún tipo de análisis de máquinas, aunque solo sea porque incluso un buen equipo de analistas no puede procesar los datos que puede procesar una máquina, incluso no muy potente. Sea como fuere, la búsqueda de patrones globales requiere poder computacional, a no ser, claro está, que accidentalmente hayamos visto algo obvio con sus propios ojos y hayamos podido comprender su física.


Escribiendo el simulador de posiciones más sencillo

Para intentar encontrar patrones globales, resultaría interesante escribir un asesor experto capaz de describir el estado de ánimo de los jugadores del mercado. Para ello, intentaremos escribir un simulador de posiciones de mercado. Las posiciones se simularán dentro de las barras más próximas al filo del mercado. Deberemos asumir que los jugadores en el mercado son diferentes, así como el peso de sus órdenes, presentando todo esto al mismo tiempo de la forma más sencilla. Si un prototipo simple muestra beneficios, podremos adoptar sus principios.

La lógica se dividirá condicionalmente en 3 simulaciones separadas y cualquier posible combinación mixta de ellas:

  • Simulación de órdenes stop
  • Simulación de órdenes límite
  • Simulación de órdenes de mercado
  • Cualquier combinación posible

La lógica de colocación de las órdenes será la siguiente:

Orders Grid Logic

Estas cuadrículas se colocan en cada nueva barra que surge, tratando de imitar el estado de ánimo de alguna parte de la multitud. Las cuadrículas de órdenes antiguas actualizan su estado según la nueva barra que ha aparecido en el gráfico. Este enfoque no resulta muy preciso, pero con la simulación por ticks simplemente nos ahogaremos en cálculos interminables, y tampoco es que confiemos en los ticks.

Existen 2 tipos de distribución de volúmenes relativos, con atenuación y uniforme, pero solo para las órdenes stop y limit. Las distribuciones del mercado son uniformes. Si así lo queremos, podemos expandir los tipos de distribución, si esto muestra cierta perspectiva. Lo ilustraremos en la figura:

Relative Volumes Filling Types

Aquí la longitud de la línea que simboliza la orden es proporcional al volumen de la propia orden. Hemos pensado que estas ilustraciones resultarán simples y comprensibles para todos.

En este caso, podemos implementar todo de una forma inteligente usando un enfoque orientado a objetos. Vamos a comenzar describiendo las listas numeradas:

enum CLOSE_MODE// how to close orders
   {
   CLOSE_FAST,// fast
   CLOSE_QUALITY// wait for an opposite signal
   };

enum WORK_MODE// operation mode
   {
   MODE_SIMPLE,// slow mode
   MODE_FAST// fast mode
   };

enum ENUM_GRID_WEIGHT//weight fill type for limit and stop orders
   {
   WEIGHT_DECREASE,// with attenuation if moving from the price
   WEIGHT_SAME// the same for the entire grid
   };

enum ENUM_STATUS_ORDER// statuses of orders or positions
   {
   STATUS_VIRTUAL,// stop or limit order
   STATUS_MARKET,// market order
   STATUS_ABORTED// canceled stop or limit order
   };

El simulador podrá funcionar en el modo lento y rápido. El modo lento se necesita principalmente para el análisis inicial. El análisis inicial se encarga del cálculo en las primeras "n" velas más próximas al mercado, y el análisis rápido asume el cálculo solo en la nueva vela que surge. Al final, resultó que el enfoque simple no era suficiente y tuvimos que complementar la funcionalidad para aumentar la velocidad del algoritmo. En la inicialización del asesor, se debe realizar el cálculo, es muy amplio. Pero luego solo tenemos que actualizar la simulación basándonos en una única vela nueva que aparece en cada vela. Asimismo, tendremos 2 tipos de distribución para los volúmenes de las órdenes límite y stop, dependiendo de la distancia del precio respecto al mercado actual, que para cada barra es Open[i]. Supusimos que en cada barra se abre una cuadrícula de stop limit y órdenes de mercado con diferentes distribuciones y pesos. Después de un tiempo, las órdenes de stop y límite se convierten en órdenes de mercado, como debería ser según las especificaciones. O bien las órdenes stop y límite se cancelan si el precio no ha alcanzado el precio necesario dentro del intervalo temporal asignado.

Vamos a comenzar a construir nuestra simulación yendo de lo simple hacia lo complejo y juntándolo todo de manera gradual. Primero, definiremos qué es una orden:

struct Order// structure symbolizing a player's order
   {
   public:
   double WantedPrice;// desired open price
   int BarsExpirationOpen;// If the order remains for certain number of bars, the player can't wait any more and cancels the order
   int BarsExpirationClose;//If this is a market order and the player does not want to wait, he closes the position
   double UpPriceToClose;//The total upward price movement at which the player closes the order (points)
   double LowPriceToClose;//The total downward price movement at which the player closes the order 
   double VolumeAlpha;// current volume equivalent [0...1]
   double VolumeStart;// starting volume equivalent [0...1]
   int IndexMarket;// the index of the bar on which the virtual market turned into market
   ENUM_STATUS_ORDER Status;// order status
   Order(ENUM_STATUS_ORDER S)// constructor that creates a certain order
      {
      Status=S;
      }
   };

No hay muchos parámetros, pero cada uno es importante en el algoritmo general. Muchos campos aquí son aplicables a cualquier orden, mientras que algunos de ellos solo se aplican a órdenes límite o stop. Por ejemplo, el precio deseado es el precio de apertura de una orden de mercado, y si la orden es límite o stop, este será directamente el precio de apertura deseado.

Los precios de cierre superior e inferior actúan como stops, pero, al mismo tiempo, deberemos asumir que la orden de la cuadrícula no es exactamente una orden, sino que contiene muchas órdenes; estas simplemente se fusionan en 1 orden que se abre con un cierto volumen y a un cierto precio. Las variables del volumen inicial y el volumen actual nos indican cómo de importantes son las órdenes en un nivel específico de una barra específica.

El volumen inicial es el volumen al que se coloca una orden. El volumen actual es su volumen durante el desarrollo de los eventos. El beneficio de órdenes específicas no será tan importante para nosotros como la relación entre los volúmenes de las órdenes que están en compra y venta. Las señales comerciales se generarán según estas consideraciones. Obviamente, podemos generar otras señales, pero esto requerirá de consideraciones adicionales. También debemos mencionar que las órdenes no se cerrarán al alcanzar ciertos niveles: todo esto sucederá de forma paulatina en cada barra, poco a poco, tratando de simular al máximo el desarrollo real de los eventos.

A continuación, debemos determinar el almacenamiento de cada barra. La barra almacenará las órdenes que se hayan abierto en ella:

class OrderBox// Order box of a specific bar
   {
   public:
   Order BuyStopOrders[];
   Order BuyLimitOrders[];
   Order BuyMarketOrders[];
   Order SellStopOrders[];
   Order SellLimitOrders[];
   Order SellMarketOrders[];
   
   OrderBox(int OrdersToOneBar)
      {
      ArrayResize(BuyStopOrders,OrdersToOneBar);
      ArrayResize(BuyLimitOrders,OrdersToOneBar);
      ArrayResize(BuyMarketOrders,OrdersToOneBar);
      ArrayResize(SellStopOrders,OrdersToOneBar);
      ArrayResize(SellLimitOrders,OrdersToOneBar);
      ArrayResize(SellMarketOrders,OrdersToOneBar);      
      for ( int i=0; i<ArraySize(BuyStopOrders); i++ )// Set types for all orders
         {
         BuyStopOrders[i]=Order(STATUS_VIRTUAL);
         BuyLimitOrders[i]=Order(STATUS_VIRTUAL);
         BuyMarketOrders[i]=Order(STATUS_MARKET);
         SellStopOrders[i]=Order(STATUS_VIRTUAL);
         SellLimitOrders[i]=Order(STATUS_VIRTUAL);
         SellMarketOrders[i]=Order(STATUS_MARKET);         
         }
      }
   };

Aquí todo parece bastante sencillo. Seis tipos de órdenes que se describen con la ayuda de matrices. Esto se ha implementado así para evitar confusiones. No usaremos esta clase en su forma básica; obviamente, se trata solo de un ladrillo en el edificio.

A continuación, definiremos el almacenamiento común de todas las barras como un solo objeto, del que luego heredaremos. Las técnicas aquí son bastante sencillas y nada exóticas; pero claro, esto ya dependerá de cómo funcione la fantasía de cada uno y cómo quiera escribir los programas.

class BarBox// Storage for all orders
   {
   protected:
   OrderBox BarOrders[];
   
   BarBox(int OrdersToOneBar,int BarsTotal)
      {
      ArrayResize(BarOrders,BarsTotal);
      for ( int i=0; i<ArraySize(BarOrders); i++ )// Set types for all orders
         {
         BarOrders[i]=OrderBox(OrdersToOneBar);
         }
      }   
   };

Se trata simplemente de un almacenamiento con los datos de barra (órdenes) y nada más. Hasta ahora, todo ha sido sencillo, pero después la cosa resulta mucho más complicada.

Una vez hayamos identificado el almacenamiento de datos adecuado para las órdenes, deberemos determinar cómo y según qué reglas se crearán las órdenes, la importancia de los tipos concretos de órdenes, etcétera. Para hacerlo, crearemos la siguiente clase:

class PositionGenerator:public BarBox// Inherit class from the box to avoid the need to include it as an internal member and to avoid multiple references
   {
   protected:
   double VolumeAlphaStop;// importance of volumes of STOP orders
   double VolumeAlphaLimit;// importance of volumes of LIMIT orders
   double VolumeAlphaMarket;// importance of volumes of MARKET orders
   double HalfCorridorLimitStop;// step of the corridor of Limit and Stop orders in points
   int ExpirationOpenLimit;// after how many bars the volumes of the grid of limit orders for opening will completely attenuate
   int ExpirationOpenStop;// after how many bars the volumes of the grid of stop orders for opening will completely attenuate
   int ExpirationClose;// after how many bars the volumes of orders for closing will completely attenuate
   int ProfitPointsCorridorPart;// half corridor size for the profit of all orders
   int LossPointsCorridorPart;// half corridor size for the loss of all orders
   int OrdersToOneBar;// orders of one type per 1 bar
   ENUM_GRID_WEIGHT WeightStopLimitFillingType;
   
   PositionGenerator( ENUM_GRID_WEIGHT WeightStopLimitFillingType0
                     ,int HalfCorridorLimitStop0,int OrdersToOneBar0,int BarsTotal0
                     ,int ExpirationOpenLimit0,int ExpirationOpenStop0
                     ,int ExpirationClose0
                     ,int ProfitPointsCorridorPart0,int LossPointsCorridorPart0
                     ,double VolumeAlphaStop0,double VolumeAlphaLimit0,double VolumeAlphaMarket0) 
                     : BarBox(OrdersToOneBar0,BarsTotal0)
      {
      VolumeAlphaStop=VolumeAlphaStop0;
      VolumeAlphaLimit=VolumeAlphaLimit0;
      VolumeAlphaMarket=VolumeAlphaMarket0;
      OrdersToOneBar=OrdersToOneBar0;
      HalfCorridorLimitStop=double(HalfCorridorLimitStop0)/double(OrdersToOneBar);
      ExpirationOpenLimit=ExpirationOpenLimit0;
      ExpirationOpenStop=ExpirationOpenStop0;
      ExpirationClose=ExpirationClose0;
      ProfitPointsCorridorPart=ProfitPointsCorridorPart0;
      LossPointsCorridorPart=LossPointsCorridorPart0;
      OrdersToOneBar=OrdersToOneBar0;
      WeightStopLimitFillingType=WeightStopLimitFillingType0;
      }
   private:
   
   double CalcVolumeDecrease(double TypeWeight,int i,int size)// attenuation volume
      {
      if ( size > 1 )
         {
         double K=1.0/(1.0-size);
         double C=1.0;
         return TypeWeight*K*i+C;
         }
      else return 0.0;
      }
      
   double CalcVolumeSimple(double TypeWeight)// equal volume
      {
      return TypeWeight;
      }
   
   void RebuildStops()// rebuild stop orders
      {
      int size=ArraySize(BarOrders[0].BuyStopOrders);
      for ( int j=ArraySize(BarOrders)-1; j>=0; j-- )
         {
         for ( int i=0; i<size; i++ )// reset all
            {
            BarOrders[j].BuyStopOrders[i].Status=STATUS_VIRTUAL;// reset status to initial
            BarOrders[j].BuyStopOrders[i].WantedPrice=Open[j+1]+HalfCorridorLimitStop*(i+1)*Point;// prices of the order grid
            if ( WeightStopLimitFillingType == WEIGHT_DECREASE ) BarOrders[j].BuyStopOrders[i].VolumeAlpha=CalcVolumeDecrease(VolumeAlphaStop,i,size);// weight of each element of the grid
            if ( WeightStopLimitFillingType == WEIGHT_SAME ) BarOrders[j].BuyStopOrders[i].VolumeAlpha=CalcVolumeSimple(VolumeAlphaStop);// current weight of each element of the grid
            BarOrders[j].BuyStopOrders[i].VolumeStart=BarOrders[j].BuyStopOrders[i].VolumeAlpha;// starting weight of each element of the grid
            BarOrders[j].BuyStopOrders[i].UpPriceToClose=BarOrders[j].BuyStopOrders[i].WantedPrice+ProfitPointsCorridorPart*Point;// upper border to close
            BarOrders[j].BuyStopOrders[i].LowPriceToClose=BarOrders[j].BuyStopOrders[i].WantedPrice-LossPointsCorridorPart*Point;// lower border to close
            BarOrders[j].BuyStopOrders[i].BarsExpirationOpen=ExpirationOpenStop;
            BarOrders[j].BuyStopOrders[i].BarsExpirationClose=ExpirationClose;
       
            BarOrders[j].SellStopOrders[i].Status=STATUS_VIRTUAL;
            BarOrders[j].SellStopOrders[i].WantedPrice=Open[j+1]-HalfCorridorLimitStop*(i+1)*Point;// prices of the order grid
            if ( WeightStopLimitFillingType == WEIGHT_DECREASE ) BarOrders[j].SellStopOrders[i].VolumeAlpha=CalcVolumeDecrease(VolumeAlphaStop,i,size);// weight of each element of the grid
            if ( WeightStopLimitFillingType == WEIGHT_SAME ) BarOrders[j].SellStopOrders[i].VolumeAlpha=CalcVolumeSimple(VolumeAlphaStop);// current weight of each element of the grid
            BarOrders[j].SellStopOrders[i].VolumeStart=BarOrders[j].SellStopOrders[i].VolumeAlpha;// starting weight of each element of the grid
            BarOrders[j].SellStopOrders[i].UpPriceToClose=BarOrders[j].SellStopOrders[i].WantedPrice+LossPointsCorridorPart*Point;// upper border to close
            BarOrders[j].SellStopOrders[i].LowPriceToClose=BarOrders[j].SellStopOrders[i].WantedPrice-ProfitPointsCorridorPart*Point;// lower border to close
            BarOrders[j].SellStopOrders[i].BarsExpirationOpen=ExpirationOpenStop;
            BarOrders[j].SellStopOrders[i].BarsExpirationClose=ExpirationClose;                    
            }         
         }      
      }
      
   void RebuildLimits()// rebuild limit orders
      {
      int size=ArraySize(BarOrders[0].BuyLimitOrders);
      for ( int j=ArraySize(BarOrders)-1; j>=0; j-- )
         {
         for ( int i=0; i<size; i++ )// reset all
            {
            BarOrders[j].BuyLimitOrders[i].Status=STATUS_VIRTUAL;// reset status to initial
            BarOrders[j].BuyLimitOrders[i].WantedPrice=Open[j+1]-HalfCorridorLimitStop*(i+1)*Point;// prices of the order grid
            if ( WeightStopLimitFillingType == WEIGHT_DECREASE ) BarOrders[j].BuyLimitOrders[i].VolumeAlpha=CalcVolumeDecrease(VolumeAlphaLimit,i,size);// weight of each element of the grid
            if ( WeightStopLimitFillingType == WEIGHT_SAME ) BarOrders[j].BuyLimitOrders[i].VolumeAlpha=CalcVolumeSimple(VolumeAlphaLimit);// current weight of each element of the grid
            BarOrders[j].BuyLimitOrders[i].VolumeStart=BarOrders[j].BuyLimitOrders[i].VolumeAlpha;// starting weight of each element of the grid
            BarOrders[j].BuyLimitOrders[i].UpPriceToClose=BarOrders[j].BuyLimitOrders[i].WantedPrice+ProfitPointsCorridorPart*Point;// upper border to close
            BarOrders[j].BuyLimitOrders[i].LowPriceToClose=BarOrders[j].BuyLimitOrders[i].WantedPrice-LossPointsCorridorPart*Point;// lower border to close
            BarOrders[j].BuyLimitOrders[i].BarsExpirationOpen=ExpirationOpenLimit;
            BarOrders[j].BuyLimitOrders[i].BarsExpirationClose=ExpirationClose;            
       
            BarOrders[j].SellLimitOrders[i].Status=STATUS_VIRTUAL;
            BarOrders[j].SellLimitOrders[i].WantedPrice=Open[j+1]+HalfCorridorLimitStop*(i+1)*Point;// prices of the order grid
            if ( WeightStopLimitFillingType == WEIGHT_DECREASE ) BarOrders[j].SellLimitOrders[i].VolumeAlpha=CalcVolumeDecrease(VolumeAlphaLimit,i,size);// weight of each element of the grid
            if ( WeightStopLimitFillingType == WEIGHT_SAME ) BarOrders[j].SellLimitOrders[i].VolumeAlpha=CalcVolumeSimple(VolumeAlphaLimit);// current weight of each element of the grid
            BarOrders[j].SellLimitOrders[i].VolumeStart=BarOrders[j].SellLimitOrders[i].VolumeAlpha;// starting weight of each element of the grid
            BarOrders[j].SellLimitOrders[i].UpPriceToClose=BarOrders[j].SellLimitOrders[i].WantedPrice+LossPointsCorridorPart*Point;// upper border to close
            BarOrders[j].SellLimitOrders[i].LowPriceToClose=BarOrders[j].SellLimitOrders[i].WantedPrice-ProfitPointsCorridorPart*Point;// lower border to close
            BarOrders[j].SellLimitOrders[i].BarsExpirationOpen=ExpirationOpenLimit;
            BarOrders[j].SellLimitOrders[i].BarsExpirationClose=ExpirationClose;
            }         
         }      
      }
      
   void RebuildMarkets()// rebuild market orders
      {
      int size=ArraySize(BarOrders[0].BuyMarketOrders);
      double MarketStep;
      for ( int j=ArraySize(BarOrders)-1; j>0; j-- )
         {
         MarketStep=(High[j+1]-Low[j+1])/double(OrdersToOneBar);
            
         for ( int i=0; i<size; i++ )// reset all
            {
            BarOrders[j].BuyMarketOrders[i].Status=STATUS_MARKET;// reset status to initial
            BarOrders[j].BuyMarketOrders[i].WantedPrice=Low[j+1]+MarketStep*i;// prices of the order grid
            BarOrders[j].BuyMarketOrders[i].VolumeAlpha=CalcVolumeSimple(VolumeAlphaMarket);// current weight of each element of the grid
            BarOrders[j].BuyMarketOrders[i].VolumeStart=BarOrders[j].BuyMarketOrders[i].VolumeAlpha;// starting weight of each element of the grid
            BarOrders[j].BuyMarketOrders[i].UpPriceToClose=BarOrders[j].BuyMarketOrders[i].WantedPrice+ProfitPointsCorridorPart*Point;// upper border to close
            BarOrders[j].BuyMarketOrders[i].LowPriceToClose=BarOrders[j].BuyMarketOrders[i].WantedPrice-LossPointsCorridorPart*Point;// lower border to close
            BarOrders[j].BuyMarketOrders[i].BarsExpirationClose=ExpirationClose;
               
            BarOrders[j].SellMarketOrders[i].Status=STATUS_MARKET;
            BarOrders[j].SellMarketOrders[i].WantedPrice=High[j+1]-MarketStep*i;// prices of the order grid
            BarOrders[j].SellMarketOrders[i].VolumeAlpha=CalcVolumeSimple(VolumeAlphaMarket);// current weight of each element of the grid
            BarOrders[j].SellMarketOrders[i].VolumeStart=BarOrders[j].SellMarketOrders[i].VolumeAlpha;// starting weight of each element of the grid
            BarOrders[j].SellMarketOrders[i].UpPriceToClose=BarOrders[j].SellMarketOrders[i].WantedPrice+LossPointsCorridorPart*Point;// upper border to close
            BarOrders[j].SellMarketOrders[i].LowPriceToClose=BarOrders[j].SellMarketOrders[i].WantedPrice-ProfitPointsCorridorPart*Point;// lower border to close
            BarOrders[j].SellMarketOrders[i].BarsExpirationClose=ExpirationClose;
            } 
         }      
      }

   /////быстрые методы
   void RebuildStopsFast()// rebuild stop orders
      {
      int size=ArraySize(BarOrders[0].BuyStopOrders);
      for ( int j=ArraySize(BarOrders)-1; j>0; j-- )
         {
         for ( int i=0; i<size; i++ )// shift orders
            {
            BarOrders[j].BuyStopOrders[i]=BarOrders[j-1].BuyStopOrders[i];
            BarOrders[j].SellStopOrders[i]=BarOrders[j-1].SellStopOrders[i];
            BarOrders[j].SellStopOrders[i].IndexMarket++;
            }
         }
         
      for ( int i=0; i<size; i++ )// create a new grid at a new bar
         {
         BarOrders[0].BuyStopOrders[i].Status=STATUS_VIRTUAL;// reset status to initial
         BarOrders[0].BuyStopOrders[i].WantedPrice=Close[1]+HalfCorridorLimitStop*(i+1)*Point;//цены сетки ордеров
         if ( WeightStopLimitFillingType == WEIGHT_DECREASE ) BarOrders[0].BuyStopOrders[i].VolumeAlpha=CalcVolumeDecrease(VolumeAlphaStop,i,size);// weight of each element of the grid
         if ( WeightStopLimitFillingType == WEIGHT_SAME ) BarOrders[0].BuyStopOrders[i].VolumeAlpha=CalcVolumeSimple(VolumeAlphaStop);// current weight of each element of the grid
         BarOrders[0].BuyStopOrders[i].VolumeStart=BarOrders[0].BuyStopOrders[i].VolumeAlpha;// starting weight of each element of the grid
         BarOrders[0].BuyStopOrders[i].UpPriceToClose=BarOrders[0].BuyStopOrders[i].WantedPrice+ProfitPointsCorridorPart*Point;//upper border to close
         BarOrders[0].BuyStopOrders[i].LowPriceToClose=BarOrders[0].BuyStopOrders[i].WantedPrice-LossPointsCorridorPart*Point;// lower border to close
         BarOrders[0].BuyStopOrders[i].BarsExpirationOpen=ExpirationOpenStop;
         BarOrders[0].BuyStopOrders[i].BarsExpirationClose=ExpirationClose;
       
         BarOrders[0].SellStopOrders[i].Status=STATUS_VIRTUAL;
         BarOrders[0].SellStopOrders[i].WantedPrice=Close[1]-HalfCorridorLimitStop*(i+1)*Point;//цены сетки ордеров
         if ( WeightStopLimitFillingType == WEIGHT_DECREASE ) BarOrders[0].SellStopOrders[i].VolumeAlpha=CalcVolumeDecrease(VolumeAlphaStop,i,size);// weight of each element of the grid
         if ( WeightStopLimitFillingType == WEIGHT_SAME ) BarOrders[0].SellStopOrders[i].VolumeAlpha=CalcVolumeSimple(VolumeAlphaStop);// current weight of each element of the grid
         BarOrders[0].SellStopOrders[i].VolumeStart=BarOrders[0].SellStopOrders[i].VolumeAlpha;// starting weight of each element of the grid
         BarOrders[0].SellStopOrders[i].UpPriceToClose=BarOrders[0].SellStopOrders[i].WantedPrice+LossPointsCorridorPart*Point;// upper border to close
         BarOrders[0].SellStopOrders[i].LowPriceToClose=BarOrders[0].SellStopOrders[i].WantedPrice-ProfitPointsCorridorPart*Point;// lower border to close
         BarOrders[0].SellStopOrders[i].BarsExpirationOpen=ExpirationOpenStop;
         BarOrders[0].SellStopOrders[i].BarsExpirationClose=ExpirationClose;                    
         }               
      }
      
   void RebuildLimitsFast()// rebuild limit orders
      {
      int size=ArraySize(BarOrders[0].BuyLimitOrders);
      for ( int j=ArraySize(BarOrders)-1; j>0; j-- )
         {
         for ( int i=0; i<size; i++ )// shift orders
            {
            BarOrders[j].BuyLimitOrders[i]=BarOrders[j-1].BuyLimitOrders[i];
            BarOrders[j].SellLimitOrders[i]=BarOrders[j-1].SellLimitOrders[i];
            BarOrders[j].SellLimitOrders[i].IndexMarket++;
            }         
         }
      
      for ( int i=0; i<size; i++ )// create a new grid at a new bar
         {
         BarOrders[0].BuyLimitOrders[i].Status=STATUS_VIRTUAL;// reset status to initial
         BarOrders[0].BuyLimitOrders[i].WantedPrice=Open[1]-HalfCorridorLimitStop*(i+1)*Point;//цены сетки ордеров
         if ( WeightStopLimitFillingType == WEIGHT_DECREASE ) BarOrders[0].BuyLimitOrders[i].VolumeAlpha=CalcVolumeDecrease(VolumeAlphaLimit,i,size);// weight of each element of the grid
         if ( WeightStopLimitFillingType == WEIGHT_SAME ) BarOrders[0].BuyLimitOrders[i].VolumeAlpha=CalcVolumeSimple(VolumeAlphaLimit);// current weight of each element of the grid
         BarOrders[0].BuyLimitOrders[i].VolumeStart=BarOrders[0].BuyLimitOrders[i].VolumeAlpha;// starting weight of each element of the grid
         BarOrders[0].BuyLimitOrders[i].UpPriceToClose=BarOrders[0].BuyLimitOrders[i].WantedPrice+ProfitPointsCorridorPart*Point;// upper border to close
         BarOrders[0].BuyLimitOrders[i].LowPriceToClose=BarOrders[0].BuyLimitOrders[i].WantedPrice-LossPointsCorridorPart*Point;// lower border to close
         BarOrders[0].BuyLimitOrders[i].BarsExpirationOpen=ExpirationOpenLimit;
         BarOrders[0].BuyLimitOrders[i].BarsExpirationClose=ExpirationClose;            
       
         BarOrders[0].SellLimitOrders[i].Status=STATUS_VIRTUAL;
         BarOrders[0].SellLimitOrders[i].WantedPrice=Open[1]+HalfCorridorLimitStop*(i+1)*Point;//цены сетки ордеров
         if ( WeightStopLimitFillingType == WEIGHT_DECREASE ) BarOrders[0].SellLimitOrders[i].VolumeAlpha=CalcVolumeDecrease(VolumeAlphaLimit,i,size);// weight of each element of the grid
         if ( WeightStopLimitFillingType == WEIGHT_SAME ) BarOrders[0].SellLimitOrders[i].VolumeAlpha=CalcVolumeSimple(VolumeAlphaLimit);// current weight of each element of the grid
         BarOrders[0].SellLimitOrders[i].VolumeStart=BarOrders[0].SellLimitOrders[i].VolumeAlpha;// starting weight of each element of the grid
         BarOrders[0].SellLimitOrders[i].UpPriceToClose=BarOrders[0].SellLimitOrders[i].WantedPrice+LossPointsCorridorPart*Point;// upper border to close
         BarOrders[0].SellLimitOrders[i].LowPriceToClose=BarOrders[0].SellLimitOrders[i].WantedPrice-ProfitPointsCorridorPart*Point;// lower order to close
         BarOrders[0].SellLimitOrders[i].BarsExpirationOpen=ExpirationOpenLimit;
         BarOrders[0].SellLimitOrders[i].BarsExpirationClose=ExpirationClose;
         }        
      }
      
   void RebuildMarketsFast()// rebuild market orders
      {
      int size=ArraySize(BarOrders[0].BuyMarketOrders);
      double MarketStep;
      for ( int j=ArraySize(BarOrders)-1; j>0; j-- )
         {
         for ( int i=0; i<size; i++ )// shift orders
            {
            BarOrders[j].BuyMarketOrders[i]=BarOrders[j-1].BuyMarketOrders[i];
            BarOrders[j].SellMarketOrders[i]=BarOrders[j-1].SellMarketOrders[i];
            }         
         }
      MarketStep=(High[1]-Low[1])/double(OrdersToOneBar);
      for ( int i=0; i<size; i++ )// create a new grid at a new bar
         {
         BarOrders[0].BuyMarketOrders[i].Status=STATUS_MARKET;// reset status to initial
         BarOrders[0].BuyMarketOrders[i].WantedPrice=Low[1]+MarketStep*i;// prices of the order grid
         BarOrders[0].BuyMarketOrders[i].VolumeAlpha=CalcVolumeSimple(VolumeAlphaMarket);// current weight of each element of the grid
         BarOrders[0].BuyMarketOrders[i].VolumeStart=BarOrders[0].BuyMarketOrders[i].VolumeAlpha;// starting weight of each element of the grid
         BarOrders[0].BuyMarketOrders[i].UpPriceToClose=BarOrders[0].BuyMarketOrders[i].WantedPrice+ProfitPointsCorridorPart*Point;// upper border to close
         BarOrders[0].BuyMarketOrders[i].LowPriceToClose=BarOrders[0].BuyMarketOrders[i].WantedPrice-LossPointsCorridorPart*Point;// lower border to close
         BarOrders[0].BuyMarketOrders[i].BarsExpirationClose=ExpirationClose;
               
         BarOrders[0].SellMarketOrders[i].Status=STATUS_MARKET;
         BarOrders[0].SellMarketOrders[i].WantedPrice=High[1]-MarketStep*i;// prices of the order grid
         BarOrders[0].SellMarketOrders[i].VolumeAlpha=CalcVolumeSimple(VolumeAlphaMarket);// current weight of each element of the grid
         BarOrders[0].SellMarketOrders[i].VolumeStart=BarOrders[0].SellMarketOrders[i].VolumeAlpha;// starting weight of each element of the grid
         BarOrders[0].SellMarketOrders[i].UpPriceToClose=BarOrders[0].SellMarketOrders[i].WantedPrice+LossPointsCorridorPart*Point;// upper border to close
         BarOrders[0].SellMarketOrders[i].LowPriceToClose=BarOrders[0].SellMarketOrders[i].WantedPrice-ProfitPointsCorridorPart*Point;// lower border to close
         BarOrders[0].SellMarketOrders[i].BarsExpirationClose=ExpirationClose;
         }         
      }   
   
   protected:
   void CreateNewOrders()// create new orders at each candlestick
      {
      if ( VolumeAlphaStop != 0.0 ) RebuildStops();
      if ( VolumeAlphaLimit != 0.0 ) RebuildLimits();
      if ( VolumeAlphaMarket != 0.0 ) RebuildMarkets();
      }
   
   void CreateNewOrdersFast()//
      {
      if ( VolumeAlphaStop != 0.0 ) RebuildStopsFast();
      if ( VolumeAlphaLimit != 0.0 ) RebuildLimitsFast();
      if ( VolumeAlphaMarket != 0.0 ) RebuildMarketsFast();      
      }
   
   public:   
   virtual void Update()// state updating function (will be expanded in child classes)
      {
      CreateNewOrders();
      }
      
   virtual void UpdateFast()// fast state update
      {
      CreateNewOrdersFast();
      }      
   };

De hecho, esta clase solo se ocupa de crear la implementación de los métodos Update() y UpdateFast(), que son similares, con la única excepción de que este último trabaja sustancialmente más rápido. Estos métodos crean nuevas órdenes en cada barra y eliminan las antiguas, preparando así los datos para la siguiente clase, que simulará su ciclo de vida. En esta clase, se asignan todos los parámetros necesarios de la orden, su tipo, los precios abiertos, los volúmenes y todos los demás parámetros importantes necesarios en un futuro.

La siguiente clase implementa el proceso de simulación de las órdenes, además de calcular los parámetros necesarios para el comercio, sobre cuya base se generarán las señales:

class Simulation:public PositionGenerator // then assemble a simulator of positions (inherited from the position generator)
   {// market parameter calculations will also performed in this class
   protected:
   double BuyPercent;// percent of open Buy positions
   double SellPercent;// percent of open Sell positions
   double StartVolume;// starting total volume of open Buy positions (the same for Buys and Sells)
   double RelativeVolume;// relative volume
   double SummVolumeBuy;// total volume for Buys
   double SummVolumeSell;// total volume for Sells
   
   public:   
   Simulation( ENUM_GRID_WEIGHT WeightStopLimitFillingType0
                     ,int HalfCorridorLimitStop0,int OrdersToOneBar0,int BarsTotal0
                     ,int ExpirationOpenLimit0,int ExpirationOpenStop0
                     ,int ExpirationClose0
                     ,int ProfitPointsCorridorPart0,int LossPointsCorridorPart0
                     ,double VolumeAlphaStop0,double VolumeAlphaLimit0,double VolumeAlphaMarket0) 
   :PositionGenerator(WeightStopLimitFillingType0
                     ,HalfCorridorLimitStop0,OrdersToOneBar0,BarsTotal0
                     ,ExpirationOpenLimit0,ExpirationOpenStop0
                     ,ExpirationClose0
                     ,ProfitPointsCorridorPart0,LossPointsCorridorPart0
                     ,VolumeAlphaStop0,VolumeAlphaLimit0,VolumeAlphaMarket0) 
      {
      CreateNewOrders();
      CalculateStartVolume();// calculate starting volumes
      UpdateVirtual();// first update virtual orders to process part of them as market orders in the next function
      UpdateMarket();// now update the state of all market orders
      CalculateCurrentVolume();// calculate current volumes of all open orders
      CalculatePercent();// calculate the percentage of positions
      CalculateRelativeVolume();// calculate relative volume
      }

   double GetBuyPercent()// get the percentage of open Buy deals
      {
      return BuyPercent;
      }
      
   double GetSellPercent()// get the percentage of open Sell deals
      {
      return SellPercent;
      }
      
   double GetRelativeVolume()// get relative volume
      {
      return RelativeVolume;
      }

   virtual void Update() override
      {
      PositionGenerator::Update();// call everything that was before
      UpdateVirtual();// first update virtual orders to process part of them as market orders in the next function
      UpdateMarket();// now update the state of all market orders
      CalculateCurrentVolume();// calculate current volumes of all open orders
      CalculatePercent();// calculate the percentage of positions
      CalculateRelativeVolume();// calculate relative volume
      }
      
   virtual void UpdateFast() override
      {
      PositionGenerator::UpdateFast();// call everything that was before
      UpdateVirtualFast();// first update virtual orders to process part of them as market orders in the next function
      UpdateMarketFast();// now update the state of all market orders
      CalculateCurrentVolume();// calculate current volumes of all open orders
      CalculatePercent();// calculate the percentage of positions
      CalculateRelativeVolume();// calculate relative volume
      }   
      
   private:
   
   void UpdateVirtual()// update the status of virtual orders
      {
      int size=ArraySize(BarOrders[0].BuyLimitOrders);
      int SizeBarOrders=ArraySize(BarOrders);
      
      if ( VolumeAlphaLimit != 0.0 )
         {
         for ( int i=SizeBarOrders; i>0; i-- )// update the state of limit orders simulating each candlestick
            {
            for ( int j=SizeBarOrders-1; j>i; j-- )// update the state of all candlesticks preceding this one
               {
               for ( int k=0; k<size; k++ )// update the state inside each preceding candlestick
                  {
                  if ( BarOrders[j].BuyLimitOrders[k].Status == STATUS_VIRTUAL 
                  && BarOrders[j].BuyLimitOrders[k].WantedPrice <= High[i] 
                  && BarOrders[j].BuyLimitOrders[k].WantedPrice >= Low[i] )// if the order is virtual and is inside a candlestick, then it turns into a market one
                     {
                     BarOrders[j].BuyLimitOrders[k].Status = STATUS_MARKET;
                     BarOrders[j].BuyLimitOrders[k].IndexMarket = i;
                     }
                  if ( BarOrders[j].SellLimitOrders[k].Status == STATUS_VIRTUAL 
                  && BarOrders[j].SellLimitOrders[k].WantedPrice <= High[i] 
                  && BarOrders[j].SellLimitOrders[k].WantedPrice >= Low[i] )// the same
                     {
                     BarOrders[j].SellLimitOrders[k].Status = STATUS_MARKET;
                     BarOrders[j].SellLimitOrders[k].IndexMarket = i;
                     } 
                 
                  ///////проверка на истечение интереса лимит игроков
                  if ( BarOrders[j].BuyLimitOrders[k].Status == STATUS_VIRTUAL )//
                     {
                     if ( BarOrders[j].BuyLimitOrders[k].IndexMarket - 1 >= BarOrders[j].BuyLimitOrders[k].BarsExpirationOpen )
                     BarOrders[j].BuyLimitOrders[k].Status=STATUS_ABORTED;
                     else
                        {
                        if ( BarOrders[j].BuyLimitOrders[k].VolumeAlpha > 0.0 )
                        BarOrders[j].BuyLimitOrders[k].VolumeAlpha-=BarOrders[j].BuyLimitOrders[k].VolumeStart/double(BarOrders[j].BuyLimitOrders[k].BarsExpirationOpen);
                        if ( BarOrders[j].BuyLimitOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyLimitOrders[k].VolumeAlpha=0.0;
                        }
                     }
                  if ( BarOrders[j].SellLimitOrders[k].Status == STATUS_VIRTUAL )//
                     {
                     if ( BarOrders[j].SellLimitOrders[k].IndexMarket - 1 >= BarOrders[j].SellLimitOrders[k].BarsExpirationOpen  )
                     BarOrders[j].SellLimitOrders[k].Status=STATUS_ABORTED;
                     else
                        {
                        if ( BarOrders[j].SellLimitOrders[k].VolumeAlpha > 0.0 )
                        BarOrders[j].SellLimitOrders[k].VolumeAlpha-=BarOrders[j].SellLimitOrders[k].VolumeStart/double(BarOrders[j].SellLimitOrders[k].BarsExpirationOpen);
                        if ( BarOrders[j].SellLimitOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellLimitOrders[k].VolumeAlpha=0.0;                  
                        }
                     } 
                  }
               }         
            }
         }
         
      if ( VolumeAlphaStop != 0.0 )
         {
         for ( int i=SizeBarOrders; i>0; i-- )// update the state of limit orders simulating each candlestick
            {
            for ( int j=SizeBarOrders-1; j>i; j-- )// update the state of all candlesticks preceding this one
               {
               for ( int k=0; k<size; k++ )// update the state inside each preceding candlestick
                  {
                  if ( BarOrders[j].SellStopOrders[k].Status == STATUS_VIRTUAL 
                  && BarOrders[j].SellStopOrders[k].WantedPrice <= High[i] 
                  && BarOrders[j].SellStopOrders[k].WantedPrice >= Low[i] )// the same
                     {
                     BarOrders[j].SellStopOrders[k].Status = STATUS_MARKET;
                     BarOrders[j].SellStopOrders[k].IndexMarket = i;
                     }
                  if ( BarOrders[j].BuyStopOrders[k].Status == STATUS_VIRTUAL 
                  && BarOrders[j].BuyStopOrders[k].WantedPrice <= High[i] 
                  && BarOrders[j].BuyStopOrders[k].WantedPrice >= Low[i] )// the same
                     {
                     BarOrders[j].BuyStopOrders[k].Status = STATUS_MARKET;
                     BarOrders[j].BuyStopOrders[k].IndexMarket = i;
                     }
                  
                  ///////проверка на истечение интереса стоп и лимит игроков
                  if ( BarOrders[j].BuyStopOrders[k].Status == STATUS_VIRTUAL )//
                     {
                     if ( BarOrders[j].BuyStopOrders[k].IndexMarket - 1 >= BarOrders[j].BuyStopOrders[k].BarsExpirationOpen  )
                     BarOrders[j].BuyStopOrders[k].Status=STATUS_ABORTED;
                     else
                        {
                        if ( BarOrders[j].BuyStopOrders[k].VolumeAlpha > 0.0 )
                        BarOrders[j].BuyStopOrders[k].VolumeAlpha-=BarOrders[j].BuyStopOrders[k].VolumeStart/double(BarOrders[j].BuyStopOrders[k].BarsExpirationOpen);
                        if ( BarOrders[j].BuyStopOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyStopOrders[k].VolumeAlpha=0.0;                  
                        } 
                     }
                  if ( BarOrders[j].SellStopOrders[k].Status == STATUS_VIRTUAL )//
                     {
                     if ( BarOrders[j].SellStopOrders[k].IndexMarket - 1 >= BarOrders[j].SellStopOrders[k].BarsExpirationOpen  )
                     BarOrders[j].SellStopOrders[k].Status=STATUS_ABORTED;
                     else
                        {
                        if ( BarOrders[j].SellStopOrders[k].VolumeAlpha > 0.0 )
                        BarOrders[j].SellStopOrders[k].VolumeAlpha-=BarOrders[j].SellStopOrders[k].VolumeStart/double(BarOrders[j].SellStopOrders[k].BarsExpirationOpen);
                        if ( BarOrders[j].SellStopOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellStopOrders[k].VolumeAlpha=0.0;                     
                        }
                     }                                                                       
                  }
               }         
            }
         }               
      }
      
   void UpdateMarket()// update the status of market orders
      {
      int size=ArraySize(BarOrders[0].BuyLimitOrders);
      int SizeBarOrders=ArraySize(BarOrders);
      
      if ( VolumeAlphaLimit != 0.0 )
         {
         for ( int i=SizeBarOrders; i>1; i-- )// update the state of orders simulating each candlestick
            {
            for ( int j=SizeBarOrders-1; j>i; j-- )// update the state of all candlesticks preceding this one
               {
               for ( int k=0; k<size; k++ )// update the state inside each preceding candlestick
                  {
                  //блок закрытия в при изменении цен
                  if ( BarOrders[j].BuyLimitOrders[k].Status == STATUS_MARKET )//
                     {
                     if ( BarOrders[j].BuyLimitOrders[k].VolumeAlpha > 0.0 )
                        {
                        BarOrders[j].BuyLimitOrders[k].VolumeAlpha-=BarOrders[j].BuyLimitOrders[k].VolumeStart*(High[i]-Open[i])/(BarOrders[j].BuyLimitOrders[k].UpPriceToClose-BarOrders[j].BuyLimitOrders[k].WantedPrice);// with profit
                        BarOrders[j].BuyLimitOrders[k].VolumeAlpha-=BarOrders[j].BuyLimitOrders[k].VolumeStart*(Open[i]-Low[i])/(BarOrders[j].BuyLimitOrders[k].WantedPrice-BarOrders[j].BuyLimitOrders[k].LowPriceToClose);// with loss
                        }
                     if ( BarOrders[j].BuyLimitOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyLimitOrders[k].VolumeAlpha=0.0;
                     }
                  if ( BarOrders[j].SellLimitOrders[k].Status == STATUS_MARKET )//
                     {
                     if ( BarOrders[j].SellLimitOrders[k].VolumeAlpha > 0.0 )
                        {
                        BarOrders[j].SellLimitOrders[k].VolumeAlpha-=BarOrders[j].SellLimitOrders[k].VolumeStart*(Open[i]-Low[i])/(BarOrders[j].SellLimitOrders[k].WantedPrice-BarOrders[j].SellLimitOrders[k].LowPriceToClose);//в профит
                        BarOrders[j].SellLimitOrders[k].VolumeAlpha-=BarOrders[j].SellLimitOrders[k].VolumeStart*(High[i]-Open[i])/(BarOrders[j].SellLimitOrders[k].UpPriceToClose-BarOrders[j].SellLimitOrders[k].WantedPrice);//в убыток
                        }
                     if ( BarOrders[j].SellLimitOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellLimitOrders[k].VolumeAlpha=0.0;
                     } 
                  ///конец блока закрытия при изменении цен
               
                  //блок закрытия при изменении времени*******************************************************
                  if ( BarOrders[j].BuyLimitOrders[k].Status == STATUS_MARKET )//
                     {
                     if ( BarOrders[j].BuyLimitOrders[k].VolumeAlpha > 0.0 )
                     BarOrders[j].BuyLimitOrders[k].VolumeAlpha-=BarOrders[j].BuyLimitOrders[k].VolumeStart/double(BarOrders[j].BuyLimitOrders[k].BarsExpirationClose);
                     if ( BarOrders[j].BuyLimitOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyLimitOrders[k].VolumeAlpha=0.0;
                     }
                  if ( BarOrders[j].SellLimitOrders[k].Status == STATUS_MARKET )//
                     {
                     if ( BarOrders[j].SellLimitOrders[k].VolumeAlpha > 0.0 )
                     BarOrders[j].SellLimitOrders[k].VolumeAlpha-=BarOrders[j].SellLimitOrders[k].VolumeStart/double(BarOrders[j].SellLimitOrders[k].BarsExpirationClose);
                     if ( BarOrders[j].SellLimitOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellLimitOrders[k].VolumeAlpha=0.0;
                     } 
                  }
               }         
            }
         }
         
      if ( VolumeAlphaStop != 0.0 )
         {
         for ( int i=SizeBarOrders; i>1; i-- )// update the state of orders simulating each candlestick
            {
            for ( int j=SizeBarOrders-1; j>i; j-- )// update the state of all candlesticks preceding this one
               {
               for ( int k=0; k<size; k++ )// update the state inside each preceding candlestick
                  {
                  //блок закрытия при изменении цен
                  if ( BarOrders[j].BuyStopOrders[k].Status == STATUS_MARKET )//
                     {
                     if ( BarOrders[j].BuyStopOrders[k].VolumeAlpha > 0.0 )
                        {
                        BarOrders[j].BuyStopOrders[k].VolumeAlpha-=BarOrders[j].BuyStopOrders[k].VolumeStart*(High[i]-Open[i])/(BarOrders[j].BuyStopOrders[k].UpPriceToClose-BarOrders[j].BuyStopOrders[k].WantedPrice);// with profit
                        BarOrders[j].BuyStopOrders[k].VolumeAlpha-=BarOrders[j].BuyStopOrders[k].VolumeStart*(Open[i]-Low[i])/(BarOrders[j].BuyStopOrders[k].WantedPrice-BarOrders[j].BuyStopOrders[k].LowPriceToClose);// with loss
                        }
                     if ( BarOrders[j].BuyStopOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyStopOrders[k].VolumeAlpha=0.0;
                     }
                  if ( BarOrders[j].SellStopOrders[k].Status == STATUS_MARKET )//
                     {
                     if ( BarOrders[j].SellStopOrders[k].VolumeAlpha > 0.0 )
                        {
                        BarOrders[j].SellStopOrders[k].VolumeAlpha-=BarOrders[j].SellStopOrders[k].VolumeStart*(Open[i]-Low[i])/(BarOrders[j].SellStopOrders[k].WantedPrice-BarOrders[j].SellStopOrders[k].LowPriceToClose);//with profit
                        BarOrders[j].SellStopOrders[k].VolumeAlpha-=BarOrders[j].SellStopOrders[k].VolumeStart*(High[i]-Open[i])/(BarOrders[j].SellStopOrders[k].UpPriceToClose-BarOrders[j].SellStopOrders[k].WantedPrice);//with loss
                        }
                     if ( BarOrders[j].SellStopOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellStopOrders[k].VolumeAlpha=0.0;
                     }
               
                  ///конец блока закрытия при изменении цен
               
                  //блок закрытия при изменении времени*******************************************************
                  if ( BarOrders[j].BuyStopOrders[k].Status == STATUS_MARKET )//
                     {
                     if ( BarOrders[j].BuyStopOrders[k].VolumeAlpha > 0.0 )
                     BarOrders[j].BuyStopOrders[k].VolumeAlpha-=BarOrders[j].BuyStopOrders[k].VolumeStart/double(BarOrders[j].BuyStopOrders[k].BarsExpirationClose);
                     if ( BarOrders[j].BuyStopOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyStopOrders[k].VolumeAlpha=0.0;
                     }
                  if ( BarOrders[j].SellStopOrders[k].Status == STATUS_MARKET )//
                     {
                     if ( BarOrders[j].SellStopOrders[k].VolumeAlpha > 0.0 )
                     BarOrders[j].SellStopOrders[k].VolumeAlpha-=BarOrders[j].SellStopOrders[k].VolumeStart/double(BarOrders[j].SellStopOrders[k].BarsExpirationClose);
                     if ( BarOrders[j].SellStopOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellStopOrders[k].VolumeAlpha=0.0;
                     }
                  }
               }         
            }
         }
         
      if ( VolumeAlphaMarket != 0.0 )
         {
         for ( int i=SizeBarOrders; i>1; i-- )// update the state of orders simulating each candlestick
            {
            for ( int j=SizeBarOrders-1; j>i; j-- )// update the state of all candlesticks preceding this one
               {
               for ( int k=0; k<size; k++ )// update the state inside each preceding candlestick
                  {
                  //блок закрытия в при изменении цен
                  ///для заведомо рыночных позиций
                  if ( BarOrders[j].BuyMarketOrders[k].VolumeAlpha > 0.0 )
                     {
                     BarOrders[j].BuyMarketOrders[k].VolumeAlpha-=BarOrders[j].BuyMarketOrders[k].VolumeStart*(High[i]-Open[i])/(BarOrders[j].BuyMarketOrders[k].UpPriceToClose-BarOrders[j].BuyMarketOrders[k].WantedPrice);// with profit
                     BarOrders[j].BuyMarketOrders[k].VolumeAlpha-=BarOrders[j].BuyMarketOrders[k].VolumeStart*(Open[i]-Low[i])/(BarOrders[j].BuyMarketOrders[k].WantedPrice-BarOrders[j].BuyMarketOrders[k].LowPriceToClose);// with loss
                     }
                  if ( BarOrders[j].BuyMarketOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyMarketOrders[k].VolumeAlpha=0.0;
                              
                  if ( BarOrders[j].SellMarketOrders[k].VolumeAlpha > 0.0 )
                     {
                     BarOrders[j].SellMarketOrders[k].VolumeAlpha-=BarOrders[j].SellMarketOrders[k].VolumeStart*(Open[i]-Low[i])/(BarOrders[j].SellMarketOrders[k].WantedPrice-BarOrders[j].SellMarketOrders[k].LowPriceToClose);// with profit
                     BarOrders[j].SellMarketOrders[k].VolumeAlpha-=BarOrders[j].SellMarketOrders[k].VolumeStart*(High[i]-Open[i])/(BarOrders[j].SellMarketOrders[k].UpPriceToClose-BarOrders[j].SellMarketOrders[k].WantedPrice);// with loss
                     }
                  if ( BarOrders[j].SellMarketOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellMarketOrders[k].VolumeAlpha=0.0;                  
                  ///конец блока закрытия при изменении цен
               
                  //блок закрытия при изменении времени*******************************************************
              
                  ///для заведомо рыночных позиций
                  if ( BarOrders[j].BuyMarketOrders[k].VolumeAlpha > 0.0 )
                  BarOrders[j].BuyMarketOrders[k].VolumeAlpha-=BarOrders[j].BuyMarketOrders[k].VolumeStart/double(BarOrders[j].BuyMarketOrders[k].BarsExpirationClose);
                  if ( BarOrders[j].BuyMarketOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyMarketOrders[k].VolumeAlpha=0.0;
                  if ( BarOrders[j].SellMarketOrders[k].VolumeAlpha > 0.0 )
                  BarOrders[j].SellMarketOrders[k].VolumeAlpha-=BarOrders[j].SellMarketOrders[k].VolumeStart/double(BarOrders[j].SellMarketOrders[k].BarsExpirationClose);
                  if ( BarOrders[j].SellMarketOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellMarketOrders[k].VolumeAlpha=0.0;               
                  //
                  }
               }         
            }
         }
      }
      
   ///быстрые методы****   
   void UpdateVirtualFast()// update the status of virtual orders
      {
      int SizeBarOrders=ArraySize(BarOrders);
      int size=ArraySize(BarOrders[0].BuyLimitOrders);
      
      if ( VolumeAlphaLimit != 0.0 )
         {
         for ( int j=SizeBarOrders-1; j>0; j-- )//обновим состояние всех свечей которые были до этой
            {
            for ( int k=0; k<size; k++ )// update the state inside each preceding candlestick
               {
               if ( BarOrders[j].BuyLimitOrders[k].Status == STATUS_VIRTUAL 
               && BarOrders[j].BuyLimitOrders[k].WantedPrice <= High[1] 
               && BarOrders[j].BuyLimitOrders[k].WantedPrice >= Low[1] )// if the order is virtual and is inside a candlestick, then it turns into a market one
                  {
                  BarOrders[j].BuyLimitOrders[k].Status = STATUS_MARKET;
                  BarOrders[j].BuyLimitOrders[k].IndexMarket = 1;
                  }
               if ( BarOrders[j].SellLimitOrders[k].Status == STATUS_VIRTUAL 
               && BarOrders[j].SellLimitOrders[k].WantedPrice <= High[1] 
               && BarOrders[j].SellLimitOrders[k].WantedPrice >= Low[1] )// the same
                  {
                  BarOrders[j].SellLimitOrders[k].Status = STATUS_MARKET;
                  BarOrders[j].SellLimitOrders[k].IndexMarket = 1;
                  } 
                  
               ///////проверка на истечение интереса лимит игроков
               if ( BarOrders[j].BuyLimitOrders[k].Status == STATUS_VIRTUAL )//
                  {
                  if ( BarOrders[j].BuyLimitOrders[k].IndexMarket - 1 >= BarOrders[j].BuyLimitOrders[k].BarsExpirationOpen )
                  BarOrders[j].BuyLimitOrders[k].Status=STATUS_ABORTED;
                  else
                     {
                     if ( BarOrders[j].BuyLimitOrders[k].VolumeAlpha > 0.0 )
                     BarOrders[j].BuyLimitOrders[k].VolumeAlpha-=BarOrders[j].BuyLimitOrders[k].VolumeStart/double(BarOrders[j].BuyLimitOrders[k].BarsExpirationOpen);
                     if ( BarOrders[j].BuyLimitOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyLimitOrders[k].VolumeAlpha=0.0;
                     }
                  }
               if ( BarOrders[j].SellLimitOrders[k].Status == STATUS_VIRTUAL )//
                  {
                  if ( BarOrders[j].SellLimitOrders[k].IndexMarket - 1 >= BarOrders[j].SellLimitOrders[k].BarsExpirationOpen  )
                  BarOrders[j].SellLimitOrders[k].Status=STATUS_ABORTED;
                  else
                     {
                     if ( BarOrders[j].SellLimitOrders[k].VolumeAlpha > 0.0 )
                     BarOrders[j].SellLimitOrders[k].VolumeAlpha-=BarOrders[j].SellLimitOrders[k].VolumeStart/double(BarOrders[j].SellLimitOrders[k].BarsExpirationOpen);
                     if ( BarOrders[j].SellLimitOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellLimitOrders[k].VolumeAlpha=0.0;                  
                     }
                  } 
               }
            }
         }
         
      if ( VolumeAlphaStop != 0.0 )
         {       
         for ( int j=SizeBarOrders-1; j>0; j-- )//обновим состояние всех свечей которые были до этой
            {
            for ( int k=0; k<size; k++ )// update the state inside each preceding candlestick
               {
               if ( BarOrders[j].SellStopOrders[k].Status == STATUS_VIRTUAL 
               && BarOrders[j].SellStopOrders[k].WantedPrice <= High[1] 
               && BarOrders[j].SellStopOrders[k].WantedPrice >= Low[1] )// the same
                  {
                  BarOrders[j].SellStopOrders[k].Status = STATUS_MARKET;
                  BarOrders[j].SellStopOrders[k].IndexMarket = 1;
                  }
               if ( BarOrders[j].BuyStopOrders[k].Status == STATUS_VIRTUAL 
               && BarOrders[j].BuyStopOrders[k].WantedPrice <= High[1] 
               && BarOrders[j].BuyStopOrders[k].WantedPrice >= Low[1] )// the same
                  {
                  BarOrders[j].BuyStopOrders[k].Status = STATUS_MARKET;
                  BarOrders[j].BuyStopOrders[k].IndexMarket = 1;
                  }
                  
               ///////проверка на истечение интереса стоп игроков
               if ( BarOrders[j].BuyStopOrders[k].Status == STATUS_VIRTUAL )//
                  {
                  if ( BarOrders[j].BuyStopOrders[k].IndexMarket - 1 >= BarOrders[j].BuyStopOrders[k].BarsExpirationOpen  )
                  BarOrders[j].BuyStopOrders[k].Status=STATUS_ABORTED;
                  else
                     {
                     if ( BarOrders[j].BuyStopOrders[k].VolumeAlpha > 0.0 )
                     BarOrders[j].BuyStopOrders[k].VolumeAlpha-=BarOrders[j].BuyStopOrders[k].VolumeStart/double(BarOrders[j].BuyStopOrders[k].BarsExpirationOpen);
                     if ( BarOrders[j].BuyStopOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyStopOrders[k].VolumeAlpha=0.0;                  
                     } 
                  }
               if ( BarOrders[j].SellStopOrders[k].Status == STATUS_VIRTUAL )//
                  {
                  if ( BarOrders[j].SellStopOrders[k].IndexMarket - 1 >= BarOrders[j].SellStopOrders[k].BarsExpirationOpen  )
                  BarOrders[j].SellStopOrders[k].Status=STATUS_ABORTED;
                  else
                     {
                     if ( BarOrders[j].SellStopOrders[k].VolumeAlpha > 0.0 )
                     BarOrders[j].SellStopOrders[k].VolumeAlpha-=BarOrders[j].SellStopOrders[k].VolumeStart/double(BarOrders[j].SellStopOrders[k].BarsExpirationOpen);
                     if ( BarOrders[j].SellStopOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellStopOrders[k].VolumeAlpha=0.0;                     
                     }
                  }                                                                       
               }
            }
         }         
      }
      
   void UpdateMarketFast()// update the status of market orders
      {
      int size=ArraySize(BarOrders[0].BuyLimitOrders);
      int SizeBarOrders=ArraySize(BarOrders);
      
      if ( VolumeAlphaLimit != 0.0 )
         {
         for ( int j=SizeBarOrders-1; j>0; j-- )//обновим состояние всех свечей которые были до этой
            {
            for ( int k=0; k<size; k++ )// update the state inside each preceding candlestick
               {
               //блок закрытия в при изменении цен
               if ( BarOrders[j].BuyLimitOrders[k].Status == STATUS_MARKET )//
                  {
                  if ( BarOrders[j].BuyLimitOrders[k].VolumeAlpha > 0.0 )
                     {
                     BarOrders[j].BuyLimitOrders[k].VolumeAlpha-=BarOrders[j].BuyLimitOrders[k].VolumeStart*(High[1]-Open[1])/(BarOrders[j].BuyLimitOrders[k].UpPriceToClose-BarOrders[j].BuyLimitOrders[k].WantedPrice);// with profit
                     BarOrders[j].BuyLimitOrders[k].VolumeAlpha-=BarOrders[j].BuyLimitOrders[k].VolumeStart*(Open[1]-Low[1])/(BarOrders[j].BuyLimitOrders[k].WantedPrice-BarOrders[j].BuyLimitOrders[k].LowPriceToClose);// with loss
                     }
                  if ( BarOrders[j].BuyLimitOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyLimitOrders[k].VolumeAlpha=0.0;
                  }
               if ( BarOrders[j].SellLimitOrders[k].Status == STATUS_MARKET )//
                  {
                  if ( BarOrders[j].SellLimitOrders[k].VolumeAlpha > 0.0 )
                     {
                     BarOrders[j].SellLimitOrders[k].VolumeAlpha-=BarOrders[j].SellLimitOrders[k].VolumeStart*(Open[1]-Low[1])/(BarOrders[j].SellLimitOrders[k].WantedPrice-BarOrders[j].SellLimitOrders[k].LowPriceToClose);// with profit
                     BarOrders[j].SellLimitOrders[k].VolumeAlpha-=BarOrders[j].SellLimitOrders[k].VolumeStart*(High[1]-Open[1])/(BarOrders[j].SellLimitOrders[k].UpPriceToClose-BarOrders[j].SellLimitOrders[k].WantedPrice);// with loss
                     }
                  if ( BarOrders[j].SellLimitOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellLimitOrders[k].VolumeAlpha=0.0;
                  } 
               ///конец блока закрытия при изменении цен
               
               //блок закрытия при изменении времени*******************************************************
               if ( BarOrders[j].BuyLimitOrders[k].Status == STATUS_MARKET )//
                  {
                  if ( BarOrders[j].BuyLimitOrders[k].VolumeAlpha > 0.0 )
                  BarOrders[j].BuyLimitOrders[k].VolumeAlpha-=BarOrders[j].BuyLimitOrders[k].VolumeStart/double(BarOrders[j].BuyLimitOrders[k].BarsExpirationClose);
                  if ( BarOrders[j].BuyLimitOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyLimitOrders[k].VolumeAlpha=0.0;
                  }
               if ( BarOrders[j].SellLimitOrders[k].Status == STATUS_MARKET )//
                  {
                  if ( BarOrders[j].SellLimitOrders[k].VolumeAlpha > 0.0 )
                  BarOrders[j].SellLimitOrders[k].VolumeAlpha-=BarOrders[j].SellLimitOrders[k].VolumeStart/double(BarOrders[j].SellLimitOrders[k].BarsExpirationClose);
                  if ( BarOrders[j].SellLimitOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellLimitOrders[k].VolumeAlpha=0.0;
                  } 
               //
               }
            }
         }
         
      if ( VolumeAlphaStop != 0.0 )
         {
         for ( int j=SizeBarOrders-1; j>0; j-- )//обновим состояние всех свечей которые были до этой
            {
            for ( int k=0; k<size; k++ )// update the state inside each preceding candlestick
               {
               //блок закрытия в при изменении цен
               if ( BarOrders[j].BuyStopOrders[k].Status == STATUS_MARKET )//
                  {
                  if ( BarOrders[j].BuyStopOrders[k].VolumeAlpha > 0.0 )
                     {
                     BarOrders[j].BuyStopOrders[k].VolumeAlpha-=BarOrders[j].BuyStopOrders[k].VolumeStart*(High[1]-Open[1])/(BarOrders[j].BuyStopOrders[k].UpPriceToClose-BarOrders[j].BuyStopOrders[k].WantedPrice);// with profit
                     BarOrders[j].BuyStopOrders[k].VolumeAlpha-=BarOrders[j].BuyStopOrders[k].VolumeStart*(Open[1]-Low[1])/(BarOrders[j].BuyStopOrders[k].WantedPrice-BarOrders[j].BuyStopOrders[k].LowPriceToClose);// with loss
                     }
                  if ( BarOrders[j].BuyStopOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyStopOrders[k].VolumeAlpha=0.0;
                  }
               if ( BarOrders[j].SellStopOrders[k].Status == STATUS_MARKET )//
                  {
                  if ( BarOrders[j].SellStopOrders[k].VolumeAlpha > 0.0 )
                     {
                     BarOrders[j].SellStopOrders[k].VolumeAlpha-=BarOrders[j].SellStopOrders[k].VolumeStart*(Open[1]-Low[1])/(BarOrders[j].SellStopOrders[k].WantedPrice-BarOrders[j].SellStopOrders[k].LowPriceToClose);// with profit
                     BarOrders[j].SellStopOrders[k].VolumeAlpha-=BarOrders[j].SellStopOrders[k].VolumeStart*(High[1]-Open[1])/(BarOrders[j].SellStopOrders[k].UpPriceToClose-BarOrders[j].SellStopOrders[k].WantedPrice);// with loss
                     }
                  if ( BarOrders[j].SellStopOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellStopOrders[k].VolumeAlpha=0.0;
                  }
               
               ///конец блока закрытия при изменении цен
               
               //блок закрытия при изменении времени*******************************************************
               if ( BarOrders[j].BuyStopOrders[k].Status == STATUS_MARKET )//
                  {
                  if ( BarOrders[j].BuyStopOrders[k].VolumeAlpha > 0.0 )
                  BarOrders[j].BuyStopOrders[k].VolumeAlpha-=BarOrders[j].BuyStopOrders[k].VolumeStart/double(BarOrders[j].BuyStopOrders[k].BarsExpirationClose);
                  if ( BarOrders[j].BuyStopOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyStopOrders[k].VolumeAlpha=0.0;
                  }
               if ( BarOrders[j].SellStopOrders[k].Status == STATUS_MARKET )//
                  {
                  if ( BarOrders[j].SellStopOrders[k].VolumeAlpha > 0.0 )
                  BarOrders[j].SellStopOrders[k].VolumeAlpha-=BarOrders[j].SellStopOrders[k].VolumeStart/double(BarOrders[j].SellStopOrders[k].BarsExpirationClose);
                  if ( BarOrders[j].SellStopOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellStopOrders[k].VolumeAlpha=0.0;
                  }
               //
               }
            }
         }
         
       if ( VolumeAlphaMarket != 0.0 )
         {
         for ( int j=SizeBarOrders-1; j>0; j-- )//обновим состояние всех свечей которые были до этой
            {
            for ( int k=0; k<size; k++ )// update the state inside each preceding candlestick
               {
               ///для заведомо рыночных позиций
               if ( BarOrders[j].BuyMarketOrders[k].VolumeAlpha > 0.0 )
                  {
                  BarOrders[j].BuyMarketOrders[k].VolumeAlpha-=BarOrders[j].BuyMarketOrders[k].VolumeStart*(High[1]-Open[1])/(BarOrders[j].BuyMarketOrders[k].UpPriceToClose-BarOrders[j].BuyMarketOrders[k].WantedPrice);// with profit
                  BarOrders[j].BuyMarketOrders[k].VolumeAlpha-=BarOrders[j].BuyMarketOrders[k].VolumeStart*(Open[1]-Low[1])/(BarOrders[j].BuyMarketOrders[k].WantedPrice-BarOrders[j].BuyMarketOrders[k].LowPriceToClose);// with loss
                  }
               if ( BarOrders[j].BuyMarketOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyMarketOrders[k].VolumeAlpha=0.0;
                              
               if ( BarOrders[j].SellMarketOrders[k].VolumeAlpha > 0.0 )
                  {
                  BarOrders[j].SellMarketOrders[k].VolumeAlpha-=BarOrders[j].SellMarketOrders[k].VolumeStart*(Open[1]-Low[1])/(BarOrders[j].SellMarketOrders[k].WantedPrice-BarOrders[j].SellMarketOrders[k].LowPriceToClose);//в профит
                  BarOrders[j].SellMarketOrders[k].VolumeAlpha-=BarOrders[j].SellMarketOrders[k].VolumeStart*(High[1]-Open[1])/(BarOrders[j].SellMarketOrders[k].UpPriceToClose-BarOrders[j].SellMarketOrders[k].WantedPrice);// with loss
                  }
               if ( BarOrders[j].SellMarketOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellMarketOrders[k].VolumeAlpha=0.0;                  
               ///конец блока закрытия при изменении цен
               
               //блок закрытия при изменении времени*******************************************************
             
               ///для заведомо рыночных позиций
               if ( BarOrders[j].BuyMarketOrders[k].VolumeAlpha > 0.0 )
               BarOrders[j].BuyMarketOrders[k].VolumeAlpha-=BarOrders[j].BuyMarketOrders[k].VolumeStart/double(BarOrders[j].BuyMarketOrders[k].BarsExpirationClose);
               if ( BarOrders[j].BuyMarketOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].BuyMarketOrders[k].VolumeAlpha=0.0;
               if ( BarOrders[j].SellMarketOrders[k].VolumeAlpha > 0.0 )
               BarOrders[j].SellMarketOrders[k].VolumeAlpha-=BarOrders[j].SellMarketOrders[k].VolumeStart/double(BarOrders[j].SellMarketOrders[k].BarsExpirationClose);
               if ( BarOrders[j].SellMarketOrders[k].VolumeAlpha < 0.0 ) BarOrders[j].SellMarketOrders[k].VolumeAlpha=0.0;               
               //
               }
            }
         }                          
      }      
   ///******
      
   void CalculateStartVolume()// calculate the starting total volume of all positions (relative to it we will estimate market fullness)
      {
      StartVolume=0;
      int size=ArraySize(BarOrders[0].BuyStopOrders);
      if ( VolumeAlphaStop != 0.0 )
         {
         for ( int j=ArraySize(BarOrders)-1; j>=0; j-- )
            {
            for ( int i=0; i<size; i++ )
               {
               StartVolume+=BarOrders[j].BuyStopOrders[i].VolumeStart;
               }
            }        
         }
         
      if ( VolumeAlphaLimit != 0.0 )
         {
         size=ArraySize(BarOrders[0].BuyLimitOrders);
         for ( int j=ArraySize(BarOrders)-1; j>=0; j-- )
            {
            for ( int i=0; i<size; i++ )
               {
               StartVolume+=BarOrders[j].BuyLimitOrders[i].VolumeStart;
               }         
            }
         }
         
      if ( VolumeAlphaMarket != 0.0 )
         {
         size=ArraySize(BarOrders[0].BuyMarketOrders);
         for ( int j=ArraySize(BarOrders)-1; j>=0; j-- )
            {
            for ( int i=0; i<size; i++ )
               {
               StartVolume+=BarOrders[j].BuyMarketOrders[i].VolumeStart;
               }         
            }
         }         
      }
      
   void CalculateCurrentVolume()// calculate the current total volume of all positions
      {
      SummVolumeBuy=0;
      SummVolumeSell=0;
      int size=ArraySize(BarOrders[0].BuyStopOrders);
      
      if ( VolumeAlphaStop != 0.0 )
         {
         for ( int j=ArraySize(BarOrders)-1; j>=0; j-- )
            {
            for ( int i=0; i<size; i++ )
               {
               if ( BarOrders[j].BuyStopOrders[i].Status == STATUS_MARKET )
               SummVolumeBuy+=BarOrders[j].BuyStopOrders[i].VolumeAlpha;
               if ( BarOrders[j].SellStopOrders[i].Status == STATUS_MARKET )
               SummVolumeSell+=BarOrders[j].SellStopOrders[i].VolumeAlpha;            
               }         
            }
         }
      
      if ( VolumeAlphaLimit != 0.0 )
         {   
         size=ArraySize(BarOrders[0].BuyLimitOrders);
         for ( int j=ArraySize(BarOrders)-1; j>=0; j-- )
            {
            for ( int i=0; i<size; i++ )
               {
               if ( BarOrders[j].BuyLimitOrders[i].Status == STATUS_MARKET )
               SummVolumeBuy+=BarOrders[j].BuyLimitOrders[i].VolumeAlpha;
               if ( BarOrders[j].SellLimitOrders[i].Status == STATUS_MARKET )
               SummVolumeSell+=BarOrders[j].SellLimitOrders[i].VolumeAlpha;            
               }         
            }
         }
      
      if ( VolumeAlphaMarket != 0.0 )
         {
         size=ArraySize(BarOrders[0].BuyMarketOrders);
         for ( int j=ArraySize(BarOrders)-1; j>=0; j-- )
            {
            for ( int i=0; i<size; i++ )
               {
               SummVolumeBuy+=BarOrders[j].BuyMarketOrders[i].VolumeAlpha;
               SummVolumeSell+=BarOrders[j].SellMarketOrders[i].VolumeAlpha;
               }         
            }
         }         
      }
      
   void CalculatePercent()// calculate the percentage of Buys and Sells relative to all positions
      {
      if ( (SummVolumeBuy+SummVolumeSell) != 0.0 ) BuyPercent=100.0*SummVolumeBuy/(SummVolumeBuy+SummVolumeSell);
      else BuyPercent=50;
      if ( (SummVolumeBuy+SummVolumeSell) != 0.0 ) SellPercent=100.0*SummVolumeSell/(SummVolumeBuy+SummVolumeSell);
      else SellPercent=50;
      }      
      
   void CalculateRelativeVolume()// calculate relative volumes of Buys and Sells (calculate only uncovered part of positions)
      {
      if ( SummVolumeBuy >= SummVolumeSell ) RelativeVolume=(SummVolumeBuy-SummVolumeSell)/StartVolume;
      else RelativeVolume=(SummVolumeSell-SummVolumeBuy)/StartVolume;
      }                   
   
   };

Todo el código que mostramos aquí resulta adecuado tanto para MetaTrader 4 como para MetaTrader 5. Estas clases se compilarán en ambas plataformas, claro está, siempre que en MetaTrader 5 implementemos de antemano las matrices predefinidas, al igual que en MQL4. Este código no lo vamos a mostrar porque no queremos resultar repetitivos. Si fuera necesario, el lector podrá consultar en el código fuente cómo se hace esto. No se muestra de forma muy original, pero funciona. Lo único que quedaría es implementar las variables encargadas del comercio y la funcionalidad comercial en sí, que tampoco mostraremos. Los asesores para ambos terminales se adjuntan al artículo.

Lo que queremos decir es que el código requiere el uso de muchos recursos, y para ello hemos realizado una implementación lenta de la lógica para el análisis inicial y una rápida para trabajar con las barras. Todos los asesores expertos del autor trabajan con barras para no tener que depender de la generación de ticks artificiales y otras consecuencias indeseables derivadas de las pruebas con ticks. Podríamos, claro está, prescindir de las funciones lentas, pero luego deberíamos abandonar el análisis inicial. Entre otras cosas, no resulta cómodo sacar la funcionalidad del cuerpo de la clase, pues de esta manera se pierde la integridad de la imagen y podríamos confundirnos.

El constructor de la última clase admitirá los siguientes parámetros, que serán parámetros de entrada, pero si lo deseamos, podremos hacer muchas simulaciones así, porque esta es solo una instancia de la clase:

input bool bPrintE=false;// print market parameters
input CLOSE_MODE CloseModeE=CLOSE_FAST;// order closing mode
input WORK_MODE ModeE=MODE_SIMPLE;// simulation mode
input ENUM_GRID_WEIGHT WeightFillingE=WEIGHT_SAME;// weight distribution type
input double LimitVolumeE=0.5;// significance of limit orders
input double StopVolumeE=0.5;// significance of stop orders
input double MarketVolume=0.5;// significance of market orders
input int ExpirationBars=100;// bars for full expiration of open orders
input int ExpirationOpenStopBars=1000;// patience of a stop player in bars, after which the order is canceled
input int ExpirationOpenLimitBars=1000;// patience of a limit player in bars, after which the order is canceled
input int ProfitPointsCloseE=200;// points to close with profit
input int LossPointsCloseE=400;// points to close with loss
input int HalfCorridorE=500;// half-corridor for limit and stop orders
input int OrdersToOneBarE=50;// orders for half-grid per 1 bar
input int BarsE=250;// bars for analysis
input double MinPercentE=60;// minimum superiority of one trading side in percentage 
input double MaxPercentE=80;// maximum percentage
input double MinRelativeVolumeE=0.0001;// minimum market filling [0...1]
input double MaxRelativeVolumeE=1.00;// maximum market filling [0...1]

No vamos a mostrar aquí las variables relacionadas con el comercio: todo es bastante simple y claro. 

En nuestro caso, la función en la que se implementará el comercio tiene este aspecto:

void Trade()
   {
   if ( Area0 == NULL )
      {
      CalcAllMQL5Values();
      Area0 = new Simulation(WeightFillingE,HalfCorridorE,OrdersToOneBarE,BarsE
       ,ExpirationOpenLimitBars,ExpirationOpenStopBars,ExpirationBars,ProfitPointsCloseE,LossPointsCloseE
       ,StopVolumeE,LimitVolumeE,MarketVolume);      
      }
   
   switch(ModeE)
      {
      case MODE_SIMPLE:
         Area0.Update();// update simulation
      case MODE_FAST:
         Area0.UpdateFast();// fast update simulation
      }
   
   if (bPrintE)
      {
      Print("BuyPercent= ",Area0.GetBuyPercent());
      Print("SellPercent= ",Area0.GetSellPercent());
      Print("RelativeVolume= ",Area0.GetRelativeVolume());
      }
      
   if ( CloseModeE == CLOSE_FAST && Area0.GetBuyPercent() > 50.0 )
      {
      if ( !bInvert ) CloseBuyF();
      else CloseSellF();
      }
      
   if ( CloseModeE == CLOSE_FAST && Area0.GetSellPercent() > 50.0 )
      {
      if ( !bInvert ) CloseSellF();
      else CloseBuyF();
      }      
      
   if ( Area0.GetBuyPercent() > MinPercentE && Area0.GetBuyPercent() < MaxPercentE 
   && Area0.GetRelativeVolume() >= MinRelativeVolumeE && Area0.GetRelativeVolume() <= MaxRelativeVolumeE )
      {
      if ( !bInvert )
         {
         CloseBuyF();
         SellF();
         }
      else
         {
         CloseSellF();
         BuyF();
         }   
      }
      
   if ( Area0.GetSellPercent() > MinPercentE && Area0.GetSellPercent() < MaxPercentE 
   && Area0.GetRelativeVolume() >= MinRelativeVolumeE && Area0.GetRelativeVolume() <= MaxRelativeVolumeE )
      {
      if ( !bInvert )    
         {
         CloseSellF();
         BuyF();
         }  
      else
         {
         CloseBuyF();
         SellF();
         }
      }
   }

Si lo deseamos, podemos mejorar las condiciones comerciales. Por el contrario, la función en sí es más complicada, aunque no le vemos sentido a ninguna mejora por el momento. Siempre intentamos mantener el comercio y la parte lógica separados. Toda la lógica tiene lugar en el objeto. Por cierto, la creación del objeto de simulación se implementa de forma dinámica en la función comercial con la primera activación; este objeto se elimina durante la desinicialización. Lo hemos hecho así porque no hay matrices predefinidas en MQL5 y debemos crear estas artificialmente para que las clases funcionen de la misma forma que en MQL4.


¿Cómo buscamos la configuración de trabajo?

Basándonos en la propia experiencia, podemos decir que es mejor buscar la configuración manualmente. Al mismo tiempo, para los asesores que trabajan con marcos temporales pequeños y con una esperanza matemática baja, resulta muy importante abstraerse del spread en la primera etapa, para no perder de vista los signos de eficiencia desde el principio. MetaTrader 4 resulta estupendo para este propósito. Una búsqueda exitosa siempre va seguida de una mejora, algunas ediciones, etcétera, lo cual aumentará la esperanza matemática final y el factor de beneficio (intensidad de la señal). También aclararemos que la estructura de los datos de entrada, de forma ideal, debería dar la posibilidad de operar con modos de trabajo independientes. En otras palabras, la modificación de una configuración debería influir de la forma más independiente posible en el rendimiento del sistema, a pesar del valor de las otras configuraciones. Este enfoque puede reforzar la señal general, estableciendo algunos ajustes equilibrados que combinarán todas las señales encontradas en una sola. Esta implementación no es ni mucho menos posible siempre, pero en nuestro caso resulta aplicable si analizamos cada tipo de orden por separado.


Simulando el asesor

Queremos señalar que el asesor fue escrito sin ninguna preparación, desde cero. El código de este asesor experto es nuevo incluso para el autor. Teníamos un asesor similar, pero era mucho más sencillo, y su lógica no tenía nada en común con este asesor. Querríamos enfatizar esto, porque el propósito del artículo es mostrar que cualquier idea con unos inicios correctos tiene muchas posibilidades de funcionar si es capaz de captar la física del mercado (aunque no describa esta completa e incluso parcialmente). Y si el sistema tiene algunas habilidades de funcionamiento básicas, podremos ponerlo a prueba, encontrando lo que funciona en él y pasando al siguiente nivel de comprensión del mercado. El siguiente nivel presupone la existencia de asesores de mejor calidad, que generaremos por nuestra cuenta.

Al probar el asesor experto, tuvimos que pasar varios días seleccionando pacientemente los parámetros comerciales. Todo resultó bastante laborioso y aburrido, pero logramos encontrar los entornos de trabajo. Obviamente, estos ajustes son muy débiles y la simulación se realiza usando solo un tipo de orden, pero lo hemos hecho así a propósito. La cuestión es que resulta mejor analizar por separado cómo afecta este o aquel tipo de orden a la señal, y después de todo esto, intentar simular otro tipo de orden. El resultado que obtuvimos en MetaTrader 4 tiene el aspecto que sigue:

EURUSD 2010.01.01 -2020.11.01

Inicialmente, creamos una versión para MetaTrader 4 y probamos allí con el margen más bajo posible. El asunto es que, para encontrar patrones, especialmente en los marcos temporales pequeños de los gráficos, necesitamos ver cada tick. Si probamos la versión para MetaTrader 5, no veremos lo que vemos aquí debido a los spreads, que MetaTester 5 puede ajustar a su discreción, si lo consideramos conveniente.

Este es un gran mecanismo para las pruebas de estrés y las valoraciones de desempeño en el mundo real. Debemos usarlo en la última etapa para verificar los ticks reales. En nuestro caso, resulta mejor poner a prueba el sistema inicialmente en MetaTrader 4. Para ello, recomendamos a todos que usen este enfoque, porque al realizar las pruebas directamente en el quinto terminal, podemos perder muchas opciones de configuración excelentes, que quizá sirvan como punto de partida para encontrar nuevas y mejores configuraciones.

No recomendamos utilizar la optimización por muchos motivos, pero la razón principal es que se trata de una simple iteración de parámetros. No podremos entender qué funciona y por qué si no operamos manualmente con todos los parámetros por nosotros mismos. Si tomamos una vieja radio, atornillamos manualmente unos motorcitos a sus manijas y los encendemos, es poco probable que capte alguna estación de radio: lo mismo ocurre aquí. Incluso si logramos captar algo, es poco probable que comprendamos qué es.

Otro aspecto muy importante es el número de órdenes. Cuanto mayor sea el número de órdenes respecto al número de barras, más fuerte será la física que hemos encontrado, en el sentido de que podremos estar seguros de su rendimiento en el futuro. También debemos recordar que los patrones encontrados pueden estar dentro del spread, lo cual puede hacer que nuestro sistema resulte inútil si no controlamos estos momentos.

Para separar el trigo de la paja, necesitamos el simulador de MetaTrader 5. Este simulador nos ofrece esa oportunidad gracias a su capacidad de realizar pruebas con ticks reales. Desafortunadamente, los ticks reales existen hace relativamente poco para todos los instrumentos y parejas de divisas. A lo largo de este año, probaremos la versión de MetaTrader 5 con ticks reales y unos requisitos de spread más estrictos, para ver si el sistema funciona en 2020 y cómo de bien lo hace, si fuera así. Pero antes, probaremos el sistema en el modo "Every tick" habitual en el mismo segmento que antes:

EURUSD M5 2010.01.01-2020.11.01

Este modo de prueba no es tan bueno como el modo de ticks reales, pero aún podemos ver que solo queda una parte muy pequeña de las señales del resultado inicial; no obstante, hay señales que cubren el spread y ofrecen un pequeño beneficio. También tenemos grandes dudas sobre la fiabilidad de esos spreads que el simulador toma de la historia profunda. En esta prueba, el lote era de 0.01, lo cual significa que la esperanza matemática es de 5 puntos, incluso más alta que en la prueba original, aunque el gráfico se vea muy torcido. Aún podemos confiar en estos datos porque hay una gran muestra de 100,000 transacciones en la prueba inicial detrás de ellos.

Ahora, vamos a echar un vistazo al año pasado:

EURUSD M5 2020.01.01-2020.11.01

En esta prueba, configuramos el lote en 0.1, por lo que la esperanza matemática es de 23.4 puntos, lo cual es bastante bueno, teniendo en cuenta que la prueba inicial en MetaTrader 4 daba solo 3 puntos de esperanza matemática. Es posible que la esperanza matemática descienda en el futuro, pero resulta poco probable que sea un descenso muy fuerte: aún bastará para una operación de equilibrio.

Adjuntamos al artículo el asesor para ambos terminales. Podemos jugar con la configuración e intentar encontrar parámetros de trabajo para las órdenes límite y las órdenes de mercado, si hubiera posibilidad. Luego podemos combinar estos conjuntos configurando algunos ajustes medios. Desafortunadamente, no hemos tenido tiempo de sacarle todo el jugo, pero todavía quedaba margen de maniobra.

Por supuesto, también nos gustaría señalar que el asesor aún está lejos de ser un asesor de trabajo que podamos colgar inmediatamente en el gráfico para disfrutar de su rendimiento. Después de esto, deberemos probar la simulación usando otros tipos de órdenes. Luego combinaremos las configuraciones y repetiremos estos dos ciclos de prueba hasta que los indicadores comerciales adopten valores suficientemente seguros; podemos introducir filtros o corregir los propios algoritmos. Como podemos ver en las pruebas, este enfoque es más que posible. Dejamos todo al lector. Lo único que no hemos podido encontrar ha sido la configuración de trabajo para los marcos temporales grandes. El asesor funciona mejor en M5, pero podríamos estar equivocados. Desafortunadamente, no hemos tenido tiempo de verificar el carácter multidivisa del conjunto con otras parejas de divisas, pero, por lo general, con líneas tan planas, todo funciona también con otras parejas de divisas. Cuando tengamos tiempo, tal vez trabajemos más a fondo con este asesor. Ya durante su escritura, encontramos muchas deficiencias y errores, y creemos que aún quedan bastantes por encontrar.


Conclusión

El simulador escrito ha mostrado las expectativas de este método de análisis. Tras obtener los primeros resultados, cabe pensar en cómo mejorarlos: podemos modernizar el algoritmo, hacerlo mejor, más variable y más rápido. La mayor ventaja de este enfoque es su enorme simplicidad. Y es que, si ignoramos todas las dificultades relacionadas con la implementación de la lógica, en realidad, la lógica general de este método resulta muy simple desde un punto de vista físico.

No podemos considerar todos los factores que influyen en el movimiento del precio, pero si tenemos al menos uno de esos factores, aunque no podamos describirlo de manera fiable, una simple descripción inexacta de este factor en el código nos ofrecerá ciertas señales. Quizás estas señales no sean de una calidad muy alta, pero bastarán para ganar dinero. No debemos esforzarnos por encontrar una fórmula ideal: esto es de entrada imposible, porque nunca podremos considerar todos los factores que influyen en el mercado.

Además, no resulta necesario usar nuestro enfoque. El propósito de este artículo era tomar una física del mercado, que, en nuestra opinión, debería al menos describir este parcialmente, y escribir un asesor experto que probara esto. Como resultado, escribimos un asesor que mostraba que cualquier suposición con al menos algo de verdadera puede redundar en un sistema de trabajo.

También debemos considerar que todavía hay muchas deficiencias en el código, posiblemente errores graves; pero a pesar de ello, el asesor, incluso en esta versión, nos indica que "voy a funcionar incluso así". Solo tenemos que refinarlo con paciencia. En el próximo artículo, presentaremos un tipo diferente de análisis multidivisa del mercado, más simple y efectivo, y que todos conocemos de sobra. También crearemos un asesor experto.

Traducción del ruso hecha por MetaQuotes Software Corp.
Artículo original: https://www.mql5.com/ru/articles/8411

Archivos adjuntos |
Simulation.zip (27.31 KB)
Algoritmo de autoadaptación (Parte III): Renunciando a la optimización Algoritmo de autoadaptación (Parte III): Renunciando a la optimización
No podemos obtener un algoritmo verdaderamente estable si para seleccionar los parámetros utilizamos la optimización basada en datos históricos. Un algoritmo estable en sí mismo debe saber qué parámetros se necesitan para trabajar con cualquier instrumento comercial en cualquier momento. El algoritmo no debe suponer ni adivinar: debe saber con certeza.
Trabajando con los precios en la biblioteca DoEasy (Parte 63): Profundidad del mercado, clase de orden abstracta de la Profundidad del mercado Trabajando con los precios en la biblioteca DoEasy (Parte 63): Profundidad del mercado, clase de orden abstracta de la Profundidad del mercado
En el presente artículo, empezaremos a desarrollar la funcionalidad para trabajar con la Profundidad del mercado. Crearemos la clase del objeto de una orden abstracta de la Profundidad del mercado y sus clases herederas.
Buscando patrones estacionales en el mercado de divisas con la ayuda del algoritmo CatBoost Buscando patrones estacionales en el mercado de divisas con la ayuda del algoritmo CatBoost
En el presente artículo, mostramos la posibilidad de crear modelos de aprendizaje automático con filtros temporales y también descubrimos la efectividad de este enfoque. Ahora, podremos descartar el factor humano, diciéndole simplemente al modelo: "Quiero que comercies a una hora determinada de un día concreto de la semana". Así, podremos delegar en el algoritmo la búsqueda de patrones.
Redes neuronales: así de sencillo (Parte 9): Documentamos el trabajo realizado Redes neuronales: así de sencillo (Parte 9): Documentamos el trabajo realizado
Ya hemos recorrido un largo camino y el código de nuestra biblioteca ha crecido de manera considerable. Resulta difícil monitorear todas las conexiones y dependencias. Y, obviamente, antes de proseguir con el desarrollo del proyecto, necesitaremos documentar el trabajo ya realizado y actualizar la documentación en cada paso posterior. Una documentación debidamente redactada nos ayudará a ver la integridad de nuestro trabajo.