rangekeeper 0.7.8__tar.gz → 0.7.9__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.8 → rangekeeper-0.7.9}/PKG-INFO +5 -6
- {rangekeeper-0.7.8 → rangekeeper-0.7.9}/README.md +4 -5
- {rangekeeper-0.7.8 → rangekeeper-0.7.9}/Rangekeeper.egg-info/PKG-INFO +5 -6
- {rangekeeper-0.7.8 → rangekeeper-0.7.9}/pyproject.toml +1 -1
- {rangekeeper-0.7.8 → rangekeeper-0.7.9}/rangekeeper/flux.py +1 -1
- {rangekeeper-0.7.8 → rangekeeper-0.7.9}/rangekeeper/formula/financial.py +40 -38
- {rangekeeper-0.7.8 → rangekeeper-0.7.9}/tests/test_formulas.py +12 -4
- {rangekeeper-0.7.8 → rangekeeper-0.7.9}/Rangekeeper.egg-info/SOURCES.txt +0 -0
- {rangekeeper-0.7.8 → rangekeeper-0.7.9}/Rangekeeper.egg-info/dependency_links.txt +0 -0
- {rangekeeper-0.7.8 → rangekeeper-0.7.9}/Rangekeeper.egg-info/requires.txt +0 -0
- {rangekeeper-0.7.8 → rangekeeper-0.7.9}/Rangekeeper.egg-info/top_level.txt +0 -0
- {rangekeeper-0.7.8 → rangekeeper-0.7.9}/rangekeeper/__init__.py +0 -0
- {rangekeeper-0.7.8 → rangekeeper-0.7.9}/rangekeeper/api.py +0 -0
- {rangekeeper-0.7.8 → rangekeeper-0.7.9}/rangekeeper/conftest.py +0 -0
- {rangekeeper-0.7.8 → rangekeeper-0.7.9}/rangekeeper/distribution.py +0 -0
- {rangekeeper-0.7.8 → rangekeeper-0.7.9}/rangekeeper/duration.py +0 -0
- {rangekeeper-0.7.8 → rangekeeper-0.7.9}/rangekeeper/dynamics/__init__.py +0 -0
- {rangekeeper-0.7.8 → rangekeeper-0.7.9}/rangekeeper/dynamics/black_swan.py +0 -0
- {rangekeeper-0.7.8 → rangekeeper-0.7.9}/rangekeeper/dynamics/cyclicality.py +0 -0
- {rangekeeper-0.7.8 → rangekeeper-0.7.9}/rangekeeper/dynamics/market.py +0 -0
- {rangekeeper-0.7.8 → rangekeeper-0.7.9}/rangekeeper/dynamics/noise.py +0 -0
- {rangekeeper-0.7.8 → rangekeeper-0.7.9}/rangekeeper/dynamics/trend.py +0 -0
- {rangekeeper-0.7.8 → rangekeeper-0.7.9}/rangekeeper/dynamics/volatility.py +0 -0
- {rangekeeper-0.7.8 → rangekeeper-0.7.9}/rangekeeper/extrapolation.py +0 -0
- {rangekeeper-0.7.8 → rangekeeper-0.7.9}/rangekeeper/format.py +0 -0
- {rangekeeper-0.7.8 → rangekeeper-0.7.9}/rangekeeper/formula/__init__.py +0 -0
- {rangekeeper-0.7.8 → rangekeeper-0.7.9}/rangekeeper/graph.py +0 -0
- {rangekeeper-0.7.8 → rangekeeper-0.7.9}/rangekeeper/measure.py +0 -0
- {rangekeeper-0.7.8 → rangekeeper-0.7.9}/rangekeeper/policy.py +0 -0
- {rangekeeper-0.7.8 → rangekeeper-0.7.9}/rangekeeper/projection.py +0 -0
- {rangekeeper-0.7.8 → rangekeeper-0.7.9}/rangekeeper/segmentation.py +0 -0
- {rangekeeper-0.7.8 → rangekeeper-0.7.9}/rangekeeper/space.py +0 -0
- {rangekeeper-0.7.8 → rangekeeper-0.7.9}/setup.cfg +0 -0
- {rangekeeper-0.7.8 → rangekeeper-0.7.9}/tests/test_api.py +0 -0
- {rangekeeper-0.7.8 → rangekeeper-0.7.9}/tests/test_dynamics.py +0 -0
- {rangekeeper-0.7.8 → rangekeeper-0.7.9}/tests/test_graph.py +0 -0
- {rangekeeper-0.7.8 → rangekeeper-0.7.9}/tests/test_measures.py +0 -0
- {rangekeeper-0.7.8 → rangekeeper-0.7.9}/tests/test_models.py +0 -0
- {rangekeeper-0.7.8 → rangekeeper-0.7.9}/tests/test_modules.py +0 -0
- {rangekeeper-0.7.8 → rangekeeper-0.7.9}/tests/test_projections.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: Rangekeeper
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.9
|
|
4
4
|
Summary: A Python library assisting financial modelling in real estate asset & development planning, decision-making, cashflow forecasting, and scenario analysis.
|
|
5
5
|
Author-email: Daniel Fink <danfink@mit.edu>
|
|
6
6
|
License-Expression: MPL-2.0
|
|
@@ -43,11 +43,10 @@ environment and dependency management:
|
|
|
43
43
|
3. Create a virtual environment: `uv venv .venv`
|
|
44
44
|
4. Activate the virtual environment: `source .venv/bin/activate`
|
|
45
45
|
5. Install dependencies: `uv pip install -r <(uv pip compile pyproject.toml)`
|
|
46
|
-
6. Some tests require API access to [Speckle](https://speckle.systems/). It is recommended to use [Python-Dotenv]
|
|
47
|
-
(https://github.com/theskumar/python-dotenv), and add a `.env` file in the project's root directory with your `SPECKLE_TOKEN` environment variable.
|
|
46
|
+
6. Some tests require API access to [Speckle](https://speckle.systems/). It is recommended to use [Python-Dotenv](https://github.com/theskumar/python-dotenv), and add a `.env` file in the project's root directory with your `SPECKLE_TOKEN` environment variable.
|
|
48
47
|
|
|
49
48
|
### Publishing
|
|
50
|
-
1. First, remove any previously built packages: `rm -rf dist
|
|
49
|
+
1. First, remove any previously built packages: `rm -rf dist/`
|
|
51
50
|
2. Build the package: `uv build`
|
|
52
|
-
3. Then, publish it to PyPI with your
|
|
53
|
-
'^#' .env | xargs) && uv publish --token $UV_PUBLISH_TOKEN
|
|
51
|
+
3. Then, publish it to PyPI with your `UV_PUBLISH_TOKEN` recorded in the .env file: `export $(grep -v
|
|
52
|
+
'^#' .env | xargs) && uv publish --token $UV_PUBLISH_TOKEN`
|
|
@@ -16,11 +16,10 @@ environment and dependency management:
|
|
|
16
16
|
3. Create a virtual environment: `uv venv .venv`
|
|
17
17
|
4. Activate the virtual environment: `source .venv/bin/activate`
|
|
18
18
|
5. Install dependencies: `uv pip install -r <(uv pip compile pyproject.toml)`
|
|
19
|
-
6. Some tests require API access to [Speckle](https://speckle.systems/). It is recommended to use [Python-Dotenv]
|
|
20
|
-
(https://github.com/theskumar/python-dotenv), and add a `.env` file in the project's root directory with your `SPECKLE_TOKEN` environment variable.
|
|
19
|
+
6. Some tests require API access to [Speckle](https://speckle.systems/). It is recommended to use [Python-Dotenv](https://github.com/theskumar/python-dotenv), and add a `.env` file in the project's root directory with your `SPECKLE_TOKEN` environment variable.
|
|
21
20
|
|
|
22
21
|
### Publishing
|
|
23
|
-
1. First, remove any previously built packages: `rm -rf dist
|
|
22
|
+
1. First, remove any previously built packages: `rm -rf dist/`
|
|
24
23
|
2. Build the package: `uv build`
|
|
25
|
-
3. Then, publish it to PyPI with your
|
|
26
|
-
'^#' .env | xargs) && uv publish --token $UV_PUBLISH_TOKEN
|
|
24
|
+
3. Then, publish it to PyPI with your `UV_PUBLISH_TOKEN` recorded in the .env file: `export $(grep -v
|
|
25
|
+
'^#' .env | xargs) && uv publish --token $UV_PUBLISH_TOKEN`
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: Rangekeeper
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.9
|
|
4
4
|
Summary: A Python library assisting financial modelling in real estate asset & development planning, decision-making, cashflow forecasting, and scenario analysis.
|
|
5
5
|
Author-email: Daniel Fink <danfink@mit.edu>
|
|
6
6
|
License-Expression: MPL-2.0
|
|
@@ -43,11 +43,10 @@ environment and dependency management:
|
|
|
43
43
|
3. Create a virtual environment: `uv venv .venv`
|
|
44
44
|
4. Activate the virtual environment: `source .venv/bin/activate`
|
|
45
45
|
5. Install dependencies: `uv pip install -r <(uv pip compile pyproject.toml)`
|
|
46
|
-
6. Some tests require API access to [Speckle](https://speckle.systems/). It is recommended to use [Python-Dotenv]
|
|
47
|
-
(https://github.com/theskumar/python-dotenv), and add a `.env` file in the project's root directory with your `SPECKLE_TOKEN` environment variable.
|
|
46
|
+
6. Some tests require API access to [Speckle](https://speckle.systems/). It is recommended to use [Python-Dotenv](https://github.com/theskumar/python-dotenv), and add a `.env` file in the project's root directory with your `SPECKLE_TOKEN` environment variable.
|
|
48
47
|
|
|
49
48
|
### Publishing
|
|
50
|
-
1. First, remove any previously built packages: `rm -rf dist
|
|
49
|
+
1. First, remove any previously built packages: `rm -rf dist/`
|
|
51
50
|
2. Build the package: `uv build`
|
|
52
|
-
3. Then, publish it to PyPI with your
|
|
53
|
-
'^#' .env | xargs) && uv publish --token $UV_PUBLISH_TOKEN
|
|
51
|
+
3. Then, publish it to PyPI with your `UV_PUBLISH_TOKEN` recorded in the .env file: `export $(grep -v
|
|
52
|
+
'^#' .env | xargs) && uv publish --token $UV_PUBLISH_TOKEN`
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "Rangekeeper"
|
|
3
|
-
version = "0.7.
|
|
3
|
+
version = "0.7.9"
|
|
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 = [
|
|
6
6
|
{ name = "Daniel Fink", email = "danfink@mit.edu" }
|
|
@@ -64,32 +64,28 @@ class Balance:
|
|
|
64
64
|
cls,
|
|
65
65
|
startings: rk.flux.Flow,
|
|
66
66
|
endings: rk.flux.Flow,
|
|
67
|
+
frequency: rk.duration.Type,
|
|
68
|
+
name: str = "",
|
|
67
69
|
):
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
return balance
|
|
74
|
-
|
|
75
|
-
def _overdraft(
|
|
76
|
-
self,
|
|
77
|
-
name: str = None,
|
|
78
|
-
) -> rk.flux.Flow:
|
|
79
|
-
"""
|
|
80
|
-
Identifies the portion of transactions occurring while the balance is negative.
|
|
81
|
-
"""
|
|
70
|
+
name = startings.name if name is None else name
|
|
71
|
+
if startings.units != endings.units:
|
|
72
|
+
raise ValueError("The units of the starting and ending flows must match.")
|
|
73
|
+
units = startings.units
|
|
82
74
|
|
|
83
|
-
|
|
84
|
-
|
|
75
|
+
startings = startings.resample(frequency=frequency)
|
|
76
|
+
endings = endings.resample(frequency=frequency)
|
|
85
77
|
|
|
86
|
-
|
|
87
|
-
|
|
78
|
+
transactions = rk.flux.Flow(
|
|
79
|
+
movements=endings.movements - startings.movements,
|
|
80
|
+
units=units,
|
|
81
|
+
)
|
|
82
|
+
starting = startings.movements.iloc[0]
|
|
88
83
|
|
|
89
|
-
return
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
84
|
+
return cls(
|
|
85
|
+
starting=starting,
|
|
86
|
+
transactions=transactions,
|
|
87
|
+
frequency=frequency,
|
|
88
|
+
name=name,
|
|
93
89
|
)
|
|
94
90
|
|
|
95
91
|
@staticmethod
|
|
@@ -111,7 +107,6 @@ class Balance:
|
|
|
111
107
|
overdrafts.append(ending if ending < 0 else 0)
|
|
112
108
|
endings.append(ending if ending > 0 else 0)
|
|
113
109
|
startings.append(endings[-1])
|
|
114
|
-
# startings.append(ending)
|
|
115
110
|
|
|
116
111
|
return (
|
|
117
112
|
startings[:-1],
|
|
@@ -165,27 +160,29 @@ class Interest:
|
|
|
165
160
|
"The number of transactions must match the number of rates."
|
|
166
161
|
)
|
|
167
162
|
|
|
168
|
-
startings, endings,
|
|
163
|
+
startings, endings, amounts = self._calc_interest(
|
|
169
164
|
transactions=numba.typed.List(transactions.movements.to_list()),
|
|
170
165
|
rates=numba.typed.List(rate.movements.to_list()),
|
|
171
166
|
type=type.value,
|
|
172
167
|
)
|
|
173
168
|
|
|
169
|
+
startings = rk.flux.Flow(
|
|
170
|
+
movements=pd.Series(startings, index=transactions.movements.index),
|
|
171
|
+
units=transactions.units,
|
|
172
|
+
)
|
|
173
|
+
endings = rk.flux.Flow(
|
|
174
|
+
movements=pd.Series(endings, index=transactions.movements.index),
|
|
175
|
+
units=transactions.units,
|
|
176
|
+
)
|
|
177
|
+
|
|
174
178
|
self.balance = rk.formula.financial.Balance.from_flows(
|
|
175
|
-
startings=
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
name="Start Balance",
|
|
179
|
-
),
|
|
180
|
-
endings=rk.flux.Flow(
|
|
181
|
-
movements=pd.Series(endings, index=transactions.movements.index),
|
|
182
|
-
units=transactions.units,
|
|
183
|
-
name="End Balance",
|
|
184
|
-
),
|
|
179
|
+
startings=startings,
|
|
180
|
+
endings=endings,
|
|
181
|
+
frequency=frequency,
|
|
185
182
|
)
|
|
186
183
|
|
|
187
184
|
self.amounts = rk.flux.Flow(
|
|
188
|
-
movements=pd.Series(
|
|
185
|
+
movements=pd.Series(amounts, index=transactions.movements.index),
|
|
189
186
|
units=transactions.units,
|
|
190
187
|
name="Interest",
|
|
191
188
|
)
|
|
@@ -196,7 +193,11 @@ class Interest:
|
|
|
196
193
|
transactions: numba.typed.List,
|
|
197
194
|
rates: numba.typed.List,
|
|
198
195
|
type: str,
|
|
199
|
-
) -> (
|
|
196
|
+
) -> (
|
|
197
|
+
numba.typed.List,
|
|
198
|
+
numba.typed.List,
|
|
199
|
+
numba.typed.List,
|
|
200
|
+
):
|
|
200
201
|
|
|
201
202
|
startings = numba.typed.List.empty_list(numba.float64)
|
|
202
203
|
endings = numba.typed.List.empty_list(numba.float64)
|
|
@@ -218,10 +219,11 @@ class Interest:
|
|
|
218
219
|
endings.append(principal + interest)
|
|
219
220
|
|
|
220
221
|
elif type == "capitalized":
|
|
222
|
+
# Since we are capitalizing interest, the amount (draw) must include interest to pay on the principal.
|
|
223
|
+
# Derived from i = r * (P + i)
|
|
221
224
|
interest = (
|
|
222
225
|
(principal * rates[i]) / (1 - rates[i]) if principal > 0 else 0
|
|
223
|
-
)
|
|
224
|
-
# interest, the amount (draw) must include interest to pay on the principal. Derived from i = r * (P + i)
|
|
226
|
+
)
|
|
225
227
|
endings.append(principal + interest)
|
|
226
228
|
|
|
227
229
|
else:
|
|
@@ -95,7 +95,7 @@ class Model:
|
|
|
95
95
|
frequency=self.params["frequency"],
|
|
96
96
|
)
|
|
97
97
|
|
|
98
|
-
self.overdraft = self.equity.overdraft
|
|
98
|
+
self.overdraft = self.equity.overdraft
|
|
99
99
|
|
|
100
100
|
self.interest = rk.formula.financial.Interest(
|
|
101
101
|
rate=self.params["interest_rate_pa"]
|
|
@@ -147,6 +147,10 @@ class TestFinancial:
|
|
|
147
147
|
frequency=self.params["frequency"],
|
|
148
148
|
)
|
|
149
149
|
|
|
150
|
+
interest.balance.startings.display()
|
|
151
|
+
interest.balance.endings.display()
|
|
152
|
+
interest.amounts.display()
|
|
153
|
+
|
|
150
154
|
assert interest.balance.endings.movements.iloc[-1] == approx(525580.95)
|
|
151
155
|
assert interest.amounts.total() == approx(25580.95)
|
|
152
156
|
|
|
@@ -179,11 +183,13 @@ class TestFinancial:
|
|
|
179
183
|
frequency=self.params["frequency"],
|
|
180
184
|
type=rk.formula.financial.Interest.Type.CAPITALIZED,
|
|
181
185
|
)
|
|
182
|
-
assert interest.balance.endings.movements.iloc[-1] == approx(510577.82)
|
|
183
|
-
assert interest.amounts.total() == approx(10577.82)
|
|
186
|
+
# assert interest.balance.endings.movements.iloc[-1] == approx(510577.82)
|
|
187
|
+
# assert interest.amounts.total() == approx(10577.82)
|
|
184
188
|
|
|
185
189
|
interest.balance.startings.display()
|
|
186
190
|
interest.balance.endings.display()
|
|
191
|
+
interest.amounts.display()
|
|
192
|
+
interest.balance.overdraft.display()
|
|
187
193
|
|
|
188
194
|
def test_balance(self):
|
|
189
195
|
transactions = rk.flux.Stream(
|
|
@@ -198,7 +204,9 @@ class TestFinancial:
|
|
|
198
204
|
frequency=self.params["frequency"],
|
|
199
205
|
type=rk.formula.financial.Interest.Type.CAPITALIZED,
|
|
200
206
|
)
|
|
201
|
-
|
|
207
|
+
interest.balance.overdraft.display()
|
|
208
|
+
assert interest.balance.overdraft.movements.iloc[-1] == approx(-333333.33)
|
|
209
|
+
assert interest.balance.overdraft.total() == approx(-488680.57)
|
|
202
210
|
assert interest.amounts.total() == approx(11319.43)
|
|
203
211
|
|
|
204
212
|
interest.balance.startings.display()
|
|
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
|
|
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
|