Skip to main content

Credit Risk Simulation and Policy Optimization — Python edition of creditools

Project description

📊 PyCrediTools

Credit Risk Simulation, Policy Optimization, and Risk Clustering for Python

Python License Status


PyCrediTools é uma biblioteca de ponta desenvolvida especificamente para equipes de Risco de Crédito. Ela fornece motores computacionais de alta performance para Simulação de Funis de Decisão (Trade-off de Políticas) e Agrupamento Autónomo de Risco (Risk Clustering).

Esqueça o método de tentativa e erro. Com o PyCrediTools, você pode testar filtros de bureau, simular e otimizar cortes de score por loja/região e agrupar seu portfólio aprovado em Ratings contíguos de risco de forma matematicamente ótima e temporalmente estável.


🚀 Instalação

O pacote pode ser instalado diretamente do repositório GitHub:

pip install git+https://github.com/matheuspasche/pycreditools.git

💡 Core Features

  • Credit Policy Simulation: Monte esteiras de decisão compostas por múltiplos estágios (Filtros duros de bureau, Regras de Corte de Score, Propensões Variáveis de Fechamento de Contrato).
  • Automated Risk Clustering: Agrupe faixas de score em "Ratings de Risco" (A a E) sob restrições rigorosas de negócio (Monotonicidade de risco, Volume Mínimo por grupo e Estabilidade Temporal de Safras).
  • Monotonic Sorting Kernel: (Novo!) Garante que ratings de score único sejam estritamente contíguos e ordenados, eliminando sobreposição ou cruzamento de notas (scores mais altos sempre recebendo classificações melhores ou iguais).
  • Longitudinal Stability: O motor Ward calcula inibições de cruzamento de risco de forma contínua ao longo de múltiplos períodos (vintages/safras).

📖 Caso de Uso: Substituição de Política Legada (Showcase V14)

Esta seção demonstra o uso real da biblioteca para simular e aprovar a substituição de uma política legada (legacy_score com corte único p78) por um novo modelo campeão (score_5) regionalizado por loja.

O fluxo de execução e validação completo está disponível em tutorial_masterclass_v14.ipynb.

1. Modelagem do Funil de Aprovação (Bureau + Regras de Entrada)

Aplicamos os novos escudos rígidos de entrada (hard filters) e incluímos o ponto de corte do score vigente para comparação direta do funil cumulativo sobre a base de propostas:

import pandas as pd
from pycreditools import CreditPolicy, col

# Criamos a política base com escudos rígidos de entrada
policy_hf = (
    CreditPolicy(
        applicant_id_col="applicant_id",
        score_cols=["score_2", "score_3", "score_4", "score_5"],
        current_approval_col="approved",
        actual_default_col="actual_default",
        time_col="safra"
    )
    .filter("CPF Válido", col("cpf_valido") == True)
    .filter("Teto Negativação", col("vl_negativacao") <= 1500)
    .filter("Teto Atraso SCR", col("vl_vencido_scr") <= 3000)
    .filter("Teto Protestos", col("vl_protestos") <= 500)
)

# Adicionamos o ponto de corte vigente na política de comparação legada
policy_legacy_hf = (
    policy_hf
    .filter("Ponto de Corte Vigente", col("legacy_score") >= LEGACY_CUT)
)

O funil de aprovação cumulativo resultante (incluindo o corte vigente):

Etapa Volume % Funil Δ Etapa
Top of Funnel 667,348 100.0%
Após CPF Válido 666,017 99.8% -0.2%
Após Teto Negativação 604,823 90.6% -9.2%
Após Teto SCR 565,019 84.7% -6.6%
Após Teto Protestos 528,232 79.2% -6.5%
Após Ponto de Corte Vigente 136,711 20.5% -74.1%
Pós-todos os HF (combinado) 136,711 20.5%

2. Curva de Otimização e Fronteira Eficiente

