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%20Var%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%20First%20we%20generate%20return%20scenario%20data%20using%20Monte%20Carlo%20method%20based%20on%20the%20moments%20of%20historical%20prices.%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%2C%20np)%3A%0A%20%20%20%20%23%20Number%20of%20scenarios%0A%20%20%20%20T%20%3D%2099999%0A%0A%20%20%20%20%23%20Mean%20and%20covariance%20of%20historical%20log-returns.%20%20%0A%20%20%20%20m_log%2C%20S_log%20%3D%20compute_inputs(df_prices%2C%20return_log%3DTrue)%0A%0A%20%20%20%20%23%20Generate%20logarithmic%20return%20scenarios%20assuming%20normal%20distribution%0A%20%20%20%20scenarios_log%20%3D%20np.random.default_rng().multivariate_normal(m_log%2C%20S_log%2C%20T)%0A%0A%20%20%20%20%23%20Convert%20logarithmic%20return%20scenarios%20to%20linear%20return%20scenarios%20%0A%20%20%20%20scenarios_lin%20%3D%20np.exp(scenarios_log)%20-%201%0A%0A%20%20%20%20%23%20Scenario%20probabilities%0A%20%20%20%20p%20%3D%20np.ones(T)%20%2F%20T%0A%20%20%20%20return%20T%2C%20p%2C%20scenarios_lin%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%20would%20like%20to%20optimize%20the%2095%25%20CVaR%20of%20the%20portfolio%20loss%20distribution.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20%23%20Confidence%20level%0A%20%20%20%20alpha%20%3D%200.95%0A%20%20%20%20return%20(alpha%2C)%0A%0A%0A%40app.cell%0Adef%20_(np)%3A%0A%20%20%20%20%23%20Primal%20CVaR%20formula%20(for%20testing)%0A%20%20%20%20def%20CVaR(alpha%2C%20p%2C%20q)%3A%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20Computes%20CVaR%20using%20primal%20formula.%20%0A%20%20%20%20%20%20%20%20NOTE%3A%20Inputs%20p%20and%20q%20should%20be%20numpy%20arrays.%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20%23%20We%20need%20to%20be%20careful%20that%20math%20index%20starts%20from%201%20but%20numpy%20starts%20from%200%20(matters%20in%20formulas%20like%20ceil(alpha%20*%20T))%0A%20%20%20%20%20%20%20%20T%20%3D%20q.shape%5B0%5D%0A%20%20%20%20%20%20%20%20sort_idx%20%3D%20np.argsort(q)%0A%20%20%20%20%20%20%20%20sorted_q%20%3D%20q%5Bsort_idx%5D%0A%20%20%20%20%20%20%20%20sorted_p%20%3D%20p%5Bsort_idx%5D%0A%0A%20%20%20%20%20%20%20%20%23%20Starting%20index%20%0A%20%20%20%20%20%20%20%20i_alpha%20%3D%20np.sort(np.nonzero(np.cumsum(sorted_p)%20%3E%3D%20alpha)%5B0%5D)%5B0%5D%0A%0A%20%20%20%20%20%20%20%20%23%20Weight%20of%20VaR%20component%20in%20CVaR%0A%20%20%20%20%20%20%20%20lambda_alpha%20%3D%20(np.sum(sorted_p%5B%3A(i_alpha%20%2B%201)%5D)%20-%20alpha)%20%2F%20(1%20-%20alpha)%20%20%20%20%0A%0A%20%20%20%20%20%20%20%20%23%20CVaR%0A%20%20%20%20%20%20%20%20var%20%3D%20sorted_q%5Bi_alpha%5D%0A%20%20%20%20%20%20%20%20cvar%20%3D%20lambda_alpha%20*%20sorted_q%5Bi_alpha%5D%20%2B%20np.dot(sorted_p%5B(i_alpha%20%2B%201)%3A%5D%2C%20sorted_q%5B(i_alpha%20%2B%201)%3A%5D)%20%2F%20(1%20-%20alpha)%0A%0A%20%20%20%20%20%20%20%20return%20var%2C%20cvar%0A%20%20%20%20return%20(CVaR%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%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_(%0A%20%20%20%20CVaR%2C%0A%20%20%20%20Domain%2C%0A%20%20%20%20Expr%2C%0A%20%20%20%20Model%2C%0A%20%20%20%20ObjectiveSense%2C%0A%20%20%20%20SolutionStatus%2C%0A%20%20%20%20Var%2C%0A%20%20%20%20df_prices%2C%0A%20%20%20%20np%2C%0A%20%20%20%20pd%2C%0A)%3A%0A%20%20%20%20def%20EfficientFrontier(N%2C%20T%2C%20m%2C%20R%2C%20p%2C%20alpha%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%20relative%20to%20the%20initial%20capital.%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20It%20is%20constrained%20to%20take%20only%20positive%20values.%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%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)%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Auxiliary%20variables.%0A%20%20%20%20%20%20%20%20%20%20%20%20t%20%3D%20M.variable(%22t%22%2C%201%2C%20Domain.unbounded())%0A%20%20%20%20%20%20%20%20%20%20%20%20u%20%3D%20M.variable(%22u%22%2C%20T%2C%20Domain.unbounded())%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Constraint%20modeling%20maximum%0A%20%20%20%20%20%20%20%20%20%20%20%20M.constraint(u%20%3E%3D%20-%20R%20*%20x%20-%20Var.repeat(t%2C%20T))%0A%20%20%20%20%20%20%20%20%20%20%20%20M.constraint(u%20%3E%3D%200)%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Objective%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%20cvar_term%20%3D%20t%20%2B%20u.T%20%40%20p%20%2F%20(1-alpha)%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*%20cvar_term)%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Create%20DataFrame%20to%20store%20the%20results.%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)%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%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)%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%20t.level()%5B0%5D%20%2B%201%2F(1-alpha)%20*%20np.dot(p%2C%20u.level())%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%20index%3Dcolumns)%0A%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%20%20%20%20%20%23%20Check%20CVaR%20value%20using%20primal%20formula%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20var%2C%20cvar%20%3D%20CVaR(alpha%2C%20p%2C%20-R%20%40%20x.level())%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20print(f%22%22%22Relative%20difference%20between%20primal%20and%20dual%20Var%20and%20CVaR%20(%25)%3A%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%7B(var%20-%20t.level()%5B0%5D)%20%2F%20t.level()%5B0%5D%20*%20100%3A.6f%7D%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%7B(cvar%20-%20portfolio_risk)%20%2F%20portfolio_risk%20*%20100%3A.6f%7D%22%22%22)%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%2C%20_%20%3D%20compute_inputs(df_prices)%0A%20%20%20%20return%20N%2C%20m%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%7B2%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%20N%2C%20T%2C%20alpha%2C%20m%2C%20np%2C%20p%2C%20scenarios_lin)%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%3D2%2C%20num%3D20)%5B%3A%3A-1%5D%0A%20%20%20%20df_result%20%3D%20EfficientFrontier(N%2C%20T%2C%20m%2C%20scenarios_lin%2C%20p%2C%20alpha%2C%20deltas)%0A%20%20%20%20return%20(df_result%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%20Check%20the%20results.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(df_result)%3A%0A%20%20%20%20df_result%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.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(df_result)%3A%0A%20%20%20%20_ax%20%3D%20df_result.plot(x%3D'risk'%2C%20y%3D'return'%2C%20style%3D'-o'%2C%20xlabel%3D'portfolio%20risk%20(CVaR)'%2C%20ylabel%3D'portfolio%20return'%2C%20grid%3DTrue)%0A%20%20%20%20_ax.legend(%5B'return'%5D)%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.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(df_result%2C%20np)%3A%0A%20%20%20%20%23%20Round%20small%20values%20to%200%20to%20make%20plotting%20work%0A%20%20%20%20mask%20%3D%20np.absolute(df_result)%20%3C%201e-7%0A%20%20%20%20mask.iloc%5B%3A%2C%20%3A-8%5D%20%3D%20False%0A%20%20%20%20df_result%5Bmask%5D%20%3D%200%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(LinearSegmentedColormap%2C%20df_result%2C%20plt)%3A%0A%20%20%20%20my_cmap%20%3D%20LinearSegmentedColormap.from_list('non-extreme%20gray'%2C%20%5B'%23111111'%2C%20'%23eeeeee'%5D%2C%20N%3D256%2C%20gamma%3D1.0)%0A%20%20%20%20_ax%20%3D%20df_result.set_index('risk').iloc%5B%3A%2C%203%3A%5D.plot.area(colormap%3Dmy_cmap%2C%20xlabel%3D'portfolio%20risk%20(CVaR)'%2C%20ylabel%3D'x')%0A%20%20%20%20_ax.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(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
aac4404d947d14fdfc4e3aefe3999ea3