Contra qué se compara: el nulo
Una señal no se compara contra cero, sino contra lo que habría dado el azar en la misma época y con la misma duración. Equivocarse de época multiplica el listón por siete.
La pregunta: Mi señal ganó un 4 % de media por operación. ¿Es eso mucho?
Qué se encontró
Depende enteramente de cuándo y durante cuánto. Una ventana aleatoria de 20 días de Bitcoin ha rendido de media un +8,63 % sobre la historia completa, un +3,86 % post-2017 y un +1,22 % en los últimos dos años. La misma operación del 4 % es un fracaso en un contexto y un éxito en otro. Por eso el nulo tiene que estar emparejado por DURACIÓN y por ÉPOCA: se sortean ventanas aleatorias del mismo largo y del mismo tramo temporal que las operaciones que se están juzgando, y la señal tiene que batir a eso, no a cero. El proyecto tenía un bug real justo aquí: su función de nulo sorteaba siempre sobre el array de precios COMPLETO, incluso al puntuar un subconjunto reciente, así que un candidato de los últimos dos años se comparaba contra un listón siete veces más rico.
Pruébalo tú mismo
Pon el resultado medio de tu señal y el del azar en la misma época y duración. La resta es lo único que cuenta.
Qué parte del resultado de la señal era simplemente el mercado (%):
Con los valores por defecto —una señal del 4 % contra el 3,86 % que daba el azar post-2017 a 20 días— el exceso es de 0,14 puntos. El 96 % del resultado era el mercado. Prueba a cambiar el nulo a 8,63 (historia completa) o a 1,22 (últimos dos años) y mira cómo cambia el veredicto sin tocar la señal.
Números de ejemplo para practicar — no son datos reales.
Cómo se probó, paso a paso
- Se toma cada operación de la señal y se anota su duración exacta y el tramo temporal en el que ocurrió.
- Se sortean miles de ventanas aleatorias con esa MISMA duración, dentro de ese MISMO tramo. Esa nube de resultados es lo que habría dado entrar al azar.
- La puntuación de la señal es su percentil dentro de esa nube. Un percentil de 0,52 significa "indistinguible del azar"; uno de 0,95, "mejor que el 95 % de las entradas aleatorias comparables".
- La corrección que adoptó el proyecto: el rango del que se sortea es ahora un argumento OBLIGATORIO, y la función lanza una excepción si ese rango no contiene las operaciones que se están evaluando. Más un test unitario que falla si alguien lo vuelve a desemparejar.