[Python] Small discrepancy in interest calculation: how to handle day-count / rounding to...

Discussão em 'Python' iniciado por Stack, Março 18, 2025.

  1. Stack

    Stack Membro Participativo

    I’m implementing a simple annual compounding function with partial-year accrual using Actual/365. My test expects a final profit of 405.6, but my code consistently yields 405.74. The difference of 0.14 seems to come from either day-count details or a rounding/truncation rule. How can I adjust my calculation so it precisely matches the expected result?

    The function:

    def calculate_annual_capitalization(today: date, series: BondSeries, user_bond: Bond, nominal_value: float = 100.0,
    cpi_data=None):

    days_in_year = 365
    first_year_rate = series.interest_rate / 100
    issue_date = series.issue_date
    maturity_date = series.maturity_date
    purchase_date = user_bond.purchase_date
    calculation_date = min(today, maturity_date)
    value = user_bond.quantity * nominal_value
    days_held = (calculation_date - purchase_date).days
    profit = round(value * first_year_rate * (days_held / days_in_year), 2)
    return profit


    Entry data:

    tos0327 = BondSeries(
    series="TOS0327",
    type="TOS",
    issue_date=date(2024, 3, 1),
    maturity_date=date(2027, 3, 31),
    duration_years=3,
    interest_rate=6.4,
    margin_rate=None
    )

    user_bond = Bond(
    account_uuid="1",
    series="TOS0327",
    purchase_date=date(2024, 3, 26),
    quantity=65
    )

    profit = calculate_annual_capitalization(self.today, tos0327, user_bond)


    Full code (you can run without dependencies by python name.py)

    from datetime import date
    from typing import Optional
    import unittest
    from dateutil.relativedelta import relativedelta
    import math

    class Bond:
    def __init__(self, account_uuid: str, series: str, purchase_date: date, quantity: int):
    self.account_uuid = account_uuid
    self.series = series
    self.purchase_date = purchase_date
    self.quantity = quantity

    class BondSeries:
    def __init__(self, series: str, type: str, issue_date: date, maturity_date: date, duration_years: int, interest_rate: float, margin_rate: Optional[float] = None):
    self.series = series
    self.type = type
    self.issue_date = issue_date
    self.maturity_date = maturity_date
    self.duration_years = duration_years
    self.interest_rate = interest_rate
    self.margin_rate = margin_rate



    def calculate_annual_capitalization(today: date, series: BondSeries, user_bond: Bond, nominal_value: float = 100.0,
    cpi_data=None):

    days_in_year = 365
    first_year_rate = series.interest_rate / 100
    issue_date = series.issue_date
    maturity_date = series.maturity_date
    purchase_date = user_bond.purchase_date
    calculation_date = min(today, maturity_date)
    value = user_bond.quantity * nominal_value
    days_held = (calculation_date - purchase_date).days
    profit = round(value * first_year_rate * (days_held / days_in_year), 2)
    return profit

    class TestBondCalculations(unittest.TestCase):
    def setUp(self):
    self.today = date(2025, 3, 17)

    def test_tos_ma1(self):
    tos0128 = BondSeries(
    series="TOS0128",
    type="TOS",
    issue_date=date(2025, 1, 1),
    maturity_date=date(2028, 1, 31),
    duration_years=3,
    interest_rate=5.95,
    margin_rate=None
    )
    user_bond = Bond(
    account_uuid="1",
    series="TOS0128",
    purchase_date=date(2025, 1, 30),
    quantity=2
    )

    profit = calculate_annual_capitalization(self.today, tos0128, user_bond)
    self.assertEqual(profit, 1.5)

    def test_tos_ma2(self):
    tos0328 = BondSeries(
    series="TOS0328",
    type="TOS",
    issue_date=date(2025, 3, 1),
    maturity_date=date(2028, 3, 31),
    duration_years=3,
    interest_rate=5.95,
    margin_rate=None
    )

    user_bond = Bond(
    account_uuid="1",
    series="TOS0328",
    purchase_date=date(2025, 3, 14),
    quantity=4
    )

    profit = calculate_annual_capitalization(self.today, tos0328, user_bond)
    print(profit)
    self.assertEqual(profit, 0.2)

    def test_tos_mi1(self):
    tos0327 = BondSeries(
    series="TOS0327",
    type="TOS",
    issue_date=date(2024, 3, 1),
    maturity_date=date(2027, 3, 31),
    duration_years=3,
    interest_rate=6.4,
    margin_rate=None
    )

    user_bond = Bond(
    account_uuid="1",
    series="TOS0327",
    purchase_date=date(2024, 3, 26),
    quantity=65
    )

    profit = calculate_annual_capitalization(self.today, tos0327, user_bond)
    print(profit)
    self.assertEqual(profit, 405.6)

    def test_tos_mi2(self):
    tos0627 = BondSeries(
    series="TOS0627",
    type="TOS",
    issue_date=date(2024, 6, 1),
    maturity_date=date(2027, 6, 30),
    duration_years=3,
    interest_rate=6.2,
    margin_rate=None
    )

    user_bond = Bond(
    account_uuid="1",
    series="TOS0627",
    purchase_date=date(2024, 6, 10),
    quantity=20
    )

    profit = calculate_annual_capitalization(self.today, tos0627, user_bond)
    print(profit)
    self.assertEqual(profit, 95.20)

    if __name__ == "__main__":
    unittest.main()

    Continue reading...

Compartilhe esta Página