import%20marimo%0A%0A__generated_with%20%3D%20%220.15.2%22%0Aapp%20%3D%20marimo.App()%0A%0Awith%20app.setup%3A%0A%20%20%20%20import%20marimo%20as%20mo%0A%20%20%20%20import%20matplotlib.pyplot%20as%20plt%0A%20%20%20%20import%20pandas%20as%20pd%0A%20%20%20%20import%20polars%20as%20pl%0A%0A%20%20%20%20from%20pyhrp.algos%20import%20risk_parity%0A%20%20%20%20from%20pyhrp.cluster%20import%20Asset%0A%20%20%20%20from%20pyhrp.hrp%20import%20build_tree%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20r%22%22%22%0A%20%20%20%20%23%20Hierarchical%20Risk%20Parity%20(HRP)%0A%0A%20%20%20%20We%20follow%20ideas%20by%20Marcos%20Lopez%20de%20Prado.%0A%0A%20%20%20%20-%20Compute%20the%201st%20dendrogram%20using%20the%20'single'%20distance%20method.%0A%20%20%20%20-%20Compute%20the%202nd%20dendrogram%20by%20using%20the%20order%20of%20leaves%0A%20%20%20%20%20%20of%20the%201st%20dendrogram%20(following%20an%20argument%20by%20Thomas%20Raffinot)%0A%20%20%20%20-%20Apply%20Risk%20Parity%20in%20a%20recursive%20bottom-up%20traverse%0A%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20%23%20Read%20CSV%20with%20Polars%0A%20%20%20%20prices_pl%20%3D%20pl.read_csv(str(mo.notebook_location()%20%2F%20%22public%22%20%2F%20%22stock_prices.csv%22))%0A%20%20%20%20%23%20Convert%20to%20pandas%20DataFrame%20with%20the%20first%20column%20as%20index%0A%20%20%20%20index_col%20%3D%20prices_pl.columns%5B0%5D%0A%20%20%20%20prices%20%3D%20prices_pl.to_pandas().set_index(index_col)%0A%20%20%20%20returns%20%3D%20prices.pct_change().dropna(axis%3D0%2C%20how%3D%22all%22).fillna(0.0)%0A%20%20%20%20%23%20Store%20original%20column%20names%20for%20later%20use%0A%20%20%20%20column_names%20%3D%20returns.columns.tolist()%0A%20%20%20%20%23%20Create%20a%20mapping%20from%20column%20names%20to%20Asset%20objects%0A%20%20%20%20assets_map%20%3D%20%7Bcolumn%3A%20Asset(name%3Dcolumn)%20for%20column%20in%20column_names%7D%0A%20%20%20%20return%20column_names%2C%20returns%0A%0A%0A%40app.cell%0Adef%20_(column_names%2C%20returns)%3A%0A%20%20%20%20cor%20%3D%20returns.corr()%0A%20%20%20%20cor.columns%20%3D%20%5BAsset(name%3Dcolumn)%20for%20column%20in%20column_names%5D%0A%20%20%20%20cor.index%20%3D%20%5BAsset(name%3Dcolumn)%20for%20column%20in%20column_names%5D%0A%20%20%20%20cov%20%3D%20returns.cov()%0A%20%20%20%20cov.columns%20%3D%20%5BAsset(name%3Dcolumn)%20for%20column%20in%20column_names%5D%0A%20%20%20%20cov.index%20%3D%20%5BAsset(name%3Dcolumn)%20for%20column%20in%20column_names%5D%0A%20%20%20%20return%20cor%2C%20cov%0A%0A%0A%40app.cell%0Adef%20_(cor)%3A%0A%20%20%20%20%23%20The%20first%20dendrogram%20is%20suffering.%20We%20observe%20the%20chaining%20effect%0A%20%20%20%20%23%20Convert%20column%20names%20to%20Asset%20objects%20for%20build_tree%0A%20%20%20%20print(cor)%0A%0A%20%20%20%20dendrogram_before%20%3D%20build_tree(cor%2C%20method%3D%22single%22)%0A%20%20%20%20dendrogram_before.plot()%0A%20%20%20%20plt.show()%0A%20%20%20%20return%20(dendrogram_before%2C)%0A%0A%0A%40app.cell%0Adef%20_(cov%2C%20dendrogram_before)%3A%0A%20%20%20%20%23%20The%20weights%20are%20not%20well%20balanced%0A%20%20%20%20%23%20No%20surprise%20given%20exposure%20of%20nodes%20like%2011%2C%2012%20or%2015%0A%20%20%20%20root_before%20%3D%20risk_parity(dendrogram_before.root%2C%20cov)%0A%20%20%20%20root_before.portfolio.plot(names%3Ddendrogram_before.names)%0A%20%20%20%20plt.show()%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(cor)%3A%0A%20%20%20%20%23%20The%20dendrogram%20suffers%20because%20of%20the%20'chaining'%20effect.%20LdP%20is%20using%0A%20%20%20%20%23%20now%20only%20the%20order%20of%20the%20leaves%20(e.g.%20the%20assets)%20and%0A%20%20%20%20%23%20constructs%20a%20second%20Dendrogram.%0A%20%20%20%20%23%20Convert%20column%20names%20to%20Asset%20objects%20for%20build_tree%0A%20%20%20%20dendrogram_bisection%20%3D%20build_tree(cor%2C%20method%3D%22single%22%2C%20bisection%3DTrue)%0A%20%20%20%20dendrogram_bisection.plot()%0A%20%20%20%20plt.show()%0A%20%20%20%20return%20(dendrogram_bisection%2C)%0A%0A%0A%40app.cell%0Adef%20_(cov%2C%20dendrogram_bisection)%3A%0A%20%20%20%20root_bisection%20%3D%20risk_parity(dendrogram_bisection.root%2C%20cov)%0A%20%20%20%20root_bisection.portfolio.plot(names%3Ddendrogram_bisection.names)%0A%20%20%20%20plt.show()%0A%20%20%20%20return%20(root_bisection%2C)%0A%0A%0A%40app.cell%0Adef%20_(cor)%3A%0A%20%20%20%20dendrogram_ward%20%3D%20build_tree(cor%2C%20method%3D%22ward%22)%0A%20%20%20%20dendrogram_ward.plot()%0A%20%20%20%20plt.show()%0A%20%20%20%20return%20(dendrogram_ward%2C)%0A%0A%0A%40app.cell%0Adef%20_(cov%2C%20dendrogram_ward)%3A%0A%20%20%20%20root_ward%20%3D%20risk_parity(dendrogram_ward.root%2C%20cov)%0A%20%20%20%20root_ward.portfolio.plot(names%3Ddendrogram_ward.names)%0A%20%20%20%20plt.show()%0A%20%20%20%20return%20(root_ward%2C)%0A%0A%0A%40app.cell%0Adef%20_(root_bisection%2C%20root_ward)%3A%0A%20%20%20%20%23%20Assuming%20root_before.portfolio.weights1%20and%20root_before.portfolio.weights2%20are%20two%20weight%20series%0A%20%20%20%20_weights_ward%20%3D%20root_ward.portfolio.weights%0A%20%20%20%20_weights_bisection%20%3D%20root_bisection.portfolio.weights%0A%0A%20%20%20%20%23%20Create%20a%20DataFrame%20from%20both%20weight%20series%20to%20align%20them%20on%20the%20same%20index%20(assuming%20they%20have%20the%20same%20index)%0A%20%20%20%20weights_df%20%3D%20pd.DataFrame(%7B%22ward%22%3A%20_weights_ward%2C%20%22single%2Fbisection%22%3A%20_weights_bisection%7D)%0A%0A%20%20%20%20%23%20Plot%20both%20weight%20series%0A%20%20%20%20weights_df.plot(kind%3D%22bar%22%2C%20width%3D0.8)%0A%0A%20%20%20%20%23%20Ensure%20all%20possible%20x-axis%20labels%20are%20shown%0A%20%20%20%20plt.xticks(ticks%3Drange(len(weights_df))%2C%20labels%3D%5Basset.name%20for%20asset%20in%20weights_df.index%5D%2C%20rotation%3D90)%0A%0A%20%20%20%20%23%20Optionally%2C%20adjust%20the%20layout%20to%20avoid%20label%20clipping%0A%20%20%20%20plt.tight_layout()%0A%0A%20%20%20%20%23%20Show%20the%20plot%0A%20%20%20%20plt.show()%0A%20%20%20%20return%0A%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20app.run()%0A
53b14b8038a4376419f0d5b99027169d82c4ef5282efe969ca9a97eda6e3ef40