bayesrisk
Librería Python open-source (Apache-2.0) de riesgo de crédito integral: PD (scorecards,
backends ML, survival), LGD y EAD, validación de modelos, provisiones IFRS 9/ECL,
forward-looking y stress testing, con informe reproducible y su lineage. Todo en un motor
reproducible por construcción, con lineage en cada corrida y una gobernanza —audit-trail y
ficha del modelo (model card)— que se enciende con una declaración de propósito.
Paquete: bayesrisk.
Antes se llamaba
nikodym. Desde la 2.0.0 la librería se llama bayesrisk, con los mismos resultados bit a bit que nikodym 1.20.0. Migrar es cambiar una línea —import bayesrisk—: ver Migrar desde nikodym.
Los estándares comunes —Basilea, IFRS 9— van en el motor. La normativa local de cada jurisdicción se aterriza encima, y hay un caso de referencia implementado que muestra cómo: Aterrizar una norma local.
Estado: 2.x (estable). El pipeline de validación de scorecard (F1) es API estable (SemVer 2.x): no rompe hasta un 3.0. Las superficies que aún crecen —modelado ML, provisiones, forward-looking, y los contratos transversales de resultados/métricas/orquestación— siguen marcadas como experimentales (fuera de la garantía SemVer 2.x).
Qué hace
Los seis dominios calculan hoy: son motores deterministas, sin stubs, con más de 500 tests
sobre los tres que no tienen interfaz (más de 4.900 en la suite completa). Lo que los separa no es
"hecho / no hecho", sino superficie (¿tiene UI, preset y capítulo en el informe, o hay que
escribir el config en Python?) y garantía de API (¿congelada bajo SemVer 2.x, o experimental?).
El único comando del paquete es bayesrisk-ui, que levanta la interfaz: no hay CLI que corra estos
dominios.
| Dominio | Superficie | Garantía |
|---|---|---|
| Scorecard (F1) — binning/WoE monotónico (optbinning), selección (IV/VIF), regresión logística, scorecard escalado (PDO/offset), calibración, desempeño (AUC/KS/Gini) y estabilidad (PSI/CSI) | UI, preset e informe | estable (SemVer 2.x) |
| Provisiones — IFRS 9/ECL y método interno (exposición por la tasa de pérdida del grupo, descompuesta en PD · LGD o provista directamente; jurisdiccionalmente neutro); la orquestación compara dos fuentes y aplica la regla declarada. La norma local se monta encima: caso de referencia | UI, preset e informe | experimental |
| Stress testing — escenarios adversos, shocks macro en escala logit, sensibilidad y reverse stress por bisección | Python | experimental |
| Markov — matrices de transición (cohorte/duración), Chapman-Kolmogorov, Aalen-Johansen, term-structure de PD | Python | experimental |
| Forward-looking — ARIMA/auto-ARIMA, VAR/VECM, Ljung-Box y modelos satélite macro → PD/LGD | Python | experimental |
| Survival — Kaplan-Meier, Cox/AFT y hazard discreto sobre datos censurados | UI, preset e informe | experimental |
- Backends ML (F2): XGBoost, LightGBM, CatBoost y tuning (Optuna) como extras selectivos, con explicabilidad (SHAP) opcional.
- No hace (por si lo estás buscando): roll rates, curvas de cosecha/vintage, ni una CLI que
ejecute pipelines desde la terminal — el comando
bayesrisk-uilevanta la interfaz, no corre nada. - Informe de validación, no un log: cada corrida produce un documento con portada, resumen
ejecutivo, metodología (redactada con los parámetros que realmente se usaron), resultados,
conclusiones y anexos técnicos. Sale en HTML y PDF, y también como base editable (
.qmdde Quarto o.docxde Word) para que escribas tu documentación encima: los capítulos que solo puede escribir un humano (contexto de la cartera, conclusión que se firma) vienen como placeholders con guía, nunca inventados. - Reproducibilidad total:
(datos + config + semilla) → resultado idéntico, con lineage bundle (git SHA, estado del working tree, hash del contenido de los datos,config_hash, semilla raíz, hash deluv.lockcon el que se construyó el paquete y versiones de las librerías) en cada corrida.
Tus datos no salen de tu infraestructura
bayesrisk es una librería, no un servicio: se instala con pip y corre donde tú la ejecutes —tu
notebook, tu servidor, tu clúster, dentro de tu red—. Para una institución financiera esto suele
importar más que cualquier métrica:
- Sin telemetría. El paquete no reporta uso, ni versiones, ni nada. No hay código de analítica.
- Sin llamadas de red en el cálculo. El pipeline no abre conexiones por sí solo: las cifras se
computan en tu proceso, con tus datos, y los artefactos se escriben en el
workdirque declaras. - Sin dependencias de un servicio nuestro. No hay una API que tenga que estar arriba para que el motor funcione, ni licencia que validar contra un servidor. Si Bayes Advisory desaparece mañana, tu corrida de pasado mañana sigue dando el mismo resultado.
- Las dos salidas posibles son tuyas y opcionales: la narración por IA (apagada por defecto, con tu clave y tu proveedor; la prosa del informe es determinista y no la escribe la IA) y el registro de experimentos MLflow, que por defecto escribe en un directorio local. Detalle en SECURITY.md.
Y como es Apache-2.0, puedes auditar el código, forkearlo y adaptarlo sin pedirnos permiso.
Instalación
pip install bayesrisk # núcleo base (config, Study, lineage)
pip install 'bayesrisk[scoring]' # MVP scorecard (optbinning + statsmodels + sklearn>=1.6)
pip install 'bayesrisk[ui]' # interfaz gráfica local, lista para correr (ver más abajo)
pip install 'bayesrisk[all]' # todo lo redistribuible (sin copyleft)
Requiere Python ≥ 3.11. El núcleo base es liviano: import bayesrisk no arrastra el stack ML;
los backends pesados viven tras extras opcionales con import perezoso.
El informe en PDF necesita librerías del sistema
El informe sale en HTML con la instalación base, y en Word (.docx) con bayesrisk[all]. El
PDF es el único formato con un requisito extra, y conviene saberlo antes de necesitarlo:
pip install 'bayesrisk[pdf]' # NO está incluido en [all] — ver nota de licencia abajo
bayesrisk[pdf] instala WeasyPrint, pero WeasyPrint no es Python puro: carga Pango (≥ 1.44),
HarfBuzz y libffi desde el sistema operativo, y pip no puede instalarlas. Hay que agregarlas por
fuera (documentación oficial de WeasyPrint):
| Sistema | Comando |
|---|---|
| macOS | brew install weasyprint |
| Debian ≥ 11 / Ubuntu ≥ 20.04 | apt install libpango-1.0-0 libpangoft2-1.0-0 libharfbuzz-subset0 |
| Windows | MSYS2: pacman -S mingw-w64-x86_64-pango |
Si faltan, no se rompe nada: la corrida termina igual, el informe se emite en HTML y un
RuntimeWarning dice exactamente cuál de los dos requisitos falta —el paquete o las librerías del
sistema—. Si prefieres que la ausencia sea un error en vez de una degradación (recomendable en un
pipeline de producción que promete PDF), pon report.pdf.fail_if_unavailable = True en el config.
En macOS con Homebrew en
/opt/homebrew(Apple Silicon), el enlazador puede no encontrar las librerías aunquebrewlas haya instalado. Se resuelve exportandoDYLD_FALLBACK_LIBRARY_PATH=/opt/homebrew/lib.
Por qué
[pdf]no entra en[all]: WeasyPrint arrastra Pyphen, de tri-licencia con GPL. El meta-extra[all]se mantiene libre de copyleft para que puedas redistribuir un cierre completo sin revisar licencias. El PDF queda a unpip installde distancia, como decisión tuya.
Quickstart
La forma más corta es la puerta guiada bayesrisk.Scorecard (estable bajo SemVer 2.x): datos,
qué es «malo», identificador, eje temporal y muestra fuera de tiempo, y
run() corre y cuenta cada etapa; está en «Tu primer scorecard en 15 líneas» de la documentación.
Debajo, la puerta completa: el experimento es un BayesRiskConfig declarativo;
bayesrisk.run(config, run_dir=...) lo ejecuta de
extremo a extremo (binning → selección → modelo → scorecard → calibración → desempeño →
estabilidad), deja la evidencia de la corrida en run_dir y devuelve un Study reproducible.
Este ejemplo usa el preset estándar F1 sobre un dataset sintético de consumo, así corre sin
rellenar ningún campo:
from pathlib import Path
from tempfile import mkdtemp
import bayesrisk
from bayesrisk.core.config import BayesRiskConfig
from bayesrisk.ui.datasets import materialize
from bayesrisk.ui.presets import standard_preset
# 1. Materializa el dataset sintético de consumo (determinista) en un workdir temporal.
workdir = Path(mkdtemp(prefix="bayesrisk-quickstart-"))
preset = standard_preset()
data_path = materialize(preset["dataset_id"], workdir=workdir)
# 2. Toma el config F1 curado y apúntalo al archivo de datos recién materializado.
cfg_dict = preset["config"]
cfg_dict["data"]["load"]["source"] = str(data_path)
config = BayesRiskConfig.model_validate(cfg_dict)
# 3. Ejecuta la corrida completa y verifica el estado ANTES de leer resultados. `run_dir` es
# donde queda su evidencia: el audit-trail que el preset trae encendido y, si declaras la
# sección `governance`, la ficha del modelo.
study = bayesrisk.run(config, run_dir=workdir / "corrida")
assert study.run_context.status == "done"
# 4. Accede a los resultados namespaced por dominio/clave.
scorecard = study.artifacts.get("scorecard", "scorecard") # tabla del scorecard
metrics = study.artifacts.get("performance", "discriminant_metrics") # AUC/KS/Gini por partición
print(metrics)
bayesrisk.run es fail-loud pero no explosivo: ante un fallo devuelve el Study parcial con
study.run_context.status == "failed", y el diagnóstico —tipo del error, mensaje del motor y paso
que falló— queda en study.run_context.error, sin configurar nada. El consumidor por código
debe chequear study.run_context.status antes de usar los resultados.
La misma corrida, sin escribir código
bayesrisk trae una interfaz gráfica local. Son dos comandos:
pip install 'bayesrisk[ui]'
bayesrisk-ui
El segundo levanta la interfaz en http://127.0.0.1:8000 y abre el navegador. Desde ahí eliges un
dataset —uno de ejemplo o el tuyo, en CSV, Excel o Parquet—, ajustas la configuración en un
formulario, ejecutas y descargas el informe.
No es una demo aparte: es el mismo motor y el mismo BayesRiskConfig. Lo que armas en el
formulario se puede exportar a YAML y correr por código, y produce el mismo config_hash. Puedes
empezar por la interfaz y terminar en un script, o al revés.
Antes de ejecutar, la interfaz compara tu configuración con las columnas de tu archivo y te dice de una vez cuáles no calzan, con un enlace al campo que hay que corregir. Es un aviso, no un bloqueo: puedes ejecutar igual.
Tres opciones, todas locales:
bayesrisk-ui --port 8123 # otro puerto (por defecto 8000)
bayesrisk-ui --workdir ./runs # dónde guardar corridas y datasets (por defecto .bayesrisk_ui)
bayesrisk-ui --no-open # no abrir el navegador
Escucha sólo en
127.0.0.1y no hay forma de cambiarlo: no existe un--host. Tus datos no salen de tu máquina, y la interfaz no es alcanzable desde la red. Cada lanzamiento genera un token propio que nunca se escribe en el log ni en la URL. Detalle en SECURITY.md.
[ui]trae todo lo que el formulario puede ejecutar, no sólo el servidor: el motor del scorecard y el de supervivencia entran con él. Son ~700 MB en disco, y es deliberado — un extra llamadouique instala la interfaz pero no lo que la interfaz dispara promete algo que no cumple. Quedan fuera el PDF (bayesrisk[pdf]), por licencia, y el backend de lecturapolars(bayesrisk[polars]), que es una opción de rendimiento: el resultado no cambia, y si lo eliges sin tenerlo instalado la corrida te lo dice con el comando exacto.
Limitaciones que debes conocer antes de usarlo en serio
El motor las publica de sí mismo —cada fila afectada emite su código de aviso declarado—, así que aquí se dicen igual de claro.
Las dos marcas, y por qué son dos. Cuando un número no sale de un dato real, el resultado lo dice con una de dos marcas, y la diferencia importa: una señala una brecha nuestra —algo que la librería todavía no trae, difirió, o no verificó contra la fuente oficial— y la otra, un dato que sólo tu institución puede fijar: los shocks macro de tu ejercicio de stress, tu taxonomía de estados, tu definición operacional de default. En el segundo caso la marca no confiesa una carencia: deja constancia de que el motor se negó a inventar un supuesto que no le corresponde. Un número que la librería no puede justificar no se rellena con un default cómodo.
Las marcas viajan en la columna
warning_codesdel resultado. El catálogo completo —qué significa cada código y qué hacer con él— está en Avisos declarados.
- Los parámetros del caso de referencia no son oficiales, y el caso está congelado. El motor
que aterriza la norma chilena (CMF, Cap. B-1) existe como ejemplo de método, no como
compromiso de mantenimiento: sus tablas se transcribieron del compendio y no provienen de la
CMF ni están validadas por ella —la Comisión no certifica implementaciones de terceros—, así
que requieren validación humana contra la norma vigente antes de cualquier uso productivo.
Lo que sí está hecho: las tablas se extrajeron del texto oficial el 2026-06-23 y la matriz
de consumo (numeral B-1 3.1.3, Circular 2.346/2024) se cotejó celda por celda el 2026-07-14
—sus 16 valores de PI, sus 6 de PDI y el PI de incumplimiento coinciden exactamente—, registrado en
docs/normativa_cmf_parametros.md§3. Quedan dos brechas abiertas, y el motor las declara: aforos y haircuts de garantías financieras, y las tablas del RAN 21-10. El alcance, las fechas por matriz y el estado de cada fuente están en Aterrizar una norma local. - Las causales de incumplimiento que el motor no puede inferir, las declara el banco. De las
tres del numeral B-1 3.2, solo la mora ≥ 90 días sale de los datos; el refinanciamiento para
dejar vigente una operación morosa y la reestructuración forzosa hay que entregarlas en la
columna
is_default. Sin ella, un deudor reestructurado y al día entra a la matriz con una PI de 6,6 % en vez del 100 % que exige la norma — y como la provisión es PI × PDI × exposición, la cifra reportada queda en una fracción de la que corresponde. - La EAD de IFRS 9 se despliega constante en el tiempo. El panel longitudinal está diferido; el
motor no lo aplana en silencio: cada fila afectada lo declara en sus avisos, y el config
rechaza
exposure_profile_colen vez de fingir que lo usa. - Experimental no es "beta marketinera": todo lo que no sea el pipeline de scorecard puede cambiar de firma dentro de la 2.x, y no está battle-tested en producción.
Principios de diseño
- Reproducibilidad total: misma entrada → resultado byte-idéntico, con lineage completo.
- Gobernanza que se enciende, no se inventa: el lineage viaja en toda corrida y el audit-trail en los ejemplos de fábrica; la ficha del modelo se emite cuando tu institución declara el propósito, porque ese dato no lo puede inventar el motor.
- Config declarativo (Pydantic v2): el config ES el experimento.
- Núcleo liviano: los backends pesados van tras extras con import perezoso.
- Una norma local nunca se funde con un estándar contable: son motores separados, nunca uno solo, y la regla que los compara la declara quien la usa. El caso de referencia lo ilustra: la regla del máximo del Capítulo B-1 (Circular N° 2.346) es entre el método estándar y el método interno del banco — no entre ese estándar e IFRS 9, porque el Compendio (Cap. A-2, num. 5) excluye el deterioro de NIIF 9 sobre colocaciones.
- Lo que falta se declara, no se disimula: un dato ausente sale marcado en el resultado, y la marca dice de quién es —una brecha nuestra, o un parámetro que sólo tu institución puede fijar—; una opción sin motor detrás se rechaza al validar el config, no al final de la corrida.
Documentación
Guía completa (conceptos, referencia de run/Study/BayesRiskConfig) en
docs.bayesadvisory.cl. La demo del scorecard —una corrida real, paso a paso—
en demo.bayesadvisory.cl. El CHANGELOG.md registra los cambios por versión.
Desarrollo
El proyecto usa uv + hatchling, con layout src/.
uv sync # entorno completo (grupo dev: test/lint/docs)
uv run ruff check . && uv run ruff format --check .
uv run mypy # type-check estricto de todo el paquete
uv run pytest # suite de tests
Quién lo construye
bayesrisk lo construye Bayes Advisory, una consultora chilena de riesgo y analítica de datos.
El motor es Apache-2.0 y no tiene edición comercial, tier de pago ni funciones reservadas: lo que
hay en src/ es todo lo que hay. Está publicado para que puedas leer el código antes de hablar con
nosotros.
Una librería calcula; no decide. El binning, la calibración y las métricas los corre el motor —pero a qué tasa central anclas (TTC o PIT), dónde pones el corte y qué supuestos sostienes ante Validación o ante tu regulador sigue siendo juicio de modelo, y eso no lo entrega ningún paquete de pip. Si ese es el problema, puedes proponer un caso. Cada caso se evalúa antes de aceptarse; si no hay caso, también te lo decimos, en menos de 48 horas hábiles.
Licencia
Apache-2.0. Sin dependencias copyleft (GPL/LGPL/AGPL) en el wheel.
Metadata
Release files for bayesrisk 2.4.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| bayesrisk-2.4.0.tar.gz | 3.4 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| bayesrisk-2.4.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 5.5 MB
Release files / bayesrisk-2.4.0.tar.gz
| Download URL | bayesrisk-2.4.0.tar.gz |
|---|---|
| Size | 3.4 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
1dc1f44ab033037eabfd707ffe930294a6362860700c7a49a356fcf538f3331d
|
|
BLAKE2b-256 checksum How to use checksums |
80f7bd13767b79478c452db7beca9a8916bca5990b1e0f7d3ec194c89a009aad
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 5, 2026.
Transparency logRelease files / bayesrisk-2.4.0-py3-none-any.whl
| Download URL | bayesrisk-2.4.0-py3-none-any.whl |
|---|---|
| Size | 2.1 MB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b2a62827ebd99a762edff45ff46a451cef1c11777a5d064d8c60d5f52d2cede0
|
|
BLAKE2b-256 checksum How to use checksums |
71cc0229e6562f86337818566dd522d04459021a6215622075ad1460332ab59b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 5, 2026.
Transparency log