
El mercado y la física de sus patrones globales
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:
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:
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:
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:
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:
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 Ltd.
Artículo original: https://www.mql5.com/ru/articles/8411





- Aplicaciones de trading gratuitas
- 8 000+ señales para copiar
- Noticias económicas para analizar los mercados financieros
Usted acepta la política del sitio web y las condiciones de uso