Skip to content

Commit

Permalink
test: add portfolio return test
Browse files Browse the repository at this point in the history
- `_calc_portfolio_return` method in `StateChangeComponents` is refactored to improve code readability and maintainability.
- A test case for calculating the portfolio return is added to `test_state_change.py`. The calculation is tested based on given inputs using the `Components` mock object.
  • Loading branch information
chriskelly committed Oct 29, 2023
1 parent 745773d commit ab6ed47
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 8 deletions.
10 changes: 7 additions & 3 deletions app/models/financial/state_change.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,7 @@ def __init__(self, state: State, controllers: Controllers):
@staticmethod
def _gen_net_transactions(components: StateChangeComponents) -> _NetTransactions:
income = Income(components)
portfolio_return = components.state.net_worth * np.dot(
components.economic_data.asset_rates, components.allocation
)
portfolio_return = StateChangeComponents._calc_portfolio_return(components)
costs = StateChangeComponents._gen_costs(
components=components, income=income, portfolio_return=portfolio_return
)
Expand All @@ -126,6 +124,12 @@ def _gen_net_transactions(components: StateChangeComponents) -> _NetTransactions
),
)

@staticmethod
def _calc_portfolio_return(components: StateChangeComponents) -> float:
return components.state.net_worth * np.dot(
components.economic_data.asset_rates, components.allocation
)

@staticmethod
def _gen_costs(
components: StateChangeComponents, income: Income, portfolio_return: float
Expand Down
32 changes: 27 additions & 5 deletions tests/models/financial/test_state_change.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,40 @@ def test_income(
):
"""Test that income is summed up correctly"""
fake_values = [1, 2, 3, 4]
controllers_mock.job_income.get_total_income = lambda *_: fake_values[0]
controllers_mock.social_security.calc_payment = lambda *_: (
controllers_mock.job_income.get_total_income = lambda *_, **__: fake_values[0]
controllers_mock.social_security.calc_payment = lambda *_, **__: (
fake_values[1],
fake_values[2],
)
controllers_mock.pension.calc_payment = lambda *_: fake_values[3]
controllers_mock.pension.calc_payment = lambda *_, **__: fake_values[3]
components_mock.controllers = controllers_mock
components_mock.state = first_state
income = Income(components_mock)
assert float(income) == pytest.approx(sum(fake_values))


def test_portfolio_return(
mocker,
components_mock: StateChangeComponents,
):
"""Test that portfolio return is calculated correctly"""
net_worth = 100
asset_rates = [0.2, -0.2]
allocation = [0.4, 0.6]
dot_product = -0.04
expected_return = net_worth * dot_product

components_mock.state = mocker.MagicMock()
components_mock.state.net_worth = net_worth
components_mock.economic_data = mocker.MagicMock()
components_mock.economic_data.asset_rates = asset_rates
components_mock.allocation = allocation
components_mock.controllers = mocker.MagicMock()

portfolio_return = StateChangeComponents._calc_portfolio_return(components_mock)
assert portfolio_return == pytest.approx(expected_return)


class TestCalcSpending:
yearly_amount = 50
retirement_change = -0.1
Expand All @@ -62,14 +84,14 @@ def components_mock(

def test_while_working(self, components_mock: StateChangeComponents):
"""Spending should be unadjusted while working"""
components_mock.controllers.job_income.is_working = lambda *_: True
components_mock.controllers.job_income.is_working = lambda *_, **__: True
assert StateChangeComponents._calc_spending(components_mock) == pytest.approx(
-self.yearly_amount / INTERVALS_PER_YEAR * self.inflation
)

def test_after_working(self, components_mock: StateChangeComponents):
"""Spending should be adjusted by the retirement change after working"""
components_mock.controllers.job_income.is_working = lambda *_: False
components_mock.controllers.job_income.is_working = lambda *_, **__: False
assert StateChangeComponents._calc_spending(components_mock) == pytest.approx(
-self.yearly_amount
/ INTERVALS_PER_YEAR
Expand Down

0 comments on commit ab6ed47

Please sign in to comment.