Avaliamos os quatro scores candidatos (score_2 a score_5) confrontando sua taxa de aprovação contra a inadimplência projetada. O gráfico de Fronteira Eficiente abaixo exibe o desempenho de cada modelo, marcando com um "X" a performance da política histórica legada.

Fronteira Eficiente

O Score 5 domina claramente a fronteira de eficiência. Ao mesmo nível de aprovação da política histórica (~20.4%), ele projeta uma inadimplência de aprovados drasticamente menor. Prosseguimos a calibração final exclusivamente com ele.


3. Calibração de Inadimplência Plana (Flat Default Rate) por Loja

Para otimizar a alocação de capital e limite de crédito por região geográfica, substituímos a estratégia de aprovação plana por uma política de Inadimplência Plana (Flat Default Rate). Calibramos os cortes por região para atingir um alvo estável de 5.80% de PD Estressada localmente, permitindo que a taxa de aprovação flutue de acordo com a qualidade do público local.

# Notas de corte regionalizadas para atingir alvo de 5.80% PD estressada
cutoffs_loja = {
    "Centro-Oeste": 804,
    "Nordeste": 806,
    "Norte": 794,
    "Sudeste": 778,
    "Sul": 776,
}

def politica_loja(df_in):
    passa = pd.Series(False, index=df_in.index)
    for loja, cut in cutoffs_loja.items():
        passa.loc[(df_in["region"]==loja) & (df_in["score_5"]>=cut)] = True
    return passa

policy_final = (
    policy_hf
    .filter("Score Regionalizado Flat PD", politica_loja)
    .rate("Propensão de Contrato", base_rate=1.0, variable="take_up_rate")
)

Estatísticas regionais resultantes da simulação da política final:

Região / Loja Nota de Corte Taxa de Aprovação Local PD Estressada
Centro-Oeste 804 18.37% 5.54%
Nordeste 806 16.94% 5.67%
Norte 794 17.67% 5.79%
Sudeste 778 22.32% 5.86%
Sul 776 23.33% 5.83%

4. Agrupamento de Risco (Clustering) nos Sobreviventes

Classificamos os clientes aprovados sob a nova política (sobreviventes) em DEV no modelo de agrupamento Ward, gerando Ratings contíguos. O motor garante a contiguidade e a ordenação de forma 100% consistente.

# Treinado na população aprovada sobrevivente
df_train_dev = res_final[
    (res_final["new_approval"] > 0.0) &
    (res_final["sample"] == "DEV")
].copy()

group_res = find_risk_groups(
    data=df_train_dev,
    score_cols="score_5",
    default_col="actual_default",
    bins=30,
    max_groups=5,
    min_vol_ratio=0.01,
    method="ward",
    time_col="safra",
    max_crossings=1
)

A estrutura de Ratings resultante e sua validação temporal:

Rating Faixa de Score 5 Inad. DEV Inad. OOT Vol. DEV (Aprovados) Vol. OOT (Aprovados)
A 941 a 1000 1.66% 1.69% 42,205 19,642
B 890 a 940 4.05% 4.31% 27,381 12,860
C 870 a 889 5.77% 5.73% 14,181 6,667
D 822 a 869 8.01% 8.11% 36,879 17,719
E 776 a 821 10.22% 10.74% 18,421 9,005

Abaixo, plotamos a estabilidade temporal das safras de performance observada dos aprovados sob a nova política, provando que a segregação se mantém robusta e livre de sobreposições ao longo de todo o histórico:

Estabilidade dos Ratings por Safra


5. Dissecção dos Swaps e Quadrantes de Decisão

A transição de modelo altera a composição da carteira. Avaliamos a performance real observada para os clientes recusados na nova mas aceitos na antiga (Swap Out) vs a performance estressada para os novos aceitos (Swap In):

Quadrante Vol. Contratado Esperado Taxa de Inadimplência Origem dos Dados
Keep In 68,912 4.54% Observado (actual_default)
Swap In 23,159 10.11% Simulado Estressado (Angulado)
Swap Out 25,952 14.33% Observado Histórico Legado
Keep Out 0 N/A Sem dados (Rejeitados por ambas)

