portfolio.py


#!/usr/bin/env python3.11

# Copyright 2023, Gurobi Optimization, LLC

# Portfolio selection: given a sum of money to invest, one must decide how to
# spend it amongst a portfolio of financial securities.  Our approach is due
# to Markowitz (1959) and looks to minimize the risk associated with the
# investment while realizing a target expected return.  By varying the target,
# one can compute an 'efficient frontier', which defines the optimal portfolio
# for a given expected return.
#
# Note that this example reads historical return data from a comma-separated
# file (../data/portfolio.csv).  As a result, it must be run from the Gurobi
# examples/python directory.
#
# This example requires the pandas (>= 0.20.3), NumPy, and Matplotlib
# Python packages, which are part of the SciPy ecosystem for
# mathematics, science, and engineering (http://scipy.org).  These
# packages aren't included in all Python distributions, but are
# included by default with Anaconda Python.

import gurobipy as gp
from gurobipy import GRB
from math import sqrt
import pandas as pd
import numpy as np
import matplotlib

matplotlib.use("Agg")
import matplotlib.pyplot as plt

# Import (normalized) historical return data using pandas
data = pd.read_csv("../data/portfolio.csv", index_col=0)
stocks = data.columns

# Calculate basic summary statistics for individual stocks
stock_volatility = data.std()
stock_return = data.mean()

# Turn off all logging and create an empty model
with gp.Env(params={"OutputFlag": 0}) as env, gp.Model(
    "portfolio", env=env) as m:

    # Add a variable for each stock
    vars = pd.Series(m.addVars(stocks), index=stocks)

    # Objective is to minimize risk (squared).  This is modeled using the
    # covariance matrix, which measures the historical correlation between stocks.
    sigma = data.cov()
    portfolio_risk = sigma.dot(vars).dot(vars)
    m.setObjective(portfolio_risk, GRB.MINIMIZE)

	# Fix budget with a constraint
    m.addConstr(vars.sum() == 1, "budget")

    # Optimize model to find the minimum risk portfolio
    m.optimize()

    # Create an expression representing the expected return for the portfolio
    portfolio_return = stock_return.dot(vars)

	# Display minimum risk portfolio
    print("Minimum Risk Portfolio:\n")
    for v in vars:
        if v.X > 0:
            print(f"\t{v.VarName}\t: {v.X:g}")
    minrisk_volatility = sqrt(portfolio_risk.getValue())
    print(f"\nVolatility      = {minrisk_volatility:g}")
    minrisk_return = portfolio_return.getValue()
    print(f"Expected Return = {minrisk_return:g}")

    # Add (redundant) target return constraint
    target = m.addConstr(portfolio_return == minrisk_return, "target")

    # Solve for efficient frontier by varying target return
    frontier = pd.Series(dtype=np.float64)
    for r in np.linspace(stock_return.min(), stock_return.max(), 100):
        target.rhs = r
        m.optimize()
        frontier.loc[sqrt(portfolio_risk.getValue())] = r

# Plot volatility versus expected return for individual stocks
ax = plt.gca()
ax.scatter(x=stock_volatility, y=stock_return, color="Blue", label="Individual Stocks")
for i, stock in enumerate(stocks):
    ax.annotate(stock, (stock_volatility[i], stock_return[i]))

# Plot volatility versus expected return for minimum risk portfolio
ax.scatter(x=minrisk_volatility, y=minrisk_return, color="DarkGreen")
ax.annotate(
    "Minimum\nRisk\nPortfolio",
    (minrisk_volatility, minrisk_return),
    horizontalalignment="right",
)

# Plot efficient frontier
frontier.plot(color="DarkGreen", label="Efficient Frontier", ax=ax)

# Format and display the final plot
ax.axis([0.005, 0.06, -0.02, 0.025])
ax.set_xlabel("Volatility (standard deviation)")
ax.set_ylabel("Expected Return")
ax.legend()
ax.grid()
plt.savefig("portfolio.png")
print("Plotted efficient frontier to 'portfolio.png'")

Try Gurobi for Free

Choose the evaluation license that fits you best, and start working with our Expert Team for technical guidance and support.

Evaluation License
Get a free, full-featured license of the Gurobi Optimizer to experience the performance, support, benchmarking and tuning services we provide as part of our product offering.
Academic License
Gurobi supports the teaching and use of optimization within academic institutions. We offer free, full-featured copies of Gurobi for use in class, and for research.
Cloud Trial

Request free trial hours, so you can see how quickly and easily a model can be solved on the cloud.

Search