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%20statsmodels.api%20as%20sm%0A%20%20%20%20import%20scipy.stats%20as%20stats%0A%20%20%20%20from%20scipy.optimize%20import%20brentq%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%20%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%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%20brentq%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%20%20%20%20sm%2C%0A%20%20%20%20%20%20%20%20stats%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%22SPY%22%2C%20%22IWM%22%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%20df_prices%2C%20list_factors%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%20np%2C%20pd)%3A%0A%20%20%20%20%23%20%7Cx%7C%20%3C%3D%20t%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%0A%20%20%20%20def%20sqrtm_symm(m)%3A%0A%20%20%20%20%20%20%20%20e%2C%20v%20%3D%20np.linalg.eigh(m)%0A%20%20%20%20%20%20%20%20sqrt_e%20%3D%20np.sqrt(e)%0A%20%20%20%20%20%20%20%20sqrt_m%20%3D%20np.dot(v%2C%20np.dot(np.diag(sqrt_e)%2C%20v.T))%0A%20%20%20%20%20%20%20%20return%20sqrt_m%0A%0A%0A%20%20%20%20def%20EfficientFrontier(N%2C%20mu0%2C%20gamma%2C%20beta0%2C%20Gmx%2C%20rho%2C%20diag_S_theta_upper%2C%20Q0%2C%20Nmx%2C%20zeta%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%20Get%20number%20of%20factors%0A%20%20%20%20%20%20%20%20%20%20%20%20K%20%3D%20Q0.shape%5B0%5D%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%20%20%20%20%20%20%20%20%20%20%20%20z%20%3D%20M.variable(%22z%22%2C%20N%2C%20Domain.greaterThan(0.0))%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Constrain%20absolute%20value%0A%20%20%20%20%20%20%20%20%20%20%20%20absval(M%2C%20x%2C%20z)%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20The%20variable%20t1%20and%20t2%20models%20the%20factor%20and%20specific%20portfolio%20variance%20terms.%0A%20%20%20%20%20%20%20%20%20%20%20%20t1%20%3D%20M.variable(%22t1%22%2C%201%2C%20Domain.greaterThan(0.0))%0A%20%20%20%20%20%20%20%20%20%20%20%20t2%20%3D%20M.variable(%22t2%22%2C%201%2C%20Domain.greaterThan(0.0))%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20The%20variables%20tau%2C%20s%2C%20u%20help%20modeling%20the%20factor%20risk.%0A%20%20%20%20%20%20%20%20%20%20%20%20tau%20%3D%20M.variable(%22tau%22%2C%201%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%201%2C%20Domain.greaterThan(0.0))%0A%20%20%20%20%20%20%20%20%20%20%20%20u%20%3D%20M.variable(%22u%22%2C%20K%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)%2C%20Domain.equalsTo(1.0))%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Objective%20(variance%20minimization)%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%20wc_return%20%3D%20x.T%20%40%20mu0%20-%20z.T%20%40%20gamma%0A%20%20%20%20%20%20%20%20%20%20%20%20M.objective('obj'%2C%20ObjectiveSense.Maximize%2C%20wc_return%20-%20delta%20*%20(t1%20%2B%20t2))%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Risk%20constraint%20(specific)%0A%20%20%20%20%20%20%20%20%20%20%20%20M.constraint('spec-risk'%2C%20Expr.vstack(t2%2C%200.5%2C%20Expr.mulElm(np.sqrt(diag_S_theta_upper)%2C%20x))%2C%20Domain.inRotatedQCone())%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Risk%20constraint%20(factor)%0A%20%20%20%20%20%20%20%20%20%20%20%20siG%20%3D%20sqrtm_symm(np.linalg.inv(Gmx))%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20H%20%3D%20siG%20%40%20(Q0%20%2B%20zeta%20*%20Nmx)%20%40%20siG%20%0A%20%20%20%20%20%20%20%20%20%20%20%20lam%2C%20V%20%3D%20np.linalg.eigh(H)%0A%20%20%20%20%20%20%20%20%20%20%20%20w%20%3D%20(V.T%20%40%20sqrtm_symm(H)%20%40%20sqrtm_symm(Gmx)%20%40%20beta0.T)%20%40%20x%0A%20%20%20%20%20%20%20%20%20%20%20%20M.constraint('fact-risk-1'%2C%20t1%20%3E%3D%20tau%20%2B%20Expr.sum(u))%0A%20%20%20%20%20%20%20%20%20%20%20%20M.constraint('fact-risk-2'%2C%20s%20%3C%3D%201.0%20%2F%20lam%5B-1%5D)%0A%20%20%20%20%20%20%20%20%20%20%20%20M.constraint('fact-risk-3'%2C%20Expr.vstack(s%2C%200.5%20*%20tau%2C%20z.T%20%40%20rho)%2C%20Domain.inRotatedQCone())%0A%20%20%20%20%20%20%20%20%20%20%20%20col1%20%3D%20Expr.constTerm(K%2C%201.0)%20-%20Expr.mulElm(Expr.repeat(s%2C%20K%2C%200)%2C%20lam)%0A%20%20%20%20%20%20%20%20%20%20%20%20M.constraint('fact-risk-4'%2C%20Expr.hstack(col1%2C%200.5%20*%20u%2C%20w)%2C%20Domain.inRotatedQCone())%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Create%20DataFrame%20to%20store%20the%20results.%20Last%20security%20names%20(the%20factors)%20are%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%2C%20%22zdiff%22%5D%20%2B%20df_prices.columns%5B%3A-K%5D.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%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%20mu0%20%40%20x.level()%20-%20gamma%20%40%20z.level()%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20portfolio_risk%20%3D%20np.sqrt((t1.level()%20%2B%20t2.level())%5B0%5D)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20zdiff%20%3D%20np.sum(np.abs(x.level())%20-%20z.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%20zdiff%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%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%20Define%20the%20factor%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%20create%20a%20function%20to%20make%20scenarios.%20The%20input%20is%20the%20expected%20return%20and%20covariance%20of%20yearly%20logarithmic%20returns.%20The%20reason%20for%20this%20is%20that%20it%20is%20easier%20to%20generate%20logarithmic%20return%20scenarios%20from%20normal%20distribution%20than%20generating%20linear%20return%20scenarios%20from%20lognormal%20distribution.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(np)%3A%0A%20%20%20%20def%20scenarios(m_log%2C%20S_log%2C%20factor_num)%3A%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20It%20is%20assumed%20that%20the%20last%20factor_num%20coordinates%20correspond%20to%20the%20factors.%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20if%20factor_num%20%3C%201%3A%20%0A%20%20%20%20%20%20%20%20%20%20%20%20raise%20Exception(%22Does%20not%20make%20sense%20to%20compute%20a%20factor%20model%20without%20factors!%22)%0A%0A%20%20%20%20%20%20%20%20%23%20Generate%20logarithmic%20return%20scenarios%0A%20%20%20%20%20%20%20%20scenarios_log%20%3D%20np.random.default_rng().multivariate_normal(m_log%2C%20S_log%2C%20100000)%0A%0A%20%20%20%20%20%20%20%20%23%20Convert%20logarithmic%20return%20scenarios%20to%20linear%20return%20scenarios%20%0A%20%20%20%20%20%20%20%20scenarios%20%3D%20np.exp(scenarios_log)%20-%201%0A%0A%20%20%20%20%20%20%20%20R%20%3D%20scenarios%5B%3A%2C%20%3A-factor_num%5D%0A%20%20%20%20%20%20%20%20F%20%3D%20scenarios%5B%3A%2C%20-factor_num%3A%5D%0A%0A%20%20%20%20%20%20%20%20return%20R%2C%20F%0A%20%20%20%20return%20(scenarios%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%20Next%20we%20define%20a%20function%20that%20computes%20the%20factor%20model%0A%0A%20%20%20%20%24%24%0A%20%20%20%20R_t%20%3D%20%5Cmu%20%2B%20%5Cbeta%20F_%7Bt%7D%20%2B%20%5Ctheta_t.%0A%20%20%20%20%24%24%0A%0A%20%20%20%20The%20function%20can%20handle%20any%20number%20of%20factors%2C%20and%20returns%20estimates%20%24%5Cmu%24%2C%20%24%5Cbeta%24%2C%20%24%5CSigma_F%24%2C%20%24%5CSigma_%5Ctheta%24%2C%20and%20the%20factor%20return%20matrix.%20The%20factors%20are%20assumed%20to%20be%20at%20the%20last%20coordinates%20of%20the%20data.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(N%2C%20np%2C%20sm)%3A%0A%20%20%20%20def%20factor_model(R%2C%20F)%3A%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20It%20is%20assumed%20that%20the%20last%20factor_num%20coordinates%20correspond%20to%20the%20factors.%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20factor_num%20%3D%20F.shape%5B1%5D%0A%0A%20%20%20%20%20%20%20%20%23%20Do%20linear%20regression%20%0A%20%20%20%20%20%20%20%20params%20%3D%20%5B%5D%0A%20%20%20%20%20%20%20%20resid%20%3D%20%5B%5D%0A%20%20%20%20%20%20%20%20X%20%3D%20F%0A%20%20%20%20%20%20%20%20X%20%3D%20sm.add_constant(X%2C%20prepend%3DTrue)%0A%0A%20%20%20%20%20%20%20%20for%20k%20in%20range(N)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20y%20%3D%20R%5B%3A%2C%20k%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20model%20%3D%20sm.OLS(y%2C%20X%2C%20hasconst%3DTrue).fit()%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20resid.append(model.resid)%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20params.append(model.params)%0A%20%20%20%20%20%20%20%20resid%20%3D%20np.array(resid)%0A%20%20%20%20%20%20%20%20params%20%3D%20np.array(params)%0A%0A%0A%20%20%20%20%20%20%20%20%23%20Get%20parameter%20estimates%0A%20%20%20%20%20%20%20%20mu%20%3D%20params%5B%3A%2C%200%5D%0A%20%20%20%20%20%20%20%20B%20%3D%20params%5B%3A%2C%201%3A%5D%0A%0A%20%20%20%20%20%20%20%20S_F%20%3D%20np.atleast_2d(np.cov(X%5B%3A%2C%201%3A%5D.T))%20%20%23%20MLE%20computed%20from%20data%0A%20%20%20%20%20%20%20%20S_theta%20%3D%20np.cov(resid%2C%20ddof%3Dfactor_num%20%2B%201)%20%20%20%0A%20%20%20%20%20%20%20%20diag_S_theta%20%3D%20np.diag(S_theta)%0A%0A%20%20%20%20%20%20%20%20return%20mu%2C%20B%2C%20S_F%2C%20diag_S_theta%2C%20X%0A%20%20%20%20return%20(factor_model%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%20Finally%2C%20we%20define%20functions%20that%20will%20compute%20the%20parametrization%20for%20the%20uncertainty%20sets%20of%20the%20factor%20model%20parameters.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(brentq%2C%20np%2C%20stats)%3A%0A%20%20%20%20def%20unc_mu(mu_est%2C%20diag_S_theta_est%2C%20A%2C%20omega)%3A%0A%20%20%20%20%20%20%20%20K%20%3D%20A.shape%5B1%5D%20-%201%0A%20%20%20%20%20%20%20%20T%20%3D%20A.shape%5B0%5D%0A%20%20%20%20%20%20%20%20iAA%20%3D%20np.linalg.inv(A.T%20%40%20A)%0A%20%20%20%20%20%20%20%20c%20%3D%20stats.f.ppf(omega%2C%20K%20%2B%201%2C%20T%20-%20K%20-%201)%0A%0A%20%20%20%20%20%20%20%20%23%20Parametrization%0A%20%20%20%20%20%20%20%20mu0%20%3D%20mu_est%0A%20%20%20%20%20%20%20%20gamma%20%3D%20np.sqrt(diag_S_theta_est)%20*%20np.sqrt((K%20%2B%201)%20*%20iAA%5B0%2C%200%5D%20*%20c)%0A%0A%20%20%20%20%20%20%20%20return%20mu0%2C%20gamma%0A%0A%20%20%20%20def%20unc_beta(beta_est%2C%20diag_S_theta_est%2C%20A%2C%20omega)%3A%0A%20%20%20%20%20%20%20%20K%20%3D%20A.shape%5B1%5D%20-%201%0A%20%20%20%20%20%20%20%20T%20%3D%20A.shape%5B0%5D%0A%20%20%20%20%20%20%20%20F%20%3D%20A%5B%3A%2C1%3A%5D.T%0A%20%20%20%20%20%20%20%20F1%20%3D%20F%20%40%20np.ones((T%2C%201))%0A%20%20%20%20%20%20%20%20c%20%3D%20stats.f.ppf(omega%2C%20K%20%2B%201%2C%20T%20-%20K%20-%201)%0A%0A%20%20%20%20%20%20%20%20%23%20Parametrization%0A%20%20%20%20%20%20%20%20beta0%20%3D%20beta_est%0A%20%20%20%20%20%20%20%20Q%20%3D%20np.array(%5B%5B0%2C%201%2C%200%5D%2C%20%5B0%2C%200%2C%201%5D%5D)%0A%20%20%20%20%20%20%20%20iAA%20%3D%20np.linalg.inv(A.T%20%40%20A)%0A%20%20%20%20%20%20%20%20Gmx%20%3D%20F%20%40%20F.T%20-%20F1%20%40%20F1.T%20%2F%20T%0A%20%20%20%20%20%20%20%20rho%20%3D%20np.sqrt(diag_S_theta_est)%20*%20np.sqrt((K%20%2B%201)%20*%20c)%0A%0A%20%20%20%20%20%20%20%20return%20beta0%2C%20Gmx%2C%20rho%0A%0A%20%20%20%20def%20unc_d(diag_S_theta_est%2C%20percent)%3A%20%20%20%20%0A%20%20%20%20%20%20%20%20%23%20Here%20we%20just%20add%20a%20percentage%20to%20the%20estimated%20error%20variance%2C%20to%20get%20an%20upper%20bound%20estimate.%20%0A%20%20%20%20%20%20%20%20diag_S_theta_upper%20%3D%20diag_S_theta_est%20*%20(1.0%20%2B%20percent)%0A%20%20%20%20%20%20%20%20return%20diag_S_theta_upper%0A%0A%20%20%20%20def%20unc_q(S_F%2C%20A%2C%20omega)%3A%0A%20%20%20%20%20%20%20%20T%20%3D%20A.shape%5B0%5D%0A%0A%0A%20%20%20%20%20%20%20%20def%20fun(eta%2C%20T%2C%20omega)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20stats.gamma.cdf(1%20%2B%20eta%2C%20(T%20%2B%201)%20%2F%202%2C%20scale%3D2%20%2F%20(T%20-%201))%20-%20%5C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20stats.gamma.cdf(1%20-%20eta%2C%20(T%20%2B%201)%20%2F%202%2C%20scale%3D2%20%2F%20(T%20-%201))%20-%20%5C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20omega%0A%0A%0A%20%20%20%20%20%20%20%20eta%20%3D%20brentq(fun%2C%200%2C%201%2C%20args%3D(T%2C%20omega))%0A%0A%20%20%20%20%20%20%20%20%23%20Parametrization%0A%20%20%20%20%20%20%20%20Q0%20%3D%20S_F%0A%20%20%20%20%20%20%20%20Nmx%20%3D%20S_F%0A%0A%20%20%20%20%20%20%20%20zeta%20%3D%20eta%20%2F%20(1%20-%20eta)%0A%20%20%20%20%20%20%20%20return%20Q0%2C%20Nmx%2C%20zeta%0A%20%20%20%20return%20unc_beta%2C%20unc_d%2C%20unc_mu%2C%20unc_q%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_(df_prices%2C%20list_factors)%3A%0A%20%20%20%20%23%20Number%20of%20factors%0A%20%20%20%20fnum%20%3D%20len(list_factors)%0A%0A%20%20%20%20%23%20Number%20of%20securities%20(We%20subtract%20fnum%20to%20account%20for%20factors%20at%20the%20end%20of%20the%20price%20data)%0A%20%20%20%20N%20%3D%20df_prices.shape%5B1%5D%20-%20fnum%0A%20%20%20%20return%20N%2C%20fnum%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%20Now%20we%20compute%20the%20same%20using%20the%20factor%20model.%20First%20we%20compute%20logarithmic%20return%20statistics%20and%20use%20them%20to%20compute%20the%20factor%20exposures%20and%20covariances.%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%20factor_model%2C%20fnum%2C%20scenarios)%3A%0A%20%20%20%20m_log%2C%20S_log%20%3D%20compute_inputs(df_prices%2C%20return_log%3DTrue)%0A%20%20%20%20R%2C%20F%20%3D%20scenarios(m_log%2C%20S_log%2C%20fnum)%0A%0A%20%20%20%20%23%20Center%20factors%2C%20so%20we%20have%20the%20same%20model%20as%20in%20the%20article%20(Goldfarb--Iyengar%202003).%20%0A%20%20%20%20F%20-%3D%20F.mean(axis%3D0)%0A%0A%20%20%20%20%23%20Compute%20factor%20model%0A%20%20%20%20mu%2C%20B%2C%20S_F%2C%20diag_S_theta%2C%20X%20%3D%20factor_model(R%2C%20F)%0A%20%20%20%20return%20B%2C%20S_F%2C%20X%2C%20diag_S_theta%2C%20mu%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%20compute%20the%20parameters%20of%20the%20uncertainty%20sets.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(B%2C%20N%2C%20S_F%2C%20X%2C%20diag_S_theta%2C%20mu%2C%20np%2C%20unc_beta%2C%20unc_d%2C%20unc_mu%2C%20unc_q)%3A%0A%20%20%20%20%23%20Uncertainty%20set%20parameters%0A%20%20%20%20omega%20%3D%200.95%0A%20%20%20%20percent%20%3D%200.2%0A%20%20%20%20mu0%2C%20gamma%20%3D%20unc_mu(mu%2C%20diag_S_theta%2C%20X%2C%20omega)%0A%20%20%20%20beta0%2C%20Gmx%2C%20rho%20%3D%20unc_beta(B%2C%20diag_S_theta%2C%20X%2C%20omega)%0A%20%20%20%20diag_S_theta_upper%20%3D%20unc_d(diag_S_theta%2C%20percent)%0A%20%20%20%20Q0%2C%20Nmx%2C%20zeta%20%3D%20unc_q(S_F%2C%20X%2C%20omega)%0A%0A%20%20%20%20%23%20To%20get%20back%20the%20non_robust%20case%2C%20we%20have%20to%20zero%20the%20bounds%0A%20%20%20%20gamma_z%20%3D%20np.zeros(N)%0A%20%20%20%20rho_z%20%3D%20np.zeros(N)%0A%20%20%20%20zeta_z%20%3D%200.0%0A%20%20%20%20return%20(%0A%20%20%20%20%20%20%20%20Gmx%2C%0A%20%20%20%20%20%20%20%20Nmx%2C%0A%20%20%20%20%20%20%20%20Q0%2C%0A%20%20%20%20%20%20%20%20beta0%2C%0A%20%20%20%20%20%20%20%20diag_S_theta_upper%2C%0A%20%20%20%20%20%20%20%20gamma%2C%0A%20%20%20%20%20%20%20%20gamma_z%2C%0A%20%20%20%20%20%20%20%20mu0%2C%0A%20%20%20%20%20%20%20%20rho%2C%0A%20%20%20%20%20%20%20%20rho_z%2C%0A%20%20%20%20%20%20%20%20zeta%2C%0A%20%20%20%20%20%20%20%20zeta_z%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%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%20factor%20model.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20EfficientFrontier%2C%0A%20%20%20%20Gmx%2C%0A%20%20%20%20N%2C%0A%20%20%20%20Nmx%2C%0A%20%20%20%20Q0%2C%0A%20%20%20%20beta0%2C%0A%20%20%20%20diag_S_theta_upper%2C%0A%20%20%20%20gamma%2C%0A%20%20%20%20gamma_z%2C%0A%20%20%20%20mu0%2C%0A%20%20%20%20np%2C%0A%20%20%20%20rho%2C%0A%20%20%20%20rho_z%2C%0A%20%20%20%20zeta%2C%0A%20%20%20%20zeta_z%2C%0A)%3A%0A%20%20%20%20%23%20Compute%20efficient%20frontier%20with%20and%20without%20factor%20model%0A%20%20%20%20deltas%20%3D%20np.logspace(start%3D-1%2C%20stop%3D2%2C%20num%3D20)%5B%3A%3A-1%5D%20%2F%202%0A%20%20%20%20df_result_orig%20%3D%20EfficientFrontier(N%2C%20mu0%2C%20gamma_z%2C%20beta0%2C%20Gmx%2C%20rho_z%2C%20diag_S_theta_upper%2C%20Q0%2C%20Nmx%2C%20zeta_z%2C%20deltas)%0A%20%20%20%20df_result_robust%20%3D%20EfficientFrontier(N%2C%20mu0%2C%20gamma%2C%20beta0%2C%20Gmx%2C%20rho%2C%20diag_S_theta_upper%2C%20Q0%2C%20Nmx%2C%20zeta%2C%20deltas)%0A%20%20%20%20df_result_orig%0A%20%20%20%20return%20df_result_orig%2C%20df_result_robust%0A%0A%0A%40app.cell%0Adef%20_(df_result_orig%2C%20df_result_robust)%3A%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_robust%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_robust%5Bmask%5D%20%3D%200%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_robust%2C%20plt)%3A%0A%20%20%20%20ax%20%3D%20df_result_robust.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%22robust%20return%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_robust%2C%20plt)%3A%0A%20%20%20%20%23%20Plot%20portfolio%20composition%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_robust.set_index('risk').iloc%5B%3A%2C%204%3A%5D.clip(0%2C%20None).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%20ax1.legend(loc%3D'lower%20right')%0A%20%20%20%20ax2%20%3D%20df_result_orig.set_index('risk').iloc%5B%3A%2C%204%3A%5D.clip(0%2C%20None).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%20ax2.legend(loc%3D'lower%20right')%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
1fd719495e80c98d0149ccfc470ce4df