[!NOTE] Para a simulação dos Swap Ins (Magnum), utilizamos uma estratégia de Agravamento Angulado de risco para precificar de forma conservadora a seleção adversa. O estresse é aplicado de forma incremental por Rating de risco (do melhor para o pior), penalizando mais os ratings mais arriscados:

  • Rating A: 1.20x (+20% de estresse)
  • Rating B: 1.30x (+30% de estresse)
  • Rating C: 1.40x (+40% de estresse)
  • Rating D: 1.40x (+40% de estresse)
  • Rating E: 1.50x (+50% de estresse)

Aqui está um trecho do código onde definimos e adicionamos esse agravamento angulado por Rating de risco através da classe CustomStress:

from pycreditools import CustomStress

# Agravamento Angulado Swap In (A=1.2× a E=1.50×)
def angulado(df_swap, pd_col):
    mapa = {"A": 1.20, "B": 1.30, "C": 1.40, "D": 1.40, "E": 1.50}
    fator = df_swap["Rating"].map(mapa).fillna(1.4)
    return (df_swap[pd_col] * fator).clip(0, 1)

policy_magnum = (
    policy_final
    .add_stress(CustomStress(angulado))
)

6. Equilíbrio de Volume e Risco no P&L

A nova política estruturada alcança um resultado extremamente equilibrado no P&L contratado esperado:

  1. Atração Saudável: Com um motor discriminatório muito superior (Score 5), aprovamos clientes de menor risco. Ao calibrar a taxa de conversão (take-up rate) refletindo o apetite real dos clientes (de 41% nos melhores scores até 95% nos scores mais baixos), mitigamos a seleção adversa.
  2. Substituição Eficiente (Swaps): Trocamos com sucesso o público de alto risco do legado (Swap Out com inadimplência de 14.33%) por um público qualificado (Swap In com inadimplência esperada mesmo com estresse angulado de 10.11%).
  3. Efeito Win-Win: O resultado final demonstra que conseguimos reduzir a inadimplência contratada global do portfólio de 7.18% para 5.94% (uma redução de -17.3% no risco total contratado sob estresse angulado rigoroso) enquanto o volume de contratos esperado ficou em 92,071 (contra 94,675 legados, uma pequena redução planejada de -2.8% para maior segurança operacional e controle de risco).

7. Resumo do Impacto de P&L Executivo (Tabela Delta)

A comparação consolidada entre as políticas prova o sucesso do novo motor de simulação:

Métrica Política Legada Nova Política (Flat PD) Delta Absoluto Delta Relativo
Aprovação Global (% ToF) 20.47% 20.50% +0.02% +0.1%
Inadimplência Contratada (P&L) 7.18% 5.94% -1.24% -17.3%
Volume Contratado Esperado 94,675 92,071 -2,604 -2.8%

8. Crash Test: Resiliência dos Swap Ins (Estresse e Breakeven)

Como a performance dos Swap Ins é simulada, realizamos um teste de estresse severo aplicando multiplicadores de inadimplência sobre essa população. Variamos o fator de estresse de 1.0x até 10.0x no TradeoffAnalyzer para determinar a resiliência da carteira até o ponto de equilíbrio (breakeven) com a política histórica.

Crash Test

O ponto de breakeven é atingido em 2.25x. Isto significa que a inadimplência real do público Swap In teria de ser 2.25 vezes maior do que a estimada pelo modelo (e já estressada angularmente) para que a perda agregada da nova carteira subisse até os 7.18% da política antiga. Esse colchão de resiliência de 125% de sobrecarga prova a segurança operacional e resiliência extrema da nova política de Inadimplência Plana.


9. Exportação e Uso em Produção (Decision Engines)

