|
26 | 26 | "\n",
|
27 | 27 | "This cell selects and verifies a global SOLVER for the notebook.\n",
|
28 | 28 | "\n",
|
29 |
| - "If run on Google Colab, the cell installs Pyomo and ipopt, then sets SOLVER to \n", |
30 |
| - "use the ipopt solver. If run elsewhere, it assumes Pyomo and the Mosek solver\n", |
31 |
| - "have been previously installed and sets SOLVER to use the Mosek solver via the Pyomo \n", |
32 |
| - "SolverFactory. It then verifies that SOLVER is available." |
| 29 | + "If run on Google Colab, the cell installs Pyomo and ipopt, then sets SOLVER to use the ipopt solver. If run elsewhere, it assumes Pyomo and the Mosek solver\n", |
| 30 | + "have been previously installed and sets SOLVER to use the Mosek solver via the Pyomo SolverFactory. It then verifies that SOLVER is available." |
33 | 31 | ]
|
34 | 32 | },
|
35 | 33 | {
|
36 | 34 | "cell_type": "code",
|
37 |
| - "execution_count": 45, |
| 35 | + "execution_count": 9, |
38 | 36 | "id": "5da22c67-5c34-4c3a-90a4-61222899e855",
|
39 | 37 | "metadata": {
|
40 | 38 | "tags": []
|
41 | 39 | },
|
42 | 40 | "outputs": [],
|
43 | 41 | "source": [
|
44 | 42 | "import sys\n",
|
45 |
| - "import os\n", |
46 | 43 | "\n",
|
47 | 44 | "if 'google.colab' in sys.modules:\n",
|
48 | 45 | " !pip install idaes-pse --pre >/dev/null 2>/dev/null\n",
|
49 | 46 | " !idaes get-extensions --to ./bin \n",
|
50 | 47 | " os.environ['PATH'] += ':bin'\n",
|
51 |
| - " SOLVER_CONIC = \"ipopt\"\n", |
52 |
| - " \n", |
| 48 | + " solver = \"ipopt\"\n", |
53 | 49 | "else:\n",
|
54 |
| - " SOLVER_CONIC = \"mosek_direct\"\n", |
| 50 | + " solver = \"mosek_direct\"\n", |
55 | 51 | "\n",
|
56 | 52 | "import pyomo.environ as pyo\n",
|
57 |
| - "if not pyo.SolverFactory(SOLVER_CONIC).available():\n", |
58 |
| - " print(f\"Solver {SOLVER_CONIC} is not available\")" |
| 53 | + "if not pyo.SolverFactory(solver).available():\n", |
| 54 | + " print(f\"Solver {solver} is not available\")\n", |
| 55 | + "else:\n", |
| 56 | + " SOLVER = pyo.SolverFactory(solver)" |
59 | 57 | ]
|
60 | 58 | },
|
61 | 59 | {
|
62 | 60 | "cell_type": "code",
|
63 |
| - "execution_count": 41, |
| 61 | + "execution_count": 10, |
64 | 62 | "id": "b13edf26",
|
65 | 63 | "metadata": {
|
66 | 64 | "tags": []
|
|
114 | 112 | },
|
115 | 113 | {
|
116 | 114 | "cell_type": "code",
|
117 |
| - "execution_count": 42, |
| 115 | + "execution_count": 11, |
118 | 116 | "id": "0e194e8d",
|
119 | 117 | "metadata": {
|
120 | 118 | "tags": []
|
|
158 | 156 | }
|
159 | 157 | ],
|
160 | 158 | "source": [
|
161 |
| - "# Specify the initial capital, the risk tolerance, and the guaranteed return rate. \n", |
| 159 | + "# Specify the initial capital, the risk tolerance, and the guaranteed return rate.\n", |
162 | 160 | "C = 1\n",
|
163 | 161 | "alpha = 0.1\n",
|
164 | 162 | "R = 1.05\n",
|
|
172 | 170 | "assert np.all(np.linalg.eigvals(Sigma) >= 0)\n",
|
173 | 171 | "\n",
|
174 | 172 | "# If you want to change the covariance matrix Sigma, ensure you input a semi-definite positive one.\n",
|
175 |
| - "# The easiest way to generate a random covariance matrix is first generating a random m x m matrix A \n", |
| 173 | + "# The easiest way to generate a random covariance matrix is first generating a random m x m matrix A\n", |
176 | 174 | "# and then taking the matrix A^T A (which is always semi-definite positive)\n",
|
177 | 175 | "# m = 3\n",
|
178 | 176 | "# A = np.random.rand(m, m)\n",
|
|
184 | 182 | "# y=Ax, |y|^2 <= s,\n",
|
185 | 183 | "# corresponding to the mathematical formulation above.\n",
|
186 | 184 | "\n",
|
| 185 | + "\n", |
187 | 186 | "def markowitz_revisited(alpha, mu, Sigma):\n",
|
188 |
| - " \n", |
189 | 187 | " model = pyo.ConcreteModel(\"Markowitz portfolio optimization revisited\")\n",
|
190 | 188 | "\n",
|
191 | 189 | " model.xtilde = pyo.Var(domain=pyo.NonNegativeReals)\n",
|
|
194 | 192 | "\n",
|
195 | 193 | " @model.Objective(sense=pyo.maximize)\n",
|
196 | 194 | " def objective(m):\n",
|
197 |
| - " return mu @ m.x + R * m.xtilde - alpha*m.s\n", |
| 195 | + " return mu @ m.x + R * m.xtilde - alpha * m.s\n", |
198 | 196 | "\n",
|
199 | 197 | " @model.Constraint()\n",
|
200 | 198 | " def bounded_variance(m):\n",
|
|
204 | 202 | " def total_assets(m):\n",
|
205 | 203 | " return sum(m.x[i] for i in range(n)) + m.xtilde == C\n",
|
206 | 204 | "\n",
|
207 |
| - " result = pyo.SolverFactory(SOLVER_CONIC).solve(model)\n", |
208 |
| - " \n", |
| 205 | + " result = SOLVER.solve(model)\n", |
| 206 | + "\n", |
209 | 207 | " return result, model\n",
|
210 | 208 | "\n",
|
| 209 | + "\n", |
211 | 210 | "result, model = markowitz_revisited(alpha, mu, Sigma)\n",
|
212 | 211 | "\n",
|
213 |
| - "display(Markdown(f\"**Solver status:** *{result.solver.status}, {result.solver.termination_condition}*\"))\n", |
214 |
| - "display(Markdown(f\"**Solution:** $\\\\tilde x = {model.xtilde.value:.3f}$, $x_1 = {model.x[0].value:.3f}$, $x_2 = {model.x[1].value:.3f}$, $x_3 = {model.x[2].value:.3f}$\"))\n", |
| 212 | + "display(\n", |
| 213 | + " Markdown(\n", |
| 214 | + " f\"**Solver status:** *{result.solver.status}, {result.solver.termination_condition}*\"\n", |
| 215 | + " )\n", |
| 216 | + ")\n", |
| 217 | + "display(\n", |
| 218 | + " Markdown(\n", |
| 219 | + " f\"**Solution:** $\\\\tilde x = {model.xtilde.value:.3f}$, $x_1 = {model.x[0].value:.3f}$, $x_2 = {model.x[1].value:.3f}$, $x_3 = {model.x[2].value:.3f}$\"\n", |
| 220 | + " )\n", |
| 221 | + ")\n", |
215 | 222 | "display(Markdown(f\"**Maximizes objective value to:** ${model.objective():.2f}$\"))"
|
216 | 223 | ]
|
217 | 224 | },
|
218 | 225 | {
|
219 | 226 | "cell_type": "code",
|
220 |
| - "execution_count": 43, |
| 227 | + "execution_count": 12, |
221 | 228 | "id": "9a2b00d7-433a-46c8-a4b6-c451244c9b0f",
|
222 | 229 | "metadata": {
|
223 | 230 | "tags": []
|
|
235 | 242 | }
|
236 | 243 | ],
|
237 | 244 | "source": [
|
238 |
| - "alpha_values = [0.005, 0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.1, 0.11, 0.12, 0.13, 0.14, 0.15, 0.16, 0.17, 0.18, 0.19, 0.20, 0.25, 0.3, 0.4, 0.5]\n", |
| 245 | + "alpha_values = [\n", |
| 246 | + " 0.005,\n", |
| 247 | + " 0.01,\n", |
| 248 | + " 0.02,\n", |
| 249 | + " 0.03,\n", |
| 250 | + " 0.04,\n", |
| 251 | + " 0.05,\n", |
| 252 | + " 0.06,\n", |
| 253 | + " 0.07,\n", |
| 254 | + " 0.08,\n", |
| 255 | + " 0.09,\n", |
| 256 | + " 0.1,\n", |
| 257 | + " 0.11,\n", |
| 258 | + " 0.12,\n", |
| 259 | + " 0.13,\n", |
| 260 | + " 0.14,\n", |
| 261 | + " 0.15,\n", |
| 262 | + " 0.16,\n", |
| 263 | + " 0.17,\n", |
| 264 | + " 0.18,\n", |
| 265 | + " 0.19,\n", |
| 266 | + " 0.20,\n", |
| 267 | + " 0.25,\n", |
| 268 | + " 0.3,\n", |
| 269 | + " 0.4,\n", |
| 270 | + " 0.5,\n", |
| 271 | + "]\n", |
239 | 272 | "objective = []\n",
|
240 | 273 | "\n",
|
241 |
| - "plt.rcParams.update({'font.size': 14})\n", |
| 274 | + "plt.rcParams.update({\"font.size\": 14})\n", |
242 | 275 | "for alpha in alpha_values:\n",
|
243 | 276 | " _, model = markowitz_revisited(alpha, mu, Sigma)\n",
|
244 |
| - " objective.append(round(model.objective(),3))\n", |
| 277 | + " objective.append(round(model.objective(), 3))\n", |
245 | 278 | "\n",
|
246 | 279 | "plt.plot(alpha_values, objective, color=plt.cm.tab20c(0))\n",
|
247 |
| - "plt.xlabel(r'Risk tolerance $\\alpha$')\n", |
248 |
| - "plt.ylabel('Optimal objective value')\n", |
| 280 | + "plt.xlabel(r\"Risk tolerance $\\alpha$\")\n", |
| 281 | + "plt.ylabel(\"Optimal objective value\")\n", |
249 | 282 | "plt.tight_layout()\n",
|
250 | 283 | "plt.show()"
|
251 | 284 | ]
|
|
267 | 300 | "name": "python",
|
268 | 301 | "nbconvert_exporter": "python",
|
269 | 302 | "pygments_lexer": "ipython3",
|
270 |
| - "version": "3.11.2" |
| 303 | + "version": "3.10.10" |
271 | 304 | },
|
272 | 305 | "varInspector": {
|
273 | 306 | "cols": {
|
|
0 commit comments