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%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%20re%2C%0A%20%20%20%20%20%20%20%20sys%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_t%24%20for%20all%20periods%20%24t%20%3D%201%2C%20%5Cdots%2C%20T%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(read_volume%3DTrue)%0A%20%20%20%20df_prices%2C%20df_volumes%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%20df_prices%2C%20df_volumes%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%20We%20will%20solve%20the%20following%20multiperiod%20optimization%20problem%3A%0A%0A%20%20%20%20%24%24%0A%20%20%20%20%20%20%20%20%5Cbegin%7Barray%7D%7Blrcl%7D%0A%20%20%20%20%20%20%20%20%5Ctext%7Bmaximize%7D%20%20%20%20%20%26%20%5Csum_%7Bt%3D1%7D%5ET%5Cmu_t%5E%5Cmathsf%7BT%7D%5Cmathbf%7Bx%7D_t%20-%20%5Cdelta_t%20%5Cmathbf%7Bx%7D_t%5E%5Cmathsf%7BT%7D%5CSigma_t%5Cmathbf%7Bx%7D_t%20-%20%5Cleft(%5Csum_%7Bi%3D1%7D%5EN%20a_%7Bt%2Ci%7D%7Cx_%7Bt%2Ci%7D-x_%7Bt-1%2Ci%7D%7C%20%2B%20%5Ctilde%7Bb%7D_%7Bt%2Ci%7D%7Cx_%7Bt%2Ci%7D-x_%7Bt-1%2Ci%7D%7C%5E%7B3%2F2%7D%5Cright)%20%20%20%20%20%20%26%20%20%20%20%20%20%20%20%20%20%26%5C%5C%0A%20%20%20%20%20%20%20%20%5Ctext%7Bsubject%20to%7D%20%20%20%26%20%5Cmathbf%7B1%7D%5E%5Cmathsf%7BT%7D%5Cmathbf%7Bx%7D_t%20%20%20%20%20%20%20%20%20%20%20%20%20%20%26%20%3D%20%20%20%20%20%20%20%20%26%201%2C%5C%5C%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%26%20%5Cmathbf%7Bx%7D_t%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%20%20%20%26%20%5Cgeq%20%20%20%20%20%26%200.%5C%5C%0A%20%20%20%20%20%20%20%20%5Cend%7Barray%7D%0A%20%20%20%20%24%24%0A%0A%20%20%20%20The%20first%20term%20is%20the%20portfolio%20return%20in%20period%20%24i%24%2C%20the%20second%20term%20is%20the%20portfolio%20risk%20in%20period%20%24i%24%2C%20and%20the%20third%20term%20is%20a%20transaction%20cost%20term%20for%20period%20%24i%24.%20The%20%24a_%7Bt%2Ci%7D%24%20are%20the%20coefficients%20of%20the%20linear%20cost%20term%2C%20and%20the%20%24%5Ctilde%7Bb%7D_%7Bt%2Ci%7D%24%20are%20the%20coefficients%20of%20the%20market%20impact%20cost%20term%3A%20%24%5Ctilde%7Bb%7D_%7Bt%2Ci%7D%20%3D%20b_%7Bt%2Ci%7D%5Csigma_%7Bt%2Ci%7D%2F%5Cleft(%5Cfrac%7Bq_%7Bt%2Ci%7D%7D%7BV_t%7D%5Cright)%5E%7B1%2F2%7D%24%2C%20where%20%24b_%7Bt%2Ci%7D%20%3D%201%24%2C%20%24%5Csigma_%7Bt%2Ci%7D%24%20is%20the%20volatility%20of%20security%20%24i%24%20in%20period%20%24t%24%2C%20and%20%24%5Cfrac%7Bq_%7Bt%2Ci%7D%7D%7BV_t%7D%24%20is%20the%20portfolio%20value%20normalized%20dollar%20volume%20of%20security%20%24i%24%20in%20period%20%24t%24.%20The%20total%20objective%20is%20the%20sum%20of%20these%20terms%20for%20all%20periods.%0A%0A%20%20%20%20Then%20we%20rewrite%20the%20above%20problem%20into%20conic%20form%2C%20and%20implement%20it%20in%20Fusion%20API%3A%0A%0A%20%20%20%20%24%24%0A%20%20%20%20%20%20%20%20%5Cbegin%7Barray%7D%7Blrcl%7D%0A%20%20%20%20%20%20%20%20%5Ctext%7Bmaximize%7D%20%20%20%20%20%26%20%5Csum_%7Bt%3D1%7D%5ET%5Cmu_t%5E%5Cmathsf%7BT%7D%5Cmathbf%7Bx%7D_t%20-%20%5Cdelta_t%20s_%7Bt%7D%20-%20%5Cleft(%5Csum_%7Bi%3D1%7D%5EN%20a_%7Bt%2Ci%7Dv_%7Bt%2Ci%7D%20%2B%20%5Ctilde%7Bb%7D_%7Bt%2Ci%7Dw_%7Bt%2Ci%7D%5Cright)%20%20%20%20%20%20%26%20%20%20%20%20%20%20%20%20%20%26%5C%5C%0A%20%20%20%20%20%20%20%20%5Ctext%7Bsubject%20to%7D%20%20%20%26%20(s_%7Bt%7D%2C%200.5%2C%20%5Cmathbf%7BG%7D_%7Bt%7D%5E%5Cmathsf%7BT%7D%5Cmathbf%7Bx%7D_t)%20%26%20%5Cin%20%20%20%20%20%20%26%20Q_%5Cmathrm%7Br%7D%5E%7BN%2B2%7D%2C%5Cquad%20t%20%3D%201%2C%5Cdots%2CT%5C%5C%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%26%20%7Cx_%7Bt%7D-x_%7Bt-1%7D%7C%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%26%20%5Cleq%20%20%20%20%20%26%20v_%7Bt%7D%2C%5Cquad%20t%20%3D%201%2C%5Cdots%2CT%5C%5C%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%26%20(w_%7Bt%2Ci%7D%2C%201%2C%20x_%7Bt%2Ci%7D-x_%7Bt-1%2Ci%7D)%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%26%20%5Cin%20%20%20%20%20%20%26%20%5Cmathcal%7BP%7D_3%5E%7B2%2F3%2C1%2F3%7D%2C%5Cquad%20t%20%3D%201%2C%5Cdots%2CT%2C%5C%20i%20%3D%201%2C%5Cdots%2CN%5C%5C%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%26%20%5Cmathbf%7B1%7D%5E%5Cmathsf%7BT%7D%5Cmathbf%7Bx%7D_t%20%20%20%20%20%20%20%20%20%20%20%20%20%20%26%20%3D%20%20%20%20%20%20%20%20%26%201%2C%5C%5C%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%26%20%5Cmathbf%7Bx%7D_t%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%20%20%20%26%20%5Cgeq%20%20%20%20%20%26%200.%5C%5C%0A%20%20%20%20%20%20%20%20%5Cend%7Barray%7D%0A%20%20%20%20%24%24%0A%0A%20%20%20%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%20np%2C%20sys)%3A%0A%20%20%20%20def%20absval(M%2C%20x%2C%20t)%3A%0A%20%20%20%20%20%20%20%20M.constraint(t%20%2B%20x%20%3E%3D%200)%0A%20%20%20%20%20%20%20%20M.constraint(t%20-%20x%20%3E%3D%200)%0A%0A%20%20%20%20def%20norm1(M%2C%20x%2C%20t)%3A%0A%20%20%20%20%20%20%20%20z%20%3D%20M.variable(x.getSize()%2C%20Domain.greaterThan(0.0))%0A%20%20%20%20%20%20%20%20absval(M%2C%20x%2C%20z)%0A%20%20%20%20%20%20%20%20M.constraint(Expr.sum(z)%20%3D%3D%20t)%0A%0A%20%20%20%20def%20multiperiod_mvo(N%2C%20T%2C%20m%2C%20G%2C%20x_0%2C%20delta%2C%20a%2C%20b)%3A%0A%0A%20%20%20%20%20%20%20%20with%20Model(%22multiperiod%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%20M.setLogHandler(sys.stdout)%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Variable%0A%20%20%20%20%20%20%20%20%20%20%20%20x%20%3D%20M.variable(%22x%22%2C%20%5BN%2C%20T%5D%2C%20Domain.greaterThan(0.0))%0A%20%20%20%20%20%20%20%20%20%20%20%20s%20%3D%20M.variable(%22s%22%2C%20T)%0A%20%20%20%20%20%20%20%20%20%20%20%20v%20%3D%20M.variable(%22v%22%2C%20%5BN%2C%20T%5D)%0A%20%20%20%20%20%20%20%20%20%20%20%20w%20%3D%20M.variable(%22w%22%2C%20%5BN%2C%20T%5D)%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Constraint%0A%20%20%20%20%20%20%20%20%20%20%20%20M.constraint(%22budget%22%2C%20Expr.sum(x%2C%200)%20%3D%3D%20np.ones(T))%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%20M.objective(%22obj%22%2C%20ObjectiveSense.Maximize%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20Expr.add(%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20x%5B%3A%2C%20t%5D.T%20%40%20m%5Bt%5D%20-%20delta%5Bt%5D%20*%20s%5Bt%5D%20-%20v%5B%3A%2C%20t%5D.T%20%40%20a%5B%3A%2C%20t%5D%20-%20w%5B%3A%2C%20t%5D.T%20%40%20b%5B%3A%2C%20t%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20for%20t%20in%20range(T)%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)%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Objective%20cones%0A%20%20%20%20%20%20%20%20%20%20%20%20for%20t%20in%20range(T)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20xt%20%3D%20x%5B%3A%2C%20t%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20xtprev%20%3D%20x_0%20if%20t%20%3D%3D%200%20else%20x%5B%3A%2C%20t%20-%201%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20xtdiff%20%3D%20xt%20-%20xtprev%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20M.constraint(f'risk_%7Bt%7D'%2C%20Expr.flatten(Expr.vstack(s%5Bt%5D%2C%200.5%2C%20G%5Bt%5D.T%20%40%20xt))%2C%20Domain.inRotatedQCone())%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20absval(M%2C%20xtdiff%2C%20v%5B%3A%2C%20t%5D)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20M.constraint(f'market_impact_%7Bt%7D'%2C%20Expr.hstack(w%5B%3A%2C%20t%5D%2C%20Expr.constTerm(N%2C%201.0)%2C%20xtdiff)%2C%20Domain.inPPowerCone(2%20%2F%203))%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Solve%20the%20problem%0A%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%23%20Check%20if%20the%20solution%20is%20an%20optimal%20point%0A%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%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%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%20raise%20Exception(%22Unexpected%20solution%20status!%22)%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Get%20the%20solution%20values%0A%20%20%20%20%20%20%20%20%20%20%20%20x_value%20%3D%20x.level().reshape(N%2C%20T)%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20x_value%0A%20%20%20%20return%20(multiperiod_mvo%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%0Adef%20_(df_prices%2C%20np)%3A%0A%20%20%20%20%23%20Number%20of%20securities%0A%20%20%20%20N%20%3D%20df_prices.shape%5B1%5D%0A%0A%20%20%20%20%23%20Number%20of%20periods%0A%20%20%20%20T%20%3D%2010%0A%0A%20%20%20%20%23%20Initial%20weights%0A%20%20%20%20x_0%20%3D%20np.array(%5B1%5D%20*%20N)%20%2F%20N%0A%20%20%20%20portfolio_value%20%3D%2010**8%0A%20%20%20%20return%20N%2C%20T%2C%20portfolio_value%2C%20x_0%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%20an%20estimate%20of%20the%20yearly%20mean%20return%20and%20covariance%20matrix%20for%20each%20trading%20period.%20These%20are%20%22dummy%22%20estimates%2C%20created%20from%20one%20sample%20mean%20and%20sample%20covariance%20based%20on%20the%20data.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(T%2C%20compute_inputs%2C%20df_prices%2C%20np)%3A%0A%20%20%20%20def%20symmat(m)%3A%0A%20%20%20%20%20%20%20%20return%20(m%20%2B%20m.T)%20%2F%202%0A%0A%20%20%20%20def%20makepsd(m)%3A%0A%20%20%20%20%20%20%20%20mineig%20%3D%20np.min(np.linalg.eigvals(m))%0A%20%20%20%20%20%20%20%20if%20mineig%20%3C%200%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20m%20%3D%20m%20-%20(mineig%20-%200.0001)%20*%20np.identity(m.shape%5B0%5D)%0A%20%20%20%20%20%20%20%20return%20m%0A%0A%20%20%20%20mu%2C%20Sigma%20%3D%20compute_inputs(df_prices)%0A%20%20%20%20m%20%3D%20%5Bmu%20%2B%20np.random.normal(0%2C%20mu%2F10)%20for%20i%20in%20range(T)%5D%0A%20%20%20%20S%20%3D%20%5Bmakepsd(Sigma%20%2B%20symmat(np.random.normal(0%2C%20Sigma%2F10)))%20for%20i%20in%20range(T)%5D%0A%20%20%20%20return%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%20for%20all%20periods.%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%20G%20%3D%20%5Bnp.linalg.cholesky(s)%20for%20s%20in%20S%5D%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%20We%20also%20compute%20the%20average%20daily%20volume%20and%20daily%20volatility%20(std.%20dev.)%20for%20all%20periods.%20These%20are%20also%20dummy%20values.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(T%2C%20df_prices%2C%20df_volumes%2C%20np)%3A%0A%20%20%20%20df_lin_returns%20%3D%20df_prices.pct_change(fill_method%3DNone)%0A%20%20%20%20volatility%20%3D%20df_lin_returns.std()%0A%20%20%20%20volume%20%3D%20(df_volumes%20*%20df_prices).mean()%0A%20%20%20%20vty%20%3D%20%5Babs(volatility%20%2B%20np.random.normal(0%2C%20volatility%2F10))%20for%20i%20in%20range(T)%5D%0A%20%20%20%20vol%20%3D%20%5Babs(volume%20%2B%20np.random.normal(0%2C%20volume%2F10))%20for%20i%20in%20range(T)%5D%0A%20%20%20%20return%20vol%2C%20vty%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%20specify%20the%20transaction%20cost%20parameters%20for%20each%20period.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(N%2C%20T%2C%20np%2C%20portfolio_value%2C%20vol%2C%20vty)%3A%0A%20%20%20%20%23%20Transaction%20cost%0A%20%20%20%20a%20%3D%200.05%20*%20np.ones((N%2C%20T))%0A%0A%20%20%20%20%23%20Market%20impact%0A%20%20%20%20beta%20%3D%203%20%2F%202%0A%20%20%20%20b%20%3D%201%0A%20%20%20%20rel_volume%20%3D%20%5Bv%20%2F%20portfolio_value%20for%20v%20in%20vol%5D%20%23%20Relative%20volume%20(the%20variable%20x%20is%20also%20portfolio%20relative).%0A%20%20%20%20impact_coef%20%3D%20np.vstack(%5B(b%20*%20v%20%2F%20r**(beta%20-%201)).to_numpy()%20for%20v%2C%20r%20in%20zip(vty%2C%20rel_volume)%5D).T%0A%0A%20%20%20%20%23%20Holding%20cost%0A%20%20%20%20s%20%3D%200.01%0A%20%20%20%20return%20a%2C%20impact_coef%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%20with%20the%20risk%20aversion%20parameter%20%24%5Cdelta%20%3D%201%24%20for%20each%20period.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(G%2C%20N%2C%20T%2C%20a%2C%20impact_coef%2C%20m%2C%20multiperiod_mvo%2C%20np%2C%20x_0)%3A%0A%20%20%20%20delta%20%3D%20np.array(%5B10%5D%20*%20T)%0A%20%20%20%20x%20%3D%20multiperiod_mvo(N%2C%20T%2C%20m%2C%20G%2C%20x_0%2C%20delta%2C%20a%2C%20impact_coef)%0A%20%20%20%20return%20(x%2C)%0A%0A%0A%40app.cell%0Adef%20_(x)%3A%0A%20%20%20%20x%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
13067a1dadcd0667a54b3eb78581b8a0