API Reference¶
MoneyWarp provides a comprehensive set of classes for financial calculations and time-based analysis.
Core Classes¶
Money¶
money_warp.Money
¶
Represents a monetary amount with high internal precision.
Maintains full precision internally for calculations but provides 'real money' representation rounded to 2 decimal places for display and comparisons.
Attributes¶
raw_amount
property
¶
Get the high-precision internal amount.
real_amount
property
¶
Get the 'real money' amount rounded to 2 decimal places.
cents
property
¶
Get real amount in cents.
Functions¶
__init__(amount)
¶
Create a Money object with high internal precision.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
amount
|
Union[Decimal, str, int, float]
|
The monetary amount (will be converted to Decimal) |
required |
zero()
classmethod
¶
Create zero money.
from_cents(cents)
classmethod
¶
Create from cents to avoid decimal issues.
__add__(other)
¶
Add two Money objects.
__sub__(other)
¶
Subtract two Money objects.
__mul__(factor)
¶
Multiply by a number - keeps high precision.
__truediv__(divisor)
¶
Divide by a number - keeps high precision.
__radd__(other)
¶
Support numeric + Money (e.g. Decimal + Money).
__rsub__(other)
¶
Support numeric - Money (e.g. Decimal - Money).
__rmul__(factor)
¶
Support numeric * Money (e.g. float * Money).
__neg__()
¶
Negative money.
__abs__()
¶
Absolute value.
__float__()
¶
Float representation using full internal precision.
__eq__(other)
¶
Compare at 'real money' precision. Accepts Money or Decimal.
__lt__(other)
¶
Less than comparison at real money precision. Accepts Money or Decimal.
__le__(other)
¶
Less than or equal comparison at real money precision. Accepts Money or Decimal.
__gt__(other)
¶
Greater than comparison at real money precision. Accepts Money or Decimal.
__ge__(other)
¶
Greater than or equal comparison at real money precision. Accepts Money or Decimal.
to_real_money()
¶
Convert to real money (rounded to 2 decimal places).
is_positive()
¶
Check if amount is positive.
is_negative()
¶
Check if amount is negative.
is_zero()
¶
Check if amount is zero.
__str__()
¶
Display as real money (2 decimal places).
__repr__()
¶
Developer representation showing internal precision.
debug_precision()
¶
Show both internal and real amounts for debugging.
InterestRate¶
money_warp.InterestRate
¶
Bases: Rate
Represents a non-negative contractual interest rate.
Inherits all conversion and comparison behaviour from Rate, but rejects negative values at construction time. Use this type for loan terms, discount rates, and other contractual parameters.
For computed metrics that may be negative (IRR, MIRR), use Rate.
Functions¶
__init__(rate, period=None, as_percentage=False, precision=None, rounding=ROUND_HALF_UP, str_style='long', year_size=YearSize.commercial, str_decimals=3, abbrev_labels=None)
¶
Create a non-negative interest rate.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
rate
|
Union[str, Decimal, float]
|
Rate as string ("5.25% a", "0.004167 m") or numeric value. Abbreviated formats ("5.25% a.a.", "0.5% a.m.") are also accepted and automatically set str_style to "abbrev". Negative values are rejected. |
required |
period
|
Optional[CompoundingFrequency]
|
Compounding frequency (required if rate is numeric) |
None
|
as_percentage
|
bool
|
If True and rate is numeric, treat as percentage |
False
|
precision
|
Optional[int]
|
Number of decimal places for the effective annual rate during conversions. None keeps full precision. |
None
|
rounding
|
str
|
Rounding mode from the decimal module (e.g. ROUND_HALF_UP, ROUND_DOWN). Only used when precision is set. |
ROUND_HALF_UP
|
str_style
|
str
|
Controls period notation in str. "long" outputs the full name (e.g. "annually"), "abbrev" outputs the abbreviated form (e.g. "a.a."). |
'long'
|
year_size
|
YearSize
|
Day-count convention for daily conversions. YearSize.commercial (365) or YearSize.banker (360). |
commercial
|
str_decimals
|
int
|
Number of decimal places for the percentage in str. Default 3 gives "5.250%". |
3
|
abbrev_labels
|
Optional[Dict[CompoundingFrequency, str]]
|
Partial or full override of the default abbreviation map. Merged with _ABBREV_MAP so you only need to pass the keys you want to change. |
None
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If the rate is negative. |
accrue(principal, days)
¶
Compute compound interest accrued on a principal over a number of days.
Formula: principal * ((1 + daily_rate) ** days - 1)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
principal
|
Money
|
The principal amount. |
required |
days
|
int
|
Number of days to accrue interest over. |
required |
Returns:
| Type | Description |
|---|---|
Money
|
The accrued interest (not including the principal). |
Percentage¶
money_warp.Percentage
¶
A non-negative percentage applied flat over a value.
Construction accepts only strings in the format "<number>%".
Numeric inputs and strings without % are rejected to eliminate the
classic 5 vs 0.05 ambiguity.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
rate
|
str
|
Percentage as a string in the format |
required |
precision
|
Optional[int]
|
Default number of decimal places used by
:meth: |
None
|
rounding
|
str
|
Rounding mode from the :mod: |
ROUND_HALF_UP
|
str_decimals
|
int
|
Number of decimal places for the percentage in
:meth: |
2
|
Raises:
| Type | Description |
|---|---|
TypeError
|
If rate is not a string. |
ValueError
|
If the string is not a valid percentage format,
contains a temporal suffix (use :class: |
Examples:
>>> p = Percentage("5%")
>>> p.as_decimal()
Decimal('0.05')
>>> p.as_percentage()
Decimal('5')
>>> str(p)
'5.00%'
Functions¶
as_decimal(precision=None)
¶
Return the percentage as a decimal (0.05 for 5%).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
precision
|
Optional[int]
|
Number of decimal places. |
None
|
as_percentage(precision=None)
¶
Return the percentage as a percentage value (5 for 5%).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
precision
|
Optional[int]
|
Number of decimal places. |
None
|
__str__()
¶
Canonical string representation: "5.00%".
Round-trips through Percentage(str(p)).
CashFlow¶
money_warp.CashFlow
¶
Container for a collection of cash flow items representing a financial stream.
Internally stores :class:CashFlowItem (temporal containers).
Public iteration and query methods resolve each item and filter out
deleted entries, so consumers see only active :class:CashFlowEntry
objects.
Attributes¶
query
property
¶
Create a query builder over active entries.
Functions¶
empty()
classmethod
¶
Create an empty cash flow.
add_item(item)
¶
Add a cash flow item to this stream.
add(amount, datetime, description=None, category=None, *, kind=CashFlowType.HAPPENED)
¶
Add a cash flow item by specifying its components.
items()
¶
Active entries at the current time (resolves and filters).
sorted_items()
¶
Active entries sorted by datetime.
raw_items()
¶
Underlying temporal containers (use for update/delete).
net_present_value()
¶
Simple sum of all active cash flows (no discounting).
total_inflows()
¶
Sum of all positive cash flows.
total_outflows()
¶
Sum of all negative cash flows (returned as positive amount).
filter_by_category(category)
¶
New CashFlow containing only items with the specified category.
filter_by_kind(kind)
¶
New CashFlow containing only items with the specified kind.
filter_by_datetime_range(start_datetime, end_datetime)
¶
New CashFlow containing only items within the datetime range.
CashFlowItem¶
money_warp.CashFlowItem
¶
Temporal container that wraps a timeline of :class:CashFlowEntry snapshots.
At any point in time exactly one entry (or None, meaning deleted)
is active. :meth:resolve returns that entry for
self.now().
The constructor accepts the same positional/keyword arguments as the
old CashFlowItem so that existing call-sites continue to work
without changes during the migration.
Loan¶
money_warp.Loan
¶
Bases: BaseLoan
Represents a personal loan where everything emerges from the CashFlow.
The CashFlow is the single source of truth. Expected items (the amortization schedule) and actual payments both live in one CashFlow. Settlements, installment views, balances, and fines are all derived on demand -- nothing is decomposed or stored at payment time.
Payment allocation priority: Fine -> Mora Interest -> Interest -> Principal. Installment 1 is fully addressed before installment 2.
Examples:
>>> from money_warp import Loan, Money, InterestRate
>>> from datetime import date, datetime
>>>
>>> loan = Loan(
... Money("10000"),
... InterestRate("5% annual"),
... [date(2024, 2, 1), date(2024, 3, 1)]
... )
>>>
>>> loan.record_payment(Money("500"), datetime(2024, 2, 1))
>>> print(f"Balance: {loan.current_balance}")
Attributes¶
tax_amounts
property
¶
Per-tax results keyed by tax class name. Computed lazily.
total_tax
property
¶
Sum of all taxes applied to this loan.
net_disbursement
property
¶
Amount the borrower actually receives (principal minus total tax).
Functions¶
record_payment(amount, payment_date, interest_date=None, processing_date=None, description=None, waive_fines=False, waive_mora=False, waive_overdue_interest=False, discount=None)
¶
Record a payment. Just one CashFlowItem -- everything else is derived.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
amount
|
Money
|
Total payment amount (positive value). |
required |
payment_date
|
datetime
|
When the money moved. |
required |
interest_date
|
Optional[datetime]
|
Cutoff date for interest accrual calculation. Defaults to payment_date. |
None
|
processing_date
|
Optional[datetime]
|
Unused, kept for API compatibility. |
None
|
description
|
Optional[str]
|
Optional description of the payment. |
None
|
waive_fines
|
bool
|
If True, all accumulated fines up to this payment are forgiven. Future fines can still accrue. |
False
|
waive_mora
|
bool
|
If True, all accrued mora interest up to this payment is forgiven. Future mora can still accrue. |
False
|
waive_overdue_interest
|
bool
|
If True, regular interest accrued between the due date and the payment date is forgiven. |
False
|
discount
|
Optional[Money]
|
Flat amount to forgive from obligations before allocating the payment. Follows the same priority as payment allocation (fines -> mora -> interest -> principal). |
None
|
Returns:
| Type | Description |
|---|---|
Settlement
|
Settlement describing how the payment was allocated. |
anticipate_payment(amount, installments=None, description=None, waive_fines=False, waive_mora=False, waive_overdue_interest=False, discount=None)
¶
Make an early payment with interest discount.
Interest is calculated only up to self.now(), so the borrower pays less interest for fewer elapsed days.
When installments is provided (1-based), the corresponding expected cash-flow items are temporally deleted.
calculate_anticipation(installments)
¶
Calculate the amount to pay today to eliminate specific installments.
get_expected_payment_amount(due_date)
¶
Get the expected payment amount for a specific due date.
generate_expected_cash_flow()
¶
Expected cash flow (schedule items only, no payments).
present_value(discount_rate=None, valuation_date=None)
¶
Present Value of the loan's expected cash flows.
irr(guess=None)
¶
Internal Rate of Return of the loan's expected cash flows.
Time Machine¶
Warp¶
money_warp.Warp
¶
Time Machine context manager for financial projections and analysis.
Allows you to temporarily "warp" any financial instrument (Loan,
CreditCard, or any object with a _time_ctx attribute) to a
specific date to analyze its state at that point in time.
Usage
instrument = Loan(...) # or CreditCard(...) with Warp(instrument, '2030-01-15') as warped: balance = warped.current_balance
Warping different objects concurrently is allowed, but nested warps on the same object are not.
The target object must expose _time_ctx (a :class:TimeContext).
If it also has an _on_warp(target_date) method, that method is
called after overriding the time context on the clone.
Attributes¶
original_loan
property
¶
Backward-compatible alias for the original target.
warped_loan
property
writable
¶
Backward-compatible alias for the warped clone.
Functions¶
__init__(target, target_date)
¶
Initialize the Warp context manager.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
target
|
Any
|
The financial instrument to warp (Loan, CreditCard, etc.).
Must have a |
required |
target_date
|
Union[str, date, datetime]
|
The date to warp to (accepts strings, date, or datetime objects) |
required |
Raises:
| Type | Description |
|---|---|
TypeError
|
If target has no |
NestedWarpError
|
If the same object is already being warped |
InvalidDateError
|
If the target_date cannot be parsed |
__enter__()
¶
Enter the Warp context and return a time-warped clone.
Returns:
| Type | Description |
|---|---|
Any
|
A deep-cloned object with its TimeContext overridden to the |
Any
|
target date. |
__exit__(exc_type, exc_val, exc_tb)
¶
Exit the Warp context and clean up.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
exc_type
|
Optional[Type[BaseException]]
|
Exception type (if any) |
required |
exc_val
|
Optional[BaseException]
|
Exception value (if any) |
required |
exc_tb
|
Optional[object]
|
Exception traceback (if any) |
required |
__str__()
¶
String representation of the Warp.
__repr__()
¶
Detailed representation for debugging.
WarpedTime¶
money_warp.warp.WarpedTime
¶
Exceptions¶
WarpError¶
money_warp.WarpError
¶
Bases: Exception
Base exception for Warp-related errors.
NestedWarpError¶
money_warp.NestedWarpError
¶
Bases: WarpError
Raised when attempting to create nested Warp contexts.
InvalidDateError¶
Schedulers¶
PriceScheduler¶
money_warp.PriceScheduler
¶
Bases: BaseScheduler
Price scheduler implementing Progressive Price Schedule (French amortization system).
This scheduler calculates a fixed payment amount (PMT) and then allocates each payment between interest and principal. Interest is calculated on the outstanding balance, and the remainder goes to principal reduction.
Based on the reference implementation from cartaorobbin/loan-calculator.
Functions¶
__init__(principal=None, daily_interest_rate=None, return_days=None, disbursement_date=None)
¶
Initialize the scheduler with loan parameters.
generate_schedule(principal, interest_rate, due_dates, disbursement_date, tz)
classmethod
¶
Generate Progressive Price Schedule with fixed payment amounts.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
principal
|
Money
|
The loan amount |
required |
interest_rate
|
InterestRate
|
The annual interest rate |
required |
due_dates
|
List[date]
|
List of payment due dates |
required |
disbursement_date
|
datetime
|
When the loan was disbursed |
required |
tz
|
tzinfo
|
Business timezone for extracting calendar dates from datetimes |
required |
Returns:
| Type | Description |
|---|---|
PaymentSchedule
|
PaymentSchedule with fixed payment amounts and interest/principal allocation |
calculate_constant_return_pmt()
¶
Calculate PMT using the reference formula from loan-calculator.
PMT = principal / sum(1 / (1 + daily_rate)^n for n in return_days)
Returns:
| Type | Description |
|---|---|
Decimal
|
The fixed payment amount (PMT) |
InvertedPriceScheduler¶
money_warp.InvertedPriceScheduler
¶
Bases: BaseScheduler
Inverted Price scheduler implementing Constant Amortization System (SAC).
This scheduler calculates a fixed principal payment amount for each period and adds variable interest calculated on the outstanding balance. This results in decreasing total payments over time.
Key characteristics: - Fixed principal payment each period - Variable interest payment (decreases over time) - Variable total payment (decreases over time) - Faster debt reduction compared to Price scheduler
Functions¶
generate_schedule(principal, interest_rate, due_dates, disbursement_date, tz)
classmethod
¶
Generate Constant Amortization Schedule with fixed principal payments.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
principal
|
Money
|
The loan amount |
required |
interest_rate
|
InterestRate
|
The annual interest rate |
required |
due_dates
|
List[date]
|
List of payment due dates |
required |
disbursement_date
|
datetime
|
When the loan was disbursed |
required |
tz
|
tzinfo
|
Business timezone for extracting calendar dates from datetimes |
required |
Returns:
| Type | Description |
|---|---|
PaymentSchedule
|
PaymentSchedule with fixed principal amounts and variable total payments |
BaseScheduler¶
money_warp.BaseScheduler
¶
Bases: ABC
Abstract base class for all payment schedulers.
All schedulers should inherit from this and implement the generate_schedule class method.
Functions¶
generate_schedule(principal, interest_rate, due_dates, disbursement_date, tz)
abstractmethod
classmethod
¶
Generate the payment schedule.
This is the only public method that schedulers need to implement.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
principal
|
Money
|
The loan amount |
required |
interest_rate
|
InterestRate
|
The annual interest rate |
required |
due_dates
|
List[date]
|
List of payment due dates |
required |
disbursement_date
|
datetime
|
When the loan was disbursed |
required |
tz
|
tzinfo
|
Business timezone for extracting calendar dates from datetimes |
required |
Returns:
| Type | Description |
|---|---|
PaymentSchedule
|
PaymentSchedule with all payment details |
Installment & Settlement¶
Installment¶
money_warp.Installment
dataclass
¶
A single installment in a loan repayment plan.
Represents the borrower's obligation for one period: what is expected, what has actually been paid, what has been covered by a discount, and the detailed per-payment allocations.
Installments are derived views -- the Loan builds them on demand from the CashFlow and the schedule.
balance_tolerance controls the sub-cent threshold under which a
positive residual is treated as zero for balance and
is_fully_paid. Defaults to R$0.01 (matching
DEFAULT_BALANCE_TOLERANCE); :class:Loan and
:class:BillingCycleLoan propagate their own configurable value
through every installment snapshot they build.
The *_discounted fields aggregate the discount portion each
allocation absorbed for that component. They default to zero so
discount-free payments produce identical installments to before.
Attributes¶
total_discounted
property
¶
Sum of discount portions absorbed by this installment.
balance
property
¶
The amount still owed to fully settle this installment.
Positive residuals within balance_tolerance collapse to zero
so is_fully_paid agrees with Allocation.is_fully_covered
on rounding artifacts that the tolerance-adjustment mechanism
absorbs. The discount portions are treated as covered, mirroring
what Settlement.discount_applied reports at the loan level.
is_fully_paid
property
¶
Whether this installment has been fully settled.
Uses balance, which absorbs sub-cent residuals within
:attr:balance_tolerance. A R\(0.005 leftover therefore counts
as fully paid; a R\)0.02 leftover does not (at the default
tolerance).
Functions¶
from_schedule_entry(entry, allocations, expected_mora, expected_fine, balance_tolerance=DEFAULT_BALANCE_TOLERANCE)
classmethod
¶
Build an Installment from a scheduler's PaymentScheduleEntry.
balance_tolerance is forwarded to the constructed Installment
so balance and is_fully_paid honor the loan-level
setting. The *_discounted fields are derived from the
allocations so callers never need to compute them.
Settlement¶
money_warp.Settlement
dataclass
¶
Result of applying a payment to a loan.
Captures the full allocation of a single payment across fines, interest, mora interest, and principal, along with per-installment detail showing which installments were covered.
Allocation¶
money_warp.Allocation
dataclass
¶
Breakdown of a payment's allocation to a single installment.
Each allocation shows how much principal, interest, mora, and fine
from a payment were attributed to a specific installment, plus how
much of the payment's discount covered each component. The
discount fields default to zero so payments without a discount keep
their original shape.
Attributes¶
total_allocated
property
¶
Sum of all components allocated to this installment.
total_discounted
property
¶
Sum of discount portions absorbed by this installment.
Mirrors :attr:total_allocated for the payment's discount
contribution. A payment without a discount has every component
at zero.
Data Classes¶
PaymentSchedule¶
money_warp.PaymentSchedule
dataclass
¶
Complete payment schedule for a loan.
Contains all payment entries and summary information.
PaymentScheduleEntry¶
money_warp.PaymentScheduleEntry
dataclass
¶
Query Interface¶
CashFlowQuery¶
money_warp.CashFlowQuery
¶
SQLAlchemy-style query builder for filtering cash flow entries.
Works with both :class:CashFlowEntry and :class:CashFlowItem
objects — both expose the same attribute interface (amount,
datetime, description, category).
Attributes¶
expected
property
¶
Shortcut for filter_by(kind=CashFlowType.EXPECTED).
happened
property
¶
Shortcut for filter_by(kind=CashFlowType.HAPPENED).
Functions¶
filter_by(predicate=None, **kwargs)
¶
Filter items using keyword arguments or a predicate function.
Keyword arguments: - kind: Filter by CashFlowType (EXPECTED or HAPPENED) - category: Filter by category - amount / amount__gt / amount__gte / amount__lt / amount__lte - datetime / datetime__gt / datetime__gte / datetime__lt / datetime__lte - description: Filter by description - is_inflow: Filter to only inflows (True) or outflows (False)
order_by(*fields)
¶
Order items by one or more fields.
Prefix with '-' for descending: '-datetime', '-amount'.
to_cash_flow()
¶
Convert query result back to a CashFlow.
If the query holds :class:CashFlowEntry objects they are
wrapped in fresh :class:CashFlowItem containers.
Enums¶
CompoundingFrequency¶
money_warp.CompoundingFrequency
¶
Bases: Enum
Frequency of compounding per year.
YearSize¶
money_warp.YearSize
¶
Bases: Enum
Number of days that constitute one year for rate conversions.
Present Value Functions¶
present_value¶
money_warp.present_value
¶
Present Value and IRR calculations for cash flows and financial instruments.
Classes¶
Functions¶
present_value(cash_flow, discount_rate, valuation_date=None)
¶
Calculate the Present Value (PV) of a cash flow stream.
The Present Value is the sum of all future cash flows discounted back to the valuation date using the specified discount rate.
Formula: PV = Σ(CF_t / (1 + r)^t) where: - CF_t = Cash flow at time t - r = Discount rate per period - t = Time periods from valuation date
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
cash_flow
|
CashFlow
|
The cash flow stream to evaluate |
required |
discount_rate
|
Rate
|
The discount rate to use for discounting |
required |
valuation_date
|
Optional[datetime]
|
Date to discount back to (defaults to earliest cash flow date) |
None
|
Returns:
| Type | Description |
|---|---|
Money
|
The present value of the cash flow stream |
Examples:
>>> from datetime import datetime
>>> from money_warp import CashFlow, CashFlowItem, Money, InterestRate, present_value
>>>
>>> # Create a simple cash flow
>>> items = [
... CashFlowItem(Money("1000"), datetime(2024, 1, 1), "Initial investment", "investment"),
... CashFlowItem(Money("-1100"), datetime(2024, 12, 31), "Return", "return"),
... ]
>>> cf = CashFlow(items)
>>>
>>> # Calculate PV with 5% discount rate
>>> pv = present_value(cf, InterestRate("5% annual"))
>>> print(f"Present Value: {pv}") # Should be close to zero for 10% return vs 5% discount
present_value_of_annuity(payment_amount, interest_rate, periods, payment_timing='end')
¶
Calculate the Present Value of an ordinary annuity or annuity due.
An annuity is a series of equal payments made at regular intervals.
Formula for ordinary annuity (payments at end of period): PV = PMT x [(1 - (1 + r)^(-n)) / r]
Formula for annuity due (payments at beginning of period): PV = PMT x [(1 - (1 + r)^(-n)) / r] x (1 + r)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
payment_amount
|
Money
|
The amount of each payment |
required |
interest_rate
|
InterestRate
|
The interest rate per period |
required |
periods
|
int
|
The number of payment periods |
required |
payment_timing
|
str
|
"end" for ordinary annuity, "begin" for annuity due |
'end'
|
Returns:
| Type | Description |
|---|---|
Money
|
The present value of the annuity |
Examples:
present_value_of_perpetuity(payment_amount, interest_rate)
¶
Calculate the Present Value of a perpetuity.
A perpetuity is a series of equal payments that continue forever.
Formula: PV = PMT / r
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
payment_amount
|
Money
|
The amount of each payment |
required |
interest_rate
|
InterestRate
|
The interest rate per period |
required |
Returns:
| Type | Description |
|---|---|
Money
|
The present value of the perpetuity |
Raises:
| Type | Description |
|---|---|
ValueError
|
If interest rate is zero or negative |
Examples:
discount_factor(interest_rate, periods)
¶
Calculate the discount factor for a given interest rate and time periods.
Formula: DF = 1 / (1 + r)^n
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
interest_rate
|
Rate
|
The interest rate per period |
required |
periods
|
Union[int, Decimal]
|
The number of periods (can be fractional) |
required |
Returns:
| Type | Description |
|---|---|
Decimal
|
The discount factor as a Decimal |
Examples:
internal_rate_of_return(cash_flow, guess=None, year_size=YearSize.commercial)
¶
Calculate the Internal Rate of Return (IRR) of a cash flow stream.
The IRR is the discount rate that makes the Net Present Value (NPV) equal to zero. It represents the effective annual rate of return of the investment.
Uses scipy.optimize.fsolve for robust numerical solution.
Note: To calculate IRR from a specific date, use the Time Machine: with Warp(loan, target_date) as warped_loan: irr = warped_loan.irr()
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
cash_flow
|
CashFlow
|
The cash flow stream to analyze |
required |
guess
|
Optional[Rate]
|
Initial guess for IRR (defaults to 10% annual) |
None
|
year_size
|
YearSize
|
Day-count convention (YearSize.commercial for 365 days, YearSize.banker for 360 days) |
commercial
|
Returns:
| Type | Description |
|---|---|
Rate
|
The internal rate of return as a Rate (may be negative) |
Raises:
| Type | Description |
|---|---|
ValueError
|
If IRR cannot be found (no solution or doesn't converge) |
Examples:
>>> from datetime import datetime
>>> from money_warp import CashFlow, CashFlowItem, Money, internal_rate_of_return
>>>
>>> # Simple investment: -$1000 now, +$1100 in 1 year
>>> items = [
... CashFlowItem(Money("-1000"), datetime(2024, 1, 1), "Investment", "investment"),
... CashFlowItem(Money("1100"), datetime(2024, 12, 31), "Return", "return"),
... ]
>>> cf = CashFlow(items)
>>>
>>> irr = internal_rate_of_return(cf)
>>> print(f"IRR: {irr}") # Should be approximately 10%
irr(cash_flow, guess=None, year_size=YearSize.commercial)
¶
Calculate the Internal Rate of Return (IRR) of a cash flow stream.
This is a convenience function that calls internal_rate_of_return() with default parameters for most common use cases.
Note: To calculate IRR from a specific date, use the Time Machine: with Warp(loan, target_date) as warped_loan: irr = warped_loan.irr()
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
cash_flow
|
CashFlow
|
The cash flow stream to analyze |
required |
guess
|
Optional[Rate]
|
Initial guess for IRR (defaults to 10% annual) |
None
|
year_size
|
YearSize
|
Day-count convention (YearSize.commercial for 365 days, YearSize.banker for 360 days) |
commercial
|
Returns:
| Type | Description |
|---|---|
Rate
|
The internal rate of return as a Rate (may be negative) |
Examples:
>>> from datetime import datetime
>>> from money_warp import CashFlow, CashFlowItem, Money, irr
>>>
>>> # Investment analysis
>>> items = [
... CashFlowItem(Money("-5000"), datetime(2024, 1, 1), "Initial investment", "investment"),
... CashFlowItem(Money("1500"), datetime(2024, 6, 1), "Return 1", "return"),
... CashFlowItem(Money("2000"), datetime(2024, 12, 1), "Return 2", "return"),
... CashFlowItem(Money("2500"), datetime(2025, 6, 1), "Final return", "return"),
... ]
>>> cf = CashFlow(items)
>>>
>>> investment_irr = irr(cf)
>>> print(f"Investment IRR: {investment_irr}")
modified_internal_rate_of_return(cash_flow, finance_rate, reinvestment_rate, year_size=YearSize.commercial)
¶
Calculate the Modified Internal Rate of Return (MIRR).
MIRR addresses some limitations of IRR by using different rates for financing negative cash flows and reinvesting positive cash flows.
Formula: MIRR = (FV of positive flows / PV of negative flows)^(1/n) - 1
Note: To calculate MIRR from a specific date, use the Time Machine: with Warp(loan, target_date) as warped_loan: mirr = modified_internal_rate_of_return(warped_loan.generate_expected_cash_flow(), ...)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
cash_flow
|
CashFlow
|
The cash flow stream to analyze |
required |
finance_rate
|
InterestRate
|
Rate for financing negative cash flows |
required |
reinvestment_rate
|
InterestRate
|
Rate for reinvesting positive cash flows |
required |
year_size
|
YearSize
|
Day-count convention (YearSize.commercial for 365 days, YearSize.banker for 360 days) |
commercial
|
Returns:
| Type | Description |
|---|---|
Rate
|
The modified internal rate of return as a Rate (may be negative) |
Examples:
>>> from datetime import datetime
>>> from money_warp import CashFlow, CashFlowItem, Money, InterestRate, modified_internal_rate_of_return
>>>
>>> # Investment with different financing and reinvestment rates
>>> items = [
... CashFlowItem(Money("-1000"), datetime(2024, 1, 1), "Investment", "investment"),
... CashFlowItem(Money("300"), datetime(2024, 6, 1), "Return 1", "return"),
... CashFlowItem(Money("400"), datetime(2024, 12, 1), "Return 2", "return"),
... CashFlowItem(Money("500"), datetime(2025, 6, 1), "Return 3", "return"),
... ]
>>> cf = CashFlow(items)
>>>
>>> mirr = modified_internal_rate_of_return(
... cf,
... InterestRate("8% annual"), # Financing rate
... InterestRate("6% annual") # Reinvestment rate
... )
>>> print(f"MIRR: {mirr}")
present_value_of_annuity¶
money_warp.present_value_of_annuity(payment_amount, interest_rate, periods, payment_timing='end')
¶
Calculate the Present Value of an ordinary annuity or annuity due.
An annuity is a series of equal payments made at regular intervals.
Formula for ordinary annuity (payments at end of period): PV = PMT x [(1 - (1 + r)^(-n)) / r]
Formula for annuity due (payments at beginning of period): PV = PMT x [(1 - (1 + r)^(-n)) / r] x (1 + r)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
payment_amount
|
Money
|
The amount of each payment |
required |
interest_rate
|
InterestRate
|
The interest rate per period |
required |
periods
|
int
|
The number of payment periods |
required |
payment_timing
|
str
|
"end" for ordinary annuity, "begin" for annuity due |
'end'
|
Returns:
| Type | Description |
|---|---|
Money
|
The present value of the annuity |
Examples:
present_value_of_perpetuity¶
money_warp.present_value_of_perpetuity(payment_amount, interest_rate)
¶
Calculate the Present Value of a perpetuity.
A perpetuity is a series of equal payments that continue forever.
Formula: PV = PMT / r
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
payment_amount
|
Money
|
The amount of each payment |
required |
interest_rate
|
InterestRate
|
The interest rate per period |
required |
Returns:
| Type | Description |
|---|---|
Money
|
The present value of the perpetuity |
Raises:
| Type | Description |
|---|---|
ValueError
|
If interest rate is zero or negative |
Examples:
discount_factor¶
money_warp.discount_factor(interest_rate, periods)
¶
Calculate the discount factor for a given interest rate and time periods.
Formula: DF = 1 / (1 + r)^n
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
interest_rate
|
Rate
|
The interest rate per period |
required |
periods
|
Union[int, Decimal]
|
The number of periods (can be fractional) |
required |
Returns:
| Type | Description |
|---|---|
Decimal
|
The discount factor as a Decimal |
Examples:
IRR Functions¶
internal_rate_of_return¶
money_warp.internal_rate_of_return(cash_flow, guess=None, year_size=YearSize.commercial)
¶
Calculate the Internal Rate of Return (IRR) of a cash flow stream.
The IRR is the discount rate that makes the Net Present Value (NPV) equal to zero. It represents the effective annual rate of return of the investment.
Uses scipy.optimize.fsolve for robust numerical solution.
Note: To calculate IRR from a specific date, use the Time Machine: with Warp(loan, target_date) as warped_loan: irr = warped_loan.irr()
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
cash_flow
|
CashFlow
|
The cash flow stream to analyze |
required |
guess
|
Optional[Rate]
|
Initial guess for IRR (defaults to 10% annual) |
None
|
year_size
|
YearSize
|
Day-count convention (YearSize.commercial for 365 days, YearSize.banker for 360 days) |
commercial
|
Returns:
| Type | Description |
|---|---|
Rate
|
The internal rate of return as a Rate (may be negative) |
Raises:
| Type | Description |
|---|---|
ValueError
|
If IRR cannot be found (no solution or doesn't converge) |
Examples:
>>> from datetime import datetime
>>> from money_warp import CashFlow, CashFlowItem, Money, internal_rate_of_return
>>>
>>> # Simple investment: -$1000 now, +$1100 in 1 year
>>> items = [
... CashFlowItem(Money("-1000"), datetime(2024, 1, 1), "Investment", "investment"),
... CashFlowItem(Money("1100"), datetime(2024, 12, 31), "Return", "return"),
... ]
>>> cf = CashFlow(items)
>>>
>>> irr = internal_rate_of_return(cf)
>>> print(f"IRR: {irr}") # Should be approximately 10%
irr¶
money_warp.irr(cash_flow, guess=None, year_size=YearSize.commercial)
¶
Calculate the Internal Rate of Return (IRR) of a cash flow stream.
This is a convenience function that calls internal_rate_of_return() with default parameters for most common use cases.
Note: To calculate IRR from a specific date, use the Time Machine: with Warp(loan, target_date) as warped_loan: irr = warped_loan.irr()
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
cash_flow
|
CashFlow
|
The cash flow stream to analyze |
required |
guess
|
Optional[Rate]
|
Initial guess for IRR (defaults to 10% annual) |
None
|
year_size
|
YearSize
|
Day-count convention (YearSize.commercial for 365 days, YearSize.banker for 360 days) |
commercial
|
Returns:
| Type | Description |
|---|---|
Rate
|
The internal rate of return as a Rate (may be negative) |
Examples:
>>> from datetime import datetime
>>> from money_warp import CashFlow, CashFlowItem, Money, irr
>>>
>>> # Investment analysis
>>> items = [
... CashFlowItem(Money("-5000"), datetime(2024, 1, 1), "Initial investment", "investment"),
... CashFlowItem(Money("1500"), datetime(2024, 6, 1), "Return 1", "return"),
... CashFlowItem(Money("2000"), datetime(2024, 12, 1), "Return 2", "return"),
... CashFlowItem(Money("2500"), datetime(2025, 6, 1), "Final return", "return"),
... ]
>>> cf = CashFlow(items)
>>>
>>> investment_irr = irr(cf)
>>> print(f"Investment IRR: {investment_irr}")
modified_internal_rate_of_return¶
money_warp.modified_internal_rate_of_return(cash_flow, finance_rate, reinvestment_rate, year_size=YearSize.commercial)
¶
Calculate the Modified Internal Rate of Return (MIRR).
MIRR addresses some limitations of IRR by using different rates for financing negative cash flows and reinvesting positive cash flows.
Formula: MIRR = (FV of positive flows / PV of negative flows)^(1/n) - 1
Note: To calculate MIRR from a specific date, use the Time Machine: with Warp(loan, target_date) as warped_loan: mirr = modified_internal_rate_of_return(warped_loan.generate_expected_cash_flow(), ...)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
cash_flow
|
CashFlow
|
The cash flow stream to analyze |
required |
finance_rate
|
InterestRate
|
Rate for financing negative cash flows |
required |
reinvestment_rate
|
InterestRate
|
Rate for reinvesting positive cash flows |
required |
year_size
|
YearSize
|
Day-count convention (YearSize.commercial for 365 days, YearSize.banker for 360 days) |
commercial
|
Returns:
| Type | Description |
|---|---|
Rate
|
The modified internal rate of return as a Rate (may be negative) |
Examples:
>>> from datetime import datetime
>>> from money_warp import CashFlow, CashFlowItem, Money, InterestRate, modified_internal_rate_of_return
>>>
>>> # Investment with different financing and reinvestment rates
>>> items = [
... CashFlowItem(Money("-1000"), datetime(2024, 1, 1), "Investment", "investment"),
... CashFlowItem(Money("300"), datetime(2024, 6, 1), "Return 1", "return"),
... CashFlowItem(Money("400"), datetime(2024, 12, 1), "Return 2", "return"),
... CashFlowItem(Money("500"), datetime(2025, 6, 1), "Return 3", "return"),
... ]
>>> cf = CashFlow(items)
>>>
>>> mirr = modified_internal_rate_of_return(
... cf,
... InterestRate("8% annual"), # Financing rate
... InterestRate("6% annual") # Reinvestment rate
... )
>>> print(f"MIRR: {mirr}")
Date Generation Utilities¶
generate_monthly_dates¶
money_warp.generate_monthly_dates(start_date, num_payments)
¶
Generate a list of monthly payment due dates.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
start_date
|
datetime
|
The starting date (first payment date) |
required |
num_payments
|
int
|
Number of monthly payments to generate |
required |
Returns:
| Type | Description |
|---|---|
List[datetime]
|
List of datetime objects representing monthly due dates |
Examples:
generate_biweekly_dates¶
money_warp.generate_biweekly_dates(start_date, num_payments)
¶
Generate a list of bi-weekly (every 14 days) payment due dates.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
start_date
|
datetime
|
The starting date (first payment date) |
required |
num_payments
|
int
|
Number of bi-weekly payments to generate |
required |
Returns:
| Type | Description |
|---|---|
List[datetime]
|
List of datetime objects representing bi-weekly due dates |
Examples:
generate_weekly_dates¶
money_warp.generate_weekly_dates(start_date, num_payments)
¶
Generate a list of weekly payment due dates.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
start_date
|
datetime
|
The starting date (first payment date) |
required |
num_payments
|
int
|
Number of weekly payments to generate |
required |
Returns:
| Type | Description |
|---|---|
List[datetime]
|
List of datetime objects representing weekly due dates |
Examples:
generate_quarterly_dates¶
money_warp.generate_quarterly_dates(start_date, num_payments)
¶
Generate a list of quarterly payment due dates.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
start_date
|
datetime
|
The starting date (first payment date) |
required |
num_payments
|
int
|
Number of quarterly payments to generate |
required |
Returns:
| Type | Description |
|---|---|
List[datetime]
|
List of datetime objects representing quarterly due dates |
Examples:
generate_annual_dates¶
money_warp.generate_annual_dates(start_date, num_payments)
¶
Generate a list of annual payment due dates.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
start_date
|
datetime
|
The starting date (first payment date) |
required |
num_payments
|
int
|
Number of annual payments to generate |
required |
Returns:
| Type | Description |
|---|---|
List[datetime]
|
List of datetime objects representing annual due dates |
Examples:
generate_custom_interval_dates¶
money_warp.generate_custom_interval_dates(start_date, num_payments, interval_days)
¶
Generate a list of payment due dates with custom day intervals.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
start_date
|
datetime
|
The starting date (first payment date) |
required |
num_payments
|
int
|
Number of payments to generate |
required |
interval_days
|
int
|
Number of days between payments |
required |
Returns:
| Type | Description |
|---|---|
List[datetime]
|
List of datetime objects representing due dates |
Examples:
Tax Classes¶
BaseTax¶
money_warp.BaseTax
¶
Bases: ABC
Abstract base class for all loan taxes.
All taxes should inherit from this and implement the calculate method. The interface mirrors BaseScheduler: simple, one method, receives what it needs.
Functions¶
calculate(schedule, disbursement_date, tz)
abstractmethod
¶
Calculate tax based on the amortization schedule.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
schedule
|
PaymentSchedule
|
The loan's payment schedule with principal breakdown per installment. |
required |
disbursement_date
|
datetime
|
When the loan was disbursed. |
required |
tz
|
tzinfo
|
Business timezone for extracting calendar dates from datetimes. |
required |
Returns:
| Type | Description |
|---|---|
TaxResult
|
TaxResult with total tax and per-installment breakdown. |
IOF¶
money_warp.IOF
¶
Bases: BaseTax
Brazilian IOF tax on loan operations.
IOF has two components applied to each installment's principal payment: - Daily rate: applied per day from disbursement to payment date (capped at max_daily_days) - Additional rate: flat percentage applied once per installment
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
daily_rate
|
Union[str, Decimal]
|
Daily IOF rate as decimal or string (e.g., Decimal("0.000082") or "0.0082%") |
required |
additional_rate
|
Union[str, Decimal]
|
Additional flat IOF rate as decimal or string (e.g., Decimal("0.0038") or "0.38%") |
required |
max_daily_days
|
int
|
Maximum number of days for daily rate calculation (default 365) |
365
|
rounding
|
IOFRounding
|
Rounding strategy for component aggregation (default PRECISE) |
PRECISE
|
Attributes¶
daily_rate
property
¶
The daily IOF rate as a decimal.
additional_rate
property
¶
The additional flat IOF rate as a decimal.
max_daily_days
property
¶
Maximum days for daily rate calculation.
rounding
property
¶
The rounding strategy used for component aggregation.
Functions¶
calculate(schedule, disbursement_date, tz)
¶
Calculate IOF for each installment in the schedule.
For each installment
days = min(days_from_disbursement_to_due_date, max_daily_days) daily_iof = principal_payment * daily_rate * days additional_iof = principal_payment * additional_rate installment_tax = daily_iof + additional_iof
IndividualIOF¶
money_warp.IndividualIOF
¶
Bases: IOF
IOF for Pessoa Fisica (PF) -- individual/natural person borrowers.
Pre-configured with the standard PF rates: - Daily rate: 0.0082% (0.000082) - Additional rate: 0.38% (0.0038)
All parameters can be overridden if the rates change by regulation.
CorporateIOF¶
money_warp.CorporateIOF
¶
Bases: IOF
IOF for Pessoa Juridica (PJ) -- legal entity/company borrowers.
Pre-configured with the standard PJ rates: - Daily rate: 0.0041% (0.000041) - Additional rate: 0.38% (0.0038)
All parameters can be overridden if the rates change by regulation.
IOFRounding¶
money_warp.IOFRounding
¶
Bases: Enum
Rounding strategy for IOF component aggregation.
sum high-precision daily and additional components, round once
per installment. This is the mathematically purer approach.
PER_COMPONENT: round each component (daily, additional) to 2 decimal places before summing. Matches the behavior of common Brazilian lending platforms.
TaxResult¶
money_warp.TaxResult
dataclass
¶
Result of a tax calculation across an entire schedule.
TaxInstallmentDetail¶
money_warp.TaxInstallmentDetail
dataclass
¶
Tax breakdown for a single installment.
Timezone Functions¶
get_tz¶
money_warp.get_tz()
¶
Return the current default timezone.
set_tz¶
money_warp.set_tz(tz)
¶
Set the default timezone.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tz
|
Union[str, tzinfo]
|
A timezone name (e.g. |
required |
now¶
money_warp.now()
¶
Return the current time in the configured timezone (always aware).
ensure_aware¶
money_warp.ensure_aware(dt)
¶
Guarantee that dt is timezone-aware and normalised to UTC.
If dt is naive, it is interpreted as being in the configured
business timezone (_default_tz) and then converted to UTC.
If dt is already aware, it is converted to UTC directly.
All datetimes stored inside the library are UTC. Use
:func:to_date when extracting a calendar date — it converts
back to the business timezone first.
tz_aware¶
money_warp.tz_aware(func)
¶
Decorator that makes every datetime argument timezone-aware.
At call time each positional and keyword argument is inspected:
datetimevalues are passed through :func:ensure_aware.listvalues whose first element is adatetimeare coerced element-wise.- Everything else is left untouched.
Grossup Functions¶
grossup¶
money_warp.grossup
¶
Grossup calculation using scipy.optimize.brentq bracketed root-finding.
Classes¶
GrossupResult
¶
Result of a grossup calculation.
Carries the grossed-up principal, the original requested amount, the
computed tax, and all the parameters needed to construct a Loan via
the convenience method to_loan().
Attributes:
| Name | Type | Description |
|---|---|---|
principal |
The grossed-up principal (loan amount including financed tax). |
|
requested_amount |
The amount the borrower actually receives. |
|
total_tax |
Total tax computed on the grossed-up principal. |
to_loan(**loan_kwargs)
¶Create a Loan from this grossup result.
All schedule parameters (principal, interest_rate, due_dates,
disbursement_date, scheduler, taxes) are forwarded automatically.
Pass any additional Loan keyword arguments (fine_rate,
grace_period_days, mora_interest_rate, mora_strategy) via
loan_kwargs.
Returns:
| Type | Description |
|---|---|
Loan
|
A fully configured Loan with the grossed-up principal. |
Functions¶
grossup(requested_amount, interest_rate, due_dates, disbursement_date, scheduler, taxes, tz)
¶
Compute the grossed-up principal so that principal - total_tax = requested_amount.
The borrower wants to receive requested_amount. Taxes are calculated on
the loan principal, which must be larger to compensate. Uses
scipy.optimize.brentq (bracketed bisection) to find the root of
f(p) = p - requested_amount - tax(p) = 0.
brentq is preferred over fsolve because the objective function has
a staircase shape (cent-level rounding in schedule/tax computation makes it
non-smooth), which can cause fsolve's numerical Jacobian to stall.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
requested_amount
|
Money
|
The net amount the borrower wants to receive. |
required |
interest_rate
|
InterestRate
|
The loan interest rate. |
required |
due_dates
|
list[date]
|
Payment due dates. |
required |
disbursement_date
|
datetime
|
When the loan is disbursed. |
required |
scheduler
|
type[BaseScheduler]
|
Scheduler class for generating the amortization schedule. |
required |
taxes
|
list[BaseTax]
|
List of taxes to finance into the principal. |
required |
tz
|
tzinfo
|
Time zone used when resolving calendar dates for tax calculation. |
required |
Returns:
| Type | Description |
|---|---|
GrossupResult
|
GrossupResult with the grossed-up principal, tax breakdown, and a |
GrossupResult
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If requested_amount is not positive, no taxes provided, or the solver fails to converge. |
grossup_loan(requested_amount, interest_rate, due_dates, disbursement_date, scheduler, taxes, tz, **loan_kwargs)
¶
Compute a grossed-up loan in a single call.
Sugar for grossup(...).to_loan(**loan_kwargs). The borrower wants to
receive requested_amount; this function finds the principal that
satisfies principal - tax = requested_amount and returns a fully
configured Loan.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
requested_amount
|
Money
|
The net amount the borrower wants to receive. |
required |
interest_rate
|
InterestRate
|
The loan interest rate. |
required |
due_dates
|
list[date]
|
Payment due dates. |
required |
disbursement_date
|
datetime
|
When the loan is disbursed. |
required |
scheduler
|
type[BaseScheduler]
|
Scheduler class for generating the amortization schedule. |
required |
taxes
|
list[BaseTax]
|
List of taxes to finance into the principal. |
required |
tz
|
tzinfo
|
Time zone used when resolving calendar dates for tax calculation. |
required |
**loan_kwargs
|
Any
|
Extra keyword arguments forwarded to the Loan constructor (e.g. fine_rate, grace_period_days, mora_interest_rate, mora_strategy). |
{}
|
Returns:
| Type | Description |
|---|---|
Loan
|
A Loan with the grossed-up principal and taxes attached. |
grossup_loan¶
money_warp.grossup_loan(requested_amount, interest_rate, due_dates, disbursement_date, scheduler, taxes, tz, **loan_kwargs)
¶
Compute a grossed-up loan in a single call.
Sugar for grossup(...).to_loan(**loan_kwargs). The borrower wants to
receive requested_amount; this function finds the principal that
satisfies principal - tax = requested_amount and returns a fully
configured Loan.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
requested_amount
|
Money
|
The net amount the borrower wants to receive. |
required |
interest_rate
|
InterestRate
|
The loan interest rate. |
required |
due_dates
|
list[date]
|
Payment due dates. |
required |
disbursement_date
|
datetime
|
When the loan is disbursed. |
required |
scheduler
|
type[BaseScheduler]
|
Scheduler class for generating the amortization schedule. |
required |
taxes
|
list[BaseTax]
|
List of taxes to finance into the principal. |
required |
tz
|
tzinfo
|
Time zone used when resolving calendar dates for tax calculation. |
required |
**loan_kwargs
|
Any
|
Extra keyword arguments forwarded to the Loan constructor (e.g. fine_rate, grace_period_days, mora_interest_rate, mora_strategy). |
{}
|
Returns:
| Type | Description |
|---|---|
Loan
|
A Loan with the grossed-up principal and taxes attached. |
GrossupResult¶
money_warp.GrossupResult
¶
Result of a grossup calculation.
Carries the grossed-up principal, the original requested amount, the
computed tax, and all the parameters needed to construct a Loan via
the convenience method to_loan().
Attributes:
| Name | Type | Description |
|---|---|---|
principal |
The grossed-up principal (loan amount including financed tax). |
|
requested_amount |
The amount the borrower actually receives. |
|
total_tax |
Total tax computed on the grossed-up principal. |
Functions¶
to_loan(**loan_kwargs)
¶
Create a Loan from this grossup result.
All schedule parameters (principal, interest_rate, due_dates,
disbursement_date, scheduler, taxes) are forwarded automatically.
Pass any additional Loan keyword arguments (fine_rate,
grace_period_days, mora_interest_rate, mora_strategy) via
loan_kwargs.
Returns:
| Type | Description |
|---|---|
Loan
|
A fully configured Loan with the grossed-up principal. |
Rate¶
Rate¶
money_warp.Rate
¶
General-purpose financial rate with explicit decimal/percentage handling.
Supports signed values (positive and negative), period conversions, and string parsing. Use this type for computed metrics like IRR and MIRR. For contractual interest rates that must be non-negative, use InterestRate.
Attributes¶
year_size
property
¶
Day-count convention used for daily conversions.
Functions¶
__init__(rate, period=None, as_percentage=False, precision=None, rounding=ROUND_HALF_UP, str_style='long', year_size=YearSize.commercial, str_decimals=3, abbrev_labels=None)
¶
Create a rate.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
rate
|
Union[str, Decimal, float]
|
Rate as string ("5.25% a", "-2.5% a", "0.004167 m") or numeric value. Abbreviated formats ("5.25% a.a.") are also accepted and automatically set str_style to "abbrev". |
required |
period
|
Optional[CompoundingFrequency]
|
Compounding frequency (required if rate is numeric) |
None
|
as_percentage
|
bool
|
If True and rate is numeric, treat as percentage |
False
|
precision
|
Optional[int]
|
Number of decimal places for the effective annual rate during conversions. None keeps full precision. |
None
|
rounding
|
str
|
Rounding mode from the decimal module (e.g. ROUND_HALF_UP, ROUND_DOWN). Only used when precision is set. |
ROUND_HALF_UP
|
str_style
|
str
|
Controls period notation in str. "long" outputs the full name (e.g. "annually"), "abbrev" outputs the abbreviated form (e.g. "a.a."). |
'long'
|
year_size
|
YearSize
|
Day-count convention for daily conversions. YearSize.commercial (365) or YearSize.banker (360). |
commercial
|
str_decimals
|
int
|
Number of decimal places for the percentage in str. Default 3 gives "5.250%". |
3
|
abbrev_labels
|
Optional[Dict[CompoundingFrequency, str]]
|
Partial or full override of the default abbreviation
map. Merged with _ABBREV_MAP so you only need to
pass the keys you want to change. Example:
|
None
|
as_decimal(precision=None)
¶
Get as decimal (0.05 = 5%).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
precision
|
Optional[int]
|
Number of decimal places. None returns the raw value. |
None
|
Returns:
| Type | Description |
|---|---|
Decimal
|
The rate as a Decimal, optionally quantized. |
as_percentage(precision=None)
¶
Get as percentage (5.0 = 5%).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
precision
|
Optional[int]
|
Number of decimal places. None returns the raw value. |
None
|
Returns:
| Type | Description |
|---|---|
Decimal
|
The rate as a percentage Decimal, optionally quantized. |
as_float(precision=None)
¶
Get as a float (0.05 = 5%).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
precision
|
Optional[int]
|
Number of decimal places to round to. None returns the unrounded float conversion. |
None
|
Returns:
| Type | Description |
|---|---|
float
|
The rate as a float, optionally rounded. |
to_daily()
¶
Convert to daily rate.
to_monthly()
¶
Convert to monthly rate.
to_annual()
¶
Convert to annual rate.
to_periodic_rate(num_periods)
¶
Convert to periodic rate for given number of periods per year.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
num_periods
|
int
|
Number of periods per year (e.g., 12 for monthly) |
required |
Returns:
| Name | Type | Description |
|---|---|---|
Decimal |
Decimal
|
Periodic rate as decimal |
__str__()
¶
Clear string representation.
__repr__()
¶
Developer representation.
__eq__(other)
¶
Compare rates by converting to effective annual.
__lt__(other)
¶
Less than comparison using effective annual rates.
__le__(other)
¶
Less than or equal comparison using effective annual rates.
__gt__(other)
¶
Greater than comparison using effective annual rates.
__ge__(other)
¶
Greater than or equal comparison using effective annual rates.
Billing Cycle Loan¶
BillingCycleLoan¶
money_warp.BillingCycleLoan
¶
Bases: BaseLoan
Loan with fixed amortization and credit-card-like billing cycles.
Combines a traditional amortization schedule (Price / SAC) with billing-cycle timing (monthly close + due date) and a mora interest rate that can change per cycle via a callable resolver.
The CashFlow is the single source of truth, just like Loan.
Settlements, installments, balances, and fines are all derived on
demand by a forward pass.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
principal
|
Money
|
Loan principal amount (must be positive). |
required |
interest_rate
|
InterestRate
|
Annual contractual interest rate. |
required |
billing_cycle
|
BaseBillingCycle
|
Billing cycle factory that generates closing and due dates. |
required |
start_date
|
datetime
|
Start of the first billing period. Closing dates are generated after this date. |
required |
num_installments
|
int
|
Number of installments in the amortization. |
required |
disbursement_date
|
Optional[datetime]
|
When funds are released. Defaults to
|
None
|
scheduler
|
Optional[Type[BaseScheduler]]
|
Amortization strategy. Defaults to
:class: |
None
|
fine_rate
|
Optional[InterestRate]
|
Rate for computing fines on missed payments.
Defaults to |
None
|
grace_period_days
|
int
|
Days after due date before fines apply. |
0
|
mora_interest_rate
|
Optional[InterestRate]
|
Base mora rate. Defaults to interest_rate. |
None
|
mora_rate_resolver
|
Optional[MoraRateResolver]
|
Optional callable that adjusts the base
mora rate per billing cycle. Receives
|
None
|
mora_strategy
|
MoraStrategy
|
How mora interest compounds. Defaults to
:attr: |
COMPOUND
|
Attributes¶
closing_dates
property
¶
Closing dates for each billing period.
statements
property
¶
Billing-period statements (one per cycle).
Functions¶
record_payment(amount, payment_date, interest_date=None, description=None, waive_fines=False, waive_mora=False, waive_overdue_interest=False, discount=None)
¶
Record a payment and return the derived settlement.
is_late(due_date, as_of_date=None)
¶
Check if a payment is late considering the grace period.
MoraRateResolver¶
money_warp.MoraRateResolver
¶
Bases: Protocol
Callable that returns the mora rate for a given billing cycle.
Receives the cycle's reference date (typically the closing date)
and the base mora rate configured on the loan. Returns the
InterestRate to use for mora computation in that cycle.
Implementations are free to ignore the base rate and return an entirely independent value, or to adjust it (e.g. add a spread on top of an external index).
BaseBillingCycle¶
money_warp.BaseBillingCycle
¶
Bases: ABC
Abstract factory for billing cycle date generation and statement building.
Subclasses define how statement closing dates and payment due dates
are derived. The credit card uses whichever implementation is
injected at construction time — same pattern as BaseScheduler
on the Loan.
Statement building is concrete: it relies on the abstract date methods and the cash-flow query API, so it works for every implementation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
due_dates
|
Optional[List[date]]
|
Optional explicit due dates. When provided, these
override the dates that would be computed from closing dates
via :meth: |
None
|
Attributes¶
explicit_due_dates
property
¶
Explicit due dates provided at construction, or None.
Functions¶
closing_dates_between(start, end)
abstractmethod
¶
Return closing dates for all complete cycles in [start, end].
The first closing date is the earliest one strictly after start. The last closing date is the latest one at or before end.
due_date_for(closing_date)
abstractmethod
¶
Payment due date for a given statement closing date.
due_dates_between(start, end, tz)
¶
Return payment due dates for all cycles in [start, end].
When explicit due_dates were provided at construction, returns
those falling strictly after start and at or before end.
Otherwise derives them from :meth:closing_dates_between and
:meth:due_date_for.
build_statements(cash_flow, opening_date, end_date, minimum_payment_rate, minimum_payment_floor)
¶
Build statements for all closed cycles in [opening_date, end_date].
Iterates the closing dates, slices the cash_flow by period,
and produces a :class:Statement for each one. Balance is
carried forward iteratively across periods.
compute_minimum_payment(closing_balance, rate, floor)
staticmethod
¶
Minimum payment for a given closing balance.
MonthlyBillingCycle¶
money_warp.MonthlyBillingCycle
¶
Bases: BaseBillingCycle
Billing cycle that closes on a fixed calendar day every month.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
closing_day
|
int
|
Day of month (1-28) when the statement closes. |
1
|
payment_due_days
|
int
|
Number of days after closing for the payment due date. Defaults to 15. |
15
|
due_dates
|
Optional[List[date]]
|
Optional explicit due dates that override the computed
ones. See :class: |
None
|
BillingCycleLoanStatement¶
money_warp.BillingCycleLoanStatement
dataclass
¶
Snapshot of a single billing period for a billing-cycle loan.
Combines the amortization schedule view (expected principal / interest) with the actual payment activity and any mora / fine charges for the period.
Credit Card¶
CreditCard¶
money_warp.CreditCard
¶
Revolving credit instrument with periodic billing statements.
The credit card is a cash flow. Transactions (purchases,
payments, refunds) are added directly to the underlying
:class:CashFlow. Statements, interest charges, and fines all
emerge from that cash flow — they are never stored as
independent state. Billing-cycle processing materialises interest
and fine items lazily, controlled by an idempotency counter.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
interest_rate
|
InterestRate
|
Annual rate applied to carried balances. |
required |
billing_cycle
|
Optional[BaseBillingCycle]
|
Strategy that generates closing / due dates.
Defaults to |
None
|
minimum_payment_rate
|
Optional[Decimal]
|
Fraction of the closing balance required as minimum payment (default 0.15 = 15 %). |
None
|
minimum_payment_floor
|
Optional[Money]
|
Absolute floor for the minimum payment (default $25). |
None
|
fine_rate
|
Optional[InterestRate]
|
Fine rate applied to the minimum payment when the minimum is not met (default 2% annual). |
None
|
opening_date
|
Optional[datetime]
|
When the card was opened. Defaults to |
None
|
credit_limit
|
Optional[Money]
|
Maximum outstanding balance. |
None
|
Attributes¶
current_balance
property
¶
Outstanding balance as of now(), after closing due cycles.
available_credit
property
¶
Remaining credit if a limit is set, else None.
is_paid_off
property
¶
Whether the balance is zero (or overpaid).
statements
property
¶
All closed billing-period statements up to now().
Functions¶
now()
¶
Current datetime (Warp-aware via shared TimeContext).
purchase(amount, date=None, description=None)
¶
Record a purchase on the card.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
amount
|
Money
|
Purchase amount (positive). |
required |
date
|
Optional[datetime]
|
Transaction date. Defaults to |
None
|
description
|
Optional[str]
|
Optional merchant / memo. |
None
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If amount is not positive or exceeds the available credit. |
pay(amount, date=None, description=None)
¶
Record a payment toward the card balance.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
amount
|
Money
|
Payment amount (positive). |
required |
date
|
Optional[datetime]
|
Transaction date. Defaults to |
None
|
description
|
Optional[str]
|
Optional memo. |
None
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If amount is not positive. |
refund(amount, date=None, description=None)
¶
Record a merchant refund.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
amount
|
Money
|
Refund amount (positive). |
required |
date
|
Optional[datetime]
|
Transaction date. Defaults to |
None
|
description
|
Optional[str]
|
Optional memo. |
None
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If amount is not positive. |
get_cash_flow()
¶
Return a signed cash-flow view of all resolved transactions.
Debits (purchases, interest, fines) are positive. Credits (payments, refunds) are negated to negative. Items are sorted by datetime.
Statement¶
money_warp.Statement
dataclass
¶
Working Day Calendars¶
WorkingDayCalendar¶
money_warp.WorkingDayCalendar
¶
BrazilianWorkingDayCalendar¶
money_warp.BrazilianWorkingDayCalendar
¶
Brazilian working-day calendar with national holidays.
Weekends and all Brazilian national holidays are non-working days. Optionally accepts extra holiday dates for state/municipal holidays.
National holidays (fixed): - Jan 1: Confraternização Universal (New Year) - Apr 21: Tiradentes - May 1: Dia do Trabalhador (Labour Day) - Sep 7: Independência do Brasil - Oct 12: Nossa Senhora Aparecida - Nov 2: Finados (All Souls' Day) - Nov 15: Proclamação da República - Nov 20: Dia da Consciência Negra (Black Consciousness Day) - Dec 25: Natal (Christmas)
National holidays (movable, computed from Easter): - Carnival Monday: Easter - 48 days - Carnival Tuesday: Easter - 47 days - Good Friday: Easter - 2 days - Corpus Christi: Easter + 60 days
EveryDayCalendar¶
money_warp.EveryDayCalendar
¶
Calendar where every day is a working day.
This is the default calendar for loans — no penalty deferral ever occurs. Equivalent to the behavior before working-day support was introduced.
WeekendCalendar¶
money_warp.WeekendCalendar
¶
Calendar where Saturdays and Sundays are non-working days.
Engines¶
MoraStrategy¶
money_warp.MoraStrategy
¶
Bases: Enum
Strategy for computing mora (late) interest.
SIMPLE: mora rate is applied to the outstanding principal only. COMPOUND: mora rate is applied to principal + accrued regular interest.
Time Context¶
TimeContext¶
money_warp.TimeContext
¶
Shared, overridable time source with a per-loan timezone.
Default behaviour delegates to :data:default_time_source (real
wall-clock time). Call :meth:override to swap the source — for
example with a WarpedTime instance inside a Warp context.
The tz attribute controls which timezone is used when
extracting calendar dates from UTC datetimes (via :meth:to_date
and :meth:to_datetime). It defaults to :func:get_tz.
Additional Models¶
AnticipationResult¶
money_warp.AnticipationResult
dataclass
¶
Result of an anticipation calculation.
Returned by :meth:Loan.calculate_anticipation. Contains the amount
the borrower should pay today and the installments being removed.
Cash Flow Types¶
CashFlowEntry¶
money_warp.CashFlowEntry
dataclass
¶
Bases: ABC
Abstract base for a monetary movement at a point in time.
Use :class:ExpectedCashFlowEntry for projected items (e.g. a loan
schedule) and :class:HappenedCashFlowEntry for recorded facts
(e.g. an actual payment).
Time-awareness and versioning live in :class:CashFlowItem, which
wraps one or more CashFlowEntry instances in a timeline.
category is a frozenset of string tags. A single string is
normalized to frozenset({string}) by :class:CashFlowItem.
interest_date is an optional secondary date used by loan payments
to indicate the cutoff for interest accrual. When None, the
entry's datetime is used as the interest accrual cutoff.
ExpectedCashFlowEntry¶
money_warp.ExpectedCashFlowEntry
dataclass
¶
HappenedCashFlowEntry¶
money_warp.HappenedCashFlowEntry
dataclass
¶
CashFlowType¶
money_warp.CashFlowType
¶
Bases: str, Enum
Whether a cash-flow entry is a projection or a recorded fact.