import%20marimo%0A%0A__generated_with%20%3D%20%220.18.3%22%0Aapp%20%3D%20marimo.App(width%3D%22medium%22)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20!%5BMOSEK%20ApS%5D(https%3A%2F%2Fwww.mosek.com%2Fstatic%2Fimages%2Fbranding%2Fwebgraphmoseklogocolor.png%20)%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%20Imports%20and%20configuration%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mo)%3A%0A%20%20%20%20import%20sys%2C%20os%2C%20re%2C%20glob%0A%20%20%20%20import%20datetime%20as%20dt%0A%20%20%20%20import%20numpy%20as%20np%0A%20%20%20%20import%20pandas%20as%20pd%0A%20%20%20%20import%20matplotlib%0A%20%20%20%20import%20matplotlib.pyplot%20as%20plt%0A%20%20%20%20from%20matplotlib.colors%20import%20LinearSegmentedColormap%0A%0A%20%20%20%20from%20mosek.fusion%20import%20Model%2C%20Domain%2C%20Expr%2C%20ObjectiveSense%2C%20Matrix%2C%20Var%2C%20SolutionStatus%0A%20%20%20%20import%20mosek.fusion.pythonic%20%20%20%20%23%20Requires%20MOSEK%20%3E%3D%2010.2%0A%0A%20%20%20%20%23%20Options%0A%20%20%20%20np.set_printoptions(precision%3D5%2C%20linewidth%3D120%2C%20suppress%3DTrue)%0A%20%20%20%20pd.set_option(%22display.max_rows%22%2C%20None)%0A%20%20%20%20plt.rcParams%5B%22figure.figsize%22%5D%20%3D%20%5B12%2C%208%5D%0A%0A%20%20%20%20%23%20Diagnostic%0A%20%20%20%20print(f%22Python%3A%20%7Bsys.version%7D%22)%0A%20%20%20%20print(f%22marimo%3A%20%7Bmo.__version__%7D%2C%20matplotlib%3A%20%7Bmatplotlib.__version__%7D%2C%20pandas%3A%20%7Bpd.__version__%7D%2C%20numpy%3A%20%7Bnp.__version__%7D%2C%20mosek%3A%20%7BModel.getVersion()%7D%22)%0A%20%20%20%20return%20(%0A%20%20%20%20%20%20%20%20Domain%2C%0A%20%20%20%20%20%20%20%20Expr%2C%0A%20%20%20%20%20%20%20%20LinearSegmentedColormap%2C%0A%20%20%20%20%20%20%20%20Model%2C%0A%20%20%20%20%20%20%20%20ObjectiveSense%2C%0A%20%20%20%20%20%20%20%20SolutionStatus%2C%0A%20%20%20%20%20%20%20%20glob%2C%0A%20%20%20%20%20%20%20%20np%2C%0A%20%20%20%20%20%20%20%20os%2C%0A%20%20%20%20%20%20%20%20pd%2C%0A%20%20%20%20%20%20%20%20plt%2C%0A%20%20%20%20%20%20%20%20re%2C%0A%20%20%20%20)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%20Prepare%20input%20data%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20Here%20we%20load%20the%20raw%20data%20that%20will%20be%20used%20to%20compute%20the%20optimization%20input%20variables%2C%20the%20vector%20%24%5Cmu%24%20of%20expected%20returns%20and%20the%20covariance%20matrix%20%24%5CSigma%24.%20The%20data%20consists%20of%20daily%20stock%20prices%20of%20%248%24%20stocks%20from%20the%20US%20market.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Download%20data%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(DataReader)%3A%0A%20%20%20%20%23%20Data%20format%3A%0A%20%20%20%20%23%0A%20%20%20%20%23%20This%20notebook%20loads%20data%20from%20the%20folder%20%22stock_data%22%2C%20containing%20files%20with%20names%0A%20%20%20%20%23%20%22TICKER.csv%22%2C%20where%20TICKER%20is%20the%20symbol%20of%20a%20stock.%20Each%20csv%20file%20contains%20(at%20least)%20columns%0A%20%20%20%20%23%20%22date%22%2C%20%22price%22%2C%20%22volume%22.%0A%20%20%20%20%23%0A%20%20%20%20%23%20The%20DataReader%20class%20(see%20Appendix)%20will%20load%20data%20into%20two%20dataframes%0A%20%20%20%20%23%20df_prices%20and%20df_volumes%2C%20which%20are%20then%20used%20in%20the%20notebook.%0A%20%20%20%20%23%0A%20%20%20%20%23%20To%20use%20your%20own%20data%20prepare%20your%20own%20files%20and%20modify%20the%20configuration%20below.%20You%20can%20also%0A%20%20%20%20%23%20modify%20the%20DataReader%20to%20consume%20a%20different%20format%20or%20plug%20in%20your%20df_prices%2C%20df_volumes%20directly.%0A%0A%20%20%20%20list_stocks%20%3D%20%5B%22PM%22%2C%20%22LMT%22%2C%20%22MCD%22%2C%20%22MMM%22%2C%20%22AAPL%22%2C%20%22MSFT%22%2C%20%22TXN%22%2C%20%22CSCO%22%5D%0A%20%20%20%20list_factors%20%3D%20%5B%5D%0A%20%20%20%20list_tickers%20%3D%20list_stocks%20%2B%20list_factors%0A%20%20%20%20investment_start%20%3D%20%222016-03-18%22%0A%20%20%20%20investment_end%20%3D%20%222021-03-18%22%0A%0A%20%20%20%20dr%20%3D%20DataReader(%0A%20%20%20%20%20%20%20%20folder_path%3D%22stock_data%22%2C%20symbol_list%3Dlist_tickers%0A%20%20%20%20)%0A%20%20%20%20dr.read_data()%0A%20%20%20%20df_prices%2C%20_%20%3D%20dr.get_period(%0A%20%20%20%20%20%20%20%20start_date%3Dinvestment_start%2C%20end_date%3Dinvestment_end%0A%20%20%20%20)%0A%20%20%20%20return%20(df_prices%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%20Run%20the%20optimization%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Define%20the%20optimization%20model%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20Below%20we%20implement%20the%20optimization%20model%20in%20Fusion%20API.%20We%20create%20it%20inside%20a%20function%20so%20we%20can%20call%20it%20later.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(Domain%2C%20Expr%2C%20Model%2C%20ObjectiveSense%2C%20SolutionStatus%2C%20df_prices%2C%20pd)%3A%0A%20%20%20%20def%20EfficientFrontier(N%2C%20m%2C%20G%2C%20deltas)%3A%0A%0A%20%20%20%20%20%20%20%20with%20Model(%22Case%20study%22)%20as%20M%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Settings%0A%20%20%20%20%20%20%20%20%20%20%20%20%23M.setLogHandler(sys.stdout)%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Variables%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20The%20variable%20x%20is%20the%20fraction%20of%20holdings%20in%20each%20security.%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20It%20is%20restricted%20to%20be%20positive%2C%20which%20imposes%20the%20constraint%20of%20no%20short-selling.%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20x%20%3D%20M.variable(%22x%22%2C%20N%2C%20Domain.greaterThan(0.0))%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20The%20variable%20s%20models%20the%20portfolio%20variance%20term%20in%20the%20objective.%0A%20%20%20%20%20%20%20%20%20%20%20%20s%20%3D%20M.variable(%22s%22%2C%201%2C%20Domain.unbounded())%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Budget%20constraint%0A%20%20%20%20%20%20%20%20%20%20%20%20M.constraint('budget'%2C%20Expr.sum(x)%20%3D%3D%201.0)%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Objective%20(quadratic%20utility%20version)%0A%20%20%20%20%20%20%20%20%20%20%20%20delta%20%3D%20M.parameter()%0A%20%20%20%20%20%20%20%20%20%20%20%20M.objective('obj'%2C%20ObjectiveSense.Maximize%2C%20x.T%20%40%20m%20-%20delta%20*%20s)%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Conic%20constraint%20for%20the%20portfolio%20variance%0A%20%20%20%20%20%20%20%20%20%20%20%20M.constraint('risk'%2C%20Expr.vstack(s%2C%20G.T%20%40%20x)%2C%20Domain.inQCone())%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Create%20DataFrame%20to%20store%20the%20results.%20Last%20security%20name%20(the%20SPY)%20is%20removed.%0A%20%20%20%20%20%20%20%20%20%20%20%20columns%20%3D%20%5B%22delta%22%2C%20%22obj%22%2C%20%22return%22%2C%20%22risk%22%5D%20%2B%20df_prices.columns.tolist()%0A%20%20%20%20%20%20%20%20%20%20%20%20df_result%20%3D%20pd.DataFrame()%0A%20%20%20%20%20%20%20%20%20%20%20%20for%20d%20in%20deltas%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20Update%20parameter%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20delta.setValue(d)%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20Solve%20optimization%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20M.solve()%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20Check%20if%20the%20solution%20is%20an%20optimal%20point%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20solsta%20%3D%20M.getPrimalSolutionStatus()%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20(solsta%20!%3D%20SolutionStatus.Optimal)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20See%20https%3A%2F%2Fdocs.mosek.com%2Flatest%2Fpythonfusion%2Faccessing-solution.html%20about%20handling%20solution%20statuses.%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20raise%20Exception(%22Unexpected%20solution%20status!%22)%20%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20Save%20results%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20portfolio_return%20%3D%20m%20%40%20x.level()%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20portfolio_risk%20%3D%20s.level()%5B0%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20row%20%3D%20pd.Series(%5Bd%2C%20M.primalObjValue()%2C%20portfolio_return%2C%20portfolio_risk%5D%20%2B%20list(x.level())%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20index%3Dcolumns)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20df_result%20%3D%20pd.concat(%5Bdf_result%2C%20pd.DataFrame(%5Brow%5D)%5D%2C%20ignore_index%3DTrue)%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20df_result%0A%20%20%20%20return%20(EfficientFrontier%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Compute%20optimization%20input%20variables%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20Here%20we%20use%20the%20loaded%20daily%20price%20data%20to%20compute%20the%20corresponding%20yearly%20mean%20return%20and%20covariance%20matrix.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(compute_inputs%2C%20df_prices)%3A%0A%20%20%20%20%23%20Number%20of%20securities%0A%20%20%20%20N%20%3D%20df_prices.shape%5B1%5D%20%20%0A%0A%20%20%20%20%23%20Get%20optimization%20parameters%0A%20%20%20%20m_orig%2C%20S_orig%20%3D%20compute_inputs(df_prices%2C%20shrinkage%3DFalse)%0A%20%20%20%20m_shrunk%2C%20S_shrunk%20%3D%20compute_inputs(df_prices%2C%20shrinkage%3DTrue)%0A%20%20%20%20return%20N%2C%20S_orig%2C%20S_shrunk%2C%20m_orig%2C%20m_shrunk%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20Next%20we%20compute%20the%20matrix%20%24G%24%20such%20that%20%24%5CSigma%3DGG%5E%5Cmathsf%7BT%7D%24%2C%20this%20is%20the%20input%20of%20the%20conic%20form%20of%20the%20optimization%20problem.%20Here%20we%20use%20Cholesky%20factorization.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(S_orig%2C%20S_shrunk%2C%20np)%3A%0A%20%20%20%20G_orig%20%3D%20np.linalg.cholesky(S_orig)%20%20%0A%20%20%20%20G_shrunk%20%3D%20np.linalg.cholesky(S_shrunk)%0A%20%20%20%20return%20G_orig%2C%20G_shrunk%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Call%20the%20optimizer%20function%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20We%20run%20the%20optimization%20for%20a%20range%20of%20risk%20aversion%20parameter%20values%3A%20%24%5Cdelta%20%3D%2010%5E%7B-1%7D%2C%5Cdots%2C10%5E%7B1.5%7D%24.%20We%20compute%20the%20efficient%20frontier%20this%20way%20both%20with%20and%20without%20using%20shrinkage%20estimation.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(EfficientFrontier%2C%20G_orig%2C%20G_shrunk%2C%20N%2C%20m_orig%2C%20m_shrunk%2C%20np)%3A%0A%20%20%20%20%23%20Compute%20efficient%20frontier%20with%20and%20without%20shrinkage%0A%20%20%20%20deltas%20%3D%20np.logspace(start%3D-1%2C%20stop%3D1.5%2C%20num%3D20)%5B%3A%3A-1%5D%0A%20%20%20%20df_result_orig%20%3D%20EfficientFrontier(N%2C%20m_orig%2C%20G_orig%2C%20deltas)%0A%20%20%20%20df_result_shrunk%20%3D%20EfficientFrontier(N%2C%20m_shrunk%2C%20G_shrunk%2C%20deltas)%0A%0A%20%20%20%20%23%20Set%20small%20negatives%20to%20zero%20to%20make%20plotting%20work%0A%20%20%20%20mask%20%3D%20df_result_orig%20%3C%200%0A%20%20%20%20mask.iloc%5B%3A%2C%20%3A-8%5D%20%3D%20False%0A%20%20%20%20df_result_orig%5Bmask%5D%20%3D%200%0A%0A%20%20%20%20%23%20Set%20small%20negatives%20to%20zero%20to%20make%20plotting%20work%0A%20%20%20%20mask%20%3D%20df_result_shrunk%20%3C%200%0A%20%20%20%20mask.iloc%5B%3A%2C%20%3A-8%5D%20%3D%20False%0A%20%20%20%20df_result_shrunk%5Bmask%5D%20%3D%200%0A%20%20%20%20return%20df_result_orig%2C%20df_result_shrunk%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20Check%20the%20results%20without%20shrinkage.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(df_result_orig)%3A%0A%20%20%20%20df_result_orig%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Visualize%20the%20results%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20Plot%20the%20efficient%20frontier%20for%20both%20cases.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(df_result_orig%2C%20df_result_shrunk%2C%20plt)%3A%0A%20%20%20%20ax%20%3D%20df_result_shrunk.plot(x%3D%22risk%22%2C%20y%3D%22return%22%2C%20style%3D%22-o%22%2C%20xlabel%3D%22portfolio%20risk%20(std.%20dev.)%22%2C%20ylabel%3D%22portfolio%20return%22%2C%20grid%3DTrue)%0A%20%20%20%20df_result_orig.plot(ax%3Dax%2C%20x%3D%22risk%22%2C%20y%3D%22return%22%2C%20style%3D%22-o%22%2C%20xlabel%3D%22portfolio%20risk%20(std.%20dev.)%22%2C%20ylabel%3D%22portfolio%20return%22%2C%20grid%3DTrue)%20%20%20%0A%20%20%20%20ax.legend(%5B%22return%20with%20shrinkage%22%2C%20%22return%22%5D)%3B%0A%20%20%20%20plt.show()%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20Plot%20the%20portfolio%20composition%20for%20both%20cases.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(LinearSegmentedColormap%2C%20df_result_orig%2C%20df_result_shrunk%2C%20plt)%3A%0A%20%20%20%20my_cmap%20%3D%20LinearSegmentedColormap.from_list(%22non-extreme%20gray%22%2C%20%5B%22%23111111%22%2C%20%22%23eeeeee%22%5D%2C%20N%3D256%2C%20gamma%3D1.0)%0A%20%20%20%20ax1%20%3D%20df_result_shrunk.set_index('risk').iloc%5B%3A%2C%203%3A%5D.plot.area(colormap%3Dmy_cmap%2C%20xlabel%3D'portfolio%20risk%20(std.%20dev.)'%2C%20ylabel%3D%22x%22)%0A%20%20%20%20ax1.grid(which%3D'both'%2C%20axis%3D'x'%2C%20linestyle%3D'%3A'%2C%20color%3D'k'%2C%20linewidth%3D1)%0A%20%20%20%20ax2%20%3D%20df_result_orig.set_index('risk').iloc%5B%3A%2C%203%3A%5D.plot.area(colormap%3Dmy_cmap%2C%20xlabel%3D'portfolio%20risk%20(std.%20dev.)'%2C%20ylabel%3D%22x%22)%20%0A%20%20%20%20ax2.grid(which%3D'both'%2C%20axis%3D'x'%2C%20linestyle%3D'%3A'%2C%20color%3D'k'%2C%20linewidth%3D1)%0A%20%20%20%20plt.show()%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%20Appendix%0A%20%20%20%20Data%20preparation%20tools%20used%20in%20this%20notebook%20can%20be%20reached%20from%20the%20functions%20defined%20below.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(glob%2C%20os%2C%20pd%2C%20re)%3A%0A%20%20%20%20class%20DataReader(object)%3A%0A%20%20%20%20%20%20%20%20def%20__init__(self%2C%20folder_path%2C%20symbol_list%3DNone)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20self.folder_path%20%3D%20folder_path%0A%20%20%20%20%20%20%20%20%20%20%20%20self.name_format%20%3D%20r%22*.csv%22%0A%20%20%20%20%20%20%20%20%20%20%20%20self.symbol_list%20%3D%20symbol_list%20if%20symbol_list%20is%20not%20None%20else%20%5B%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20self.df_prices%20%3D%20None%0A%20%20%20%20%20%20%20%20%20%20%20%20self.df_volumes%20%3D%20None%0A%0A%20%20%20%20%20%20%20%20def%20read_data(self%2C%20read_volume%3DFalse)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Get%20list%20of%20files%20from%20path%2C%20named%20as%20name_format%20%0A%20%20%20%20%20%20%20%20%20%20%20%20list_files%20%3D%20glob.glob(os.path.join(self.folder_path%2C%20self.name_format))%0A%20%20%20%20%20%20%20%20%20%20%20%20file_names%20%3D%20%22%5Cn%22.join(list_files)%0A%20%20%20%20%20%20%20%20%20%20%20%20print(%22Found%20data%20files%3A%20%5Cn%7B%7D%5Cn%22.format(file_names))%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Keep%20only%20ones%20in%20symbol%20list%20(if%20given)%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20self.symbol_list%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20list_to_read%20%3D%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20os.path.join(self.folder_path%2C%20self.name_format.replace(%22*%22%2C%20symbol))%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20for%20symbol%20in%20self.symbol_list%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20list_missing%20%3D%20%5Bfname%20for%20fname%20in%20list_to_read%20if%20fname%20not%20in%20list_files%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20list_missing%3A%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20raise%20Exception(f%22Files%20are%20missing%3A%20%7Blist_missing%7D%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20file_names%20%3D%20%22%5Cn%22.join(list_to_read)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20print(%22Using%20data%20files%3A%20%5Cn%7B%7D%5Cn%22.format(file_names))%0A%20%20%20%20%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20list_to_read%20%3D%20list_files%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20print(%22Using%20all%20data%20files.%22)%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Collect%20data%20from%20the%20files%20into%20a%20Dataframe%0A%20%20%20%20%20%20%20%20%20%20%20%20dict_prices%20%3D%20%7B%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20dict_volumes%20%3D%20%7B%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20for%20file_name%20in%20list_to_read%3A%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20m%20%3D%20re.search(self.name_format.replace(%22*%22%2C%20%22(.%2B)%22)%2C%20os.path.basename(file_name))%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20Get%20symbol%20name%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20symbol%20%3D%20m.group(1)%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20Read%20data%20file%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20df_data%20%3D%20pd.read_csv(file_name)%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20Set%20timestamp%20as%20index%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20df_data%5B'date'%5D%20%3D%20pd.to_datetime(df_data%5B'date'%5D)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20df_data%20%3D%20df_data.set_index('date')%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20df_data.index.name%20%3D%20%22date%22%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20Obtain%20adjusted%20close%20price%20data%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20dict_prices%5Bsymbol%5D%20%3D%20df_data%5B'price'%5D%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20Obtain%20volumes%20data%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20read_volume%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20dict_volumes%5Bsymbol%5D%20%3D%20df_data%5B'volume'%5D%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20self.df_prices%20%3D%20pd.concat(dict_prices.values()%2C%20axis%3D1%2C%20keys%3Ddict_prices.keys()).sort_index()%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20read_volume%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20self.df_volumes%20%3D%20pd.concat(dict_volumes.values()%2C%20axis%3D1%2C%20keys%3Ddict_volumes.keys()).sort_index()%0A%0A%20%20%20%20%20%20%20%20def%20get_period(self%2C%20start_date%2C%20end_date)%3A%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20start_idx%20%3D%20self.df_prices.index.get_indexer(%5Bpd.to_datetime(start_date)%5D%2C%20method%3D'nearest')%5B0%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20end_idx%20%3D%20self.df_prices.index.get_indexer(%5Bpd.to_datetime(end_date)%5D%2C%20method%3D'nearest')%5B0%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20df_prices%20%3D%20self.df_prices.iloc%5Bstart_idx%3A(end_idx%20%2B%201)%5D.copy()%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20self.df_volumes%20is%20not%20None%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20df_volumes%20%3D%20self.df_volumes.iloc%5Bstart_idx%3A(end_idx%20%2B%201)%5D.copy()%0A%20%20%20%20%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20df_volumes%20%3D%20pd.DataFrame()%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20df_prices%2C%20df_volumes%0A%20%20%20%20return%20(DataReader%2C)%0A%0A%0A%40app.cell%0Adef%20_(cov_shrinkage_LW%2C%20mean_shrinkage_JS%2C%20np%2C%20pd)%3A%0A%20%20%20%20def%20compute_inputs(%0A%20%20%20%20%20%20%20%20%20%20%20%20list_df_prices%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20sample_period%3D'W'%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20investment_horizon%3D1%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20show_histograms%3DFalse%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20shrinkage%3DFalse%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20security_num%3DNone%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20return_log%3DFalse%0A%20%20%20%20%20%20%20%20)%3A%0A%20%20%20%20%20%20%20%20map_period%20%3D%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20'W'%3A%2052%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%23%20We%20can%20generate%20return%20distribution%20based%20on%20multiple%20periods%20of%20price%20data%0A%20%20%20%20%20%20%20%20if%20not%20isinstance(list_df_prices%2C%20list)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20list_df_prices%20%3D%20%5Blist_df_prices%5D%0A%0A%20%20%20%20%20%20%20%20df_weekly_log_returns%20%3D%20pd.DataFrame()%0A%20%20%20%20%20%20%20%20for%20df_prices%20in%20list_df_prices%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20PREPROC%3A%20Remove%20factors%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20security_num%20is%20not%20None%3A%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20df_prices%20%3D%20df_prices.iloc%5B%3A%2C%200%3Asecurity_num%5D%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%201.%20Compute%20weekly%20logarithmic%20return%0A%20%20%20%20%20%20%20%20%20%20%20%20df_weekly_prices%20%3D%20df_prices.resample(sample_period).last()%0A%20%20%20%20%20%20%20%20%20%20%20%20df_weekly_log_returns_part%20%3D%20np.log(df_weekly_prices)%20-%20np.log(df_weekly_prices.shift(1))%0A%20%20%20%20%20%20%20%20%20%20%20%20df_weekly_log_returns_part%20%3D%20df_weekly_log_returns_part.dropna(how%3D'all')%0A%20%20%20%20%20%20%20%20%20%20%20%20df_weekly_log_returns_part%20%3D%20df_weekly_log_returns_part.fillna(0)%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20df_weekly_log_returns%20%3D%20pd.concat(%5Bdf_weekly_log_returns%2C%20df_weekly_log_returns_part%5D%2C%20ignore_index%3DTrue)%0A%0A%20%20%20%20%20%20%20%20if%20show_histograms%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20df_weekly_log_returns.hist(bins%3D50)%0A%0A%20%20%20%20%20%20%20%20%23%202.%20Compute%20the%20distribution%20of%20weekly%20logarithmic%20return%0A%20%20%20%20%20%20%20%20return_array%20%3D%20df_weekly_log_returns.to_numpy()%0A%20%20%20%20%20%20%20%20T%20%3D%20return_array.shape%5B0%5D%0A%20%20%20%20%20%20%20%20m_weekly_log%20%3D%20np.mean(return_array%2C%20axis%3D0)%0A%20%20%20%20%20%20%20%20S_weekly_log%20%3D%20np.cov(return_array.transpose())%0A%0A%20%20%20%20%20%20%20%20%23%20Apply%20shrinkage%20if%20needed%0A%20%20%20%20%20%20%20%20if%20shrinkage%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20m_weekly_log%20%3D%20mean_shrinkage_JS(m_weekly_log%2C%20S_weekly_log%2C%20return_array)%0A%20%20%20%20%20%20%20%20%20%20%20%20S_weekly_log%20%3D%20cov_shrinkage_LW(m_weekly_log%2C%20S_weekly_log%2C%20return_array)%0A%0A%20%20%20%20%20%20%20%20%23%203.%20Project%20the%20distribution%20to%20the%20investment%20horizon%0A%20%20%20%20%20%20%20%20scale_factor%20%3D%20investment_horizon%20*%20map_period%5Bsample_period%5D%0A%20%20%20%20%20%20%20%20m_log%20%3D%20scale_factor%20*%20m_weekly_log%0A%20%20%20%20%20%20%20%20S_log%20%3D%20scale_factor%20*%20S_weekly_log%0A%0A%20%20%20%20%20%20%20%20if%20return_log%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20m_log%2C%20S_log%0A%0A%20%20%20%20%20%20%20%20%23%204.%20Compute%20the%20distribution%20of%20yearly%20linear%20return%0A%20%20%20%20%20%20%20%20p_0%20%3D%20np.ones(len(m_log))%20%20%23%20We%20use%20a%20dummy%20price%20here%20to%20see%20the%20method%20in%20two%20steps.%20It%20will%20be%20canceled%20out%20later.%20%0A%20%20%20%20%20%20%20%20m_P%20%3D%20p_0%20*%20np.exp(m_log%20%2B%201%2F2*np.diag(S_log))%0A%20%20%20%20%20%20%20%20S_P%20%3D%20np.outer(m_P%2C%20m_P)%20*%20(np.exp(S_log)%20-%201)%0A%0A%20%20%20%20%20%20%20%20m%20%3D%201%20%2F%20p_0%20*%20m_P%20-%201%0A%20%20%20%20%20%20%20%20S%20%3D%201%20%2F%20np.outer(p_0%2C%20p_0)%20*%20S_P%0A%0A%20%20%20%20%20%20%20%20return%20m%2C%20S%0A%20%20%20%20return%20(compute_inputs%2C)%0A%0A%0A%40app.cell%0Adef%20_(np)%3A%0A%20%20%20%20def%20alpha_numerator(Z%2C%20S)%3A%0A%20%20%20%20%20%20%20%20s%20%3D%200%0A%20%20%20%20%20%20%20%20T%20%3D%20Z.shape%5B1%5D%0A%20%20%20%20%20%20%20%20for%20k%20in%20range(T)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20z%20%3D%20Z%5B%3A%2C%20k%5D%5B%3A%2C%20np.newaxis%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20X%20%3D%20z%20%40%20z.T%20-%20S%0A%20%20%20%20%20%20%20%20%20%20%20%20s%20%2B%3D%20np.trace(X%20%40%20X)%0A%20%20%20%20%20%20%20%20s%20%2F%3D%20(T**2)%0A%20%20%20%20%20%20%20%20return%20s%0A%20%20%20%20return%20(alpha_numerator%2C)%0A%0A%0A%40app.cell%0Adef%20_(np)%3A%0A%20%20%20%20def%20mean_shrinkage_JS(m_estim%2C%20S_estim%2C%20return_array)%3A%0A%20%20%20%20%20%20%20%20%23%20James--Stein%20estimator%0A%20%20%20%20%20%20%20%20T%20%3D%20return_array.shape%5B0%5D%0A%20%20%20%20%20%20%20%20N%20%3D%20m_estim.shape%5B0%5D%0A%20%20%20%20%20%20%20%20m%20%3D%20m_estim%5B%3A%2C%20np.newaxis%5D%0A%20%20%20%20%20%20%20%20o%20%3D%20np.ones(N)%5B%3A%2C%20np.newaxis%5D%0A%20%20%20%20%20%20%20%20iS%20%3D%20np.linalg.inv(S_estim)%0A%20%20%20%20%20%20%20%20b%20%3D%20(o.T%20%40%20m%20%2F%20N)%20*%20o%0A%20%20%20%20%20%20%20%20N_eff%20%3D%20np.trace(S_estim)%20%2F%20np.max(np.linalg.eigvalsh(S_estim))%0A%20%20%20%20%20%20%20%20alpha_num%20%3D%20max(N_eff%20-%203%2C%200)%20%20%23%20Negative%20value%20would%20not%20give%20improvement.%0A%20%20%20%20%20%20%20%20alpha_den%20%3D%20T%20*%20(m%20-%20b).T%20%40%20iS%20%40%20(m%20-%20b)%20%0A%20%20%20%20%20%20%20%20alpha%20%3D%20alpha_num%20%2F%20alpha_den%0A%20%20%20%20%20%20%20%20m_shrunk%20%3D%20b%20%2B%20max(1%20-%20alpha%2C%200)%20*%20(m%20-%20b)%0A%20%20%20%20%20%20%20%20m_shrunk%20%3D%20m_shrunk%5B%3A%2C%200%5D%0A%0A%20%20%20%20%20%20%20%20return%20m_shrunk%0A%20%20%20%20return%20(mean_shrinkage_JS%2C)%0A%0A%0A%40app.cell%0Adef%20_(alpha_numerator%2C%20np)%3A%0A%20%20%20%20def%20cov_shrinkage_LW(m_estim%2C%20S_estim%2C%20return_array)%3A%0A%20%20%20%20%20%20%20%20%23%20Ledoit--Wolf%20shrinkage%0A%20%20%20%20%20%20%20%20N%20%3D%20S_estim.shape%5B0%5D%0A%20%20%20%20%20%20%20%20s2_avg%20%3D%20np.trace(S_estim)%20%2F%20N%20%0A%20%20%20%20%20%20%20%20B%20%3D%20s2_avg%20*%20np.eye(N)%0A%20%20%20%20%20%20%20%20Z%20%3D%20return_array.T%20-%20m_estim%5B%3A%2C%20np.newaxis%5D%0A%20%20%20%20%20%20%20%20alpha_num%20%3D%20alpha_numerator(Z%2C%20S_estim)%20%0A%20%20%20%20%20%20%20%20alpha_den%20%3D%20np.trace((S_estim%20-%20B)%20%40%20(S_estim%20-%20B))%0A%20%20%20%20%20%20%20%20alpha%20%3D%20alpha_num%20%2F%20alpha_den%0A%20%20%20%20%20%20%20%20S_shrunk%20%3D%20(1%20-%20alpha)%20*%20S_estim%20%2B%20alpha%20*%20B%0A%0A%20%20%20%20%20%20%20%20return%20S_shrunk%0A%20%20%20%20return%20(cov_shrinkage_LW%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%3Ca%20rel%3D%22license%22%20href%3D%22http%3A%2F%2Fcreativecommons.org%2Flicenses%2Fby%2F4.0%2F%22%3E%3Cimg%20alt%3D%22Creative%20Commons%20License%22%20style%3D%22border-width%3A0%22%20src%3D%22https%3A%2F%2Fi.creativecommons.org%2Fl%2Fby%2F4.0%2F80x15.png%22%20%2F%3E%3C%2Fa%3E%3Cbr%20%2F%3EThis%20work%20is%20licensed%20under%20a%20%3Ca%20rel%3D%22license%22%20href%3D%22http%3A%2F%2Fcreativecommons.org%2Flicenses%2Fby%2F4.0%2F%22%3ECreative%20Commons%20Attribution%204.0%20International%20License%3C%2Fa%3E.%20The%20**MOSEK**%20logo%20and%20name%20are%20trademarks%20of%20%3Ca%20href%3D%22http%3A%2F%2Fmosek.com%22%3EMosek%20ApS%3C%2Fa%3E.%20The%20code%20is%20provided%20as-is.%20Compatibility%20with%20future%20release%20of%20**MOSEK**%20or%20the%20%60Fusion%20API%60%20are%20not%20guaranteed.%20For%20more%20information%20contact%20our%20%5Bsupport%5D(mailto%3Asupport%40mosek.com).%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_()%3A%0A%20%20%20%20import%20marimo%20as%20mo%0A%20%20%20%20return%20(mo%2C)%0A%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20app.run()%0A
8595475b50b9cce0d390b2c7a134febc