Skip to content

Commit

Permalink
test: add tests for DCF methods in Portfolio
Browse files Browse the repository at this point in the history
  • Loading branch information
chilango74 committed Feb 22, 2024
1 parent ed5245e commit e9dce7e
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 13 deletions.
38 changes: 38 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,9 @@ def init_portfolio_values():
inflation=True,
rebalancing_period="year",
symbol="pf1.PF",
cashflow=0,
initial_amount=1_000,
discount_rate=None,
)


Expand Down Expand Up @@ -152,6 +155,35 @@ def portfolio_dividends(init_portfolio_values):
return ok.Portfolio(**_portfolio_dividends)


# DCF Scenarios
@pytest.fixture(scope="package")
def portfolio_cashflows_inflation(init_portfolio_values):
_portfolio_cashflows_inflation = deepcopy(init_portfolio_values)
_portfolio_cashflows_inflation["cashflow"] = -100
_portfolio_cashflows_inflation["initial_amount"] = 100_000
return ok.Portfolio(**_portfolio_cashflows_inflation)


@pytest.fixture(scope="package")
def portfolio_cashflows_NO_inflation(init_portfolio_values):
_portfolio_cashflows_NO_inflation = deepcopy(init_portfolio_values)
_portfolio_cashflows_NO_inflation["cashflow"] = -100.
_portfolio_cashflows_NO_inflation["initial_amount"] = 100_000.
_portfolio_cashflows_NO_inflation["inflation"] = False
_portfolio_cashflows_NO_inflation["discount_rate"] = 0.09
return ok.Portfolio(**_portfolio_cashflows_NO_inflation)


@pytest.fixture(scope="package")
def portfolio_cashflows_NO_inflation_NO_discount_rate(init_portfolio_values):
_portfolio_cashflows_NO_inflation_NO_discount_rate = deepcopy(init_portfolio_values)
_portfolio_cashflows_NO_inflation_NO_discount_rate["cashflow"] = -100.
_portfolio_cashflows_NO_inflation_NO_discount_rate["initial_amount"] = 100_000.
_portfolio_cashflows_NO_inflation_NO_discount_rate["inflation"] = False
_portfolio_cashflows_NO_inflation_NO_discount_rate["discount_rate"] = None
return ok.Portfolio(**_portfolio_cashflows_NO_inflation_NO_discount_rate)


# Macro
@pytest.fixture(scope="function")
def _init_inflation(request):
Expand All @@ -173,6 +205,12 @@ def _init_indicator(request):
request.cls.cape10_usd = ok.Indicator(symbol="USA_CAPE10.RATIO", first_date="2021-01", last_date="2022-02")


@pytest.fixture(scope="package")
def portfolio_short_history(init_portfolio_values):
_portfolio_short_history = deepcopy(init_portfolio_values)
_portfolio_short_history["first_date"] = "2019-02"
return ok.Portfolio(**_portfolio_short_history)

# Efficient Frontier Single Period
@pytest.fixture(scope="module")
def init_efficient_frontier_values1():
Expand Down
79 changes: 66 additions & 13 deletions tests/test_portfolio.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,12 @@ def test_ror(portfolio_rebalanced_month):


def test_wealth_index(portfolio_rebalanced_year):
assert portfolio_rebalanced_year.wealth_index.iloc[-1, 1] == approx(1315.848, rel=1e-2)
assert portfolio_rebalanced_year.wealth_index.iloc[-1, 1] == approx(999.99, rel=1e-2)


def test_wealth_index_with_assets(portfolio_rebalanced_year, portfolio_no_inflation):
result = portfolio_rebalanced_year.wealth_index_with_assets.iloc[-1, :].values
assert_allclose(np.array(result), np.array([2490.845572, 2079.757278, 2924.031272, 1315.848632]), rtol=1e-02)
assert_allclose(np.array(result), np.array([2259.244689, 2056.11199, 2889.930097, 1310.606208]), rtol=1e-02)


def test_weights(portfolio_rebalanced_month):
Expand Down Expand Up @@ -114,7 +114,7 @@ def test_assets_close_monthly(portfolio_not_rebalanced):


def test_close_monthly(portfolio_not_rebalanced):
assert portfolio_not_rebalanced.close_monthly.iloc[-1] == approx(2501.89, rel=1e-2)
assert portfolio_not_rebalanced.close_monthly.iloc[-1] == approx(1731.40, rel=1e-2)


def test_get_assets_dividends(portfolio_dividends):
Expand All @@ -127,16 +127,16 @@ def test_get_assets_dividends(portfolio_dividends):


def test_number_of_securities(portfolio_not_rebalanced, portfolio_dividends):
assert portfolio_not_rebalanced.number_of_securities.iloc[-1, 0] == approx(1.798, rel=1e-2) # RGBITR.INDX
assert portfolio_not_rebalanced.number_of_securities.iloc[-1, 1] == approx(0.2787, abs=1e-2) # MCFTR.INDX
assert portfolio_not_rebalanced.number_of_securities.iloc[-1, 0] == approx(1.244, rel=1e-2) # RGBITR.INDX
assert portfolio_not_rebalanced.number_of_securities.iloc[-1, 1] == approx(0.1928, abs=1e-2) # MCFTR.INDX
# with dividends
assert portfolio_dividends.number_of_securities.iloc[-1, 0] == approx(4.185, rel=1e-2) # SBER.MOEX
assert portfolio_dividends.number_of_securities.iloc[-1, 1] == approx(0.448, abs=1e-2) # T.US
assert portfolio_dividends.number_of_securities.iloc[-1, 0] == approx(2.770, rel=1e-2) # SBER.MOEX
assert portfolio_dividends.number_of_securities.iloc[-1, 1] == approx(0.296, abs=1e-2) # T.US
assert portfolio_dividends.number_of_securities.iloc[-1, 2] == approx(0.004137, abs=1e-2) # GNS.LSE


