[{"data":1,"prerenderedAt":612},["ShallowReactive",2],{"dev-\u002Fdeveloper\u002Fguides\u002Fportfolio-optimization":3},{"id":4,"title":5,"body":6,"description":591,"extension":592,"faq":593,"meta":606,"navigation":113,"noindex":607,"path":608,"seo":609,"stem":610,"__hash__":611},"content\u002Fdeveloper\u002Fguides\u002Fportfolio-optimization.md","Solve portfolio optimization (minimum variance) in Python",{"type":7,"value":8,"toc":585},"minimark",[9,13,29,34,42,46,66,503,508,512,526,529,533,566,576,581],[10,11,5],"h1",{"id":12},"solve-portfolio-optimization-minimum-variance-in-python",[14,15,16,20,21,24,25,28],"p",{},[17,18,19],"strong",{},"Portfolio optimization"," — choosing how to weight a set of assets to minimize\nrisk (variance) for a target expected return — is the textbook Markowitz\nmean-variance problem, and it is a ",[17,22,23],{},"quadratic program (QP)",": a convex quadratic\nobjective under linear constraints. With ",[17,26,27],{},"quicopt"," it is a few lines in Python.",[30,31,33],"h2",{"id":32},"the-naive-approach","The naive approach",[14,35,36,37,41],{},"A common tutorial move is Monte-Carlo: draw thousands of random weight vectors,\ncompute each one's risk and return, and keep the best. It only ",[38,39,40],"em",{},"approximates"," the\nefficient frontier, degrades fast as the number of assets grows, and never proves\noptimality. The problem is a convex QP — so solve it exactly.",[30,43,45],{"id":44},"model-it-as-a-qp","Model it as a QP",[14,47,48,49,53,54,57,58,61,62,65],{},"A continuous weight per asset, fully invested (",[50,51,52],"code",{},"sum(w) == 1","), long-only\n(",[50,55,56],{},"w >= 0","), meeting a target return. The objective is the portfolio variance\n",[50,59,60],{},"wᵀΣw",", where ",[50,63,64],{},"Σ"," is the covariance matrix:",[67,68,74],"pre",{"className":69,"code":70,"filename":71,"language":72,"meta":73,"style":73},"language-python shiki shiki-themes github-dark","from ortools.math_opt.python import mathopt\nfrom quicopt import Client\n\n# Minimum-variance portfolio (Markowitz): choose asset weights minimizing\n# variance w'Σw, fully invested, meeting a target expected return.\nSigma = [[0.10, 0.02, 0.04, 0.00],\n         [0.02, 0.08, 0.01, 0.02],\n         [0.04, 0.01, 0.12, 0.03],\n         [0.00, 0.02, 0.03, 0.06]]\nmu = [0.10, 0.08, 0.12, 0.07]\ntarget = 0.09\nN = len(mu)\n\nmodel = mathopt.Model(name=\"portfolio\")\nw = [model.add_variable(lb=0.0, name=f\"w_{i}\") for i in range(N)]\nmodel.add_linear_constraint(sum(w) == 1.0)\nmodel.add_linear_constraint(sum(mu[i] * w[i] for i in range(N)) >= target)\nmodel.minimize(sum(Sigma[i][j] * w[i] * w[j] for i in range(N) for j in range(N)))\n\nclient = Client(\"https:\u002F\u002Ftry.quicoptapi.pgi.fz-juelich.de\")\nprint(client.solve(model).display)\n","portfolio.py","python","",[50,75,76,95,108,115,122,128,163,187,210,233,262,273,287,292,316,377,397,430,473,478,494],{"__ignoreMap":73},[77,78,81,85,89,92],"span",{"class":79,"line":80},"line",1,[77,82,84],{"class":83},"snl16","from",[77,86,88],{"class":87},"s95oV"," ortools.math_opt.python ",[77,90,91],{"class":83},"import",[77,93,94],{"class":87}," mathopt\n",[77,96,98,100,103,105],{"class":79,"line":97},2,[77,99,84],{"class":83},[77,101,102],{"class":87}," quicopt ",[77,104,91],{"class":83},[77,106,107],{"class":87}," Client\n",[77,109,111],{"class":79,"line":110},3,[77,112,114],{"emptyLinePlaceholder":113},true,"\n",[77,116,118],{"class":79,"line":117},4,[77,119,121],{"class":120},"sAwPA","# Minimum-variance portfolio (Markowitz): choose asset weights minimizing\n",[77,123,125],{"class":79,"line":124},5,[77,126,127],{"class":120},"# variance w'Σw, fully invested, meeting a target expected return.\n",[77,129,131,134,137,140,144,147,150,152,155,157,160],{"class":79,"line":130},6,[77,132,133],{"class":87},"Sigma ",[77,135,136],{"class":83},"=",[77,138,139],{"class":87}," [[",[77,141,143],{"class":142},"sDLfK","0.10",[77,145,146],{"class":87},", ",[77,148,149],{"class":142},"0.02",[77,151,146],{"class":87},[77,153,154],{"class":142},"0.04",[77,156,146],{"class":87},[77,158,159],{"class":142},"0.00",[77,161,162],{"class":87},"],\n",[77,164,166,169,171,173,176,178,181,183,185],{"class":79,"line":165},7,[77,167,168],{"class":87},"         [",[77,170,149],{"class":142},[77,172,146],{"class":87},[77,174,175],{"class":142},"0.08",[77,177,146],{"class":87},[77,179,180],{"class":142},"0.01",[77,182,146],{"class":87},[77,184,149],{"class":142},[77,186,162],{"class":87},[77,188,190,192,194,196,198,200,203,205,208],{"class":79,"line":189},8,[77,191,168],{"class":87},[77,193,154],{"class":142},[77,195,146],{"class":87},[77,197,180],{"class":142},[77,199,146],{"class":87},[77,201,202],{"class":142},"0.12",[77,204,146],{"class":87},[77,206,207],{"class":142},"0.03",[77,209,162],{"class":87},[77,211,213,215,217,219,221,223,225,227,230],{"class":79,"line":212},9,[77,214,168],{"class":87},[77,216,159],{"class":142},[77,218,146],{"class":87},[77,220,149],{"class":142},[77,222,146],{"class":87},[77,224,207],{"class":142},[77,226,146],{"class":87},[77,228,229],{"class":142},"0.06",[77,231,232],{"class":87},"]]\n",[77,234,236,239,241,244,246,248,250,252,254,256,259],{"class":79,"line":235},10,[77,237,238],{"class":87},"mu ",[77,240,136],{"class":83},[77,242,243],{"class":87}," [",[77,245,143],{"class":142},[77,247,146],{"class":87},[77,249,175],{"class":142},[77,251,146],{"class":87},[77,253,202],{"class":142},[77,255,146],{"class":87},[77,257,258],{"class":142},"0.07",[77,260,261],{"class":87},"]\n",[77,263,265,268,270],{"class":79,"line":264},11,[77,266,267],{"class":87},"target ",[77,269,136],{"class":83},[77,271,272],{"class":142}," 0.09\n",[77,274,276,279,281,284],{"class":79,"line":275},12,[77,277,278],{"class":87},"N ",[77,280,136],{"class":83},[77,282,283],{"class":142}," len",[77,285,286],{"class":87},"(mu)\n",[77,288,290],{"class":79,"line":289},13,[77,291,114],{"emptyLinePlaceholder":113},[77,293,295,298,300,303,307,309,313],{"class":79,"line":294},14,[77,296,297],{"class":87},"model ",[77,299,136],{"class":83},[77,301,302],{"class":87}," mathopt.Model(",[77,304,306],{"class":305},"s9osk","name",[77,308,136],{"class":83},[77,310,312],{"class":311},"sU2Wk","\"portfolio\"",[77,314,315],{"class":87},")\n",[77,317,319,322,324,327,330,332,335,337,339,341,344,347,350,353,356,359,362,365,368,371,374],{"class":79,"line":318},15,[77,320,321],{"class":87},"w ",[77,323,136],{"class":83},[77,325,326],{"class":87}," [model.add_variable(",[77,328,329],{"class":305},"lb",[77,331,136],{"class":83},[77,333,334],{"class":142},"0.0",[77,336,146],{"class":87},[77,338,306],{"class":305},[77,340,136],{"class":83},[77,342,343],{"class":83},"f",[77,345,346],{"class":311},"\"w_",[77,348,349],{"class":142},"{",[77,351,352],{"class":87},"i",[77,354,355],{"class":142},"}",[77,357,358],{"class":311},"\"",[77,360,361],{"class":87},") ",[77,363,364],{"class":83},"for",[77,366,367],{"class":87}," i ",[77,369,370],{"class":83},"in",[77,372,373],{"class":142}," range",[77,375,376],{"class":87},"(N)]\n",[77,378,380,383,386,389,392,395],{"class":79,"line":379},16,[77,381,382],{"class":87},"model.add_linear_constraint(",[77,384,385],{"class":142},"sum",[77,387,388],{"class":87},"(w) ",[77,390,391],{"class":83},"==",[77,393,394],{"class":142}," 1.0",[77,396,315],{"class":87},[77,398,400,402,404,407,410,413,415,417,419,421,424,427],{"class":79,"line":399},17,[77,401,382],{"class":87},[77,403,385],{"class":142},[77,405,406],{"class":87},"(mu[i] ",[77,408,409],{"class":83},"*",[77,411,412],{"class":87}," w[i] ",[77,414,364],{"class":83},[77,416,367],{"class":87},[77,418,370],{"class":83},[77,420,373],{"class":142},[77,422,423],{"class":87},"(N)) ",[77,425,426],{"class":83},">=",[77,428,429],{"class":87}," target)\n",[77,431,433,436,438,441,443,445,447,450,452,454,456,458,461,463,466,468,470],{"class":79,"line":432},18,[77,434,435],{"class":87},"model.minimize(",[77,437,385],{"class":142},[77,439,440],{"class":87},"(Sigma[i][j] ",[77,442,409],{"class":83},[77,444,412],{"class":87},[77,446,409],{"class":83},[77,448,449],{"class":87}," w[j] ",[77,451,364],{"class":83},[77,453,367],{"class":87},[77,455,370],{"class":83},[77,457,373],{"class":142},[77,459,460],{"class":87},"(N) ",[77,462,364],{"class":83},[77,464,465],{"class":87}," j ",[77,467,370],{"class":83},[77,469,373],{"class":142},[77,471,472],{"class":87},"(N)))\n",[77,474,476],{"class":79,"line":475},19,[77,477,114],{"emptyLinePlaceholder":113},[77,479,481,484,486,489,492],{"class":79,"line":480},20,[77,482,483],{"class":87},"client ",[77,485,136],{"class":83},[77,487,488],{"class":87}," Client(",[77,490,491],{"class":311},"\"https:\u002F\u002Ftry.quicoptapi.pgi.fz-juelich.de\"",[77,493,315],{"class":87},[77,495,497,500],{"class":79,"line":496},21,[77,498,499],{"class":142},"print",[77,501,502],{"class":87},"(client.solve(model).display)\n",[504,505],"term-result",{":rows":506,"cmd":507},"[\"├── status:     optimal\",\"├── feasible:   true\",\"├── objective:  0.03552848683416981\",\"├── x:          w_0=0.2632, w_1=0.2362, w_2=0.1949, w_3=0.3058  (4 variables)\",\"└── solve_time: 0.0073 s\"]","$ python portfolio.py",[30,509,511],{"id":510},"what-you-get","What you get",[14,513,514,517,518,521,522,525],{},[50,515,516],{},"status: optimal"," means these weights are the ",[17,519,520],{},"proven"," minimum-variance\nportfolio for the target return — variance ",[50,523,524],{},"0.0355",", fully invested, meeting the\n9% return floor, in a few milliseconds. No sampling, no frontier scan.",[14,527,528],{},"The same model scales from four assets to hundreds — you change the covariance\nmatrix and expected returns, not the method. (This is an illustrative optimization\nexample, not financial advice.)",[30,530,532],{"id":531},"next","Next",[534,535,536,545,552,559],"ul",{},[537,538,539,540],"li",{},"The problem class behind this: ",[541,542,544],"a",{"href":543},"\u002Fproblems\u002Fqp","Quadratic programming (QP)",[537,546,547,548],{},"Hold only a few assets: ",[541,549,551],{"href":550},"\u002Fdeveloper\u002Fguides\u002Fcardinality-portfolio","Cardinality-constrained portfolio (MINLP)",[537,553,554,555],{},"A runnable model for every supported class: ",[541,556,558],{"href":557},"\u002Fdeveloper\u002Fexamples","Examples",[537,560,561,562],{},"Set up the client and solve your first model: ",[541,563,565],{"href":564},"\u002Fdeveloper\u002Fgetting-started","Getting started",[14,567,568,571,572,575],{},[17,569,570],{},"Reference:"," H. Markowitz, ",[38,573,574],{},"Portfolio Selection",", The Journal of Finance, 1952.",[577,578],"contact-cta",{"sub":579,"title":580},"Tell us what you're optimizing. We'll help you model it and point you at the right approach.","A bigger portfolio — or a different problem?",[582,583,584],"style",{},"html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html pre.shiki code .s9osk, html code.shiki .s9osk{--shiki-default:#FFAB70}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":73,"searchDepth":97,"depth":97,"links":586},[587,588,589,590],{"id":32,"depth":97,"text":33},{"id":44,"depth":97,"text":45},{"id":510,"depth":97,"text":511},{"id":531,"depth":97,"text":532},"Choose asset weights that minimize portfolio variance for a target return in Python — Markowitz mean-variance portfolio optimization as a quadratic program (QP), solved with quicopt in a few lines instead of Monte-Carlo sampling.","md",[594,597,600,603],{"q":595,"a":596},"Why not Monte-Carlo sample random weights and pick the best?","Random sampling only approximates the efficient frontier and needs enormous sample counts to get close in more than a few assets. The QP returns the provably minimum-variance portfolio directly.",{"q":598,"a":599},"Can I add a maximum weight per asset, sector limits, or no short-selling?","Yes — weight bounds, group limits and long-only (w >= 0) are linear constraints on the same QP.",{"q":601,"a":602},"What about a limit on the number of assets held?","A cardinality limit turns it into a mixed-integer QP (MINLP) with binary hold\u002Fskip variables — a related model quicopt also solves.",{"q":604,"a":605},"Is quicopt free to use?","Yes — pip install quicopt and your first call sets up a free key, no license.",{},false,"\u002Fdeveloper\u002Fguides\u002Fportfolio-optimization",{"title":5,"description":591},"developer\u002Fguides\u002Fportfolio-optimization","PLODfSv47lJl3w-OM5rFG5qrVfNDksAQ2SdY4I66fNo",1784110685996]