Skip to content

Instantly share code, notes, and snippets.

@JensRantil
Created January 7, 2023 11:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JensRantil/d6fbdfc5f9a2e28c9d517d7d75fd972b to your computer and use it in GitHub Desktop.
Save JensRantil/d6fbdfc5f9a2e28c9d517d7d75fd972b to your computer and use it in GitHub Desktop.
Script that generates monthly plain text accounting (https://plaintextaccounting.org) interest records for fixed-rate savings accounts. Amounts are faked.
#!/bin/python
from collections import namedtuple
from datetime import date, timedelta
import itertools
import logging
logging.basicConfig(level=logging.DEBUG)
RiskFreeInvestment = namedtuple('RiskFreeInvestment', ['name', 'start_date', 'duration_months', 'amount', 'yearly_interest'])
MonthlyValue = namedtuple('MonthlyValue', ['date', 'investment', 'value', 'days', 'status'])
TAX = 0.3
INVESTMENTS = [
RiskFreeInvestment(
'nordax',
date(2022, 3, 24),
12,
100000,
0.0125,
),
RiskFreeInvestment(
'aros kapital',
date(2022, 3, 24),
12,
100000,
0.012,
),
RiskFreeInvestment(
'bluestep',
date(2022, 3, 23),
12,
100000,
0.0125,
),
]
record = '''{payout.date}{payout.status} Interest payout after {payout.days} days
assets:banks:restricted:{payout.investment.name} = {payout.value:.2f} SEK
income:interest:other
'''
def main():
nested_dividends = [generate_monthly_interests(i) for i in INVESTMENTS]
dividends = list(itertools.chain.from_iterable(nested_dividends)) # https://stackoverflow.com/a/953097/260805
dividends.sort() # sort to print them in date order.
for d in dividends:
print(record.format(payout=d).replace('.', ','))
def generate_monthly_interests(investment):
start_date_truncated_to_first = date(investment.start_date.year, investment.start_date.month, 1)
# Using start_date_truncated_to_first instead of investment.start_date
# below since increment_month can't handle overflowing days for certain
# months.
investment_end = increment_month(start_date_truncated_to_first, investment.duration_months)
logging.debug("%s %s %s", investment.start_date, start_date_truncated_to_first, investment_end)
interests = []
d = increment_month(start_date_truncated_to_first)
while d <= investment_end:
time_since_investment = d - investment.start_date
logging.debug("%s %s %s", investment.start_date, d, time_since_investment.days)
dividend = (1-TAX) * investment.amount * ((1+investment.yearly_interest) ** (1.0*time_since_investment.days/365) - 1)
inv = MonthlyValue(d, investment, investment.amount + dividend, time_since_investment.days, ' *' if d < date.today() else ' ')
interests.append(inv)
d = increment_month(d)
return interests
def increment_month(d, months=1):
"""Increment a month for a date.
>>> increment_month(date(2022, 1, 1))
datetime.date(2022, 2, 1)
>>> increment_month(date(2022, 1, 1), 2)
datetime.date(2022, 3, 1)
>>> increment_month(date(2022, 12, 1))
datetime.date(2023, 1, 1)
"""
if months > 1:
for i in range(months):
d = increment_month(d)
return d
# BUG: This will fail if d.day doesn't exist for the new month. Not needed at the moment.
return date(
d.year + 1 if d.month==12 else d.year,
1 if d.month==12 else d.month+1,
d.day)
if __name__=='__main__':
main()
@JensRantil
Copy link
Author

Execute the tests with python3 -m doctest generate-interest-posts.py.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment