Skip to content

Instantly share code, notes, and snippets.

@plinionaves
Last active October 9, 2025 17:28
Show Gist options
  • Save plinionaves/5d9c31f4a86a6fa42431480a38545c79 to your computer and use it in GitHub Desktop.
Save plinionaves/5d9c31f4a86a6fa42431480a38545c79 to your computer and use it in GitHub Desktop.
Infer installments principal from payment schedule (price table)
import pandas as pd
from pyxirr import xirr, xnpv
from typing import List, Dict
# Esta função já estava correta, pois usa a taxa anual para o XNPV.
# Nenhuma alteração é necessária aqui.
def get_inferred_principal(
proposed_schedule: List[Dict],
loan_monthly_rate: float,
disbursement_date_str: str,
) -> float:
"""Calcula e retorna o principal implícito de um cronograma."""
disbursement_date = pd.to_datetime(disbursement_date_str)
payment_dates = [pd.to_datetime(p['due_date']) for p in proposed_schedule]
payment_amounts = [-p['fixed_amount'] for p in proposed_schedule]
# Converte a taxa mensal em uma taxa anual efetiva para o cálculo do XNPV
loan_annual_rate = (1 + loan_monthly_rate)**12 - 1
# O XNPV dos pagamentos nos dá o valor presente. O principal é o valor absoluto.
inferred_principal = -xnpv(loan_annual_rate, [disbursement_date] + payment_dates, [0] + payment_amounts)
return inferred_principal
# --- FUNÇÃO AJUSTADA ---
def generate_amortization_schedule(
proposed_schedule: List[Dict],
loan_monthly_rate: float,
disbursement_date_str: str,
) -> pd.DataFrame:
"""
Gera a Tabela de Amortização detalhada para um cronograma, inferindo seu principal.
"""
# 1. Obter o Saldo Devedor Inicial, que é o nosso principal inferido
initial_balance = get_inferred_principal(proposed_schedule, loan_monthly_rate, disbursement_date_str)
# --- AJUSTE PRINCIPAL AQUI ---
# 2. Calcular a taxa de juros anual e diária equivalentes
# A taxa anual é a base para um cálculo preciso em dias corridos (convenção Actual/365)
loan_annual_rate = (1 + loan_monthly_rate)**12 - 1
daily_rate = (1 + loan_annual_rate)**(1 / 365) - 1
# --- FIM DO AJUSTE ---
# 3. Preparando os dados para o loop de cálculo da amortização
amortization_data = []
outstanding_balance = initial_balance
previous_date = pd.to_datetime(disbursement_date_str)
print(f"--- Gerando Tabela de Amortização ---")
print(f"Principal Inferido (Saldo Devedor Inicial): {initial_balance:,.2f}")
print(f"Taxa Mensal: {loan_monthly_rate:.2%}")
print(f"Taxa Anual Equivalente: {loan_annual_rate:.4%}")
print(f"Taxa Diária Equivalente (base 365): {daily_rate:.8f}\n")
for i, installment in enumerate(proposed_schedule):
payment_date = pd.to_datetime(installment['due_date'])
payment_amount = installment['fixed_amount']
# Calcula os juros para o período exato em dias usando a nova taxa diária
days_in_period = (payment_date - previous_date).days
interest_paid = outstanding_balance * ((1 + daily_rate)**days_in_period - 1)
# A amortização é o que sobra da parcela após pagar os juros
principal_paid = payment_amount - interest_paid
# Atualiza o saldo devedor
outstanding_balance -= principal_paid
# Ajuste final na última parcela para zerar o saldo devido a arredondamentos
if i == len(proposed_schedule) - 1:
principal_paid += outstanding_balance
outstanding_balance = 0.0
amortization_data.append({
"Parcela #": i + 1,
"Data Venc.": payment_date.date(),
"Dias Período": days_in_period,
"Valor Parcela": payment_amount,
"Juros": interest_paid,
"Amortização (Principal)": principal_paid,
"Saldo Devedor": outstanding_balance
})
previous_date = payment_date
# Cria um DataFrame do Pandas para uma visualização clara
df = pd.DataFrame(amortization_data)
# Formatando as colunas para melhor leitura
for col in ["Valor Parcela", "Juros", "Amortização (Principal)", "Saldo Devedor"]:
df[col] = df[col].map('{:,.2f}'.format)
return df
# --- DEMONSTRAÇÃO DE USO ---
if __name__ == "__main__":
schedule_recebido = [
{"due_date": "2025-11-10", "fixed_amount": 5867.00},
{"due_date": "2025-12-10", "fixed_amount": 5867.00},
{"due_date": "2026-01-10", "fixed_amount": 5867.00},
{"due_date": "2026-02-10", "fixed_amount": 5867.00},
{"due_date": "2026-03-10", "fixed_amount": 5867.00},
]
taxa_do_contrato = 0.12 # 12% a.m.
data_desembolso = '2025-09-30'
# Gerando a tabela de amortização completa
tabela_amortizacao = generate_amortization_schedule(
schedule_recebido,
taxa_do_contrato,
data_desembolso
)
print(tabela_amortizacao.to_string(index=False))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment