Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1from datetime import date, time, datetime 

2from decimal import Decimal 

3from typing import Optional 

4import pytz 

5from django.utils.timezone import now 

6from jacc.models import AccountEntry 

7 

8 

9def calculate_simple_interest(entries, rate_pct: Decimal, # pylint: disable=too-many-locals 

10 interest_date: Optional[date] = None, begin: Optional[date] = None) -> Decimal: 

11 """ 

12 Calculates simple interest of specified entries over time. 

13 Does not accumulate interest to interest. 

14 :param entries: AccountEntry iterable (e.g. list/QuerySet) ordered by timestamp (ascending) 

15 :param rate_pct: Interest rate %, e.g. 8.00 for 8% 

16 :param interest_date: Interest end date. Default is current date. 

17 :param begin: Optional begin date for the interest. Default is whole range from the timestamp of account entries. 

18 :return: Decimal accumulated interest 

19 """ 

20 if interest_date is None: 

21 interest_date = now().date() 

22 

23 bal = None 

24 cur_date = None 

25 daily_rate = rate_pct / Decimal(36500) 

26 accum_interest = Decimal('0.00') 

27 done = False 

28 

29 entries_list = list(entries) 

30 nentries = len(entries_list) 

31 if nentries > 0: 

32 # make sure we calculate interest over whole range until interest_date 

33 last = entries_list[nentries-1] 

34 assert isinstance(last, AccountEntry) 

35 if last.timestamp.date() < interest_date: 

36 timestamp = pytz.utc.localize(datetime.combine(interest_date, time(0, 0))) 

37 e = AccountEntry(timestamp=timestamp, amount=Decimal('0.00'), type=last.type) 

38 entries_list.append(e) 

39 

40 # initial values from the first account entry 

41 e = entries_list[0] 

42 bal = e.amount or Decimal('0.00') 

43 cur_date = e.timestamp.date() 

44 if begin and begin > cur_date: 

45 cur_date = begin 

46 

47 for e in entries_list[1:]: 

48 assert isinstance(e, AccountEntry) 

49 next_date = e.timestamp.date() 

50 if begin and begin > next_date: 

51 next_date = begin 

52 if next_date > interest_date: 

53 next_date = interest_date 

54 done = True 

55 assert cur_date 

56 assert bal is not None 

57 time_days = (next_date - cur_date).days 

58 if time_days > 0: 

59 day_interest = bal * daily_rate 

60 interval_interest = day_interest * Decimal(time_days) 

61 accum_interest += interval_interest 

62 cur_date = next_date 

63 if e.amount is not None: 

64 bal += e.amount 

65 if done: 

66 break 

67 

68 return accum_interest