{"id":1404,"date":"2026-07-31T22:00:00","date_gmt":"2026-08-01T02:00:00","guid":{"rendered":"https:\/\/commoncents.blogwyrm.com\/?p=1404"},"modified":"2026-07-24T20:40:23","modified_gmt":"2026-07-25T00:40:23","slug":"a-look-at-finance-part-6-real-world-portfolio-optimization","status":"publish","type":"post","link":"https:\/\/commoncents.blogwyrm.com\/?p=1404","title":{"rendered":"A Look at Finance Part 6: Real-World Portfolio Optimization"},"content":{"rendered":"\n<p>In this post we extend the analysis of the previous post into the case where our portfolio is comprised of more than 1 asset\/instrument. This is an important extension since it offers a genuine optimization problem that requires a sophisticated numerical technique rather than the one-dimensional, parametric search used in the last example.<\/p>\n\n\n\n<p>For this case, we again turn to van der Wijst&#8217;s examples in his Chapter 3. The specific case is found in Section 3.2.2 under the whimsical story or helping our &#8216;Uncle Bob&#8217;. The narrative starts with Uncle Bob&#8217;s existing portfolio whose fractional division and returns for the past year and estimated returns for the next year can be summarized as follows<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Stock<\/th><th>Ticker Symbol<\/th><th>Fraction<\/th><th>Past Year&#8217;s Return<\/th><th>Estimated Return<\/th><\/tr><\/thead><tbody><tr><td>Google<\/td><td>GOOG<\/td><td>0.1<\/td><td>0.000<\/td><td>0.080<\/td><\/tr><tr><td>Cisco Systems<\/td><td>CSCO<\/td><td>0.1<\/td><td>-0.205<\/td><td>0.075<\/td><\/tr><tr><td>Logitech International<\/td><td>LOGI<\/td><td>0.6<\/td><td>-0.525<\/td><td>0.060<\/td><\/tr><tr><td>Amazon<\/td><td>AMZN<\/td><td>0.1<\/td><td>0.560<\/td><td>0.125<\/td><\/tr><tr><td>Apple<\/td><td>AAPL<\/td><td>0.1<\/td><td>0.314<\/td><td>0.100<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>Obviously, the past year&#8217;s returns seem to be radically different from Uncle Bob&#8217;s estimations of what will happy in the next year, reflecting that during the past year there was a downturn in computer peripherals (Cisco &amp; Logitech) but healthy sales in core system and related products (Amazon &amp; Apple). This coming year, Uncle Bob feels very strongly that the economic head winds have shifted and van der Wijst urges us not to second guess Uncle Bob but simply to try to rebalance the fractional weights in his portfolio to try to give him a healthy return while minimizing his risk.<\/p>\n\n\n\n<p>van der Wijst provides an aside that if we were tighter with Uncle Bob we would check that he downloaded the past year&#8217;s financial data from each of the stocks found within his portfolio and properly calculated the returns as well as the corresponding covariance matrix but that we should trust his results.<\/p>\n\n\n\n<p>Assuming that our less-than-favorite uncle got it right, van der Wijst gives us the individual standard deviations and the corresponding correlation matrix he derived. I won&#8217;t bother to repeat those values here but will instead simply give the reconstructed covariance matrix<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th><\/th><th>GOOG<\/th><th>CSCO<\/th><th>LOGI<\/th><th>AMZN<\/th><th>AAPL<\/th><\/tr><\/thead><tbody><tr><td><strong>GOOG<\/strong><\/td><td>0.082<\/td><td>0.045<\/td><td>0.045<\/td><td>0.054<\/td><td>0.044<\/td><\/tr><tr><td><strong>CSCO<\/strong><\/td><td>0.045<\/td><td>0.132<\/td><td>0.047<\/td><td>0.042<\/td><td>0.039<\/td><\/tr><tr><td><strong>LOGI<\/strong><\/td><td>0.045<\/td><td>0.047<\/td><td>0.213<\/td><td>0.055<\/td><td>0.045<\/td><\/tr><tr><td><strong>AMZN<\/strong><\/td><td>0.054<\/td><td>0.042<\/td><td>0.055<\/td><td>0.116<\/td><td>0.049<\/td><\/tr><tr><td><strong>AAPL<\/strong><\/td><td>0.044<\/td><td>0.039<\/td><td>0.045<\/td><td>0.049<\/td><td>0.062<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>While we are taking some things on faith, we are, at least, happy to see that the covariance matrix is symmetric (as it should be).<\/p>\n\n\n\n<p>Now, the highest possible return Uncle Bob could expect to achieve is 12.5% if he were to put all of his eggs into the Amazon basket but his risk, as represented by $\\sigma_{\\textrm{AMZN}}$, is 34%. The lowest risk, single stock option is a solo investment into Apple with $\\sigma_{\\textrm{AAPL}} = 25%$ with a return of 10%. Uncle Bob would like to know if he can get a comparable return with a lower risk.<\/p>\n\n\n\n<p>Mathematically, his goal translates into the following computational optimization problem:<\/p>\n\n\n\n<p>\\[ \\textrm{Minimize: } \\sigma[P] = \\sqrt{ \\sum_{i,j} f_i f_j \\sigma_{ij} } \\; \\]<\/p>\n\n\n\n<p>\\[  \\textrm{Subject to: } \\left\\{ \\begin{array}{l} E[P] \\ge 0.1 \\\\ \\sum_i f_i = 1 \\end{array} \\right. \\; .\\]<\/p>\n\n\n\n<p>Since the objective function $\\sigma[P]$ involves the square root of a weighted sum of the squares, the optimization technique that we need to use has to be a non-linear optimizer. A gradient-based method is a good choice and both Microsoft Excel (which van der Wijst seems to reluctantly use while poking some fun at it) and the Scipy package in the Python ecosystem provide this functionality. I choose to implement this within Python.<\/p>\n\n\n\n<p>Since it is easy to confuse what element of an array translates to what ticker symbol, it was easier to use dictionaries for everything except the fractional weights. These dictionaries look like:<\/p>\n\n\n\n<div class = \"myQuoteDiv\">\n<pre class=\"wp-block-code\"><code>tickers     = &#91;'GOOG','CSCO','LOGI','AMZN','APPL']\nest_rets    = {'GOOG':0.080,'CSCO':0.075,'LOGI':0.060,'AMZN':0.125,'APPL':0.100}\nann_std     = {'GOOG':0.287,'CSCO':0.363,'LOGI':0.462,'AMZN':0.340,'APPL':0.250}\n\ncorr = {('GOOG','GOOG'):1,   ('CSCO','CSCO'):1,   ('LOGI','LOGI'):1,   ('AMZN','AMZN'):1,    ('APPL','APPL'):1,\n        ('GOOG','CSCO'):0.43,('GOOG','LOGI'):0.34,('GOOG','AMZN'):0.55,('GOOG','APPL'):0.62,\n        ('CSCO','LOGI'):0.28,('CSCO','AMZN'):0.34,('CSCO','APPL'):0.43,\n        ('LOGI','AMZN'):0.35,('LOGI','APPL'):0.39,\n        ('AMZN','APPL'):0.58}\n<\/code><\/pre>\n<\/div>\n\n\n\n<p>Note that <code>ann_std<\/code> and <code>corr<\/code> are the data structures I used to translate the statistics provided by van der Wijst into a covariance matrix. van der Wijst provided only the lower-diagonal portion of the correlation matrix in a table; the following function constructs a full 5\\times5 covariance matrix:<\/p>\n\n\n\n<div class = \"myQuoteDiv\">\n<pre class=\"wp-block-code\"><code>#Symmetrize the data structure to be a full 5x5 correlation matrix\ndef sym_corr(tickers,corr):\n    for t1 in tickers:\n        for t2 in tickers:\n            key = (t1,t2)\n            if key in corr:\n                pass\n            else:\n                key1 = (t2,t1)\n                corr&#91;key] = corr&#91;key1]\n    return corr\n\n#Combine the correlation matrix with the standard deviations to produce a covariance matrix\ndef create_cov(tickers,corr):    \n    if len(corr) &lt; len(tickers)**2:\n        corr = sym_corr(tickers,corr)\n    cov = {}\n    for t1 in tickers:\n        for t2 in tickers:\n            cov&#91;(t1,t2)] = ann_std&#91;t1]*ann_std&#91;t2]*corr&#91;(t1,t2)]\n\n    return cov\n<\/code><\/pre><\/div>\n\n\n\n<p>The first constraint, that E[P] \\ge 10% was implemented by<\/p>\n\n\n\n<div class = \"myQuoteDiv\"><pre class=\"wp-block-code\"><code>def calc_port_return(fs,tickers,est_rets):\n    port_ret = 0\n    ticker_idx  = {v: i for i, v in enumerate(tickers)}\n    for t1 in tickers:\n        port_ret += fs&#91;ticker_idx&#91;t1]]*est_rets&#91;t1]\n    return port_ret\n<\/code><\/pre><\/div>\n\n\n\n<p>The objective function was implemented by<\/p>\n\n\n\n<div class = \"myQuoteDiv\"><pre class=\"wp-block-code\"><code>def calc_port_std(fs,tickers,corr):\n    ticker_idx  = {v: i for i, v in enumerate(tickers)}\n    cov = create_cov(tickers,corr)\n    port_var = 0\n    for t1 in tickers:\n        for t2 in tickers:\n            port_var += fs&#91;ticker_idx&#91;t1]]*cov&#91;(t1,t2)]*fs&#91;ticker_idx&#91;t2]]\n    return np.sqrt(port_var)\n<\/code><\/pre><\/div>\n\n\n\n<p>Finally, the optimization was performed using:<\/p>\n\n\n\n<div class = \"myQuoteDiv\"><pre class=\"wp-block-code\"><code># targeted return value of 10%\ntarget = 0.10\n\n# list of constraint dictionaries - the first one ensures that the fractional \n# weights sum to 1; the second one ensures that at least 10% return is achieved\nconstraints = &#91;\n    {'type': 'eq',   'fun': lambda f: np.sum(f) - 1},\n    {'type': 'ineq', 'fun': lambda f: calc_port_return(f, tickers, est_rets) - target}\n]\n\n#provide a list of bounds consistent with each fractional weight having to \n#be between 0 and 1\nbounds = &#91;(0, 1)] * 5\n\n#provide a first guess for the fractional weights\nx0 = np.array(&#91;0.2, 0.2, 0.2, 0.2, 0.2])\n\n#run the scipy 'minimize' function\nresult = minimize(\n    calc_port_std,\n    x0,\n    args=(tickers, corr),   # &lt;-- extra args passed here\n    method='SLSQP',\n    bounds=bounds,\n    constraints=constraints,\n    options={'ftol': 1e-9, 'maxiter': 1000, 'disp': True}\n)\n<\/code><\/pre><\/div>\n\n\n\n<p>The optimizer ran nearly instantaneously and produced the following output<\/p>\n\n\n\n<div class = \"myQuoteDiv\"><pre class=\"wp-block-code\"><code>Optimization terminated successfully    (Exit mode 0)\n            Current function value: 0.2357533346589545\n            Iterations: 9\n            Function evaluations: 54\n            Gradient evaluations: 9\n<\/code><\/pre><\/div>\n\n\n\n<p>The resulting fractional weights are<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Ticker Symbol<\/th><th>Fractional Weight<\/th><\/tr><\/thead><tbody><tr><td>GOOG<\/td><td>0.12<\/td><\/tr><tr><td>CSCO<\/td><td>0.10<\/td><\/tr><tr><td>LOGI<\/td><td>0.00<\/td><\/tr><tr><td>AMZN<\/td><td>0.19<\/td><\/tr><tr><td>AAPL<\/td><td>0.59<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>with a return of 10% with a risk of 23%. We managed to lower the risk from the Apple-only solution by 2% while maintaining the same return. This shows the power of diversification.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In this post we extend the analysis of the previous post into the case where our portfolio is comprised of more than 1 asset\/instrument. This is an important extension since&#8230; <a class=\"read-more-button\" href=\"https:\/\/commoncents.blogwyrm.com\/?p=1404\">Read more &gt;<\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-1404","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/commoncents.blogwyrm.com\/index.php?rest_route=\/wp\/v2\/posts\/1404","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/commoncents.blogwyrm.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/commoncents.blogwyrm.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/commoncents.blogwyrm.com\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/commoncents.blogwyrm.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1404"}],"version-history":[{"count":13,"href":"https:\/\/commoncents.blogwyrm.com\/index.php?rest_route=\/wp\/v2\/posts\/1404\/revisions"}],"predecessor-version":[{"id":1417,"href":"https:\/\/commoncents.blogwyrm.com\/index.php?rest_route=\/wp\/v2\/posts\/1404\/revisions\/1417"}],"wp:attachment":[{"href":"https:\/\/commoncents.blogwyrm.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1404"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/commoncents.blogwyrm.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1404"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/commoncents.blogwyrm.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1404"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}