import%20marimo%0A%0A__generated_with%20%3D%20%220.18.4%22%0Aapp%20%3D%20marimo.App(width%3D%22medium%22)%0A%0Awith%20app.setup%3A%0A%20%20%20%20import%20marimo%20as%20mo%0A%20%20%20%20import%20numpy%20as%20np%0A%20%20%20%20import%20polars%20as%20pl%0A%20%20%20%20from%20cvx.linalg%20import%20rand_cov%0A%0A%20%20%20%20from%20cvx.core.variable%20import%20Variable%0A%20%20%20%20from%20cvx.risk.portfolio%20import%20minrisk_problem%0A%20%20%20%20from%20cvx.risk.sample%20import%20SampleCovariance%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20This%20notebook%20is%20based%20on%20an%20idea%20by%20Nicholas%20Gunther%0A%0A%20%20%20%20We%20welcome%20such%20ideas%20very%20much%20as%20they%20help%20to%20improve%20our%20tools%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20Can%20you%20give%20some%20guidance%20on%20how%20to%20include%20constraints%20in%20cvxrisk%3F%0A%20%20%20%20It%20doesn't%20seem%20to%20appear%20in%20the%20short%20documentation.%0A%20%20%20%20For%20specificity%2C%20here's%20a%20question%20related%20to%20price-earnings%20%22tilts%22.%0A%20%20%20%20I%20illustrate%20it%20with%20a%20toy%20example.%0A%0A%20%20%20%20Let%20S%20be%20a%20sample%20covariance%20matrix%2C%20let%20w_SP%20be%20the%20(market%20cap)%20weights%20of%20stocks%20in%20the%20S%26P%20500%20index%0A%20%20%20%20and%20let%20V%20be%20a%20vector%20of%20earning-price%20normalized%20%22z-scores%22.%20%20I%20want%20to%20solve%20the%20following%20for%20w%3A%0A%0A%20%20%20%20Minimize%20(w%20-%20w_SP).transpose%20%40%20S%20%40%20(w%20-%20w_SP)%0A%0A%20%20%20%20subject%20to%3A%0A%0A%20%20%20%20w%20%3E%3D%200%0A%0A%20%20%20%20sum(w)%20%3D%201%0A%0A%20%20%20%20w.dot(V)%20%3D%200.5%0A%0A%20%20%20%20The%20exercise%20is%20to%20find%20a%20portfolio%20that%20is%20%22close%22%20to%20the%20S%26P%20500%20index%20but%0A%20%20%20%20has%20lower%20price-earnings%20ratios%2C%20a%20%22value%22%20tilt.%20%20I%20can%20work%20through%20this%20in%20cvxpy%2C%0A%20%20%20%20somewhat%20laboriously%2C%20but%20can%20cvxrisk%20accommodate%20these%20constraints%20easily%3F%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20We%20shall%20first%20observe%20that%20if%20we%20drop%20the%20constraint%20w.dot(V)%0A%20%20%20%20the%20optimal%20vector%20is%20just%20w%20%3D%20w_SP%20assuming%20that%20w_SP%20%3E%3D%200%20and%20sum(w_SP)%20%3D%201.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20For%20this%20problem%20we%20have%20enhanced%20the%20builtin%20minrisk%20function.%0A%20%20%20%20One%20can%20now%20inject%20linear%20constraints%20as%20(a%2C%20lb%2C%20ub)%20tuples.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20%23%20Let's%20start%20without%20the%20tilting%20constraint%0A%20%20%20%20assets%20%3D%20%5B%22A%22%2C%20%22B%22%2C%20%22C%22%2C%20%22D%22%2C%20%22E%22%5D%0A%20%20%20%20s%20%3D%20pl.DataFrame(dict(zip(assets%2C%20rand_cov(len(assets)).T%2C%20strict%3DFalse)))%0A%0A%20%20%20%20%23%20those%20are%20the%20market%20weights%20for%20our%205%20markets%0A%20%20%20%20w_sp%20%3D%20pl.Series(values%3D%5B0.1%2C%200.2%2C%200.3%2C%200.1%2C%200.3%5D)%0A%0A%20%20%20%20%23%20the%20Variable%20for%20the%20weights%0A%20%20%20%20weights%20%3D%20Variable(len(assets))%0A%0A%20%20%20%20%23%20Let's%20define%20a%20sample%20covariance%20riskmodel%0A%20%20%20%20riskmodel%20%3D%20SampleCovariance(num%3Dlen(assets))%0A%20%20%20%20riskmodel.update(cov%3Ds.to_numpy()%2C%20lower_assets%3Dnp.zeros(len(assets))%2C%20upper_assets%3Dnp.ones(len(assets)))%0A%0A%20%20%20%20%23%20the%20tilting%20vector%0A%20%20%20%20v%20%3D%20pl.Series(values%3D%5B0.1%2C%200.1%2C%200.5%2C%200.0%2C%200.5%5D)%0A%20%20%20%20return%20assets%2C%20riskmodel%2C%20v%2C%20w_sp%2C%20weights%0A%0A%0A%40app.cell%0Adef%20_(assets%2C%20riskmodel%2C%20w_sp%2C%20weights)%3A%0A%20%20%20%20%23%20without%20the%20tilting%20constraint.%20We%20reproduce%20(as%20predicted)%20w_sp.%0A%20%20%20%20problem%20%3D%20minrisk_problem(riskmodel%3Driskmodel%2C%20weights%3Dweights%2C%20base%3Dw_sp.to_numpy()%2C%20constraints%3D%5B%5D)%0A%0A%20%20%20%20problem.solve()%0A%0A%20%20%20%20solution%20%3D%20pl.DataFrame(%7B%22asset%22%3A%20assets%2C%20%22weight%22%3A%20weights.value%7D)%0A%20%20%20%20print(solution)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(assets%2C%20riskmodel%2C%20v%2C%20w_sp%2C%20weights)%3A%0A%20%20%20%20%23%20Now%20we%20specify%20the%20tilting%20constraint%3A%20v%20%40%20w%20%3D%3D%200.5%20as%20(a%2C%20lb%2C%20ub)%20with%20lb%20%3D%3D%20ub%0A%20%20%20%20constraints%20%3D%20%5B(v.to_numpy()%2C%200.5%2C%200.5)%5D%0A%20%20%20%20%23%20We%20inject%20the%20constraints%0A%20%20%20%20problem_tilt%20%3D%20minrisk_problem(riskmodel%3Driskmodel%2C%20weights%3Dweights%2C%20base%3Dw_sp.to_numpy()%2C%20constraints%3Dconstraints)%0A%0A%20%20%20%20problem_tilt.solve()%0A%0A%20%20%20%20%23%20The%20solution%20is%20different%20from%20the%20previous%20problem%0A%20%20%20%20solution_tilt%20%3D%20pl.DataFrame(%7B%22asset%22%3A%20assets%2C%20%22weight%22%3A%20weights.value%7D)%0A%20%20%20%20print(solution_tilt)%0A%20%20%20%20%23%20We%20check%20whether%20the%20tilting%20constraint%20is%20respected%0A%20%20%20%20print(%22Tilting%20value.%20Should%20be%20close%20to%200.5%3A%22)%0A%20%20%20%20print(weights.value%20%40%20v.to_numpy())%0A%20%20%20%20return%0A%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20app.run()%0A
5379693cfa00fc4c87654bd618fc3c8a