<jupyter_start><jupyter_text><jupyter_code>!pip install okama
import matplotlib.pyplot as plt
plt.rcParams['figure.figsize'] = [12.0, 6.0]

import okama as ok<jupyter_output><empty_output><jupyter_text>*okama* has several methods to forecast portfolio perfomance:- according to historical data (without distribution models)- according to normal distribution- according to lognormal distribution  Testing distribution Before we use normal or lognormal distribution models, we should test the portfolio returns historical distribution and see if it fits.  There is a notebook dedicated to backtesting distributions.<jupyter_code>ls = ['GLD.US', 'SPY.US', 'VNQ.US', 'AGG.US']
al = ok.AssetList(ls, inflation=False)
al
al.names
al.kstest(distr='norm')
al.kstest(distr='lognorm')<jupyter_output><empty_output><jupyter_text>We see that at least SPY is failed zero hypothesis  (didn't match 5% threshold) for both normal and lognormal distributions.  But AGG has distribution close to normal. For GLD lognormal fits slightly better.  Now we can construct the portfolio.<jupyter_code>weights = [0.20, 0.10, 0.10, 0.60]
pf = ok.Portfolio(ls, ccy='USD', weights=weights, inflation=False)
pf
pf.table
pf.kstest(distr='norm')
pf.kstest(distr='lognorm')<jupyter_output><empty_output><jupyter_text>As expected Kolmogorov-Smirnov test shows that normal distribution fits much better. AGG has 60% weight in the allocation.  Forecasting The most intuitive way to present forecasted portfolio performance is to use **plot_forecast** method to draw the accumulated return chart (historical return and forecasted data).  It is possible to use arbitrary percentiles set (10, 50, 90 is a default attribute value).  Maximum forecast period is limited with 1/2 historical data period. For example, if the historical data period is 10 years, it's possible to use forecast periods up to 5 years.<jupyter_code>pf.plot_forecast(distr='norm', years=5, figsize=(12,5));<jupyter_output><empty_output><jupyter_text>Another way to visualize the normally distributed random forecasted data is with Monte Carlo simulation ...<jupyter_code>pf.plot_forecast_monte_carlo(distr='norm', years=5, n=20)  # Generates 20 forecasted wealth indexes (for random normally distributed returns time series)<jupyter_output><empty_output><jupyter_text>We can get numeric CAGR percentiles for each period with **percentile_distribution_cagr** method. To get credible forecast results high n values should be used.<jupyter_code>pf.percentile_distribution_cagr(distr='norm', years=5, percentiles=[1, 20, 50, 80, 99], n=10000)<jupyter_output><empty_output><jupyter_text>The same could be used to get VAR (Value at Risk):<jupyter_code>pf.percentile_distribution_cagr(distr='norm', years=1, percentiles=[1], n=10000)  # 1% perecentile corresponds to 99% confidence level<jupyter_output><empty_output><jupyter_text>One-year VAR (99% confidence level) is equal to 8%. It's a fair value for conservative portfolio.  The probability of getting negative result in forecasted period is the percentile rank for zero CAGR value (score=0).<jupyter_code>pf.percentile_inverse_cagr(distr='norm', years=1, score=0, n=10000) # one year period<jupyter_output><empty_output><jupyter_text>Lognormal distribution Some financial assets returns have returns distribution close to lognormal.  The same calculations could be repeated for lognormal distribution by setting dist='lognorm'.<jupyter_code>ln = ok.Portfolio(['EDV.US'], inflation=False)
ln
ln.names<jupyter_output><empty_output><jupyter_text>We can visualize the distribution and compare it with the lognormal PDF (Probability Distribution Function).<jupyter_code>ln.plot_hist_fit(distr='lognorm', bins=30)
ln.kstest(distr='norm')  # Kolmogorov-Smirnov test for normal distribution
ln.kstest(distr='lognorm')  # Kolmogorov-Smirnov test for lognormal distribution<jupyter_output><empty_output><jupyter_text>What is more important Kolmogorov-Smirnov test shows that historical distribution is slightly closer to lognormal.  Therefore, we can use lognormal distribution to forecast.<jupyter_code>ln.plot_forecast(distr='lognorm', percentiles=[30, 50, 70], years=2, n=10000);
pf.percentile_distribution_cagr(distr='lognorm', years=1, percentiles=[1, 20, 50, 80, 99], n=10000)<jupyter_output><empty_output><jupyter_text>Forecasting using historical data If it's not possible to fit the data to normal or lognormal distributions, percentiles from the historical data could be used.<jupyter_code>ht = ok.Portfolio(['SPY.US'])
ht
ht.kstest('norm')
ht.kstest('lognorm')<jupyter_output><empty_output><jupyter_text>Kolmogorov-Smirnov test is not passing 5% threshold... Big deviation in the tails could be seen in Quantile-Quantile Plot.<jupyter_code>ht.plot_percentiles_fit('norm')<jupyter_output><empty_output><jupyter_text>Then we can use percentiles from the historical data to forecast.<jupyter_code>ht.plot_forecast(years=5, percentiles=[20, 50, 80]);
ht.percentile_wealth(distr='hist', years=5)<jupyter_output><empty_output><jupyter_text>Quantitative CAGR percentiles could be obtained from **percentile_history_cagr** method:<jupyter_code>ht.percentile_history_cagr(years=5)<jupyter_output><empty_output><jupyter_text>We can visualize the same to see how CAGR ranges were narrowing with investment horizon.<jupyter_code>ht.percentile_history_cagr(years=5).plot();<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.09510515540666295, 'fraction_numerical': 0.021589428624604504, 'mean_word_length': 7.626003210272873, 'pattern_counts': {'":': 0, '<': 65, '<?xml version=': 0, '>': 65, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '16', 'dir': 'jupyter-structured-clean-dedup', 'id': '10438', 'license': 'MIT', 'n_tokens_mistral': 1762, 'n_tokens_neox': 1655, 'n_words': 508, 'path': 'examples/07 forecasting.ipynb', 'repo_name': 'Aadityajoshi151/okama', 'size': '5373'}