Functional Weave
Code in Python

property.mortgage-affordability@1.0.1

impl/python.py

2,344 bytes · the Python implementation · view raw

Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.

from .lending_affordability import affordability  ← from lending.affordability ^1.0.0 · built alongside by fune
from .math_round_div import round_div  ← from math.round-div ^1.0.0 · built alongside by fune
from .money_amount import Money, money  ← from money.amount ^1.0.0 · built alongside by fune
from .property_mortgage_affordability_types import MortgageAffordability

_MAX_INCOME = 10_000_000_000
_MAX_MULTIPLE = 200_000


def mortgage_affordability(
    annual_income: Money,
    monthly_commitments: Money,
    loan: Money,
    annual_rate_basis_points: int,
    stress_rate_basis_points: int,
    term_months: int,
    max_income_multiple_basis_points: int,
    max_debt_to_income_basis_points: int,
) -> MortgageAffordability:
    """Loan-to-income against the lender's multiple, and the stressed
    repayment against income via lending.affordability. Every rounding goes
    against the borrower, and the multiple comparison itself is exact."""
    if loan.currency != annual_income.currency:
        raise ValueError("currency mismatch: %s and %s" % (annual_income.currency, loan.currency))
    if annual_income.minor < 12 or annual_income.minor > _MAX_INCOME:
        raise ValueError(
            "annualIncome must be between 12 and %d minor units, received %r" % (_MAX_INCOME, annual_income.minor)
        )
    if loan.minor <= 0 or loan.minor > _MAX_INCOME * 20:
        raise ValueError("loan must be between 1 and %d minor units, received %r" % (_MAX_INCOME * 20, loan.minor))
    m = max_income_multiple_basis_points
    if isinstance(m, bool) or not isinstance(m, int) or m < 1 or m > _MAX_MULTIPLE:
        raise ValueError("maxIncomeMultipleBasisPoints must be between 1 and %d, received %r" % (_MAX_MULTIPLE, m))
    currency = annual_income.currency
    monthly_income = money(annual_income.minor // 12, currency)
    repayment = affordability(
        monthly_income,
        monthly_commitments,
        loan,
        annual_rate_basis_points,
        stress_rate_basis_points,
        term_months,
        max_debt_to_income_basis_points,
    )
    within = loan.minor * 10000 <= annual_income.minor * m
    return MortgageAffordability(
        income_multiple_basis_points=round_div(loan.minor * 10000, annual_income.minor, "up"),
        max_loan_by_multiple=money(round_div(annual_income.minor * m, 10000, "down"), currency),
        within_multiple=within,
        monthly_income=monthly_income,
        repayment=repayment,
        affordable=within and repayment.affordable,
    )