okama.PortfolioDCF

class PortfolioDCF(parent, discount_rate=None, use_discounted_values=False)

Bases: object

Class to access discounted cash flow (DCF) methods of Portfolio. All methods can be used in Portfolio instances trough construction: ` pf = Portfolio() pf.dcf.weatlh_index pf.dсf.cashflow_pv `

Parameters:
discount_rate: float or None, default None

Cash flow discount rate required to calculate Present value (PV) or Future (FV) of cashflow. If not provided geometric mean of inflation is taken. For portfolios without inflation the default value from settings is used.

use_discounted_values: bool, default False

Defines whether to use discounted values in backtesting wealth indexes. If True the initial investments and cashflow size are discounted.

Methods & Attributes

cashflow_pv

The discounted value (PV) of the cash flow amount (contributions/withdrawals) at the historical first date.

discount_rate

Portfolio cash flow discount rate.

find_the_largest_withdrawals_size(...[, ...])

Find the largest withdrawals size for Monte Carlo simulation according to Cashflow Strategy.

initial_investment_fv

The future value (FV) of the initial investments at the end of forecast period.

initial_investment_pv

The discounted value (PV) of the initial investments at the historical first date.

monte_carlo_survival_period([threshold])

Generate a survival period distribution for a portfolio with cash flows by Monte Carlo simulation.

monte_carlo_wealth

Portfolio random wealth indexes with cash flows (withdrawals/contributions) by Monte Carlo simulation.

monte_carlo_wealth_pv

Portfolio discounted random wealth indexes with cash flows (withdrawals/contributions) by Monte Carlo simulation.

plot_forecast_monte_carlo([backtest, figsize])

Plot Monte Carlo simulation for portfolio future wealth indexes optionally together with historical wealth index.

set_mc_parameters(distribution, period, number)

Add Monte Carlo simulation parameters to PortfolioDCF.

survival_date_hist([threshold])

Get the date when the portfolio balance become negative considering withdrawals on the historical data.

survival_period_hist([threshold])

Calculate the period when the portfolio has positive balance considering withdrawals on the historical data.

use_discounted_values

The value of attribute to define weather to use discounted values in backtesting wealth indexes.

wealth_index

Wealth index time series for the portfolio with cash flow (contributions and withdrawals).

wealth_index_with_assets

Wealth index time series for the portfolio and all assets considering cash flow (contributions and withdrawals).

property discount_rate

Portfolio cash flow discount rate.

Returns:
float

Cash flow discount rate.

property use_discounted_values

The value of attribute to define weather to use discounted values in backtesting wealth indexes. If True the initial investments and cashflow size are discounted.

Returns:
bool

Weather to use discounted values in backtesting wealth indexes

set_mc_parameters(distribution, period, number)

Add Monte Carlo simulation parameters to PortfolioDCF.

Parameters:
distribution: str

The type of a distribution to generate random rate of return. Allowed values for distribution: -‘norm’ for normal distribution -‘lognorm’ for lognormal distribution -‘t’ for Student’s (t-distribution)

period: int

Forecast period for portfolio wealth index time series (in years).

number: int

Number of random wealth indexes to generate with Monte Carlo simulation.

Examples

>>> import matplotlib.pyplot as plt
>>> pf = ok.Portfolio(first_date="2015-01", last_date="2024-10")  # create Portfolio with default parameters
>>> # Set Monte Carlo parameters
>>> pf.dcf.set_mc_parameters(distribution="lognorm", period=10, number=100)
>>> # Set the cash flow strategy. It's required to generate random wealth indexes.
>>> ind = ok.IndexationStrategy(pf) # create IndexationStrategy linked to the portfolio
>>> ind.initial_investment = 10_000  # add initial investments size
>>> ind.frequency = "year"  # set cash flow frequency
>>> ind.amount = -1_500  # set withdrawal size
>>> ind.indexation = "inflation"
>>> # Assign the strategy to Portfolio
>>> pf.dcf.cashflow_parameters = ind
>>> pf.dcf.use_discounted_values = False  # do not discount initial investment value
>>> # Plot wealth index with cash flow
>>> pf.dcf.wealth_index.plot()
>>> plt.show()
../_images/okama-PortfolioDCF-1.png
property wealth_index

Wealth index time series for the portfolio with cash flow (contributions and withdrawals).

