101 lines
3.3 KiB
Python
101 lines
3.3 KiB
Python
from __future__ import annotations
|
|
|
|
from datetime import date
|
|
from enum import Enum
|
|
from typing import Literal
|
|
from pydantic import BaseModel, Field
|
|
|
|
|
|
class InstallmentType(str, Enum):
|
|
equal = "equal"
|
|
decreasing = "decreasing"
|
|
|
|
|
|
class OverpaymentEffect(str, Enum):
|
|
shorten = "shorten"
|
|
lower_payment = "lower_payment"
|
|
|
|
|
|
class GraceType(str, Enum):
|
|
none = "none"
|
|
interest_only = "interest_only"
|
|
full = "full"
|
|
|
|
|
|
class RateChange(BaseModel):
|
|
month: int = Field(ge=1, description="Miesiac od startu kredytu")
|
|
annual_rate: float = Field(ge=0, le=30, description="Roczne oprocentowanie procentowo")
|
|
|
|
|
|
class Overpayment(BaseModel):
|
|
month: int = Field(ge=1)
|
|
amount: float = Field(gt=0)
|
|
repeat: Literal["once", "monthly", "yearly"] = "once"
|
|
until_month: int | None = Field(default=None, ge=1)
|
|
commission_percent: float = Field(default=0, ge=0, le=20, description="Prowizja od nadplaty w procentach")
|
|
commission_until_month: int | None = Field(default=None, ge=1, description="Ostatni miesiac naliczania prowizji od nadplaty")
|
|
|
|
|
|
class HistoricalMonth(BaseModel):
|
|
month: int = Field(ge=1)
|
|
annual_rate: float | None = Field(default=None, ge=0, le=30)
|
|
grace_type: GraceType = GraceType.none
|
|
overpayment: float = Field(default=0, ge=0)
|
|
overpayment_commission_percent: float = Field(default=0, ge=0, le=20)
|
|
note: str = ""
|
|
|
|
|
|
class SimulationRequest(BaseModel):
|
|
principal: float = Field(gt=0)
|
|
years: int = Field(default=25, ge=1, le=50)
|
|
term_months: int | None = Field(default=None, ge=1, le=600, description="Okres kredytu w miesiacach")
|
|
margin: float = Field(ge=0, le=20, default=2.0)
|
|
base_rate: float = Field(ge=0, le=30, default=5.75)
|
|
installment_type: InstallmentType = InstallmentType.equal
|
|
overpayment_effect: OverpaymentEffect = OverpaymentEffect.shorten
|
|
loan_start_date: date = Field(default_factory=date.today)
|
|
due_day: int = Field(default=5, ge=1, le=28, description="Dzien splaty raty")
|
|
move_due_date_to_business_day: bool = True
|
|
overpayment_protection_months: int | None = Field(default=None, ge=1, description="Okres ochronny prowizji od nadplat w miesiacach")
|
|
overpayment_protection_commission_percent: float = Field(default=0, ge=0, le=20, description="Domyslna prowizja od nadplat w okresie ochronnym")
|
|
rate_changes: list[RateChange] = Field(default_factory=list)
|
|
overpayments: list[Overpayment] = Field(default_factory=list)
|
|
historical_months: list[HistoricalMonth] = Field(default_factory=list)
|
|
|
|
|
|
class ScheduleRow(BaseModel):
|
|
month: int
|
|
due_date: str
|
|
days: int
|
|
rate: float
|
|
payment: float
|
|
principal_part: float
|
|
interest_part: float
|
|
overpayment: float
|
|
overpayment_fee: float
|
|
remaining: float
|
|
grace_type: GraceType = GraceType.none
|
|
cumulative_interest: float
|
|
cumulative_cost: float
|
|
cumulative_overpayment: float
|
|
|
|
|
|
class Summary(BaseModel):
|
|
months: int
|
|
total_paid: float
|
|
total_interest: float
|
|
total_overpayment: float
|
|
total_overpayment_fees: float
|
|
interest_saved: float
|
|
months_saved: int
|
|
baseline_interest: float
|
|
baseline_months: int
|
|
average_payment: float
|
|
max_payment: float
|
|
payoff_date: str | None = None
|
|
|
|
|
|
class SimulationResponse(BaseModel):
|
|
schedule: list[ScheduleRow]
|
|
summary: Summary
|