|
| 1 | +module OperationsResearchModelsIpoptExt |
| 2 | + |
| 3 | +using OperationsResearchModels |
| 4 | +using Ipopt |
| 5 | +using JuMP |
| 6 | +using LinearAlgebra |
| 7 | +using Statistics |
| 8 | + |
| 9 | + |
| 10 | +import OperationsResearchModels: solve |
| 11 | +import OperationsResearchModels.Portfolio: PortfolioProblem, PortfolioResult |
| 12 | + |
| 13 | +""" |
| 14 | + solve(problem) |
| 15 | +
|
| 16 | +# Description |
| 17 | +
|
| 18 | +Solves a portfolio optimization problem given by an object of in type `PortfolioProblem`. |
| 19 | +The optimization problem is formulated as a quadratic programming problem where the objective |
| 20 | +is to minimize the portfolio variance (risk) subject to constraints on the expected return and the weights. |
| 21 | +
|
| 22 | +Mathematically, the problem can be stated as: |
| 23 | +
|
| 24 | +Minimize: w' * Covmat * w |
| 25 | +Subject to: |
| 26 | +- sum(w) == 1 (the weights must sum to 1) |
| 27 | +- sum(w[i] * means[i] for i in 1:m) >= thresholdreturn |
| 28 | +- 0 <= w[i] <= 1 for all i (weights must be between 0 and 1) |
| 29 | +
|
| 30 | +Where: |
| 31 | +- w is the vector of asset weights |
| 32 | +- Covmat is the covariance matrix of asset returns |
| 33 | +- means is the vector of expected returns for each asset |
| 34 | +- thresholdreturn is the minimum expected return required for the portfolio |
| 35 | +
|
| 36 | +# Note |
| 37 | +
|
| 38 | +Ipopt is used to solve the quadratic programming problem. Make sure to have |
| 39 | +Ipopt installed and properly configured in your Julia environment to use this function. |
| 40 | +
|
| 41 | +
|
| 42 | +# Arguments |
| 43 | +
|
| 44 | +- `problem::PortfolioProblem`: The problem in type of PortfolioProblem |
| 45 | +
|
| 46 | +# Returns |
| 47 | +
|
| 48 | +- `PortfolioResult`: The result of the portfolio optimization problem, containing the optimal weights, expected return, and the JuMP model used to solve the problem. |
| 49 | +
|
| 50 | +""" |
| 51 | +function solve(p::PortfolioProblem)::PortfolioResult |
| 52 | + model = Model(Ipopt.Optimizer) |
| 53 | + |
| 54 | + MOI.set(model, MOI.Silent(), true) |
| 55 | + |
| 56 | + means = mean(p.returns, dims = 1) |
| 57 | + covmat = cov(p.returns) |
| 58 | + |
| 59 | + _, m = size(p.returns) |
| 60 | + |
| 61 | + @variable(model, 0 <= w[1:m] <= 1) |
| 62 | + @constraint(model, sum(w) == 1) |
| 63 | + @constraint(model, sum(w[i] * means[i] for i in 1:m) >= p.thresholdreturn) |
| 64 | + @objective(model, Min, w' * covmat * w) |
| 65 | + |
| 66 | + optimize!(model) |
| 67 | + |
| 68 | + weights = value.(w) |
| 69 | + expectedreturn = sum(weights[i] * means[i] for i in 1:m) |
| 70 | + |
| 71 | + return PortfolioResult(weights, expectedreturn, model) |
| 72 | +end |
| 73 | + |
| 74 | +end |
0 commit comments