Wealth index (Cumulative Wealth Index) is a time series that presents the value of portfolio over historical time period considering cash flows.

Accumulated inflation time series is added if inflation=True in the Portfolio.

If there is no cash flow, Wealth index is obtained from the accumulated return multiplicated by the initial investments. That is: initial_amount_pv * (Acc_Return + 1)

Returns:
Time series of wealth index values for portfolio and accumulated inflation.

Examples

>>> import matplotlib.pyplot as plt
>>> pf = ok.Portfolio(['VOO.US', 'GLD.US'], weights=[0.8, 0.2])
>>> ind = ok.IndexationStrategy(pf)  # Set Cash Flow Strategy parameters
>>> ind.initial_investment = 100  # initial investments value
>>> ind.frequency = "year"  # withdrawals frequency
>>> ind.amount = -0.5 * 12  # initial withdrawals amount
>>> ind.indexation = "inflation"  # the indexation is equal to inflation
>>> pf.dcf.cashflow_parameters = ind  # assign the strategy to Portfolio
>>> pf.dcf.wealth_index.plot()
>>> plt.show()
../_images/okama-PortfolioDCF-2.png
property wealth_index_with_assets

Wealth index time series for the portfolio and all assets considering cash flow (contributions and withdrawals).

Wealth index (Cumulative Wealth Index) is a time series that presents the value of portfolio over historical time period. Accumulated inflation time series is added if inflation=True in the Portfolio.

Wealth index is obtained from the accumulated return multiplicated by the initial investments. initial_amount_pv * (Acc_Return + 1)

If there is no cash flow, Wealth index is obtained from the accumulated return multiplicated by the initial investments. That is: initial_amount_pv * (Acc_Return + 1)

Returns:
DataFrame

Time series of wealth index values for portfolio, each asset and accumulated inflation.

Examples

>>> import matplotlib.pyplot as plt
>>> pf = ok.Portfolio(['VOO.US', 'GLD.US'], weights=[0.8, 0.2])
>>> ind = ok.IndexationStrategy(pf)  # Set Cash Flow Strategy parameters
>>> ind.initial_investment = 100  # initial investments value
>>> ind.frequency = "year"  # withdrawals frequency
>>> ind.amount = -0.5 * 12  # initial withdrawals amount
>>> ind.indexation = "inflation"  # the indexation is equal to inflation
>>> pf.dcf.cashflow_parameters = ind  # assign the strategy to Portfolio
>>> pf.dcf.wealth_index_with_assets.plot()
>>> plt.show()
../_images/okama-PortfolioDCF-3.png
survival_period_hist(threshold=0)

Calculate the period when the portfolio has positive balance considering withdrawals on the historical data.

The portfolio survival period (longevity period) depends on the investment strategy: asset allocation, rebalancing, withdrawals rate etc.

Parameters:
thresholdfloat, default 0

The percentage of the initial investments when the portfolio balance considered voided. This parameter is important to use in cash flow strategies with a fixed whtdrawal percentage (PercentageStrategy).

Returns:
float

The portfolio survival period (longevity period) in years.

Examples

>>> pf = ok.Portfolio(
        ['SPY.US', 'AGG.US'],
        ccy='USD',
        first_date='2010-01',
        last_date='2024-10'
    )
>>> # set cash flow strategy
>>> ind = ok.IndexationStrategy(pf)  # create cash flow strategy linked to the portfolio
>>> ind.initial_investment = 10_000  # add initial investment to cash flow strategy
>>> ind.amount = -2_500  # set annual withdrawal size
>>> ind.frequency = "year"  # set withdrawal frequency to year
>>> pf.dcf.cashflow_parameters = ind
>>> # Calculate the historical survival period for the cash flow strategy.
>>> # The balance is considered voided when it's equal to 0 (threshold=0)
>>> pf.dcf.survival_period_hist(threshold=0)
5.1
survival_date_hist(threshold=0)

Get the date when the portfolio balance become negative considering withdrawals on the historical data.

The portfolio survival date (longevity date) depends on the investment strategy: asset allocation, rebalancing, withdrawals rate etc.

Parameters:
thresholdfloat, default 0

The percentage of the initial investments when the portfolio balance considered voided. This parameter is important to use in cash flow strategies with a fixed whtdrawal percentage (PercentageStrategy).

