rangekeeper 0.7.0__tar.gz → 0.7.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {rangekeeper-0.7.0 → rangekeeper-0.7.1}/PKG-INFO +2 -2
- {rangekeeper-0.7.0 → rangekeeper-0.7.1}/pyproject.toml +2 -2
- {rangekeeper-0.7.0 → rangekeeper-0.7.1}/rangekeeper/duration.py +9 -0
- {rangekeeper-0.7.0 → rangekeeper-0.7.1}/rangekeeper/formula.py +43 -12
- {rangekeeper-0.7.0 → rangekeeper-0.7.1}/README.md +0 -0
- {rangekeeper-0.7.0 → rangekeeper-0.7.1}/rangekeeper/__init__.py +0 -0
- {rangekeeper-0.7.0 → rangekeeper-0.7.1}/rangekeeper/api.py +0 -0
- {rangekeeper-0.7.0 → rangekeeper-0.7.1}/rangekeeper/conftest.py +0 -0
- {rangekeeper-0.7.0 → rangekeeper-0.7.1}/rangekeeper/distribution.py +0 -0
- {rangekeeper-0.7.0 → rangekeeper-0.7.1}/rangekeeper/dynamics/__init__.py +0 -0
- {rangekeeper-0.7.0 → rangekeeper-0.7.1}/rangekeeper/dynamics/black_swan.py +0 -0
- {rangekeeper-0.7.0 → rangekeeper-0.7.1}/rangekeeper/dynamics/cyclicality.py +0 -0
- {rangekeeper-0.7.0 → rangekeeper-0.7.1}/rangekeeper/dynamics/market.py +0 -0
- {rangekeeper-0.7.0 → rangekeeper-0.7.1}/rangekeeper/dynamics/noise.py +0 -0
- {rangekeeper-0.7.0 → rangekeeper-0.7.1}/rangekeeper/dynamics/trend.py +0 -0
- {rangekeeper-0.7.0 → rangekeeper-0.7.1}/rangekeeper/dynamics/volatility.py +0 -0
- {rangekeeper-0.7.0 → rangekeeper-0.7.1}/rangekeeper/extrapolation.py +0 -0
- {rangekeeper-0.7.0 → rangekeeper-0.7.1}/rangekeeper/flux.py +0 -0
- {rangekeeper-0.7.0 → rangekeeper-0.7.1}/rangekeeper/format.py +0 -0
- {rangekeeper-0.7.0 → rangekeeper-0.7.1}/rangekeeper/graph.py +0 -0
- {rangekeeper-0.7.0 → rangekeeper-0.7.1}/rangekeeper/measure.py +0 -0
- {rangekeeper-0.7.0 → rangekeeper-0.7.1}/rangekeeper/policy.py +0 -0
- {rangekeeper-0.7.0 → rangekeeper-0.7.1}/rangekeeper/projection.py +0 -0
- {rangekeeper-0.7.0 → rangekeeper-0.7.1}/rangekeeper/segmentation.py +0 -0
- {rangekeeper-0.7.0 → rangekeeper-0.7.1}/rangekeeper/space.py +0 -0
- {rangekeeper-0.7.0 → rangekeeper-0.7.1}/rangekeeper/span.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: Rangekeeper
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.1
|
|
4
4
|
Summary: A Python library assisting financial modelling in real estate asset & development planning, decision-making, cashflow forecasting, and scenario analysis.
|
|
5
5
|
License: MPL-2.0
|
|
6
6
|
Author: Daniel Fink
|
|
@@ -22,7 +22,7 @@ Requires-Dist: pandas (>=2.2,<3.0)
|
|
|
22
22
|
Requires-Dist: pint (>=0.24,<0.25)
|
|
23
23
|
Requires-Dist: plotly (>=5.22,<6.0)
|
|
24
24
|
Requires-Dist: py-moneyed (>=3.0,<4.0)
|
|
25
|
-
Requires-Dist: pyarrow (>=
|
|
25
|
+
Requires-Dist: pyarrow (>=18.1,<19.0)
|
|
26
26
|
Requires-Dist: pyvis (>=0.3,<0.4)
|
|
27
27
|
Requires-Dist: pyxirr (>=0.10,<0.11)
|
|
28
28
|
Requires-Dist: pyyaml (>=6.0,<7.0)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "Rangekeeper"
|
|
3
|
-
version = "0.7.
|
|
3
|
+
version = "0.7.1"
|
|
4
4
|
description = "A Python library assisting financial modelling in real estate asset & development planning, decision-making, cashflow forecasting, and scenario analysis."
|
|
5
5
|
authors = ["Daniel Fink <danfink@mit.edu>"]
|
|
6
6
|
license = "MPL-2.0"
|
|
@@ -24,7 +24,7 @@ seaborn = "^0.13"
|
|
|
24
24
|
pyvis = "^0.3"
|
|
25
25
|
plotly = "^5.22"
|
|
26
26
|
mpld3 = "^0.5"
|
|
27
|
-
pyarrow = "^
|
|
27
|
+
pyarrow = "^18.1"
|
|
28
28
|
aenum = "^3.1"
|
|
29
29
|
numpy-financial = "^1.0"
|
|
30
30
|
|
|
@@ -213,6 +213,15 @@ class Period:
|
|
|
213
213
|
|
|
214
214
|
|
|
215
215
|
class Sequence:
|
|
216
|
+
@staticmethod
|
|
217
|
+
def from_datestamps(
|
|
218
|
+
datestamps: pd.DatetimeIndex,
|
|
219
|
+
frequency: Type) -> pd.PeriodIndex:
|
|
220
|
+
"""
|
|
221
|
+
Returns a pd.PeriodIndex from a pd.DatetimeIndex with Datetimes being Dates
|
|
222
|
+
"""
|
|
223
|
+
return datestamps.to_period(freq=Type.period(frequency))
|
|
224
|
+
|
|
216
225
|
@staticmethod
|
|
217
226
|
def from_bounds(
|
|
218
227
|
include_start: pd.Timestamp,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from typing import Callable, Optional
|
|
2
|
-
|
|
2
|
+
import enum
|
|
3
3
|
import numpy as np
|
|
4
4
|
from numba import jit
|
|
5
5
|
import numba
|
|
@@ -11,12 +11,19 @@ import rangekeeper as rk
|
|
|
11
11
|
|
|
12
12
|
class Financial:
|
|
13
13
|
|
|
14
|
+
class Instrument(enum.Enum):
|
|
15
|
+
INTEREST_ONLY = 'interest_only'
|
|
16
|
+
COMPOUND = 'compound'
|
|
17
|
+
CAPITALIZED = 'capitalized'
|
|
18
|
+
|
|
19
|
+
|
|
14
20
|
@staticmethod
|
|
15
21
|
def interest(
|
|
16
|
-
rate: float,
|
|
22
|
+
rate: float | rk.flux.Flow,
|
|
17
23
|
transactions: rk.flux.Flow,
|
|
18
24
|
frequency: rk.duration.Type,
|
|
19
|
-
capitalized: bool = False)
|
|
25
|
+
# capitalized: bool = False)\
|
|
26
|
+
instrument: Instrument = Instrument.COMPOUND) -> (rk.flux.Stream, rk.flux.Flow):
|
|
20
27
|
"""
|
|
21
28
|
Calculate interest expense on a loan or other interest-bearing liability,
|
|
22
29
|
described by a series of transactions.
|
|
@@ -27,10 +34,23 @@ class Financial:
|
|
|
27
34
|
|
|
28
35
|
transactions = transactions.resample(frequency=frequency)
|
|
29
36
|
|
|
37
|
+
if isinstance(rate, float):
|
|
38
|
+
rate = rk.flux.Flow.from_projection(
|
|
39
|
+
name='Interest Rates',
|
|
40
|
+
value=rate,
|
|
41
|
+
proj=rk.projection.Extrapolation(
|
|
42
|
+
form=rk.extrapolation.Recurring(),
|
|
43
|
+
sequence=rk.duration.Sequence.from_datestamps(
|
|
44
|
+
datestamps=transactions.movements.index,
|
|
45
|
+
frequency=frequency)))
|
|
46
|
+
|
|
47
|
+
if len(transactions.movements) != len(rate.movements):
|
|
48
|
+
raise ValueError('The number of transactions must match the number of rates.')
|
|
49
|
+
|
|
30
50
|
startings, endings, interests = rk.formula.Financial._calc_interest(
|
|
31
51
|
transactions=numba.typed.List(transactions.movements.to_list()),
|
|
32
|
-
|
|
33
|
-
|
|
52
|
+
rates=numba.typed.List(rate.movements.to_list()),
|
|
53
|
+
instrument=instrument.value)
|
|
34
54
|
|
|
35
55
|
balance = rk.flux.Stream(
|
|
36
56
|
name='Balance',
|
|
@@ -58,8 +78,8 @@ class Financial:
|
|
|
58
78
|
@numba.jit
|
|
59
79
|
def _calc_interest(
|
|
60
80
|
transactions: numba.typed.List,
|
|
61
|
-
|
|
62
|
-
|
|
81
|
+
rates: numba.typed.List,
|
|
82
|
+
instrument: str) -> (numba.typed.List, numba.typed.List, numba.typed.List):
|
|
63
83
|
|
|
64
84
|
startings = numba.typed.List.empty_list(numba.float64)
|
|
65
85
|
endings = numba.typed.List.empty_list(numba.float64)
|
|
@@ -70,13 +90,24 @@ class Financial:
|
|
|
70
90
|
startings.append(0)
|
|
71
91
|
else:
|
|
72
92
|
startings.append(endings[-1])
|
|
93
|
+
|
|
73
94
|
principal = startings[i] + transactions[i]
|
|
74
|
-
if
|
|
75
|
-
interest =
|
|
95
|
+
if instrument == 'interest_only':
|
|
96
|
+
interest = principal * rates[i] if principal > 0 else 0
|
|
97
|
+
endings.append(principal)
|
|
98
|
+
|
|
99
|
+
elif instrument == 'compound':
|
|
100
|
+
interest = principal * rates[i] if principal > 0 else 0
|
|
76
101
|
endings.append(principal + interest)
|
|
77
|
-
|
|
78
|
-
|
|
102
|
+
|
|
103
|
+
elif instrument == 'capitalized':
|
|
104
|
+
interest = (principal * rates[i]) / (1 - rates[i]) if principal > 0 else 0 # Since we are capitalizing
|
|
105
|
+
# interest, the interest amount (draw) must include interest to pay on the principal. Derived from i = r * (P + i)
|
|
79
106
|
endings.append(principal + interest)
|
|
107
|
+
|
|
108
|
+
else:
|
|
109
|
+
raise ValueError(f'Invalid instrument: {instrument}')
|
|
110
|
+
|
|
80
111
|
interests.append(interest)
|
|
81
112
|
|
|
82
113
|
return (startings, endings, interests)
|
|
@@ -87,7 +118,7 @@ class Financial:
|
|
|
87
118
|
starting: float,
|
|
88
119
|
transactions: rk.flux.Flow,
|
|
89
120
|
frequency: rk.duration.Type,
|
|
90
|
-
name: str = None) -> rk.flux.Stream
|
|
121
|
+
name: str = None) -> rk.flux.Stream:
|
|
91
122
|
"""
|
|
92
123
|
Calculate the balance of a financial account given a starting balance and a series of transactions.
|
|
93
124
|
"""
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|