Spaces:
Running
Running
File size: 16,003 Bytes
1cce1df |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="./favicon.ico" />
<!-- Preload is necessary because we show these images when we disconnect from the server,
but at that point we cannot load these images from the server -->
<link rel="preload" href="./assets/gradient-yHQUC_QB.png" as="image" />
<link rel="preload" href="./assets/noise-60BoTA8O.png" as="image" />
<!-- Preload the fonts -->
<link rel="preload" href="./assets/Lora-VariableFont_wght-B2ootaw-.ttf" as="font" crossorigin="anonymous" />
<link rel="preload" href="./assets/PTSans-Regular-CxL0S8W7.ttf" as="font" crossorigin="anonymous" />
<link rel="preload" href="./assets/PTSans-Bold-D9fedIX3.ttf" as="font" crossorigin="anonymous" />
<link rel="preload" href="./assets/FiraMono-Regular-BTCkDNvf.ttf" as="font" crossorigin="anonymous" />
<link rel="preload" href="./assets/FiraMono-Medium-DU3aDxX5.ttf" as="font" crossorigin="anonymous" />
<link rel="preload" href="./assets/FiraMono-Bold-CLVRCuM9.ttf" as="font" crossorigin="anonymous" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta name="description" content="a marimo app" />
<link rel="apple-touch-icon" href="./apple-touch-icon.png" />
<link rel="manifest" href="./manifest.json" />
<script data-marimo="true">
function __resizeIframe(obj) {
var scrollbarHeight = 20; // Max between windows, mac, and linux
function setHeight() {
var element = obj.contentWindow.document.documentElement;
// If there is no vertical scrollbar, we don't need to resize the iframe
if (element.scrollHeight === element.clientHeight) {
return;
}
// Create a new height that includes the scrollbar height if it's visible
var hasHorizontalScrollbar = element.scrollWidth > element.clientWidth;
var newHeight = element.scrollHeight + (hasHorizontalScrollbar ? scrollbarHeight : 0);
// Only update the height if it's different from the current height
if (obj.style.height !== `${newHeight}px`) {
obj.style.height = `${newHeight}px`;
}
}
// Resize the iframe to the height of the content and bottom scrollbar height
setHeight();
// Resize the iframe when the content changes
const resizeObserver = new ResizeObserver((entries) => {
setHeight();
});
resizeObserver.observe(obj.contentWindow.document.body);
}
</script>
<marimo-filename hidden>notebook.py</marimo-filename>
<marimo-mode data-mode='edit' hidden></marimo-mode>
<marimo-version data-version='0.11.9' hidden></marimo-version>
<marimo-user-config data-config='{"completion": {"activate_on_typing": true, "copilot": false}, "display": {"cell_output": "above", "default_width": "medium", "dataframes": "rich", "code_editor_font_size": 14, "theme": "light"}, "formatting": {"line_length": 79}, "keymap": {"preset": "default", "overrides": {}}, "runtime": {"auto_instantiate": true, "auto_reload": "off", "on_cell_change": "autorun", "watcher_on_save": "lazy", "output_max_bytes": 8000000, "std_stream_max_bytes": 1000000}, "save": {"autosave": "off", "autosave_delay": 1000, "format_on_save": false}, "package_management": {"manager": "pip"}, "server": {"browser": "default", "follow_symlink": false}, "snippets": {"custom_paths": [], "include_default_snippets": true}}' data-overrides='{}' hidden></marimo-user-config>
<marimo-app-config data-config='{"width": "compact"}' hidden></marimo-app-config>
<marimo-server-token data-token='123' hidden></marimo-server-token>
<title>05 portfolio optimization</title>
<script type="module" crossorigin src="./assets/index-BiV-b1K2.js"></script>
<link rel="stylesheet" crossorigin href="./assets/index-DkqMrX_B.css">
<marimo-wasm hidden=""></marimo-wasm>
<script>
if (window.location.protocol === 'file:') {
alert('Warning: This file must be served by an HTTP server to function correctly.');
}
</script>
<style>
#save-button {
display: none !important;
}
#filename-input {
display: none !important;
}
</style>
<marimo-code hidden="" data-show-code="false">import%20marimo%0A%0A__generated_with%20%3D%20%220.11.9%22%0Aapp%20%3D%20marimo.App()%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20import%20marimo%20as%20mo%0A%20%20%20%20return%20(mo%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%23%20Portfolio%20optimization%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(%0A%20%20%20%20%20%20%20%20r%22%22%22%0A%20%20%20%20%20%20%20%20In%20this%20example%20we%20show%20how%20to%20use%20CVXPY%20to%20design%20a%20financial%20portfolio%3B%20this%20is%20called%20_portfolio%20optimization_.%0A%0A%20%20%20%20%20%20%20%20In%20portfolio%20optimization%20we%20have%20some%20amount%20of%20money%20to%20invest%20in%20any%20of%20%24n%24%20different%20assets.%0A%20%20%20%20%20%20%20%20We%20choose%20what%20fraction%20%24w_i%24%20of%20our%20money%20to%20invest%20in%20each%20asset%20%24i%24%2C%20%24i%3D1%2C%20%5Cldots%2C%20n%24.%20The%20goal%20is%20to%20maximize%20return%20of%20the%20portfolio%20while%20minimizing%20risk.%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20r%22%22%22%0A%20%20%20%20%20%20%20%20%23%23%20Asset%20returns%20and%20risk%0A%0A%20%20%20%20%20%20%20%20We%20will%20only%20model%20investments%20held%20for%20one%20period.%20The%20initial%20prices%20are%20%24p_i%20%3E%200%24.%20The%20end%20of%20period%20prices%20are%20%24p_i%5E%2B%20%3E0%24.%20The%20asset%20(fractional)%20returns%20are%20%24r_i%20%3D%20(p_i%5E%2B-p_i)%2Fp_i%24.%20The%20portfolio%20(fractional)%20return%20is%20%24R%20%3D%20r%5ETw%24.%0A%0A%20%20%20%20%20%20%20%20A%20common%20model%20is%20that%20%24r%24%20is%20a%20random%20variable%20with%20mean%20%24%7B%5Cbf%20E%7Dr%20%3D%20%5Cmu%24%20and%20covariance%20%24%7B%5Cbf%20E%7B(r-%5Cmu)(r-%5Cmu)%5ET%7D%7D%20%3D%20%5CSigma%24.%0A%20%20%20%20%20%20%20%20It%20follows%20that%20%24R%24%20is%20a%20random%20variable%20with%20%24%7B%5Cbf%20E%7DR%20%3D%20%5Cmu%5ET%20w%24%20and%20%24%7B%5Cbf%20var%7D(R)%20%3D%20w%5ET%5CSigma%20w%24.%20In%20real-world%20applications%2C%20%24%5Cmu%24%20and%20%24%5CSigma%24%20are%20estimated%20from%20data%20and%20models%2C%20and%20%24w%24%20is%20chosen%20using%20a%20library%20like%20CVXPY.%0A%0A%20%20%20%20%20%20%20%20%24%7B%5Cbf%20E%7DR%24%20is%20the%20(mean)%20*return*%20of%20the%20portfolio.%20%24%7B%5Cbf%20var%7D(R)%24%20is%20the%20*risk*%20of%20the%20portfolio.%20Portfolio%20optimization%20has%20two%20competing%20objectives%3A%20high%20return%20and%20low%20risk.%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20r%22%22%22%0A%20%20%20%20%20%20%20%20%23%23%20Classical%20(Markowitz)%20portfolio%20optimization%0A%0A%20%20%20%20%20%20%20%20Classical%20(Markowitz)%20portfolio%20optimization%20solves%20the%20optimization%20problem%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20r%22%22%22%0A%20%20%20%20%20%20%20%20%24%24%0A%20%20%20%20%20%20%20%20%5Cbegin%7Barray%7D%7Bll%7D%20%5Ctext%7Bmaximize%7D%20%26%20%5Cmu%5ET%20w%20-%20%5Cgamma%20w%5ET%5CSigma%20w%5C%5C%0A%20%20%20%20%20%20%20%20%5Ctext%7Bsubject%20to%7D%20%26%20%7B%5Cbf%201%7D%5ET%20w%20%3D%201%2C%20w%20%5Cgeq%200%2C%0A%20%20%20%20%20%20%20%20%5Cend%7Barray%7D%0A%20%20%20%20%20%20%20%20%24%24%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20r%22%22%22%0A%20%20%20%20%20%20%20%20where%20%24w%20%5Cin%20%7B%5Cbf%20R%7D%5En%24%20is%20the%20optimization%20variable%20and%20%24%5Cgamma%20%3E0%24%20is%20a%20constant%20called%20the%20*risk%20aversion%20parameter*.%20The%20constraint%20%24%5Cmathbf%7B1%7D%5ETw%20%3D%201%24%20says%20the%20portfolio%20weight%20vector%20must%20sum%20to%201%2C%20and%20%24w%20%5Cgeq%200%24%20says%20that%20we%20can't%20invest%20a%20negative%20amount%20into%20any%20asset.%0A%0A%20%20%20%20%20%20%20%20The%20objective%20%24%5Cmu%5ETw%20-%20%5Cgamma%20w%5ET%5CSigma%20w%24%20is%20the%20*risk-adjusted%20return*.%20Varying%20%24%5Cgamma%24%20gives%20the%20optimal%20*risk-return%20trade-off*.%0A%20%20%20%20%20%20%20%20We%20can%20get%20the%20same%20risk-return%20trade-off%20by%20fixing%20return%20and%20minimizing%20risk.%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20r%22%22%22%0A%20%20%20%20%20%20%20%20%23%23%20Example%0A%0A%20%20%20%20%20%20%20%20In%20the%20following%20code%20we%20compute%20and%20plot%20the%20optimal%20risk-return%20trade-off%20for%20%2410%24%20assets.%20First%20we%20generate%20random%20problem%20data%20%24%5Cmu%24%20and%20%24%5CSigma%24.%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20import%20numpy%20as%20np%0A%20%20%20%20return%20(np%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo%2C%20np)%3A%0A%20%20%20%20import%20wigglystuff%0A%0A%20%20%20%20mu_widget%20%3D%20mo.ui.anywidget(%0A%20%20%20%20%20%20%20%20wigglystuff.Matrix(%0A%20%20%20%20%20%20%20%20%20%20%20%20np.array(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5B1.6%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5B0.6%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5B0.5%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5B1.1%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5B0.9%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5B2.3%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5B1.7%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5B0.7%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5B0.9%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5B0.3%5D%2C%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%20%20%20%20%20%20%20%20)%0A%20%20%20%20)%0A%0A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20rf%22%22%22%0A%20%20%20%20%20%20%20%20The%20value%20of%20%24%5Cmu%24%20is%20%0A%0A%20%20%20%20%20%20%20%20%7Bmu_widget.center()%7D%0A%0A%20%20%20%20%20%20%20%20_Try%20changing%20the%20entries%20of%20%24%5Cmu%24%20and%20see%20how%20the%20plots%20below%20change._%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%20mu_widget%2C%20wigglystuff%0A%0A%0A%40app.cell%0Adef%20_(mu_widget%2C%20np)%3A%0A%20%20%20%20np.random.seed(1)%0A%20%20%20%20n%20%3D%2010%0A%20%20%20%20mu%20%3D%20np.array(mu_widget.matrix)%0A%20%20%20%20Sigma%20%3D%20np.random.randn(n%2C%20n)%0A%20%20%20%20Sigma%20%3D%20Sigma.T.dot(Sigma)%0A%20%20%20%20return%20Sigma%2C%20mu%2C%20n%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%22%22%22Next%2C%20we%20solve%20the%20problem%20for%20100%20different%20values%20of%20%24%5Cgamma%24%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(Sigma%2C%20mu%2C%20n)%3A%0A%20%20%20%20import%20cvxpy%20as%20cp%0A%0A%20%20%20%20w%20%3D%20cp.Variable(n)%0A%20%20%20%20gamma%20%3D%20cp.Parameter(nonneg%3DTrue)%0A%20%20%20%20ret%20%3D%20mu.T%20%40%20w%0A%20%20%20%20risk%20%3D%20cp.quad_form(w%2C%20Sigma)%0A%20%20%20%20prob%20%3D%20cp.Problem(cp.Maximize(ret%20-%20gamma%20*%20risk)%2C%20%5Bcp.sum(w)%20%3D%3D%201%2C%20w%20%3E%3D%200%5D)%0A%20%20%20%20return%20cp%2C%20gamma%2C%20prob%2C%20ret%2C%20risk%2C%20w%0A%0A%0A%40app.cell%0Adef%20_(cp%2C%20gamma%2C%20np%2C%20prob%2C%20ret%2C%20risk)%3A%0A%20%20%20%20_SAMPLES%20%3D%20100%0A%20%20%20%20risk_data%20%3D%20np.zeros(_SAMPLES)%0A%20%20%20%20ret_data%20%3D%20np.zeros(_SAMPLES)%0A%20%20%20%20gamma_vals%20%3D%20np.logspace(-2%2C%203%2C%20num%3D_SAMPLES)%0A%20%20%20%20for%20_i%20in%20range(_SAMPLES)%3A%0A%20%20%20%20%20%20%20%20gamma.value%20%3D%20gamma_vals%5B_i%5D%0A%20%20%20%20%20%20%20%20prob.solve()%0A%20%20%20%20%20%20%20%20risk_data%5B_i%5D%20%3D%20cp.sqrt(risk).value%0A%20%20%20%20%20%20%20%20ret_data%5B_i%5D%20%3D%20ret.value%0A%20%20%20%20return%20gamma_vals%2C%20ret_data%2C%20risk_data%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%22%22%22Plotted%20below%20are%20the%20risk%20return%20tradeoffs%20for%20two%20values%20of%20%24%5Cgamma%24%20(blue%20squares)%2C%20and%20the%20risk%20return%20tradeoffs%20for%20investing%20fully%20in%20each%20asset%20(red%20circles)%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(Sigma%2C%20cp%2C%20gamma_vals%2C%20mu%2C%20n%2C%20ret_data%2C%20risk_data)%3A%0A%20%20%20%20import%20matplotlib.pyplot%20as%20plt%0A%0A%20%20%20%20markers_on%20%3D%20%5B29%2C%2040%5D%0A%20%20%20%20fig%20%3D%20plt.figure()%0A%20%20%20%20ax%20%3D%20fig.add_subplot(111)%0A%20%20%20%20plt.plot(risk_data%2C%20ret_data%2C%20%22g-%22)%0A%20%20%20%20for%20marker%20in%20markers_on%3A%0A%20%20%20%20%20%20%20%20plt.plot(risk_data%5Bmarker%5D%2C%20ret_data%5Bmarker%5D%2C%20%22bs%22)%0A%20%20%20%20%20%20%20%20ax.annotate(%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%24%5C%5Cgamma%20%3D%20%25.2f%24%22%20%25%20gamma_vals%5Bmarker%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20xy%3D(risk_data%5Bmarker%5D%20%2B%200.08%2C%20ret_data%5Bmarker%5D%20-%200.03)%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20for%20_i%20in%20range(n)%3A%0A%20%20%20%20%20%20%20%20plt.plot(cp.sqrt(Sigma%5B_i%2C%20_i%5D).value%2C%20mu%5B_i%5D%2C%20%22ro%22)%0A%20%20%20%20plt.xlabel(%22Standard%20deviation%22)%0A%20%20%20%20plt.ylabel(%22Return%22)%0A%20%20%20%20plt.show()%0A%20%20%20%20return%20ax%2C%20fig%2C%20marker%2C%20markers_on%2C%20plt%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20r%22%22%22%0A%20%20%20%20%20%20%20%20We%20plot%20below%20the%20return%20distributions%20for%20the%20two%20risk%20aversion%20values%20marked%20on%20the%20trade-off%20curve.%0A%20%20%20%20%20%20%20%20Notice%20that%20the%20probability%20of%20a%20loss%20is%20near%200%20for%20the%20low%20risk%20value%20and%20far%20above%200%20for%20the%20high%20risk%20value.%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(gamma%2C%20gamma_vals%2C%20markers_on%2C%20np%2C%20plt%2C%20prob%2C%20ret%2C%20risk)%3A%0A%20%20%20%20import%20scipy.stats%20as%20spstats%0A%0A%20%20%20%20plt.figure()%0A%20%20%20%20for%20midx%2C%20_idx%20in%20enumerate(markers_on)%3A%0A%20%20%20%20%20%20%20%20gamma.value%20%3D%20gamma_vals%5B_idx%5D%0A%20%20%20%20%20%20%20%20prob.solve()%0A%20%20%20%20%20%20%20%20x%20%3D%20np.linspace(-2%2C%205%2C%201000)%0A%20%20%20%20%20%20%20%20plt.plot(%0A%20%20%20%20%20%20%20%20%20%20%20%20x%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20spstats.norm.pdf(x%2C%20ret.value%2C%20risk.value)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20label%3D%22%24%5C%5Cgamma%20%3D%20%25.2f%24%22%20%25%20gamma.value%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20plt.xlabel(%22Return%22)%0A%20%20%20%20plt.ylabel(%22Density%22)%0A%20%20%20%20plt.legend(loc%3D%22upper%20right%22)%0A%20%20%20%20plt.show()%0A%20%20%20%20return%20midx%2C%20spstats%2C%20x%0A%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20app.run()%0A</marimo-code></head>
<body>
<div id="root"></div>
</body>
</html>
|