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%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%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%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%0A%20%20%20%20The%20parameters%3A%0A%20%20%20%20-%20%60vp%60%2F%60vm%60%3A%20Variable%20cost%20coefficients%2C%0A%20%20%20%20-%20%60fp%60%2F%60fm%60%3A%20Fixed%20cost%20coefficients%2C%0A%20%20%20%20-%20%60up%60%2F%60um%60%3A%20Upper%20bound%20of%20long%20and%20short%20side%20of%20the%20portfolio%2C%0A%20%20%20%20-%20%60lp%60%2F%60lm%60%3A%20Lower%20bound%20of%20long%20and%20short%20side%20of%20the%20portfolio%2C%0A%20%20%20%20-%20%60pcoef%60%3A%20Penalty%20term%20coefficient.%20(We%20use%20a%20penalty%20term%20to%20force%20either%20%60xp%60%20or%20%60xm%60%20to%20zero.)%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20Domain%2C%0A%20%20%20%20Expr%2C%0A%20%20%20%20Model%2C%0A%20%20%20%20N%2C%0A%20%20%20%20ObjectiveSense%2C%0A%20%20%20%20SolutionStatus%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%20%23%20x%20%3D%20xp%20-%20xm%0A%20%20%20%20%23%20NOTE%3A%20Uses%20integer%20variables!%0A%20%20%20%20def%20posneg(M%2C%20x%2C%20bigm_p%2C%20bigm_m%3DNone)%3A%0A%20%20%20%20%20%20%20%20bigm_m%20%3D%20bigm_p%20if%20bigm_m%20is%20None%20else%20bigm_m%0A%0A%20%20%20%20%20%20%20%20%23%20Positive%20and%20negative%20part%20of%20x%0A%20%20%20%20%20%20%20%20xp%20%3D%20M.variable(%22_xp%22%2C%20N%2C%20Domain.greaterThan(0.0))%0A%20%20%20%20%20%20%20%20xm%20%3D%20M.variable(%22_xm%22%2C%20N%2C%20Domain.greaterThan(0.0))%0A%0A%20%20%20%20%20%20%20%20%23%20Binary%20variables%0A%20%20%20%20%20%20%20%20yp%20%3D%20M.variable(%22_yp%22%2C%20N%2C%20Domain.binary())%0A%20%20%20%20%20%20%20%20ym%20%3D%20M.variable(%22_ym%22%2C%20N%2C%20Domain.binary())%0A%0A%20%20%20%20%20%20%20%20%23%20Constraint%20assigning%20xp%20and%20xm%20to%20be%20the%20positive%20and%20negative%20part%20of%20x.%0A%20%20%20%20%20%20%20%20M.constraint('_pos-neg-part'%2C%20x%20%3D%3D%20xp%20-%20xm)%0A%0A%20%20%20%20%20%20%20%20%23%20Constraints%20making%20sure%20xp%20and%20xm%20are%20never%20both%20positive.%0A%20%20%20%20%20%20%20%20M.constraint('_ubound-p'%2C%20xp%20%3C%3D%20bigm_p%20*%20yp)%0A%20%20%20%20%20%20%20%20M.constraint('_ubound-m'%2C%20xm%20%3C%3D%20bigm_m%20*%20ym)%0A%20%20%20%20%20%20%20%20M.constraint('_exclusion'%2C%20yp%20%2B%20ym%20%3C%3D%201.0)%0A%0A%20%20%20%20%20%20%20%20return%20xp%2C%20xm%2C%20yp%2C%20ym%0A%0A%0A%20%20%20%20def%20EfficientFrontier(N%2C%20m%2C%20G%2C%20deltas%2C%20vp%2C%20vm%2C%20fp%2C%20fm%2C%20up%2C%20um%2C%20lp%2C%20lm%2C%20pcoef)%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%20Real%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%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.unbounded())%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%20Auxiliary%20variable%20for%20130%2F30%20leverage%20constraint%20%0A%20%20%20%20%20%20%20%20%20%20%20%20z%20%3D%20M.variable(%22z%22%2C%20N%2C%20Domain.unbounded())%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Positive%20and%20negative%20part%20of%20x%0A%20%20%20%20%20%20%20%20%20%20%20%20xp%2C%20xm%2C%20yp%2C%20ym%20%3D%20posneg(M%2C%20x%2C%20up%2C%20um)%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Buy-in%0A%20%20%20%20%20%20%20%20%20%20%20%20M.constraint('lbound-p'%2C%20xp%20%3E%3D%20lp%20*%20yp)%0A%20%20%20%20%20%20%20%20%20%20%20%20M.constraint('lbound-m'%2C%20xm%20%3E%3D%20lm%20*%20ym)%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Budget%20constraint%20with%20transaction%20cost%20terms%0A%20%20%20%20%20%20%20%20%20%20%20%20fixcost_terms%20%3D%20yp.T%20%40%20fp%20%2B%20ym.T%20%40%20fm%0A%20%20%20%20%20%20%20%20%20%20%20%20varcost_terms%20%3D%20xp.T%20%40%20vp%20%2B%20xm.T%20%40%20vm%0A%20%20%20%20%20%20%20%20%20%20%20%20budget_terms%20%3D%20Expr.sum(x)%20%2B%20varcost_terms%20%2B%20fixcost_terms%0A%20%20%20%20%20%20%20%20%20%20%20%20M.constraint('budget'%2C%20budget_terms%20%3D%3D%201.0)%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20130%2F30%20leverage%20constraint%0A%20%20%20%20%20%20%20%20%20%20%20%20M.constraint('leverage-gt'%2C%20z%20%3E%3D%20x)%0A%20%20%20%20%20%20%20%20%20%20%20%20M.constraint('leverage-ls'%2C%20z%20%3E%3D%20-x)%0A%20%20%20%20%20%20%20%20%20%20%20%20M.constraint('leverage-sum'%2C%20Expr.sum(z)%20%2B%20varcost_terms%20%2B%20fixcost_terms%20%3D%3D%201.6)%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%20penalty%20%3D%20pcoef%20*%20Expr.sum(xp%20%2B%20xm)%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-%20penalty%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%201%2C%20G.T%20%40%20x)%2C%20Domain.inRotatedQCone())%0A%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%2C%20%22x_sum%22%2C%20%22tcost%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%20idx%2C%20d%20in%20enumerate(deltas)%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%20np.sqrt(2%20*%20s.level()%5B0%5D)%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20tcost%20%3D%20np.dot(vp%2C%20xp.level())%20%2B%20np.dot(vm%2C%20xm.level())%20%2B%20np.dot(fp%2C%20yp.level())%20%2B%20np.dot(fm%2C%20ym.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%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%20sum(x.level())%2C%20tcost%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%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%2C%20S%20%3D%20compute_inputs(df_prices)%0A%20%20%20%20return%20N%2C%20S%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%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%2C%20np)%3A%0A%20%20%20%20%23%20Cholesky%20factor%20of%20S%20to%20use%20in%20conic%20risk%20constraint%0A%20%20%20%20G%20%3D%20np.linalg.cholesky(S)%0A%20%20%20%20return%20(G%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%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-0.5%7D%2C%5Cdots%2C10%5E%7B2%7D%24.%20We%20compute%20and%20plot%20the%20efficient%20frontier%20this%20way%20both%20with%20and%20without%20transaction%20cost.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(EfficientFrontier%2C%20G%2C%20N%2C%20m%2C%20np%2C%20plt)%3A%0A%20%20%20%20deltas%20%3D%20np.logspace(start%3D-0.5%2C%20stop%3D2%2C%20num%3D20)%5B%3A%3A-1%5D%0A%20%20%20%20ax%20%3D%20plt.gca()%0A%20%20%20%20for%20a%20in%20%5B0%2C%201%5D%3A%0A%20%20%20%20%20%20%20%20pcoef%20%3D%20a%20*%200.03%0A%20%20%20%20%20%20%20%20fp%20%3D%20a%20*%200.005%20*%20np.ones(N)%20%23%20Depends%20on%20portfolio%20value%0A%20%20%20%20%20%20%20%20fm%20%3D%20a%20*%200.01%20*%20np.ones(N)%20%23%20Depends%20on%20portfolio%20value%0A%20%20%20%20%20%20%20%20vp%20%3D%20a%20*%200.01%20*%20np.ones(N)%0A%20%20%20%20%20%20%20%20vm%20%3D%20a%20*%200.02%20*%20np.ones(N)%0A%20%20%20%20%20%20%20%20up%20%3D%202.0%0A%20%20%20%20%20%20%20%20um%20%3D%202.0%0A%20%20%20%20%20%20%20%20lp%20%3D%20a%20*%200.05%0A%20%20%20%20%20%20%20%20lm%20%3D%20a%20*%200.05%0A%0A%20%20%20%20%20%20%20%20df_result%20%3D%20EfficientFrontier(N%2C%20m%2C%20G%2C%20deltas%2C%20vp%2C%20vm%2C%20fp%2C%20fm%2C%20up%2C%20um%2C%20lp%2C%20lm%2C%20pcoef)%0A%20%20%20%20%20%20%20%20df_result.plot(ax%3Dax%2C%20x%3D%22risk%22%2C%20y%3D%22return%22%2C%20style%3D%22-o%22%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%20xlabel%3D%22portfolio%20risk%20(std.%20dev.)%22%2C%20ylabel%3D%22portfolio%20return%22%2C%20grid%3DTrue)%0A%20%20%20%20ax.legend(%5B%22return%20without%20transaction%20cost%22%2C%20%22return%20with%20transaction%20cost%22%5D)%3B%0A%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
7aeea4bc570e7e0c496ff447f9c48f0e