![Aplicando la teoría de probabilidades usando el trading con gaps](https://c.mql5.com/i/articles/overlay.png)
Aplicando la teoría de probabilidades usando el trading con gaps
Índice
- Introducción
- Consideraciones generales sobre los Asesores Expertos
- Estrategia comercial como un intento de rebatir la hipótesis del paseo aleatorio
- Estrategia comercial usando los Gaps
- Simulación de la estrategia y cálculo de un riesgo óptimo en la transacción
- Conclusiones
- Archivos adjuntos
Introducción
En el presente artículo continuamos desarrollando el tema de la aplicación de la teoría de probabilidades y estadística matemática en el trading, empezado en los artículos anteriores del autor. Consideraremos un posible uso de los métodos apropiados para crear y testear las estrategias comerciales.
Para empezar, analizaremos la manera de buscar las posibilidades para el trading como la detección de las desviaciones de la hipótesis del paseo aleatorio. Ha sido demostrado que si los precios se comportan como un paseo aleatorio sin desviación (falta de una tendencia con dirección), el trading rentable es imposible. Eso nos da una causa para buscar las maneras de refutar esta hipótesis. Si encontramos esta refutación, podemos intentar usarla para desarrollar la estrategia comercial.
Además, en este artículo seguiremos estudiando el tema del riesgo empezado en los artículos anteriores. Más adelante, vamos a referirnos a ellos hablando del primer y el segundo artículo.
Puesto que la base de nuestro enfoque es la teoría de probabilidades, el entendimiento de sus fundamentos será útil pero no obligatorio. Es importante comprender la esencia de los métodos probabilistas: cuanto más frecuentemente y sistemáticamente se usen, el resultado será más evidente y más notable (debido a la ley de números grandes). Evidentemente, su aplicación tiene que ser suficientemente fundamentada y adecuada.
Consideraciones generales sobre los Asesores Expertos
El desarrollo de los EAs puede ser dividido en tres fases:
- Generación de la idea.
- Comprobación de la idea usando todos los tipos de simplificaciones.
- Adaptación de la idea comprobada a las realidades del mercado.
Este artículo va a referirse principalmente a la segunda etapa. Eso ayudará a centrarnos en el tema declarado. Además es evidente que esta fase no se discute con tanta frecuencia en el foro que las demás.
Vamos a describir las simplificaciones implementadas. Nos limitaremos con los EAs que operan con un solo activo. Supongamos que el precio del activo se expresa en la moneda de la cuenta. Vamos a excluir las operaciones no negociables (swap, retirada de los fondos del depósito) y no vamos a considerar diferentes tipos de las órdenes (supongamos que hay sólo la compra y la venta por el mercado). Vamos a desatender el deslizamiento durante la ejecución de las órdenes, considerando que el spread s tendrá un valor fijo. También vamos a asumir que el EA controla todos los fondos de nuestra cuenta, y no hay otros EAs en la cuenta.
Con todas estas simplificaciones, el resultado del trabajo del EA se determina univocamente como la función v(t) -el volumen de la posición depende del tiempo. El valor positivo v(t) corresponde a la compra, el valor negativo corresponde a la venta. Aparte de eso, existe la función p(t) (precio del activo) y c0 (capital inicial). En la imagen de abajo, se muestra un posible ejemplo del gráfico de la posición v=v(t).
Usamos la media aritmética entre los precios de compra y de venta como el precio.
Las funciones v(t) y p(t) son constantes por partes (paso a paso) puesto que sus valores son múltiples de algunos pasos mínimos del incremento. Si hace falta una definición matemática más estricta, se puede considerarlas continuas de la derecha, con un límite de la izquierda en los puntos de la ruptura. Consideramos que los puntos de la ruptura v(t) nunca coinciden con los puntos para p(t). Es decir, en cualquier momento de tiempo, sólo un valor de dos puede cambiarse (sólo el precio, sólo el volumen de la posición, o bien ambos permanecen inalteradas). Vale la pena mencionar que los momentos de tiempo en los que se realizan los cambios del precio o del volumen también son múltiples de un paso mínimo.
A base de estos datos, podemos encontrar la función c(t) (valor del capital dependiendo del tiempo). Se define como un valor del balance para una parte de la cuenta controlada por el EA en el caso de cerrar la posición en el momento t. Puesto que tenemos el único EA en la cuenta, este valor coincide con la equidad de la cuenta determinada en MetaTrader 5.
Determinamos el cambio de c(t) en el momento t. Si el volumen y el precio no se cambian en este momento, está claro que es nulo. Si se cambia el precio, el incremento del capital es igual al producto del volumen por el incremento del precio. Si se cambia el volumen, hay dos opciones: en caso de la reducción del valor absoluto del volumen de la posición, el capital no se cambia; en caso del aumento, se reduce por el valor igual al producto del spread por el valor absoluto del cambio del volumen. Es decir, en caso del cierre parcial de la posición, la equidad no se altera; en caso del incremento de la posición, la equidad se disminuye un poco. Por tanto, el valor del capital c(t) en el momento t es igual a la suma de c0=c(0) y de todos los cambios realizadas durante el intervalo desde el momento cero hasta t.
Nótese que durante el desarrollo de nuestra teoría del riesgo (en dos artículos anteriores), usábamos el concepto de transacciones. Este concepto no coincide exactamente con lo que se llama como la «transacción» en MetaTrader 5, y corresponde más con lo que ahí se llama el «trade». Para ser más exacto, corresponde a lo que llamamos una «posición simple». Según nuestra definición, una posición simple se determina por los momentos de la apertura y del cierre, entre los cueles su volumen y la dirección son constantes. A continuación, se muestra el ejemplo del gráfico v=v(t) para una posición simple.
Podemos imaginar mentalmente cualquier posición (puesto que siempre es constante por partes) como una suma de posiciones simples. Esta representación puede ser hecha de un número infinito de maneras. En la imagen de abajo, se muestra un ejemplo de como podemos representar la misma posición de dos maneras diferentes en forma de la suma de otras simples. La posición inicial se muestra en azul, el color verde y rojo se usa para las transacciones en las que se divide. Cuando se usa el concepto de los trades de MetaTrader 5, obtenemos otra opción más. Cada uno de estos métodos puede ser bastante razonable.
Hay algunos EAs para los cuales esta representación no tiene mucho sentido. Por ejemplo, pueden haber EAs donde la posición se incrementa gradualmente, luego se reduce gradualmente. No obstante, hay EAs para los cuales esta representación es bastante natural. Por ejemplo, la posición puede componerse de una secuencia de posiciones simples que no se cruzan en el tiempo. En el gráfico de abajo, se muestran los ejemplos de estas posiciones.
El cambio relativo del capital c1/c0 después de cada transacción (de una posición simple) se expresa a través de dos valores: la rentabilidad a y el riesgo r: c1/c0=1+ra. La rentabilidad es igual a la razón entre el incremento del precio durante la transacción y la diferencia de los precios de entrada y el Stop Loss, mientras que el riesgo es proporcional al volumen de la transacción y significa una parte del capital que ha sido perdida en caso de la activación exacta del Stop Loss.
De esta manera, en vez de considerar las funciones del tiempo v(t), p(t) y c(t), pasamos a analizar las secuencias numéricas que caracterizan la secuencia de transacciones. Eso simplifica considerablemente el siguiente estudio. En particular, evitaremos la necesidad de aplicar la teoría de procesos aleatorios, limitándonos con conjuntos finitos de variables aleatorias, cuando procedemos al modelo probabilista de indeterminación.
La teoría de probabilidades es un método generalmente aceptado de modelación matemática de la indeterminación del comportamiento de los precios del activo, y por tanto, de los resultados del trading. De acuerdo con este enfoque, tenemos que considerar las funciones v(t), p(t) y c(t) como implementaciones (trayectorias) específicas de algunos procesos aleatorios. En términos generales, esta tarea prácticamente no tiene solución. La principal razón consiste en la ausencia de los modelos probabilistas adecuados que describen con precisión el comportamiento de los precios. Por eso, tiene sentidos considerar los casos particulares donde la solución es posible. Como ya ha sido mencionado antes, en este artículo, vamos a considerar los EAs que forman las posiciones representadas de manera adecuada en forma de una secuencia de posiciones (transacciones) simples.
Vale la pena resaltar otra cuestión relacionada con los EAs. Se trata de sus parámetros. Sería útil considerarlos más detalladamente con el fin de conseguir una cierta normalización (estandartización) del proceso del desarrollo de los EAs. Vamos a dividir los parámetros en tres tipos:
- Parámetros históricos. Los parámetro que pueden alterarse durante el trabajo del EA, de una transacción a otra. Puede tratarse de los valores de indicadores, hora del día, noticias, fases de la luna, etc. En general, representan las funciones del tiempo, los precios o el volumen de la posición. En caso de la simplificación aplicada, se puede considerarlas como secuencias de los números que se saben en el momento de la entrada en las transacciones. A base de los valores de los parámetros históricos van a definirse los parámetros de cada transacción en particular: dirección, volumen, SL y TP.
- Parámetros en sí. Para brevedad, vamos a llamarlos simplemente «parámetros». Se definen cuando el EA inicia el trading. Pueden alterarse sólo durante la simulación y optimización del EA.
- Meta-parámetros. Definen el algoritmo de la optimización del EA, por ejemplo, el parámetro del criterio personalizado de la optimización. Supongamos que queremos optimizar el EA según dos criterios, pero eso puede ser hecho sólo uno por uno. Formamos un criterio nuevo de dos originales, tomando su suma con algunos pesos. Estos pesos nos servirán de meta-parámetros.
Por ejemplo, en el EA con brechas (gap) considerado a continuación, el intervalo mínimo considerado como gap es el parámetro del EA, mientras que el tamaño de cada gap es el parámetro histórico. En este caso, un meta-parámetro puede incluir, por ejemplo, el número del criterio de la optimización (supongamos que están numerados según un cierto orden, por ejemplo, la optimización por el beneficio es №1, y por la reducción es №2, etc.)
Nótese que en este artículo vamos a usar una importante simplificación relacionada con los parámetros históricos. Cuando hablamos de la distribución de las rentabilidades en una transacción, generalmente puede depender de estos parámetros. Pero nosotros suponemos que esta dependencia es insignificante. La principal razón consiste en los siguiente: el intento de tomar en cuenta esta dependencia a menudo complica excesivamente el modelo, lo que a su vez puede llevar a un ajuste excesivo.
Estrategia comercial como un intento de rechazar la hipótesis del paseo aleatorio
Antes ya hemos mencionado sobre la ausencia de los modelos exactos que describen el comportamiento de los precios. A pesar de eso, existen unos modelos aproximados que pueden ser útiles. Por ejemplo, existe un modelo del comportamiento de los precios bien conocido, que los considera como un paseo aleatorio con desviación cero (falta de la tendencia con dirección). Normalmente, este modelo se llama la hipótesis del paseo aleatorio. De acuerdo con ella, cualquier EA va a tener un beneficio medio cero; y una pequeña perdida si consideramos el spread.
Es bastante complicado demostrar la imposibilidad de ganar dinero usando el paseo aleatorio, porque hace falta aplicar un aparato matemático complejo de la teoría de procesos aleatorios (Integral estocástico de Ito, tiempo de Markov, etc.). En general, se resume a la afirmación de que durante el trading en el paseo aleatorio sin tendencia, el capital representa una martingala (no debe confundirse con martingale). La martingala es un proceso aleatorio cuyo valor medio (expectativa matemática) no se varía con el tiempo. En nuestro caso, eso significa que la expectativa matemática del valor del capital será igual a su valor inicial en cualquier momento de tiempo.
De esta manera, tenemos que empezar a considerar la idea comercial buscando las desviaciones significativas estocásticamente en el comportamiento del precio del paseo aleatorio. Usaremos para eso las ideas de la teoría de probabilidades y estadística matemática, pero primero hagamos algunas observaciones:
- Cualquier solución de este tipo tiene un carácter probabilista: siempre existe una probabilidad diferente de cero de que nuestras conclusiones son erróneas.
- Si nuestro método no detecta desviaciones, eso indica en su ausencia absoluta. Tal vez, algún otro método podría detectarlas.
- La presencia de las desviaciones estatisticamente significativas no significa la posibilidad automática de obtener un beneficio positivo estatisticamente significativo: la presencia de una desviación es una condición necesaria pero no es suficiente para eso.
Vamos a construir un método para la búsqueda de las desviaciones del paseo aleatorio. Para eso, vamos a considerar alguna variable aleatoria, para la cual construiremos una distribución empírica de probabilidades basándose en una muestra construida usando los precios reales. Por otro lado, también construiremos una distribución teórica de probabilidades para el mismo valor, suponiendo que el comportamiento del precio es exactamente un paseo aleatorio. Comparando estas distribuciones, vamos a tomar una decisión sobre el rechazo (o imposibilidad del rechazo) de la hipótesis del paseo aleatorio.
Construimos un ejemplo de un valor apropiado. Supongamos que en el momento inicial t0 es igual a p0. Usamos otro valor del precio p1 diferente de p0. Esperamos el momento t1, cuando el precio alcance este valor p(t1)=p1. Buscamos el precio p2 que se encuentra a una distancia máxima de p1 entre los precios del intervalo temporal t0-t1. Introducimos el valor K=(p2-p0)/(p0-p1). Siempre se cumple p1<p0≤p2 o p2≤p0<p1, por tanto, K≥0 en todos los momentos. A continuación, se muestra el gráfico que explica esta idea. La línea azul significa el nivel del precio p0, el momento de su intersección con el gráfico de precios es el momento de tiempo t0. La línea roja significa el nivel del precio p1, y el momento de su primer toque del gráfico de precios después del momento t0 es el momento de tiempo t1. La línea verde significa el nivel del precio p2 a una distancia máxima de p1.
Este valor tiene un sentido simple. Supongamos que entramos en la transacción en el momento t0. Que sea una venta por el precio p0, y el precio p1, p1>p0 es Stop Loss. En este caso, p2 es el precio mínimo posible alcanzable para el Take Profit, el valor K es la rentabilidad máxima posible en la transacción. Está claro que en realidad el valor exacto K se desconoce en el momento de la entrada en la transacción. En el marco del modelo probabilista de esta indeterminación, podemos hablar sólo del conocimiento de la ley de su distribución probabilista. Supongamos que conocemos la función de la distribución de probabilidades Fk(x), que se define como la probabilidad de que K<x. Supongamos también que usamos un precio pk como Take Profit: pk-p0=k(p0-p1). Entonces, el número Fk(k) es igual a la probabilidad de que el Stop Loss será alcanzado antes que Take Profit. Por tanto, el número 1-Fk(k) es igual a la probabilidad de que Take Profit será alcanzado antes. Vamos a dejar el spread igual a cero por un tiempo. Entonces, en caso de la activación de Stop Loss, la rentabilidad es igual a -1, y si se activa Take Profit, es igual a k. La expectativa matemática de la rentabilidad en esta transacción: M=(-1)*Fk(k)+k*(1-Fk(k))=k-(k+1)*Fk(k), que es igual a cero si Fk(k)=k/(k+1).
Si sabemos el tipo de Fk(x), incluso podemos realizar la optimización previa del EA. Por ejemplo, se puede buscar la razón óptima del TP y SL que maximiza la expectativa matemática de la rentabilidad de la transacción. Luego, se puede encontrar un valor óptimo del riesgo en la transacción. De esta manera, se puede realizar la optimización del EA incluso antes del momento cuando el ya está completamente escrito. Eso puede ser útil para ahorrar el tiempo, permitiendo descartar las ideas obviamente inapropiadas en la fase inicial de su verificación.
Si suponemos que los precios se comportan como un paseo aleatorio sin tendencia, la distribución del valor K se establece por la función de la distribución Fk(x)=Fk0(x), donde Fk0(x)=0, si x≤0 y Fk0(x)=x/(x+1), si x>0. Para mayor certeza, se puede considerar que el paseo aleatorio utilizado aquí es el proceso Wiener sin desviación (sin tendencia). Como vemos, cuando se ejecuta la hipótesis del paseo aleatorio y el spread es igual a cero, la expectativa matemática de la rentabilidad de transacciones es igual a cero con cualquier razón de TP/SL. Si el spread no es nulo, será negativo.
En vez del valor K, se puede considerar el valor Q=K/(K+1)=(p2-p0)/(p2-p1), K=Q/(1-Q). Se puede representar este valor como la razón de TP y la suma de Sl y TP. Resulta ser más conveniente ya que adquiere los valores en el intervalo [0;1), y tiene una distribución más simple que K (uniforme en este intervalo), en caso del paseo aleatorio.
A continuación, hablaremos principalmente del valor Q. Consideraremos en términos generales cómo se construye y se aplica su función empírica de la distribución Fq(x). Supongamos que tenemos alguna idea para el trading que comprobamos en el historial de precios. Tenemos un conjunto de n puntos para entrar en las transacciones. Para cada punto se determina el precio de entrada p0,i y Stop Los p1,i, donde i=1,...,n. Ahora hay que hay que definir si esta idea puede traer beneficios. Para cada transacción, encontramos el precio p2,i a una distancia máxima desde el SL hasta el momento de su disparo. A base de estos precios, obtenemos la muestra n Qi=(p2,i-p0,i)/(p2,i-p1,i), i=1,...,n. La función empírica de la distribución construida a base de esta muestra se determina por la ecuación Fq(x)=m(x)/n, donde m(x) es igual al número de los elementos de la muestra Qi menores que x. Si los precios se comportan como el paseo aleatorio sin tendencia (proceso de Wiener sin desviación), entonces la función de la distribución Fq0(x) del valor Q va a tener una forma simple: Fq0(x)=0 cuando x≤0, Fq0(x)=x cuando 0<x≤1, и Fq0(x)=1, cuando x>1.
Si Fq(x) se diferencia considerablemente de la función teórica de la distribución con el paseo aleatorio Fq0(x), tenemos que verificar la importancia de esta diferencia desde el punto de vista de la rentabilidad. Si la rentabilidad es bastante positiva incluso tomando en cuenta el spread, entonces se puede elegir la razón conveniente TP/SL. Se puede hacer eso maximizando la expectativa matemática de rentabilidad. Después de eso, se puede seleccionar un valor óptimo para el riesgo en la transacción, y proceder a la simulación previa de la idea. Si el resultado será positivo, tiene sentido proceder a la creación de un EA de trading real. Más adelante intentaremos demostrar este esquema en la práctica.
Surge la pregunta, ¿cómo podemos hacer semejante comparación con el paseo aleatorio para los algoritmos más complicados de la salida de las transacciones? En general, se puede decir que eso se hace de la misma forma como para el caso considerado más arriba. El problema principal consiste en que raras veces se puede obtener la distribución de rentabilidades en el paseo aleatorio de forma analítica. Pero siempre hay posibilidad de obtener su aproximación empírica usando el método de modelación de Monte Carlo.
Estrategia comercial usando los Gaps
Antes de empezar el análisis de la idea, hay que hacer la siguiente advertencia. Nuestra tarea principal consiste en demostrarlos métodos del análisis, en vez de las estrategias rentable del trading. La concentración demasiado grande en la segunda cuestión nos enterraría en los detalles pequeños, distrayéndonos de la primera cuestión. De acuerdo con un dicho conocido, no vamos a repartir el pescado, sino consideraremos las ideas que podrán ser útiles para elaborar su propia caña de pescar.
Los precios del activo son discretos, y por eso, siempre se cambian a saltos. Estos saltos pueden ser diferentes en cuanto al tamaño, y cuando son grandes, suelen llamarse los «gaps». No existe un límite específico que separa los gaps de las alteraciones de los precios habituales. Podemos establecerlo en un nivel que nos conviene.
Los gaps convienen muy bien para demostrar la teoría expuesta en el apartado anterior. Cada uno de ellos se establece por dos momentos de tiempo y por los precios del activo dentro de ellos. Usando los signos para los precios introducidos anteriormente, consideramos que p0 es un precio posterior, y p1 es un precio anterior. Entramos en la transacción en cuanto ocurra un gap. En este caso, el precio p1 puede considerarse no sólo como SL, sino también como TP. Eso significa que podemos elegir un tipo del sistema de dos tipos disponibles: esperar un cierre rápido del gap, o esperar un movimiento grande en la dirección del gap. El cierre del gap significa la vuelta del precio hasta el nivel p1 o su ruptura.
Puesto que los gaps en su forma clásica son relativamente raros para los activos negociados en Forex, cuando el tema del artículo se discutía con la administración del foro, al autor se le fue propuesto generalizar este concepto. Al definir el gap, se puede renunciar al requerimiento de considerar el gap sólo entre dos precios subsiguientes. Obviamente, el número de posibles gaps en este caso será increíblemente enorme, y por eso, vale la pena limitarnos con opciones razonables, desde el punto de vista comercial. Por ejemplo, al autor se le fue propuesto considerar los gaps entre el precio del cierre de una sesión americana y la apertura de la siguiente.
Explicaré cómo se formaliza el concepto de la sesión. Se define por tres intervalos temporales: período, duración y desplazamiento. Las sesiones son periódicas y tienen una duración que no supera el período. Cualquier tick pertenece a alguna sesión, o (es posible si su duración es estrictamente menos que el período) no pertenece a ninguna de ellas. El desplazamiento es un intervalo de tiempo entre el punto cero del tiempo y el inicio de la primera sesión después de él, tiene que ser menos que el período. Este concepto de la sesión es un poco más amplio y permite considerar, por ejemplo, los gaps entre las barras de minutos. Vamos a ilustrarlo en el esquema de abajo. La flecha verde representa el intervalo de tiempo que define el desplazamiento, la roja representa el período y la azul, la duración de la sesión.
Vamos a usar dos EAs un poco diferentes para recopilar la estadística relacionada con gaps. El primer EA "gaps_reg_stat.mq5" considera los gaps entre dos ticks subsiguientes, el segundo EA "gaps_ses_stat.mq5", entre las sesiones. Naturalmente, estos EAs no tradean, se inician sólo en el modo de simulación. El arranque del primero tiene sentido sólo en los ticks reales, y el segundo, en OHLC de las barras de minutos. Los códigos de estos EAs se muestran a continuación.
// gaps_reg_stat.mq5 #define ND 100 input double gmin=0.1; // minimal gap size: USDJPY - 0.1, EURUSD - 0.001 input string fname="gaps\\stat.txt"; // name of file for statistics struct SGap { double p0; double p1; double p2; double p3; double s; void set(double p_1,double p,double sprd); bool brkn(); void change(double p); double gap(); double Q(); }; class CGaps { SGap gs[]; int ngs; int go[]; int ngo; public: void init(); void add(double p_1,double p,double sprd); void change(double p); void gs2f(string fn); }; CGaps gaps; MqlTick tick0; bool is0=false; void OnTick() { MqlTick tick; if (!SymbolInfoTick(_Symbol, tick)) return; if(is0) { double p=(tick.bid+tick.ask)*0.5, p0=(tick0.bid+tick0.ask)*0.5; gaps.change(p); if(MathAbs(p-p0)>=gmin) gaps.add(p0,p,tick.ask-tick.bid); } else is0=true; tick0=tick; } int OnInit() { gaps.init(); return(INIT_SUCCEEDED); } void OnDeinit(const int reason) { gaps.gs2f(fname); } void SGap :: set(double p_1,double p,double sprd) { p1=p_1; p0=p2=p3=p; s=sprd; } bool SGap :: brkn() { return ((p0>p1)&&(p3<=p1))||((p0<p1)&&(p3>=p1)); } void SGap :: change(double p) { if(brkn()) return; if((p0>p1&&p>p2) || (p0<p1&&p<p2)) p2=p; p3=p; } double SGap :: gap() { return MathAbs(p0-p1); } double SGap :: Q() { double q=p2-p1; if(q==0.0) return 0.0; return (p2-p0)/q; } void CGaps :: init() { ngs=ngo=0; } void CGaps :: add(double p_1,double p,double sprd) { ++ngs; if(ArraySize(gs)<ngs) ArrayResize(gs,ngs,ND); gs[ngs-1].set(p_1,p,sprd); int i=0; for(; i<ngo; ++i) if(go[i]<0) break; if(i==ngo) { ++ngo; if(ArraySize(go)<ngo) ArrayResize(go,ngo,ND); } go[i]=ngs-1; } void CGaps :: change(double p) { for(int i=0; i<ngo; ++i) { if(go[i]<0) continue; gs[go[i]].change(p); if(gs[go[i]].brkn()) go[i]=-1; } } void CGaps :: gs2f(string fn) { int f=FileOpen(fn, FILE_WRITE|FILE_COMMON|FILE_ANSI|FILE_TXT), c; for(int i=0;i<ngs;++i) { if (gs[i].brkn()) c=1; else c=0; FileWriteString(f,(string)gs[i].gap()+" "+(string)gs[i].Q()+" "+(string)c+" "+(string)gs[i].s); if(i==ngs-1) break; FileWriteString(f,"\n"); } FileClose(f); } // gaps_ses_stat.mq5 #define ND 100 input double gmin=0.001; // minimal gap size: USDJPY - 0.1, EURUSD - 0.001 input uint mperiod=1; // session period in minutes input uint mlength=1; // session length in minutes input uint mbias=0; // first session bias in minutes input string fname="gaps\\stat.txt"; // name of file for statistics struct SGap { double p0; double p1; double p2; double p3; double s; void set(double p_1,double p,double sprd); bool brkn(); void change(double p); double gap(); double Q(); }; class CGaps { SGap gs[]; int ngs; int go[]; int ngo; public: void init(); void add(double p_1,double p,double sprd); bool change(double p); void gs2f(string fn); }; CGaps gaps; MqlTick tick0; int ns0=-1; ulong sbias=mbias*60, speriod=mperiod*60, slength=mlength*60; void OnTick() { MqlTick tick; if (!SymbolInfoTick(_Symbol, tick)) return; double p=(tick.bid+tick.ask)*0.5; gaps.change(p); int ns=nsession(tick.time); if(ns>=0) { double p0=(tick0.bid+tick0.ask)*0.5; if(ns0>=0&&ns>ns0&&MathAbs(p-p0)>=gmin) gaps.add(p0,p,tick.ask-tick.bid); ns0=ns; tick0=tick; } } int OnInit() { if(speriod==0||slength==0||speriod<slength||speriod<=sbias) { Print("wrong session format"); return(INIT_FAILED); } gaps.init(); return(INIT_SUCCEEDED); } void OnDeinit(const int reason) { gaps.gs2f(fname); } int nsession(datetime t) { ulong t0=(ulong)t; if(t0<sbias) return -1; t0-=sbias; if(t0%speriod>slength) return -1; return (int)(t0/speriod); } void SGap :: set(double p_1,double p,double sprd) { p1=p_1; p0=p2=p3=p; s=sprd; } bool SGap :: brkn() { return ((p0>p1)&&(p3<=p1))||((p0<p1)&&(p3>=p1)); } void SGap :: change(double p) { if(brkn()) return; if((p0>p1&&p>p2) || (p0<p1&&p<p2)) p2=p; p3=p; } double SGap :: gap() { return MathAbs(p0-p1); } double SGap :: Q() { double q=p2-p1; if(q==0.0) return 0.0; return (p2-p0)/q; } void CGaps :: init() { ngs=ngo=0; } void CGaps :: add(double p_1,double p,double sprd) { ++ngs; if(ArraySize(gs)<ngs) ArrayResize(gs,ngs,ND); gs[ngs-1].set(p_1,p,sprd); int i=0; for(; i<ngo; ++i) if(go[i]<0) break; if(i==ngo) { ++ngo; if(ArraySize(go)<ngo) ArrayResize(go,ngo,ND); } go[i]=ngs-1; } bool CGaps :: change(double p) { bool chngd=false; for(int i=0; i<ngo; ++i) { if(go[i]<0) continue; gs[go[i]].change(p); if(gs[go[i]].brkn()) {go[i]=-1; chngd=true;} } return chngd; } void CGaps :: gs2f(string fn) { int f=FileOpen(fn, FILE_WRITE|FILE_COMMON|FILE_ANSI|FILE_TXT), c; for(int i=0;i<ngs;++i) { if (gs[i].brkn()) c=1; else c=0; FileWriteString(f,(string)gs[i].gap()+" "+(string)gs[i].Q()+" "+(string)c+" "+(string)gs[i].s); if(i==ngs-1) break; FileWriteString(f,"\n"); } FileClose(f); }
Los EAs son bastante simples. Mencionaremos sólo el array go[] en la clase CGaps, donde se guardan los índices de los gaps no cerrados, lo que permite acelerar un poco el trabajo de los EAs.
En cualquier caso, los siguientes datos se registran para cada gap: valor del gap absoluto, valor de Q, información sobre su cierre y el alor del spread en el momento de la aparición del gap. Luego se verifica la diferencia entre la distribución empírica del valor Q y la uniforme, y se toma la decisión sobre el análisis posterior. Para comprobar la diferencia, se usa el método gráfico y el método de cálculo (cálculo de la estadística de Kolmogorov). Para simplificar, nos limitaremos a la demostración del valor p-value de la prueba de Kolmogorov-Smirnov como resultado de los cálculos. Recibe el valor entre 0 y 1, y cuanto menos sea este valor, menor será la probabilidad de que la distribución de la muestra coincida con la teórica.
La prueba de Kolmogorov-Smirnov fue elegida por nosotros entre otros criterios por consideraciones matemáticas. La principal razón es que estamos interesados en distinguir las funciones de la distribución en la métrica de convergencia uniforme, y no en alguna métrica integral. Esta prueba no fue encontrada en las bibliotecas de MQL5, por eso, tuve que usar el lenguaje R. Obsérvese, si en la muestra hay números que coinciden, la precisión de este criterio se disminuye un poco (R avisa de ello), pero permanece bastante aceptable.
Si se detecta una discrepancia significativa entre la distribución teórica e impérica, hay que estudiar la posibilidad de sacar beneficio de eso. Si no hay una discrepancia importante, abandonamos esta idea, o bien, intentamos mejorarla.
Como hemos mencionado antes, hay dos posibilidades de entrar en la transacción por el precio p0 cuando se forma un gap (en su dirección, o en contra). Vamos a calcular la expectativa de rentabilidades para ambos casos. Además, vamos a tomar en cuenta el spread considerándolo como un valor constante y denotando a través de s. El valor absoluto del gap se determina a través de g, y su valor mínimo aceptable, a través de g0.
- Entrada en la dirección del gap. En este caso, g+s es el valor del Stop Loss, kg-s es el valor del Take Profit. Aquí, k es la razón entre TP y SL. Valores de rentabilidades: -1 en caso del disparo del SL y (kg-s)/(g+s) − en caso del disparo del TP. Posibilidades correspondientes: Fq(q) и 1-Fq(q). Expresamos k a través de q: k=k(q)=q/(1-q). Entonces, para la expectativa matemática de la rentabilidad M es cierto que M=Fq(q)*(-1)+(1-Fq(q))*(k(q)g-s)/(g+s). Nos convienen sólo aquellos valores q para los cuales M es bastante positivo para todos los g≥g0. Para eso, valen aquellos qque hacen que Fq(q) sea mucho más bajo que el valor teórico durante el paseo aleatorio Fq(q)<Fq0(q) con g=g0.
- Entrada contra el gap. En caso contrario, g-s es el valor del TP, kg+s es el valor de SL. Aquí, k es la razón entre SL y TP. Valores de rentabilidades: -1 en caso del disparo del SL y (g-s)/(kg+s) − en caso del disparo del TP. Probabilidades correspondientes: 1-Fq(q) и Fq(q). La expresión de k a través de q es la misma que en el punto anterior: k=k(q)=q/(1-q). Para la expectativa matemática de rentabilidad de M obtenemos M=(1-Fq(q))*(-1)+*Fq(q)(g-s)/(k(q)g+s). Nos convienen sólo aquellos valores q para los cuales M es bastante positivo para todos los g≥g0. Para eso, valen aquellos qque hacen que Fq(q) sea mucho más alto que el valor teórico Fq(q)>Fq0(q) con g=g0.
La estadística se recopilaba para dos símbolos:
- EURUSD
- USDJPY
Para cada uno de ellos, se consideraban los siguientes tipos de los gaps:
- Entre los ticks consecutivos.
- Entre las barras de minutos.
- Entre las sesiones comerciales. Para EURUSD es una sesión americana (combinación de Chicago y de Nueva York), y para USDJPY es la de Tokio.
Para cada una de estas seis opciones se analizaba la estadística para los más recientes:
- 200 gaps
- 50 gaps
Como resultado, tenemos 12 variantes. Para cada una de ellas, se muestran los siguientes resultados:
- Valor p-value para la estadística de Kolmogorov
- Valor medio del spread mean spread en los momentos de formación de spread
- Gráfico de la función empírica y teórica (línea roja) de la distribución del valor Q
- Gráfico de expectativa matemática de rentabilidad M_cont para el trading en la dirección del gap dependiendo de q en comparación con la línea teórica M_cont=0 (línea roja). Aquí, q significa la razón del TP y la suma de TP y SL.
- Gráfico de expectativa matemática de rentabilidad M_rvrs para el trading en la dirección opuesta al gap dependiendo de q en comparación con la línea teórica M_rvrs=0 (línea roja). Aquí, q significa la razón del SL y la suma de TP y SL.
Abajo se muestra todas estas opciones de los resultados.
- EURUSD, 200 últimos gaps entre los ticks consecutivos. p-value: 3.471654e-07, mean spread: 0.000695
- EURUSD, 50 últimos gaps entre los ticks consecutivos. p-value: 0.2428457, mean spread: 0.0005724
- EURUSD, 200 últimos gaps entre las barras de minutos. p-value: 8.675995e-06, mean spread: 0.0004352
- EURUSD, 50 últimos gaps entre las barras de minutos. p-value: 0.0125578, mean spread: 0.000404
- EURUSD, 200 últimos gaps entre las barras de minutos. p-value: 0.6659917, mean spread: 0.0001323
- EURUSD, 50 últimos gaps entre las sesiones comerciales. p-value: 0.08915716, mean spread: 0.0001282
- USDJPY, 200 últimos gaps entre los ticks consecutivos. p-value: 2.267454e-06, mean spread: 0.09563
- USDJPY, 50 últimos gaps entre los ticks consecutivos. p-value: 0.03259067, mean spread: 0.0597
- USDJPY, 200 últimos gaps entre las barras de minutos. p-value: 0.0003737335, mean spread: 0.05148
- USDJPY, 50 últimos gaps entre las barras de minutos. p-value: 0.005747542, mean spread: 0.0474
- USDJPY, 200 últimos gaps entre las sesiones comerciales. p-value: 0.07743524, mean spread: 0.02023
- USDJPY, 50 últimos gaps entre las sesiones comerciales. p-value: 0.009191665, mean spread: 0.0185
Se puede hacer la siguiente conclusión a base de estos resultados:
- La desviación del paseo aleatorio es bastantes importante.
- El trading en la dirección de los gaps no es nada prometedor. El trading en la dirección del cierre de los gaps parece más preferible, pero el beneficio no es grande (sobre todo para EURUSD).
- En el momento de la formación de los gaps, los spreads pueden crecer varias veces en comparación con su valor medio (es cierto para los gaps entre los ticks y las barras de minutos).
- Los gaps entre las sesiones comerciales dan una desviación del paseo aleatorio sólo en algunos intervalos de 1-2 meses, mientras que en los intervalos de un año, la desviación es poco importante. A primera vista, eso se determina por la tendencia que prevalece en aquel momento. Aparentemente, los gaps se cierran mejor cuando hay un flat y peor cuando hay una tendencia arriba o abajo, pero hace falta analizar más detalladamente esta variante.
- Para el análisis posterior, elegimos la variante de los gaps entre las barras de minutos para USDJPY.
Simulación de la estrategia y cálculo de un riesgo óptimo en la transacción
La versión del sistema basada en las rupturas entre las barras de minutos de USDJPY parece más prometedora. El aumento significativo del spread que detectamos en el momento de la formación del gap requiere más atención a su definición. Vamos a especificarla de la siguiente manera. No vamos a considerar el gap para el precio medio, sino para bid y ask. Además de eso, vamos a elegir uno de ellos que usaremos para entrar en la transacción. Eso significa que vamos a determinar el gap arriba usando el bid, y hacia abajo, usando el ask. Lo mismo se refiere al cierre.
Diseñamos el EA cambiando un poco aquél que usábamos para la recopilación de la estadística sobre los gaps entre las sesiones. La principal modificación se hará en la estructura del gap. Puesto que tenemos una correspondencia definitiva entre los gaps y las transacciones, toda la información necesarias para el trading (volumen de la transacción y condición de su cierre) va a almacenarse en este estructura. Han sido añadidas dos funciones para realizar el trading. Una de ellas, pp2v(), calcula el volumen para cada transacción separada, otra función, trade(), guarda la correspondencia entre la suma de los volúmenes de transacciones y el volumen de la posición comercial. El código del EA "gaps_ses_test.mq5" se muestra a continuación.
// gaps_ses_test.mq5 #define ND 100 input uint mperiod=1; // session period in minutes input uint mlength=1; // session length in minutes input uint mbias=0; // first session bias in minutes input double g0=0.1; // minimal gap size: USDJPY - 0.1, EURUSD - 0.001 input double q0=0.4; // q0=sl/(sl+tp) input double r=0.01; // risk in deal input double s=0.02; // approximate spread input string fname="gaps\\stat.txt"; // name of file for statistics struct SGap { double p0; double p1; double p2; double v; int brkn(); bool up(); void change(double p); double gap(); double Q(); double a(); }; class CGaps { SGap gs[]; int ngs; int go[]; int ngo; public: void init(); void add(double p_1,double p); bool change(double pbid,double pask); double v(); void gs2f(string fn); }; CGaps gaps; MqlTick tick0; int ns0=-1; ulong sbias=mbias*60, speriod=mperiod*60, slength=mlength*60; double dv=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP); void OnTick() { MqlTick tick; if (!SymbolInfoTick(_Symbol, tick)) return; bool chngd=gaps.change(tick.bid,tick.ask); int ns=nsession(tick.time); if(ns>=0) { if(ns0>=0&&ns>ns0) { if(tick0.ask-tick.ask>=g0) {gaps.add(tick0.ask,tick.ask); chngd=true;} else if(tick.bid-tick0.bid>=g0) {gaps.add(tick0.bid,tick.bid); chngd=true;} } ns0=ns; tick0=tick; } if(chngd) trade(gaps.v()); } int OnInit() { gaps.init(); return(INIT_SUCCEEDED); } void OnDeinit(const int reason) { gaps.gs2f(fname); } int nsession(datetime t) { ulong t0=(ulong)t; if(t0<sbias) return -1; t0-=sbias; if(t0%speriod>slength) return -1; return (int)(t0/speriod); } double pp2v(double psl, double pen) { if(psl==pen) return 0.0; double dc, dir=1.0; double c0=AccountInfoDouble(ACCOUNT_EQUITY); bool ner=true; if (psl<pen) ner=OrderCalcProfit(ORDER_TYPE_BUY,_Symbol,dv,pen+s,psl,dc); else {ner=OrderCalcProfit(ORDER_TYPE_SELL,_Symbol,dv,pen,psl+s,dc); dir=-1.0;} if(!ner) return 0.0; return -dir*r*dv*c0/dc; } void trade(double vt) { double v0=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN); if(-v0<vt<v0) vt=v0*MathRound(vt/v0); double vr=0.0; if(PositionSelect(_Symbol)) { vr=PositionGetDouble(POSITION_VOLUME); if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_SELL) vr=-vr; } int vi=(int)((vt-vr)/dv); if(vi==0) return; MqlTradeRequest request={0}; MqlTradeResult result={0}; request.action=TRADE_ACTION_DEAL; request.symbol=_Symbol; if(vi>0) { request.volume=vi*dv; request.type=ORDER_TYPE_BUY; } else { request.volume=-vi*dv; request.type=ORDER_TYPE_SELL; } if(!OrderSend(request,result)) PrintFormat("OrderSend error %d",GetLastError()); } int SGap :: brkn() { if(((p0>p1)&&(p2<=p1))||((p0<p1)&&(p2>=p1))) return 1; if(Q()>=q0) return -1; return 0; } bool SGap :: up() { return p0>p1; } void SGap :: change(double p) { if(brkn()==0) p2=p; } double SGap :: gap() { return MathAbs(p0-p1); } double SGap :: Q() { if(p2==p1) return 0.0; return (p2-p0)/(p2-p1); } double SGap :: a() { double g=gap(), k0=q0/(1-q0); return (g-s)/(k0*g+s); } void CGaps :: init() { ngs=ngo=0; } void CGaps :: add(double p_1,double p) { ++ngs; if(ArraySize(gs)<ngs) ArrayResize(gs,ngs,ND); gs[ngs-1].p0=gs[ngs-1].p2=p; gs[ngs-1].p1=p_1; double ps=p+(p-p_1)*q0/(1-q0); gs[ngs-1].v=pp2v(ps,p); int i=0; for(; i<ngo; ++i) if(go[i]<0) break; if(i==ngo) { ++ngo; if(ArraySize(go)<ngo) ArrayResize(go,ngo,ND); } go[i]=ngs-1; } bool CGaps :: change(double pbid,double pask) { bool ch=false; for(int i=0; i<ngo; ++i) { if(go[i]<0) continue; if(gs[go[i]].up()) gs[go[i]].change(pbid); else gs[go[i]].change(pask); if(gs[go[i]].brkn()!=0) {go[i]=-1; ch=true;} } return ch; } double CGaps :: v(void) { double v=0; for(int i=0; i<ngo; ++i) if(go[i]>=0) v+=gs[go[i]].v; return v; } void CGaps :: gs2f(string fn) { int f=FileOpen(fn, FILE_WRITE|FILE_COMMON|FILE_ANSI|FILE_TXT); int na=0, np=0, bk; double kt=0.0, pk=0.0; for(int i=0;i<ngs;++i) { bk=gs[i].brkn(); if(bk==0) continue; ++na; if(bk>0) ++np; kt+=gs[i].a(); } if(na>0) { kt/=na; pk=((double)np)/na; } FileWriteString(f,"na = "+(string)na+"\n"); FileWriteString(f,"kt = "+(string)kt+"\n"); FileWriteString(f,"pk = "+(string)pk); FileClose(f); }
Vamos a testear este EA en el año 20017 y definir el valor del riesgo para tredear en el año 2018 a base de sus resultados. A continuación, se muestra el gráfico del balance/equidad a base de los resultados de la prueba para 2017.
Hay que hacer algunas aclaraciones antes de proceder al cálculo del riesgo declarado en el título del artículo. En primer lugar, es necesario justificar la necesidad de determinar el nivel del riesgo. En segundo lugar, es necesario explicar la ventaja de la aplicación de nuestra teoría para este propósito.
El trading especulativo siempre está relacionado con una indeterminación. Prácticamente, cualquier sistema comercial a veces realiza las transacciones con pérdidas. Por esta razón, el riesgo no tiene que ser demasiado grande, de lo contrario, la reducción puede ser excesiva. Por otro lado, en cualquier momento, el mercado puede cambiar tanto que un sistema rentable se convierte en un sistema no rentable. De aquí se desprende que el «tiempo de vida» de un sistema se desconoce exactamente. Por esta razón, el riesgo no tiene que ser demasiado pequeño. De lo contrario, Usted no podrá obtener todos los beneficios posibles de su sistema comercial.
Ahora, vamos a considerar los enfoques principales (diferentes del nuestro) en la definición del valor del riesgo, acompañándolos con breves características:
- Definir el riesgo por un valor numérico especificado basándose en la opinión de algunos «traders experimentados». Normalmente, se indica un valor dentro del rango 0,5-3% del capital. Este enfoque es bastante adecuado a pesar de la falta de su justificación. Su principal desventaja es la falta de la regla de la selección de un valor de riesgo determinado para un sistema determinado.
- Método de la «F óptima» de Ralph Vince. Este método está bastante argumentado teóricamente, pero generalmente ofrece un valor de riesgo inadecuadamente elevado, lo que puede provocar una reducción muy grande.
- La inclusión del valor del riesgo en los parámetros del EA y su definición durante el testeo y optimización. Es difícil decir algo con certeza sobre la consistencia y adecuación del resultado, es que, todo depende mucho de la estructura del EA y de la manera exacta de la ejecución de la optimización. Uno de los posibles problemas es la falta de contabilización de la indeterminación en los resultados del trading posterior. Además, el reajuste también es posible, lo que puede causar un aumento injustificado del valor del riesgo (prácticamente, el método anterior es un ejemplo claro). Principalmente, el método propuesto por nosotros es una manera razonable de la optimización del riesgo.
Nuestro método, a diferencia de los descritos anteriormente, permite obtener los valor adecuados y justificados para el riesgo. Dispone de los parámetros ajustables que pueden ser adaptados a un estilo específico del trading. Vamos a describir la esencia de nuestro enfoque del cálculo del riesgo. Supongamos que estamos usando el sistema del trading exactamente hasta que su rentabilidad media durante el número de transacciones especificado no caiga por debajo de la rentabilidad mínima especificada, o la reducción en la misma secuencia de transacciones no supere el valor máximo establecido. Después de eso, el sistema comercial deja de negociar (por ejemplo, sus parámetros serán reoptimizados). El valor del riesgo se selecciona de modo que la probabilidad de que el sistema siga siendo rentable (y la reducción o la disminc¡ución de la rentabilidad tengan la naturaleza de una fluctuación aleatoria natural), no sea mayor que el valor especificado.
Puede encontrar la descripción más detallada de nuestro método en los artículos anteriores. En el segundo artículo, se encuentra el script conveniente para calcular el valor óptimo del riesgo. Este script se aplica para salir de las transacciones con SL y TP especificados durante al entrada, y con su razón fija para todas las transacciones. Se llama "bn.mq5" en el artículo mencionado.
Como resultado de un paso de la prueba, nuestro EA guarda los datos necesarios como una parte de los parámetros para el script del calculo del riesgo, almacenándolos en un archivo de texto. El resto de los parámetros se saben de antemano, o se seleccionan de una búsqueda exaustiva. Si resulta que el riesgo propuesto por el script es cero, tenemos que descartar esta idea comercial, o aflojar nuestros requerimientos en cuanto a la reducción/rentabilidad (modificando los parámetros), o usar los datos sobre las transacciones de un historial más grande. Vamos a mostrar una parte del script con los valores de los parámetros que serán definidos.
input uint na=26; // número de transacciones en la serie input double kt=1.162698185452029 // razón TP/SL input double pk=0.5769230769230769 // probabilidad del beneficio double G0=0.0; // rentabilidad media mínima double D0=0.9; // incremento mínimo más pequeño double dlt=0.17; // nivel de importancia
Puesto que los resultados de la prueba para 2017 no impresionan mucho, pues nuestras exigencias también serán moderadas. Establecemos una condición de que dentro de 26 transacciones (na=26) el EA no sea no rentable (G0=0.0) y la reducción no exceda un 10% (D0=0.9). Para obtener un valor de riesgo no nulo, tenemos que establecer el nivel de la importancia bastante alto (dlt=0.17). En realidad, será mejor que no sea más de una decima. El hecho de que necesitamos establecerlo tan alto, indica en los resultados deficitarios, y es mejor no usar este EA con estos parámetros en el trading real con este símbolo. Usando estos parámetros, el script muestra el siguiente resultado para el riesgo: r=0.014. Aquí, se muestra el resultado de la prueba del EA con este valor del riesgo para el año 2018.
A pesar de la rentabilidad mostrada por el EA durante la prueba, es poco probable que ella sea la misma en el trading real. La inviabilidad del trading con los gaps comunes es evidente para los instrumentos analizados. Estos gaps son muy raros (y se hacen aún más raros con el tiempo) y pequeños en cuanto a su tamaño. Una consideración más profunda de la generalización de gaps -variación de precios entre sesiones comerciales- parece más prometedora. Además, tiene sentido prestar atención en los instrumentos donde los gaps comunes son más frecuentes.
Conclusiones
Los métodos probabilistas son bastante convenientes para crear y configurar los EAs. Al mismo tiempo, de ninguna forma son contradictorios a otros métodos posibles. En muchas ocasiones, ellos permiten completar o evaluarlos de nuevo.
En este artículo, hemos dejado aparte el tema de la optimización general del EApor sus parámetros. Hemos mencionado solamente una parte de esta cuestión. Existe una relación importante de esta área con el enfoque probabilista (con la teoría de soluciones estadísticas). Tal vez, sea posible aclarar esta cuestión más tarde.
Archivos adjuntos
Se adjuntan dos EAs usados para recopilar la estadística y un EA usado para el trading de prueba.
№ | Nombre | Tipo | Descripción |
---|---|---|---|
1 | gaps_reg_stat.mq5 | Asesor Experto | Recopilación de la estadística de los gaps entre los ticks consecutivos |
2 | gaps_ses_stat.mq5 | Asesor Experto | Recopilación de la estadística de los gaps entre las sesiones |
3 | gaps_ses_test.mq5 | Asesor Experto | Trading de prueba de los gaps entre las sesiones |
Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/5373
![Diagramas horizontales en los gráficos de MеtaTrader 5](https://c.mql5.com/2/33/HorizontalVolumesM0taTrader5.png)
![Patrones de reversión: Testeando el patrón "Cabeza-Hombros"](https://c.mql5.com/2/34/5358_avatar.png)
![Cómo crear y testear personalmente los instrumentos de la Bolsa de Moscú en MetaTrader 5](https://c.mql5.com/2/35/CustSymbols_MOEX.png)
![Reversión: creando un punto de entrada y escribiendo un algoritmo de comercio manual](https://c.mql5.com/2/34/Reverse_trade.png)
![MQL5 - Lenguaje de estrategias comerciales para el terminal de cliente MetaTrader 5](https://c.mql5.com/i/registerlandings/logo-2.png)
- 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