def test_dividends(portfolio_dividends):
assert portfolio_dividends.dividends.iloc[-1] == approx(14.70, rel=1e-2)
assert portfolio_dividends.dividends.iloc[-1] == approx(9.73, rel=1e-2)


def test_dividend_yield(portfolio_dividends):
Expand Down Expand Up @@ -305,14 +305,33 @@ def test_get_rolling_cagr_failing_no_inflation(portfolio_no_inflation):


def test_monte_carlo_wealth(portfolio_rebalanced_month):
assert portfolio_rebalanced_month._monte_carlo_wealth(distr="norm", years=1, n=1000).iloc[-1, :].mean() == approx(
3005.763, rel=1e-1
df = portfolio_rebalanced_month._monte_carlo_wealth(
first_value=1000,
distr="norm",
years=1,
n=1000
)
assert df.shape == (12, 1000)
assert df.iloc[-1, :].mean() == approx(
1179.63, rel=1e-1
)


def test_monte_carlo_returns_ts(portfolio_rebalanced_month):
df = portfolio_rebalanced_month.monte_carlo_returns_ts(
distr="lognorm",
years=1,
n=1000
)
assert df.shape == (12, 1000)
assert df.iloc[-1, :].mean() == approx(
0.0156, abs=1e-1
)


@mark.parametrize(
"distribution, expected",
[("hist", 2931.484), ("norm", 3062.036), ("lognorm", 3030.9024)],
[("hist", 2210.96), ("norm", 1188.55), ("lognorm", 1170.76)],
)
def test_percentile_wealth(portfolio_rebalanced_month, distribution, expected):
dic = portfolio_rebalanced_month.percentile_wealth(distr=distribution, years=1, n=100, percentiles=[50])
Expand All @@ -322,8 +341,6 @@ def test_percentile_wealth(portfolio_rebalanced_month, distribution, expected):
def test_forecast_monte_carlo_cagr(portfolio_rebalanced_month):
dic = portfolio_rebalanced_month.percentile_distribution_cagr(years=2, distr="lognorm", n=100, percentiles=[50])
assert dic[50] == approx(0.1905, abs=5e-2)
with pytest.raises(ValueError):
portfolio_rebalanced_month.percentile_distribution_cagr(years=10, distr="lognorm", n=100, percentiles=[50])


def test_skewness(portfolio_rebalanced_month):
Expand Down Expand Up @@ -367,3 +384,39 @@ def test_init_portfolio_failing():
ok.Portfolio(["RGBITR.INDX", "MCFTR.INDX"], weights=[0.1, 0.2, 0.7])
with pytest.raises(ValueError, match="Weights sum is not equal to one."):
ok.Portfolio(["RGBITR.INDX", "MCFTR.INDX"], weights=[0.1, 0.2])


# DCF Methods
def test_dcf_discount_rate(
portfolio_cashflows_inflation,
portfolio_cashflows_NO_inflation,
portfolio_cashflows_NO_inflation_NO_discount_rate
):
assert portfolio_cashflows_inflation.discount_rate == approx(0.0554, abs=1e-3) # average inflation
assert portfolio_cashflows_NO_inflation.discount_rate == approx(0.09, abs=1e-3) # defined discount rate
assert portfolio_cashflows_NO_inflation_NO_discount_rate.discount_rate == approx(0.05, abs=1e-3) # default rate


def test_dcf_wealth_index(portfolio_cashflows_inflation, portfolio_cashflows_NO_inflation):
assert portfolio_cashflows_inflation.wealth_index.iloc[-1, 0] == approx(164459.78, rel=1e-2)
assert portfolio_cashflows_inflation.wealth_index.iloc[-1, 1] == approx(100050.78, rel=1e-2)
assert portfolio_cashflows_NO_inflation.wealth_index.iloc[-1, 0] == approx(164459.78, rel=1e-2)


def test_survival_period(portfolio_cashflows_inflation):
assert portfolio_cashflows_inflation.survival_period == approx(5.0, rel=1e-2)


def test_survival_date(portfolio_cashflows_inflation):
assert portfolio_cashflows_inflation.survival_date == pd.to_datetime("2020-01")


def test_cashflow_pv(portfolio_cashflows_inflation, portfolio_cashflows_NO_inflation_NO_discount_rate):
assert portfolio_cashflows_inflation.cashflow_pv == approx(-76.33, rel=1e-2)
assert portfolio_cashflows_NO_inflation_NO_discount_rate.cashflow_pv == approx(-78.35, rel=1e-2)


def test_initial_amount_pv(portfolio_cashflows_inflation, portfolio_cashflows_NO_inflation_NO_discount_rate):
assert portfolio_cashflows_inflation.initial_amount_pv == approx(76339.31, rel=1e-2)
assert portfolio_cashflows_NO_inflation_NO_discount_rate.initial_amount_pv == approx(78352.61, rel=1e-2)

0 comments on commit e9dce7e

Please sign in to comment.