Returns:
pd.Timestamp

The portfolio survival date (longevity period) in years.

Examples

>>> pf = ok.Portfolio(
        ['SPY.US', 'AGG.US'],
        ccy='USD',
        first_date='2010-01',
        last_date='2024-10'
    )
>>> # set cash flow strategy
>>> ind = ok.IndexationStrategy(pf)  # create cash flow strategy linked to the portfolio
>>> ind.initial_investment = 10_000  # add initial investment to cash flow strategy
>>> ind.amount = -2_500  # set annual withdrawal size
>>> ind.frequency = "year"  # set withdrawal frequency to year
>>> pf.dcf.cashflow_parameters = ind
>>> # Calculate the historical survival period for the cash flow strategy
>>> pf.dcf.survival_date_hist(threshold=0)
Timestamp('2015-01-31 00:00:00')
property initial_investment_pv

The discounted value (PV) of the initial investments at the historical first date.

The future value (FV) is defined by initial_amount parameter.

Returns:
float, None

The discounted value (PV) of the initial investments at the historical first date.

Examples

>>> # Get discounted PV value of `initial_investment` for a portfolio with 4 years of history (at 2020-04).
>>> pf = ok.Portfolio(['EQMX.MOEX', 'SBGB.MOEX'], ccy='RUB', last_date="2024-10")
>>> ind = ok.IndexationStrategy(pf)  # create cash flow strategy linked to the portfolio
>>> ind.initial_investment = 10_000  # add initial investment to cash flow strategy
>>> pf.dcf.cashflow_parameters = ind  # assign cash flow strategy to portfolio
>>> pf.dcf.discount_rate = 0.10  # define discount rate as 10%
>>> pf.dcf.initial_investment_pv
6574.643143611553
property initial_investment_fv

The future value (FV) of the initial investments at the end of forecast period.

The forecast period is defined in Monte Carlo parameters (‘period’).

FV is defined by the discount rate and the initial investments: initial_investment_fv = initial_investment * (1 + discount_rate) ** period

When ‘initial_investment’ parameter is not defined, initial_investment_fv set to None.

Returns:
float, None

The future value (FV) of the initial investments.

Examples

>>> # Get discounted FV of initial_investment value for a period of 10 years.
>>> pf = ok.Portfolio(['EQMX.MOEX', 'SBGB.MOEX'], ccy='RUB')
>>> ind = ok.IndexationStrategy(pf)  # create cash flow strategy linked to the portfolio
>>> ind.initial_investment = 10_000  # add initial investment to cash flow strategy
>>> pf.dcf.cashflow_parameters = ind  # assign cash flow strategy to portfolio
>>> pf.dcf.mc.period = 10  # define forecast period
>>> pf.dcf.discount_rate = 0.10  # define discount rate as 10%
>>> pf.dcf.initial_investment_fv
25937.424601000024
property cashflow_pv

The discounted value (PV) of the cash flow amount (contributions/withdrawals) at the historical first date.

PV is defined by the discount rate and the cash flow amount: cashflow_pv = amount / (1 + discount_rate) ** period_length

When cash flow ‘amount’ is not defined, cashflow_pv set to None.

Returns:
float, None

The discounted value (PV) of the cash flow amount at the historical first date.

Examples

>>> # Get discounted PV value of of the cash flow amount for a portfolio with 20 years of history (at 2003-10).
>>> pf = ok.Portfolio(['SPY.US', 'AGG.US'], ccy='USD', last_date="2024-10")
>>> ind = ok.IndexationStrategy(pf)  # create cash flow strategy linked to the portfolio
>>> ind.initial_investment = 10_000  # add initial investment to cash flow strategy
>>> ind.amount = -500  # set withdrawal size
>>> ind.frequency = "year"  # set withdrawal frequency
>>> pf.dcf.cashflow_parameters = ind  # assign cash flow strategy to portfolio
>>> pf.dcf.discount_rate = 0.10  # define discount rate
>>> pf.dcf.cashflow_pv
-68.86557103941368
property monte_carlo_wealth

Portfolio random wealth indexes with cash flows (withdrawals/contributions) by Monte Carlo simulation.

Monte Carlo simulation generates n random monthly time series. Each wealth index is calculated with rate of return time series of a given distribution.

