Planning tools
Modern Portfolio Theory (MPT)
The modern portfolio theory (MPT) is one of the oldest applications in modern finance still used today. The technical implication can be found in the theory subsection. We will now demonstrate how MPT is implemented in the Peccon package and lastly cover some import limitations and recommendations of the tool.
Example
First extract the daily price data of all the assets you are considering in your portfolio.
using Peccon
Tickers = ["IUSA.AS", "IBCI.AS", "IEMA.AS", "WTCH.AS", "VWRL.AS"];
# data = data_alpha(Tickers, "your_api_key", 252);
data[1][1:5,:]
5 rows × 7 columns
timestamp | open | high | low | close | volume | ticker | |
---|---|---|---|---|---|---|---|
SubStrin… | Float64 | Float64 | Float64 | Float64 | Int64 | String | |
1 | 2025-01-31 | 58.349 | 58.731 | 58.343 | 58.563 | 47119 | IUSA.AS |
2 | 2025-01-30 | 57.993 | 58.16 | 57.6 | 57.797 | 24106 | IUSA.AS |
3 | 2025-01-29 | 58.092 | 58.211 | 57.793 | 57.82 | 41871 | IUSA.AS |
4 | 2025-01-28 | 57.488 | 57.85 | 57.236 | 57.654 | 44404 | IUSA.AS |
5 | 2025-01-27 | 57.171 | 57.206 | 56.171 | 56.774 | 64342 | IUSA.AS |
Then calculated the daily log returns for each asset in the portfolio.
returns = daily_returns(data, Tickers);
returns[1:5,:]
5 rows × 5 columns
IUSA.AS | IBCI.AS | IEMA.AS | WTCH.AS | VWRL.AS | |
---|---|---|---|---|---|
Float64 | Float64 | Float64 | Float64 | Float64 | |
1 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
2 | 0.0131662 | 0.0054125 | 0.00324697 | 0.0223171 | 0.00933904 |
3 | -0.000397865 | 0.00205919 | 0.0117772 | -0.00816234 | 0.00367188 |
4 | 0.00287511 | -0.00061382 | 0.0156543 | 0.00259526 | 0.00412858 |
5 | 0.0153812 | -0.000131484 | 0.00616955 | 0.0275434 | 0.0120403 |
Subsequently, simulate 5000 possible portfolio combinations with the assets in the portfolio.
port_sim = sim_mpt(returns);
port_sim[1:5,:]
5 rows × 8 columns
exp_return | port_var | weight_IUSA.AS | weight_IBCI.AS | weight_IEMA.AS | weight_WTCH.AS | weight_VWRL.AS | port_std | |
---|---|---|---|---|---|---|---|---|
Float64 | Float64 | Float64 | Float64 | Float64 | Float64 | Float64 | Float64 | |
1 | 0.181331 | 0.0149429 | 0.204309 | 0.170985 | 0.208544 | 0.311248 | 0.104914 | 0.122241 |
2 | 0.212908 | 0.0199284 | 0.194008 | 0.0334221 | 0.0721905 | 0.356203 | 0.344176 | 0.141168 |
3 | 0.201239 | 0.0166689 | 0.270243 | 0.1019 | 0.0696849 | 0.292321 | 0.265852 | 0.129108 |
4 | 0.143812 | 0.00929965 | 0.131702 | 0.393206 | 0.0524517 | 0.252973 | 0.169667 | 0.0964347 |
5 | 0.165391 | 0.00969673 | 0.275364 | 0.159073 | 0.34354 | 0.0186386 | 0.203384 | 0.098472 |
Plot the expected return and variance of each simulated portfolio to visualize the efficient frontier.
using StatsPlots
@df port_sim scatter(:port_var, :exp_return)
savefig("sim_fig.svg"); nothing # hide
Resolving package versions...
Updating `~/work/Peccon.jl/Peccon.jl/docs/Project.toml`
[f3b207a7] + StatsPlots v0.15.7
No Changes to `~/work/Peccon.jl/Peccon.jl/docs/Manifest.toml`
qt.qpa.xcb: could not connect to display
qt.qpa.plugin: From 6.5.0, xcb-cursor0 or libxcb-cursor0 is needed to load the Qt xcb platform plugin.
qt.qpa.plugin: Could not load the Qt platform plugin "xcb" in "" even though it was found.
This application failed to start because no Qt platform plugin could be initialized. Reinstalling the application may fix this problem.
Available platform plugins are: eglfs, offscreen, vnc, minimal, minimalegl, xcb, linuxfb, vkkhrdisplay.
Aborted (core dumped)
connect: Connection refused
GKS: can't connect to GKS socket application
GKS: Open failed in routine OPEN_WS
GKS: GKS not in proper state. GKS must be either in the state WSOP or WSAC in routine ACTIVATE_WS
calculate the efficient frontier of the combinations of stocks.
port_opt = opt_mpt(returns, 0.0:0.02:2.0, 0.00) ;
port_opt[1:5,:]
5 rows × 9 columns
exp_return | port_var | risk_aversion | weight_IUSA.AS | weight_IBCI.AS | weight_IEMA.AS | weight_WTCH.AS | weight_VWRL.AS | port_std | |
---|---|---|---|---|---|---|---|---|---|
Float64 | Float64 | Float64 | Float64 | Float64 | Float64 | Float64 | Float64 | Float64 | |
1 | 0.0415182 | 0.00232498 | 0.0 | 8.34115e-17 | 0.884329 | 0.0292085 | 1.38247e-18 | 0.086463 | 0.048218 |
2 | 0.0746304 | 0.00270269 | 0.02 | 0.217538 | 0.744574 | 0.037888 | 2.50826e-19 | 6.45323e-18 | 0.0519874 |
3 | 0.107231 | 0.0038213 | 0.04 | 0.374706 | 0.597355 | 0.0279393 | 4.90218e-18 | 7.56357e-17 | 0.0618167 |
4 | 0.139832 | 0.00568564 | 0.06 | 0.531873 | 0.450136 | 0.0179907 | 1.21673e-17 | 1.5762e-16 | 0.0754032 |
5 | 0.172432 | 0.00829573 | 0.08 | 0.689041 | 0.302917 | 0.00804202 | 6.47313e-17 | 7.66562e-16 | 0.0910809 |
In the dataframe the optimal portfolios with their respective risk-aversions are shown.
subsequently, add the efficient frontier to the simulated plot.
@df port_opt scatter!(:port_var, :exp_return)
savefig("opt_fig.svg"); nothing # hide
qt.qpa.xcb: could not connect to display
qt.qpa.plugin: From 6.5.0, xcb-cursor0 or libxcb-cursor0 is needed to load the Qt xcb platform plugin.
qt.qpa.plugin: Could not load the Qt platform plugin "xcb" in "" even though it was found.
This application failed to start because no Qt platform plugin could be initialized. Reinstalling the application may fix this problem.
Available platform plugins are: eglfs, offscreen, vnc, minimal, minimalegl, xcb, linuxfb, vkkhrdisplay.
Aborted (core dumped)
connect: Connection refused
GKS: can't connect to GKS socket application
GKS: Open failed in routine OPEN_WS
GKS: GKS not in proper state. GKS must be either in the state WSOP or WSAC in routine ACTIVATE_WS
Lastly, calculate the sharp ratio to find the portfolio with the optimal return variation ratio
port_sim_sharp = sharp_ratio(port_sim) ;
@show port_sim_sharp[end,:]
port_opt_sharp = sharp_ratio(port_sim) ;
port_opt_sharp[end,:]
DataFrameRow (9 columns)
exp_return | port_var | weight_IUSA.AS | weight_IBCI.AS | weight_IEMA.AS | weight_WTCH.AS | weight_VWRL.AS | port_std | sharp_ratio | |
---|---|---|---|---|---|---|---|---|---|
Float64 | Float64 | Float64 | Float64 | Float64 | Float64 | Float64 | Float64 | Float64 | |
5000 | 0.212045 | 0.0131112 | 0.740625 | 0.0492321 | 0.17605 | 0.0218004 | 0.0122924 | 0.114504 | 1.67719 |
limitations
There are three main limitation to this tool. The first limitation is that the MPT is a historical measurement of the portfolio performance. It does not say anything about future performance of the portfolio. Different Macro-economic situations might lead to total different end results. The second issue is that the tool is based on the expected return and variance of the portfolio. This captures the risk return relationship quite well but it does not take into account skewness and tail risk. It therefore gives rise to a reduced volatility and an inflated growth rate for a portfolio. Lastly, the risk measurement is probabilistic in nature. It does not reflect the structural roots of the risk. For example, the risk of a stock are off a total different nature then that of a commodity, but to tool will still account for them the same way.
Recommended usage
Never use this tool for individual stock picking and never but then also never rely only on the MPT. Always do your own due diligence before creating your portfolio and again this is no way or form financial advice.
So why should you use this tool and for what purpose? It is highly recommended to use this tool with exchange traded funds (ETF) as these products are already substantially diversified and issue two of the MPT is therefore greatly diminished. Also, the structural risk that certain ETF are exposed is difficult the estimate and the MPT can help you gain insights into which ETF have less or more risk compared to the returns they offer. Lastly, MPT also works better if you invest in all assets classes as each class has risks of a different nature and you are then therefore not fully exposed to one particular kind of risk.
To know which portfolio weights you should apply, you have to understand your risk preference. If you do not want to take a lot of risk, it is beneficial to look at optimal portfolio's with low values in $P$. The reverse is true for people who are risk seeking.