Hide navigation sidebar
Hide table of contents sidebar
Back to top
Toggle table of contents sidebar

# portfolio.py

    #!/usr/bin/env python3

    # Copyright 2025, 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 stock in stocks:
        ax.annotate(stock, (stock_volatility[stock], stock_return[stock]))

    # 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'")

Help and Feedback

Press `/` or click to search this documentation | Gurobi website | Gurobi community forum
Go to other Gurobi documentation | Gurobi download | Contact support