First date of forecasted returns is portfolio last_date. First value for the forecasted wealth indexes is the last historical portfolio index value. It is useful for a chart with historical wealth index and forecasted values.

Returns:
DataFrame

Table with n random wealth indexes monthly time series.

Examples

>>> import matplotlib.pyplot as plt
>>> pf = ok.Portfolio(['SPY.US', 'AGG.US', 'GLD.US'], weights=[.60, .35, .05], rebalancing_period='month')
>>> pf.dcf.set_mc_parameters(distribution="t", period=10, number=100)  # Set Monte Carlo parameters
>>> # set cash flow parameters
>>> ind = ok.IndexationStrategy(pf)  # create cash flow strategy linked to the portfolio
>>> ind.initial_investment = 10_000  # add initial investment to cash flow strategy
>>> ind.amount = -500  # set withdrawal size
>>> ind.frequency = "year"  # set withdrawal frequency
>>> pf.dcf.cashflow_parameters = ind  # assign cash flow strategy to portfolio
>>> pf.dcf.monte_carlo_wealth.plot()
>>> plt.legend("")  # don't show legend for each line
>>> plt.show()
../_images/okama-PortfolioDCF-4.png
property monte_carlo_wealth_pv

Portfolio discounted random wealth indexes with cash flows (withdrawals/contributions) by Monte Carlo simulation.

Random Monte Carlo simulation monthly time series are discounted using discount_rate parameter. Each wealth index is calculated with rate of return time series of a given distribution.

discount_rate parameter can be set in Portfolio.dcf.discount_rate.

Monte Carlo parameters are defined by Portfolio.dcf.set_mc_parameters() method.

Returns:
DataFrame

Table with random discounted wealth indexes monthly time series.

Examples

>>> import matplotlib.pyplot as plt
>>> pf = ok.Portfolio(['SPY.US', 'AGG.US', 'GLD.US'], weights=[.60, .35, .05], rebalancing_period='month')
>>> pc = ok.PercentageStrategy(pf)  # Define withdrawals strategy with fixed percentage
>>> pc.frequency = "year"  # set withdrawals frequency
>>> pc.percentage = -0.08  # investor would take 8% every year
>>> pf.dcf.cashflow_parameters = pc  # Assign the strategy to Portfolio
>>> pf.dcf.discount_rate = 0.05  # set dicount rate value to 5%
>>> pf.dcf.set_mc_parameters(distribution="t", period=10, number=100)  # Set Monte Carlo parameters
>>> df = pf.dcf.monte_carlo_wealth_pv  # calculate discounted random wealth indexes
>>> df.plot()  # create a chart
>>> plt.legend("")  # no legend is required
>>> plt.show()
../_images/okama-PortfolioDCF-5.png
plot_forecast_monte_carlo(backtest=True, figsize=None)

Plot Monte Carlo simulation for portfolio future wealth indexes optionally together with historical wealth index.

Wealth index (Cumulative Wealth Index) is a time series that presents the value of portfolio over time period considering cash flows (portfolio withdrawals/contributions).

Random wealth indexes are generated according to a given distribution.

Parameters:
backtestbool, default ‘True’

Include historical wealth index if ‘True’.

figsize(float, float), optional

Width, height in inches. If None default matplotlib figsize value is used.

Returns:
None

Examples

>>> import matplotlib.pyplot as plt
>>> pf = ok.Portfolio(assets=['SPY.US', 'AGG.US', 'GLD.US'], weights=[.60, .35, .05], rebalancing_period='year')
>>> # Set Monte Carlo parameters
>>> pf.dcf.set_mc_parameters(distribution="norm", period=50, number=200)
>>> # set cash flow parameters
>>> ind = ok.IndexationStrategy(pf)  # create cash flow strategy linked to the portfolio
>>> ind.initial_investment = 10_000  # add initial investment to cash flow strategy
>>> ind.amount = -500  # set withdrawal size
>>> ind.frequency = "year"  # set withdrawal frequency
>>> pf.dcf.cashflow_parameters = ind  # assign cash flow strategy to portfolio
>>> pf.dcf.plot_forecast_monte_carlo(backtest=True)
>>> plt.yscale("log")  # Y-axis has logarithmic scale
>>> plt.show()
../_images/okama-PortfolioDCF-6.png
monte_carlo_survival_period(threshold=0)

