![MQL5 - Lenguaje de estrategias comerciales para el terminal de cliente MetaTrader 5](https://c.mql5.com/i/registerlandings/logo-2.png)
Está perdiendo oportunidades comerciales:
- Aplicaciones de trading gratuitas
- 8 000+ señales para copiar
- Noticias económicas para analizar los mercados financieros
Registro
Entrada
Usted acepta la política del sitio web y las condiciones de uso
Si no tiene cuenta de usuario, regístrese
Empecé con esta premisa básica sobre la igualdad de los precios (más bien sólo la igualdad de los dobles)
(P1). Suponiendo que y = 1,50000: x == y, siempre que x sea cualquier número real que sea (i) mayor o igual que 1,499995 y (ii) menor que 1,500005.
Partiendo de P1, he llegado a la conclusión de que
(P2). Suponiendo y = 1,50000: a == y, b == y, y a == b, siempre que a y b sean números reales que sean (i) mayores o iguales que 1,499995 y (ii) menores que 1,500005.
Algunos ejemplos son: 1,500055 == 1,50006, 1,500055 == 1,500064, 1,500051 != 1,500059 y 1,500054 != 1,500056.
Utilizando lo anterior, he creado una función (abajo) que (1) toma dos precios como argumentos, (2) redondea esos precios al equivalente en puntos más cercano, y (3) determina si esos dos precios son iguales.
Esta función es simple y directa, pero debo comentar algo sobre la parte del "Acondicionamiento del precio". Como muchos de nosotros sabemos, los dobles (es decir, Como muchos sabemos, los dobles (es decir, las variables con formato de punto flotante de doble precisión ) son, a veces, ligeramente imprecisos y tienen problemas ocasionales de redondeo. Descubrí que cuando redondeaba 1,5000551 y 1,5000550 al punto más cercano y comparaba el resultado (1,50006 y 1,50005, respectivamente), parecían no ser iguales aunque, según P1 y P2 anteriores, deberían serlo. Concluí (después de realizar un par de pruebas) que el literal 1,5000550 se almacenaba en la variable como ~1,5000549999. Para remediarlo, decidí que si el precio estaba a menos de 15 diezmilésimas de punto del punto medio (x.xxxxx5), asumiría que el precio ha alcanzado el umbral mínimo para redondear al punto más cercano. En consecuencia, añado 15 diezmilésimas de punto a cada precio antes de redondear al punto más cercano. Por el momento, no creo que esta adición tenga ninguna consecuencia no deseada. Además, esos valores pueden ajustarse para aumentar/disminuir la suposición de redondear al punto más cercano.
RaptorUK y WHRoeder(y otros):Utilizando lo anterior como modelo, he construido la siguiente función llamada ComparePrices() que se basa en el post anterior de RaptorUK:
Como siempre, los comentarios instructivos/constructivos son bienvenidos. :)
He jugado un poco con esto, tratando de alcanzar un compromiso aceptable de legibilidad y rendimiento.
Me he conformado con las funciones individuales eq(a,b), ne(a,b), lt(a,b) etc...
Por ejemplo,
if (eq(a,b)) { ...}
En cuanto al rendimiento en mi VM lenta para 4999999 iteraciones obtengo las siguientes mediciones de referencia:
Bucle vacío : 370ms
inline MathAbs(a-b) < gHalfPoint (global) : 2482ms
Función bool vacía: 4266ms <-- Pretendo acercarme lo más posible a esta cifra.
Las implementaciones más rápidas de eq() que he conseguido son las siguientes.
Son unas 2,3 veces más lentas que la llamada a MathsAbs() en línea y 1,3 veces más lentas que una llamada a una función booleana vacía... que sólo devuelve true.
También como un aparte he descubierto que MQL no cortocircuita las expresiones booleanas.
en 5558ms
O si prefieres estáticas a globales (para mantener todo el código en un solo lugar):
en 5718ms
lt(), gt() etc deberían ser más rápidos ya que eq() y ne() son más complicados.
No lo haces. El punto flotante NUNCA es exacto para algunos números.
https://en.wikipedia.org/wiki/Floating_point
Los números en coma flotante son números racionales porque se pueden representar como un entero dividido por otro. Por ejemplo 1,45×103 es (145/100)*1000 o 145000/100. Sin embargo, la base determina las fracciones que pueden representarse. Por ejemplo, 1/5 no puede representarse exactamente como un número de punto flotante utilizando una base binaria, pero puede representarse exactamente utilizando una base decimal (0,2, o 2×10-1). Sin embargo, 1/3 no puede representarse exactamente ni en binario (0,010101...) ni en decimal (0,333....), pero en base 3 es trivial (0,1 o 1×3-1) .El valor doble del corredor podría estar en cualquier lugar desde 1,23457500000000 hasta 1,234584999999999 y seguir siendo considerado el mismo precio de 1,23458.
Sin embargo, su función dice que 1,23457500000000 no es GToE de 1,234584999999999
Sin embargo, su función dice que 1,234584999999999999 NO es LToE de 1,234575000000000000
Debes usar un punto/2 en las comparaciones https://www.mql5.com/en/forum/136997/page3#780837
Yo mismo he jugado un poco con esto, intentando alcanzar un compromiso aceptable de legibilidad y rendimiento.
El valor doble del corredor podría estar en cualquier lugar desde 1,23457500000000 hasta 1,234584999999999 y seguir siendo considerado el mismo precio de 1,23458.
En general, estoy de acuerdo. Ver mi P1 y P2 en mi post anterior.
WHRoeder:
Sin embargo, su función dice que 1,23457500000000 es NO GToE de 1,234584999999999
Sin embargo, su función dice que 1,2345849999999999 es NO LToE de 1,234575000000000000
El problema surge de cómo MT4/MQL almacena los valores de punto flotante en las variables. Por ejemplo
imprime las dos variables en el registro/diario:
Como puede ver, p2 ya no es 1,234584999999999, sino que se convierte en 1,23458500--debido, creo, al redondeo. Esa es la razón por la que mi función dice que p1 no es GToE a p2; y como puedes ver en el código de abajo, tu código también sugiere lo mismo--es decir, que p1 no es GToE a p2 y que p1 no es igual a p2.
Debes usar un punto/2 en las comparaciones
Existe la posibilidad de que el Punto/2 sea una desviación máxima demasiado pequeña. Por ejemplo:
Si la suposición es que 1,234575 es igual a 1,234580, entonces ¿por qué #2 muestra NEQ? Además, si asumimos que 1,23458 es un precio que podría significar un precio del corredor que está en cualquier lugar desde 1,23457500000000 hasta 1,234584999999999, ¿por qué #1 debería mostrar NEQ? ¿No deberían ser iguales si comparten el mismo punto de precio (de ahí mi Premisa #2 en mi post anterior)?
@Thirteen,
En tu código estás viendo diferencias de redondeo intencionadas debidas a la lógica de la aplicación, no errores de redondeo no intencionados debidos a errores de coma flotante, de ahí la diferencia:
Los dos tipos de "redondeo" son:
a) Errores de redondeo intrínsecos debidos a fracciones binarias en el formato IEEE. - Estos números deberían ser exactamente iguales pero no lo son debido a la representación binaria de las fracciones decimales. Se redondean por la representación MQ4 de los decimales.
b) Redondeo explícito a algún número o decimales. (por ejemplo, cuando se imprime, o se envían los precios a un Broker). - Estos no son realmente los mismos valores, sino que están siendo redondeados por la lógica de la aplicación para la conveniencia de alguien.
Esto no es realmente un error. Los errores debidos únicamente a la representación en coma flotante no serán probablemente tan grandes (a menos que se calcule mal una serie). Pero usted puede querer hacer este tipo de comparación en su aplicación de acuerdo a su propia lógica.
Los errores de redondeo intrínsecos[a] suelen ser muy pequeños ( órdenes de magnitud inferiores a Punto) y no intencionados. La aplicación no es capaz de redondear estos números para que sean exactamente el valor previsto, utilizando el tipo de datos double .
Las diferencias de redondeo explícitas[b] son intencionadas y mucho mayores (+/- 0,5 puntos). (en este caso). Así que dos números redondeados por su lógica de aplicación al mismo valor de punto pueden ser casi un punto completo de diferencia originalmente.
Lo ideal sería redondear primero los números [b] (sólo si se requiere el redondeo) y luego compararlos [a] en cuyo momento el error es muy pequeño debido a las limitaciones del doble. (por ejemplo, < 0,0000001)
Pero tu código es para comparar antes de redondearlas, en cuyo caso tienes que detallar con las posibles diferencias mucho mayores. Sin embargo el redondeo no siempre es necesario. Yo sólo lo usaría cuando se envíen los precios al broker.
Piénsalo de otra manera (Si MQ4 hubiera usado Decimal Codificado Binario - que permite la representación exacta de fracciones Decimales - entonces todos los problemas relacionados con Precio != Precio desaparecerían,
pero todavía tendría que redondear y comparar los números en su aplicación al punto más cercano para ciertas operaciones. (Principalmente las funciones de OrderXXX)
>> "si asumimos que 1,23458 es un precio que podría significar un precio del corredor que está en cualquier lugar de 1,234575000000000000 hasta 1,234584999999999"
Podría estar equivocado aquí (no estoy seguro de cómo funcionan los corredores) pero creo que un precio del corredor de 1,23458 es exactamente eso. especialmente con tamaños de lote de 100.000 dólares y más grandes a considerar. De lo contrario, un montón de dinero para ser hecho (por el corredor) mediante la explotación de la diferencia en los precios publicados.
Mi entendimiento es que es realmente sólo al enviar al corredor tiene que redondear, no en toda su aplicación. En cuyo caso las comparaciones por pequeño error deberían ser suficientes.
La inexactitud del punto flotante es independiente del redondeo para los precios del corredor. Pero si quieres tratar con ambos al mismo tiempo, supongo que es una preferencia personal (aunque podría ser confuso).
Aquí está mi versión completa, (espero que no haya errores)..
Esto proporciona 6 funciones:
eq(a,b) = ne(a,b) != gt(a,b) > lt(a,b) < ge(a,b) >= le(a,b) <= if (ge(Bid,target)) sell sell sell...
La razón es mantener el código legible (OMI), y reducir la probabilidad de errores de escritura, sin demasiado de un golpe de rendimiento.A todos los efectos, estas funciones deben ser tan rápidas como se puede hacer utilizando las funciones de usuario MQ4,
(para el rendimiento frente a MathAbs(a-b) < HalfPoint ver https://www.mql5.com/en/forum/136997/page5#822505 aunque en un EA real (a diferencia de un benchmark) sospecho que la diferencia es insignificante.
Aquí está mi versión completa, (espero que no haya errores)..
...
La premisa tan citada es:
Teniendo en cuenta esta premisa y utilizando su código como telón de fondo, ¿podría explicarme por qué dice que (a) 1,234576 y 1,234584 se consideran no iguales, (b) 1,234577 y 1,234583 se consideran no iguales, pero (c) 1,234578 y 1,234582 se consideran iguales? ¿Por qué (y cómo) el ejemplo (b) es menos igual que el ejemplo (c)?
Como he dicho anteriormente, considero que todos esos precios son iguales porque cada uno comparte el mismo punto de precio, es decir, 1,23458. Este ejemplo ilustra por qué creo (y he dicho más arriba) que Punto/2 puede ser una desviación máxima demasiado pequeña.
@Thirteen, mi respuesta a tus observaciones sigue siendo la misma 3 posts más arriba https://www.mql5.com/en/forum/136997/page5#822672 enlace. Voy a repetir la parte que podría conducir a la luz momento en la comprensión de mi punto de vista: (con un poco de revisión y énfasis añadido)
Think of it another way (If MQ4 had used Binary Coded Decimal - which allows exact representation of Decimal fractions - then most of the original issues regarding Price != Price would go away, (and is often used on financial platforms for that very reason )
pero todavía tendría que redondear y comparar números en su aplicación al punto más cercano para ciertas operaciones. (Principalmente las funciones de OrderXXX)
Sólo depende de cómo escribas tu código, y de si quieres diferenciar entre el redondeo en la aplicación (donde dos números diferentes son conceptualmente/lógicamente tratados como el mismo por simplicidad/conveniencia),
y los errores de punto flotante. No hay un bien y un mal, pero creo que un enfoque más confuso que el otro....
Además, personalmente soy un poco escéptico de la premisa fuera de cita (¡pero abierto a la corrección!), de nuevo mencionada en el post anterior.