Dos primitivas para no mentirte evaluando experimentos temporales: split walk-forward con embargo (sin leakage) y gate de significancia apareado con n efectivo de Kish y cota inferior de confianza.
Project description
honest-eval
Dos primitivas, en Python puro y sin dependencias, para no mentirte al evaluar un experimento sobre datos temporales:
temporal_split— separa train/test dejando el tramo más reciente como test, con embargo, para no filtrar el futuro (leakage).select_best_variant— decide si una variante le gana de verdad a un baseline, o si fue suerte: comparación apareada, tamaño de muestra efectivo de Kish y cota inferior de confianza.
Nacieron dentro de un bot de trading, para decidir con evidencia qué estrategia desplegar. Pero el rigor no tiene nada de específico al trading: sirve para cualquier A/B sobre muestras pareadas y cualquier validación de un modelo temporal.
Instalación
pip install honest-eval
Sin dependencias. Requiere Python ≥ 3.9.
1. temporal_split — el test es el futuro, no una muestra al azar
En una serie temporal, sklearn.train_test_split mete muestras de mañana en el
train y la métrica sale inflada: el modelo "predice" cosas que en producción
aún no habrían pasado. El test honesto es siempre el tramo más reciente.
from honest_eval import temporal_split
train_idx, test_idx = temporal_split(timestamps, test_frac=0.20, embargo=24)
X_tr, X_te = X[train_idx], X[test_idx]
y_tr, y_te = y[train_idx], y[test_idx]
Devuelve índices (no copia tus datos), así lo aplicas a numpy, pandas o listas por igual.
El embargo. Si tu label mira h pasos adelante, una muestra de train a
menos de h del corte ya "conoce" parte del resultado del test. embargo=h
descarta esas muestras del borde. La métrica baja, pero deja de mentir.
2. select_best_variant — ¿ganó, o tuvo suerte?
Elegir la variante de mayor media premia la varianza, no la ventaja: con pocas muestras, la más ruidosa suele quedar arriba por azar. Dos correcciones:
- Aparear. Mide variante y baseline sobre el mismo ensayo y trabaja con
δ = variante − baseline. La varianza común del ensayo se cancela en la resta. - Exigir cota inferior > 0. Gradúa solo si
mean − z·SE > 0: "incluso siendo pesimista dentro del margen de confianza, sigue por encima del baseline".
from honest_eval import select_best_variant
# Por cada variante, sus deltas (variante − baseline) apareados por ensayo:
variants = {
"chandelier": [0.8, 1.1, -0.2, 0.9, 1.0, 0.7],
"momentum": [2.0, -1.5, 3.0, -0.5, 1.2, -1.0], # media alta, muy dispersa
}
elegido = select_best_variant(variants, z=1.6449, min_effective_n=5)
if elegido is None:
politica = "baseline" # nadie superó al baseline con significancia
else:
politica = elegido.name # p.ej. "chandelier": mayor LCB, no mayor media
momentum puede tener media más alta y aun así no graduar: su dispersión
hunde la cota inferior. Eso es exactamente lo que quieres que pase.
Ponderación por recencia
Si el proceso cambia con el tiempo, pesa lo reciente más que lo viejo:
from honest_eval import halflife_weight
weights = [halflife_weight(now - t, halflife=7*86400) for t in exit_ts]
variants = {"chandelier": (deltas, weights)}
El tamaño de muestra efectivo de Kish (Σw)² / Σw² se encarga de que unas
pocas muestras muy pesadas no se hagan pasar por muchas: con pesos desiguales,
n_eff < n, y el gate lo tiene en cuenta.
La estadística cruda
Si solo quieres los números de una comparación apareada:
from honest_eval import paired_lcb
r = paired_lcb(deltas, weights=weights, z=1.6449)
r.mean, r.lcb, r.se, r.n_eff
Por qué existe
Casi todo el "edge" que se mide en un backtest es una de estas dos ilusiones: el modelo vio el futuro, o la variante ganadora ganó por ruido. Estas dos funciones son el mínimo para descartar ambas antes de arriesgar nada real. No crean señal —eso son datos y features— pero dejan de fabricarla donde no la hay.
Tests
pip install "honest-eval[test]"
pytest
23 tests, con los valores esperados calculados a mano para anclar la matemática, y verificados por mutación (alterar el signo de la cota o el corte del split rompe los tests que deben romperse).
Licencia
Apache-2.0.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file honest_eval-0.1.0.tar.gz.
File metadata
- Download URL: honest_eval-0.1.0.tar.gz
- Upload date:
- Size: 12.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1f6c6a64f5fa6e92f3ed0df55e755750b6a11f1d497d2d5bb1bac4bfd5f34402
|
|
| MD5 |
590bbc3c544003b1b638fc376f92bc43
|
|
| BLAKE2b-256 |
5cc949cff1146430e2cffb884adb606f885c440cdc89ec8fc210e03b78e0147c
|
Provenance
The following attestation bundles were made for honest_eval-0.1.0.tar.gz:
Publisher:
publish.yml on isazajuancarlos/honest-eval
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
honest_eval-0.1.0.tar.gz -
Subject digest:
1f6c6a64f5fa6e92f3ed0df55e755750b6a11f1d497d2d5bb1bac4bfd5f34402 - Sigstore transparency entry: 2191919375
- Sigstore integration time:
-
Permalink:
isazajuancarlos/honest-eval@155912d7abce76a4bba274040ffd310aafd27823 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/isazajuancarlos
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@155912d7abce76a4bba274040ffd310aafd27823 -
Trigger Event:
push
-
Statement type:
File details
Details for the file honest_eval-0.1.0-py3-none-any.whl.
File metadata
- Download URL: honest_eval-0.1.0-py3-none-any.whl
- Upload date:
- Size: 11.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0b2f20a9f3ab2b346bdaf60294e734e06296239976fea50b1289fe20b41712b0
|
|
| MD5 |
740b94b5fe2196ab1db11631c299924e
|
|
| BLAKE2b-256 |
f110febf1176f4e22a7d65b578a3fa6b66866c66ee5c056a3764e6f70ac704d2
|
Provenance
The following attestation bundles were made for honest_eval-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on isazajuancarlos/honest-eval
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
honest_eval-0.1.0-py3-none-any.whl -
Subject digest:
0b2f20a9f3ab2b346bdaf60294e734e06296239976fea50b1289fe20b41712b0 - Sigstore transparency entry: 2191919380
- Sigstore integration time:
-
Permalink:
isazajuancarlos/honest-eval@155912d7abce76a4bba274040ffd310aafd27823 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/isazajuancarlos
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@155912d7abce76a4bba274040ffd310aafd27823 -
Trigger Event:
push
-
Statement type: