PocketAccountant / src /engine /ratios.py
eldinosaur's picture
PocketAccountant: custom ledger UI + deterministic agent (engine, ledger, retrieval, classifier)
c55ab5e verified
Raw
History Blame Contribute Delete
3.35 kB
"""Financial-health formulas — regime-agnostic, useful for any small business.
These are the numbers a good accountant brings up unprompted: am I liquid, am I
profitable, when do I break even, how long does my cash last. All deterministic.
"""
from __future__ import annotations
from decimal import Decimal
from .money import D, money
from .result import CalcResult
def _ratio(numerator: Decimal, denominator: Decimal) -> Decimal:
if denominator == 0:
return D(0)
return numerator / denominator
def current_ratio(current_assets, current_liabilities) -> CalcResult:
"""Liquidity: can short-term assets cover short-term debts? (>1 is healthy.)"""
ca, cl = D(current_assets), D(current_liabilities)
r = _ratio(ca, cl)
notes = []
if cl == 0:
notes.append("No current liabilities — ratio undefined, reported as 0.")
elif r < 1:
notes.append("Below 1.0 — short-term obligations exceed liquid assets.")
return CalcResult(
amount=r.quantize(Decimal("0.01")),
label="Razón circulante (current ratio)",
breakdown=[("Activo circulante", money(ca)), ("Pasivo circulante", money(cl))],
notes=notes,
)
def profit_margin(net_profit, revenue) -> CalcResult:
"""Net margin as a fraction of revenue."""
np_, rev = D(net_profit), D(revenue)
r = _ratio(np_, rev)
return CalcResult(
amount=r.quantize(Decimal("0.0001")),
label="Margen de utilidad neta",
breakdown=[("Utilidad neta", money(np_)), ("Ingresos", money(rev))],
notes=[f"≈ {(r * 100).quantize(Decimal('0.01'))}% of revenue is profit."],
)
def break_even_units(fixed_costs, price_per_unit, variable_cost_per_unit) -> CalcResult:
"""Units to sell to cover all costs: FC / (price − variable cost)."""
fc = D(fixed_costs)
contribution = D(price_per_unit) - D(variable_cost_per_unit)
if contribution <= 0:
return CalcResult(
amount=D(0),
label="Punto de equilibrio (unidades)",
breakdown=[
("Costos fijos", money(fc)),
("Margen de contribución por unidad", money(contribution)),
],
notes=["Contribution margin ≤ 0 — break-even impossible at this price."],
)
units = fc / contribution
return CalcResult(
amount=units.quantize(Decimal("0.01")),
label="Punto de equilibrio (unidades)",
breakdown=[
("Costos fijos", money(fc)),
("Margen de contribución por unidad", money(contribution)),
],
)
def cash_runway_months(cash_on_hand, monthly_net_burn) -> CalcResult:
"""How many months the cash lasts at the current net burn rate."""
cash = D(cash_on_hand)
burn = D(monthly_net_burn)
if burn <= 0:
return CalcResult(
amount=D(0),
label="Meses de pista (runway)",
breakdown=[("Efectivo disponible", money(cash)), ("Quema neta mensual", money(burn))],
notes=["Non-positive burn — cash-flow positive, runway effectively infinite."],
)
months = cash / burn
return CalcResult(
amount=months.quantize(Decimal("0.1")),
label="Meses de pista (runway)",
breakdown=[("Efectivo disponible", money(cash)), ("Quema neta mensual", money(burn))],
)