O pacote permite integrar e implantar a inteligência de decisão calibrada diretamente em motores e microsserviços de produção. Para isso, o DeploymentPolicy expõe exportadores simplificados para pontuar proponentes:

  • Exportar Regras Limpas (Produção): Gera um arquivo JSON contendo apenas as expressões lógicas e faixas contínuas de score de cada Rating. Este formato é ideal para motores que rodam em Go, Java, Node.js ou SQL.

    dep_policy = policy.export(rating_recipe=group_res.recipe)
    dep_policy.save_production_rules("politica_producao.json")
    
  • JSON Limpo Exportado:

    {
        "etapas_funil": [
            { "posicao": 1, "nome": "CPF Válido", "tipo": "filtro", "expressao": "(cpf_valido == True)" },
            { "posicao": 2, "nome": "Corte Regional por Loja", "tipo": "filtro", "expressao": "(region == 'Sudeste' & score_5 >= 778) | (region == 'Nordeste' & score_5 >= 806)" }
        ],
        "classificacao_ratings": {
            "score_coluna": "score_5",
            "coluna_segmentacao": "region",
            "segmentos": {
                "Sudeste": [
                    { "rating": "A", "nota_minima": 928, "nota_maxima": 1000 },
                    { "rating": "B", "nota_minima": 890, "nota_maxima": 927 },
                    { "rating": "C", "nota_minima": 778, "nota_maxima": 889 }
                ],
                "Nordeste": [
                    { "rating": "A", "nota_minima": 915, "nota_maxima": 1000 },
                    { "rating": "B", "nota_minima": 860, "nota_maxima": 914 },
                    { "rating": "C", "nota_minima": 806, "nota_maxima": 859 }
                ]
            }
        }
    }
    
  • Bases de Retorno Padronizadas e Imutáveis: O método .predict(df, simple=True) retorna uma cópia isolada (.copy()) contendo exatamente as colunas padrão de controle (decisao, motivo, contratou, inadimplente, cenario, rating).

    • Base Simplificada (simple=True): Ótima para auditoria e logs de API de produção.
    • Base Bruta/Analítica (simple=False): Retorna todas as colunas intermediárias (PDs, status por etapa do funil) para modelagem e estudos analíticos de risco.

🛠️ Contribuir e Desenvolver

Para executar a suíte de testes unitários e verificar o comportamento do motor:

git clone https://github.com/matheuspasche/pycreditools.git
cd pycreditools
pip install -e .
pytest tests/

📜 Licença

Distribuído sob a licença MIT. Desenvolvido para modelagem e engenharia de risco financeiro moderno.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pycreditools-0.2.6.tar.gz (969.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pycreditools-0.2.6-py3-none-any.whl (508.7 kB view details)

Uploaded Python 3

File details

Details for the file pycreditools-0.2.6.tar.gz.

File metadata

  • Download URL: pycreditools-0.2.6.tar.gz
  • Upload date:
  • Size: 969.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.5

File hashes

Hashes for pycreditools-0.2.6.tar.gz
Algorithm Hash digest
SHA256 421ab6bcdfbb47f9b616b03c23730e048f11ed5a22b0766d847d311dd501094f
MD5 388b0b7b87102017e19969f07929e8ec
BLAKE2b-256 e381fd3b27a050c0a05bd199b8d0522763f872d6b39a8d5b777d40b2d75714c2

See more details on using hashes here.

File details

Details for the file pycreditools-0.2.6-py3-none-any.whl.

File metadata

  • Download URL: pycreditools-0.2.6-py3-none-any.whl
  • Upload date:
  • Size: 508.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.5

File hashes

Hashes for pycreditools-0.2.6-py3-none-any.whl
Algorithm Hash digest
SHA256 5464e951beb8de8bc993a60cf54c750eb19f0c7d64a16165c46f4a708b9eb93c
MD5 67db17db064221f34bd31000dd1ddfe1
BLAKE2b-256 22418a8168ca47d7583f28cac798db70fb1f482a5476f888912dc2209841cfd7

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page