Generate a survival period distribution for a portfolio with cash flows by Monte Carlo simulation.

Analyzing the result, finding “min”, “max” and percentiles it’s possible to see for how long will last the investment strategy - possible longevity period.

Parameters:
thresholdfloat, default 0

The percentage of the initial investments when the portfolio balance considered voided. This parameter is important to use in cash flow strategies with a fixed whtdrawal percentage (PercentageStrategy).

Returns:
Series

Survival period distribution for a portfolio with cash flows.

Examples

>>> pf = ok.Portfolio(['SPY.US', 'AGG.US', 'GLD.US'], weights=[.60, .35, .05])
>>> # set Monte Carlos parameters
>>> pf.dcf.set_mc_parameters(
...        distribution="t",  # use Student's distribution (t-distribution)
...        period=50,  # make forecast for 50 years
...        number=200  # create 200 randow wealth indexes
...    )
>>> # Set Cash Flow parameters
>>> pc = ok.PercentageStrategy(pf)  # create PercentageStrategy linked to the portfolio
>>> pc.initial_investment = 10_000  # add initial investments size
>>> pc.frequency = "year"  # set cash flow frequency
>>> pc.percentage = -0.20  # set withdrawal percentage
>>> # Assign the strategy to Portfolio
>>> pf.dcf.cashflow_parameters = pc
>>> s = pf.dcf.monte_carlo_survival_period(threshold=0.10)  # the balance is considered voided at 10%
>>> s.min()
np.float64(10.5)
>>> s.max()
np.float64(33.5)
>>> s.mean()
np.float64(17.9055)
>>> s.quantile(50 / 100)
np.float64(17.5)
find_the_largest_withdrawals_size(withdrawal_steps, confidence_level, goal, threshold=0, target_survival_period=25)

Find the largest withdrawals size for Monte Carlo simulation according to Cashflow Strategy.

It’s possible to find the largest withdrawl with 2 kind of goals:

— ‘maintain_balance’ to keep the purchasing power of the invesments after inflation

for the whole period defined in Monte Carlo parameteres.

— ‘survival_period’ to keep positive balance for a period defined by ‘target_survival_period’.

The method works with IndexationStrategy and PercentageStrategy only.

The withdrawal size defined in cash flow strategy must be negative.

Parameters:
withdrawal_stepsint

The number of intermediate steps during the iteration of values fom maximum to minimum of the withdrawal size. The withdrawal size varies from 100% of the initial investment to zero.

confidence_levelfloat

Confidence level must be form 0 to 1. Confidence level defines the percentile of Monte Carlo time series. 0.01 or 0.05 are the examples of “bad” scenarios. 0.50 is mediane (50% percentile). 0.95 or 0.99 are optimiststic scenarios.

goal{‘maintain_balance’, ‘survival_period’}

‘maintain_balance’ - the goal is to keep the purchasing power of the invesments after inflation for the whole period defined in Monte Carlo parameteres. ‘survival_period’ - the goal is to keep positive balance for a period defined by ‘target_survival_period’.

thresholdfloat, default 0

The percentage of initial investments when the portfolio balance is considered voided. Important for the “fixed_percentage” Cash flow strategy.

target_survival_period: int, default 25

The smallest acceptable survival period. It wokrs with the ‘survival_period’ goal only.

Returns:
float

the largest withdrawals size according to Cashflow Strategy.

Examples

>>> pf = ok.Portfolio(
 ...       assets=["MCFTR.INDX", "RUCBTRNS.INDX"],
 ...       weights=[.3, .7],
 ...       inflation=True,
 ...       ccy="RUB",
 ...       rebalancing_period="year",
 ...   )
>>> # Fixed Percentage strategy
>>> pc = ok.PercentageStrategy(pf)
>>> pc.initial_investment = 10_000
>>> pc.frequency = "year"
>>> # Assign a strategy
>>> pf.dcf.cashflow_parameters = pc
>>> # Set Monte Carlo parameters
>>> pf.dcf.set_mc_parameters(
...    distribution="norm",
...    period=50,
...    number=200
...)
>>> pf.dcf.find_the_largest_withdrawals_size(
...    withdrawal_steps=30,
...    confidence_level=0.50,
...    goal="survival_period",
...    threshold=0.05,
...    target_survival_period=25
...)
np.float64(-0.10344827586206895)