2  Relative Portfolio Optimization

Source

2.1 Learning Objectives

After completing this chapter, you will be able to …

  • explain the fundamental concepts of relative portfolio optimization and clearly distinguish them from classical (absolute) portfolio optimization, particularly with respect to the role of a benchmark.

  • correctly calculate and interpret active returns and active risk (tracking error), and classify them as the central control variables of relative portfolio optimization.

  • formulate a relative portfolio-optimization problem, including a suitable target function (e.g., minimizing the tracking error or maximizing the active return) and typical constraints relative to a benchmark.

  • determine active portfolio weights (active weights) and quantitatively analyze their influence on risk and return relative to the benchmark.

  • implement a relative portfolio optimization in practice using Python, apply relevant optimization libraries, and critically evaluate the results in the context of a benchmark-based investment strategy.

2.2 Introduction and motivation

The following remarks on relative portfolio optimization are based on Poddig et al. (2009), pp. 197-216. The fundamental approach of relative optimization in the context of portfolio construction goes back to Grinold and Kahn (2000).


One natural starting point for a relative-optimization approach is the way responsibility for results is often split in active management. For externally managed portfolios, a separation of responsibility for return and risk is, strictly speaking, necessary. The investor delegates asset management to an (external) manager, subject to a benchmark that is binding for that manager. In doing so, the investor accepts — and is willing to bear — the return and risk of the benchmark. The manager is therefore not responsible for the benchmark’s return and risk, but only for the additional return and additional risk of the active portfolio. As a result, absolute optimization of the portfolio is of no use to the manager: what matters is only the additional return (active return) relative to the additional risk incurred (active risk). The optimal “relative” portfolio is the one with the best possible trade-off between active return and active risk relative to the benchmark. In other words, what is sought is the portfolio that is optimal relative to the benchmark. Such a split of responsibility also arises, for example, in the management of public mutual funds, whose strategic orientation is usually fixed for the manager by a benchmark; an active strategy can then be implemented via relative optimization.

Relative optimization can also be motivated by some points of criticism of the absolute-optimization approach. Absolute optimization relies on two key premises: the “precise” predictability of the required input parameters (the first two moments of the return distribution) and the assumption that the investor knows their utility function and can describe it exactly. Given these premises, absolute optimization reliably delivers the optimal portfolio.

One main criticism concerns the premises of expected-utility theory — in particular, the identification of the utility function, which is difficult in practice.

Moreover, the forecasts of the input parameters are inherently uncertain. Financial-market forecasts cannot be made with the precision actually required, even when they concern “only” the moments of a distribution of random variables — and this is precisely what causes problems in absolute portfolio optimization. Portfolio optimization is generally quite sensitive to relatively small changes in the estimated input parameters, especially the forecasted expected returns. As a result, even small changes in the return estimates call for relatively large reallocations, and hence a certain “instability” of the portfolio structure.

Besides this instability, practical portfolio optimization also tends to produce “extreme” positions in the optimal portfolio, and even economically implausible portfolio structures. This too is a consequence of the forecasting problem rather than of the optimization as such, but because of it absolute optimization often yields portfolios that the investor does not want to hold, owing to extreme positions in individual assets. A more detailed discussion of the weaknesses of the mean-variance approach, with examples, can be found in Drobetz (2003, p. 206 ff.) and in a later chapter.

So neither the efficient frontier (because of the forecasting problem) nor the utility function (because of the identification problem) can be determined with sufficient precision — yet, strictly speaking, finding the optimal portfolio requires both.

To address this problem, various approaches have been developed in academia and in practice:

  • given the impossibility of precise financial-market forecasts, forgoing active portfolio management altogether in favor of passive management with index tracking;
  • approaches to robust portfolio optimization: the use of a wide variety of constraints, robust estimation of the input parameters, the Black-Litterman approach, portfolio resampling, and risk-based approaches;

A further approach is relative optimization. Here the assets’ input parameters are considered relative to a benchmark, and the portfolio structure is optimized in terms of these relative quantities. This keeps the optimal portfolio tied to the benchmark, which tends to rule out unacceptable solutions.

2.3 Foundations and basic terms of relative optimization

In relative optimization, the return \(r_i\) of an asset \(i\) is commonly expressed as an excess return \(r^´_i\) over the risk-free rate \(r_f\): \(r^´_i=r_i-r_f\). If the risk-free rate is assumed constant over time, the variance-covariance matrix of the excess returns equals that of the total (absolute) returns, i.e. \(\Sigma^´=\Sigma\).

2.3.1 Linear return-generating process

Relative optimization assumes a univariate return-generating process for each asset \(i\):

\[ (1) \quad r^´_i=\alpha_i + \beta_i*r^´_B+\epsilon_i, \]

with:

  • \(r^´_i\): excess return of the i-th asset
  • \(r^´_B\): excess return of the benchmark portfolio B
  • \(\alpha_i\): autonomous own return of the i-th asset, constant
  • \(\beta_i\): sensitivity to the benchmark portfolio
  • \(\epsilon_i\): unsystematic, random, unexplainable residual quantity (also called residual return).

Furthermore, the following is assumed for the residual returns:

\((2) \quad E(\epsilon_i)=0\) \(\qquad\) for all \(i\), with \(E()\): expectation operator;

\((3) \quad Var(\epsilon_i)=\sigma^2_{\epsilon_i}\) \(\quad\) finite and constant for all \(i\)

\((4) \quad Cov(\epsilon_i, r^´_B)=E((\epsilon_i-0)(r^´_B-\mu^´_B))=0\) \(\quad\) with \(\mu^´_B=E(r^´_B)\): expected value of the benchmark return

\((5) \quad Cov(\epsilon_i, \epsilon_j)=E((\epsilon_i-0)(\epsilon_j-0))=0\) \(\qquad\) for all \(i, j, i \neq j.\)

This makes it straightforward to determine the expected asset returns, their variances, and the covariances between them.

Under these assumptions, the expected (excess) return, variance, and covariance of asset \(i\) are therefore:

\((6) \quad E(r^´_i)=\mu^´_i=\alpha_i+\beta_i*\mu^´_B\)

\((7) \quad \sigma^2_i=\beta^2_i*\sigma^2_B+\sigma^2_{\epsilon_i}\)

\((8) \quad \sigma_{ij}=\beta_i*\beta_j*\sigma^2_B\)

By (7), the variance — i.e. the risk — of asset \(i\) splits into two components. One part \((\beta^2_i\sigma^2_B)\) is explained by the benchmark; the second part \((\sigma^2_{\epsilon_i})\) is the so-called residual risk. The formulas relevant for relative portfolio optimization now follow from the assumptions and definitions made so far.

2.3.2 Portfolio alpha and beta

The portfolio excess return is:

\((1) \quad r^´_p=\alpha_p+\beta_p*r^´_B+\epsilon_P=\alpha^*_P+\beta_P*r^´_B\) \(\qquad\) with \(\alpha^*_P=\alpha_P+\epsilon_P\)

The portfolio alpha and beta are determined by the asset weights in the portfolio and the assets’ own alpha and beta values:

\((2) \quad \alpha_P=w^T_P\alpha\)

with \(\qquad\) \(\alpha:\) \(\quad\) vector of the autonomous own returns of the assets

\((3) \quad \beta_P=w^T_P\beta\)

with \(\qquad\) \(\beta:\) \(\quad\) vector of the sensitivities of the included assets to the benchmark portfolio \(B\).

Just as in equation (7) above, the risk (variance) of a portfolio splits into two components. One part \((\beta^2_P\sigma^2_B)\) is explained by the benchmark; the second part \((\sigma^2_{\epsilon_P})\) is the residual risk:

\((4) \quad \sigma^2_P=\beta^2_P*\sigma^2_B+\sigma^2_{\epsilon_P}\)

or, equivalently, the portfolio’s residual risk is:

\((5) \quad \sigma^2_{\epsilon_P}=\sigma^2_P-\beta^2_P*\sigma^2_B.\)

The risks of portfolio \(P\) and benchmark \(B\) are, as usual:

\((6) \quad \sigma^2_P=w^T_P\Sigma w_P\)

\((7) \quad \sigma^2_B=w^T_B\Sigma w_B\)

Substituting into (5) gives:

\((8) \quad \sigma^2_{\epsilon_P}=w^T_P\Sigma w_P-(w^T_P\beta)^2*w^T_B\Sigma w_B.\)

2.3.3 Active position and active risk

Unlike absolute optimization, relative optimization is defined by its reference to the benchmark — without, however, amounting to passive portfolio management. The deviation of the portfolio’s asset weights from those of the benchmark is called the active position. It is thus defined as the difference in weights between the portfolio held, \(P\), and the benchmark \(B\):

\((1) \quad w_A=w_P-w_B.\)

The risk attributable to the active position is called the active risk (active variance) and is given by:

\((2) \quad \sigma^2_{AP}=w^T_A\Sigma w_A.\)

2.3.4 Active beta

In the same way as the active risk, an active beta arises as well. It is the difference between the portfolio beta and the benchmark beta (which equals one by definition):

\((1) \quad \beta_{AP}=\beta_P-\beta_B=\beta_P-1\)

with \(\qquad\) \(\beta_{AP}:\) \(\quad\) active beta.

The active variance (active risk) is therefore (see Poddig et al., 2009, pp. 240-241, for the derivation):

\((2) \quad \sigma^2_{AP}=\beta^2_{AP}\sigma^2_B + \sigma^2_{\epsilon_P}\)

2.3.5 Selection risk and timing risk

To interpret equation (2), we need to define two different types of risk, distinguishing risk due to selection from risk due to timing. Selection ability is a manager’s skill in identifying securities with above-average return potential — i.e. good portfolio performance driven by the choice of individual assets. A manager is credited with selection ability, for example, when the portfolio they have assembled shows a significantly positive alpha. Timing ability, by contrast, is the manager’s ability, in periods when the benchmark earns a positive (excess) return, to make the managed portfolio’s (excess) return rise more strongly than the benchmark’s by actively managing the sensitivity \(\beta\). Conversely, with timing ability the actively managed portfolio should fall less than the benchmark in declining markets.

The active risk (cf. formula (2)) thus splits into the timing risk \(\beta^2_{AP}\sigma^2_B\) and the selection risk \(\sigma^2_{\epsilon_P}\).

If the timing component is deliberately excluded in relative optimization, i.e. if:

\((1) \quad \beta_{P}=\beta_B = 1 \Leftrightarrow \beta_{AP}=0\)

is explicitly required, then (2) simplifies to:

\((2^´) \quad \sigma^2_{AP}= \sigma^2_{\epsilon_P}\)

This equates the active risk with the residual risk, which captures the “selection risk”. Condition \((2^´)\) is imposed throughout what follows (see Grinold and Kahn, 2000, p. 102, and Poddig et al., 2009, p. 213f., for why the timing component is excluded in relative optimization).

2.4 Target function of relative optimization

From the discussion so far, and neglecting the timing component of the active risk, the following target function of relative optimization can be derived from that of absolute optimization (see Poddig et al., 2009, pp. 209-216):

\[ \begin{split} \\(1) \quad TF(w) = \alpha_P-\lambda\sigma^2_{\epsilon_P} \rightarrow \max_{w}!, \\ \end{split} \]

where \(\lambda\) is the investor-specific risk-aversion coefficient. In matrix notation:

\[(2) \quad TF(w) = w_p ^T\alpha-\lambda(w_P^T\Sigma w_P-(w_P^T\beta)^2*w_B^T\Sigma w_B)\]

or alternatively (since \(\beta_P=1\) or \(\beta_{AP}=0\) are imposed in the following)

\((3) \quad TF(w) = w_p ^T\alpha-\lambda(w_A^T\Sigma w_A).\)

This amounts to maximizing the difference between the portfolio alpha and the risk-aversion-weighted selection risk (residual risk) — which, absent a timing component, also equals the active risk.

The central constraints (the budget and long-only constraints) are supplemented by the exclusion of the timing component:

\[ \begin{split} & \text{(a)}\ w^{T}\iota = 1 \quad or \quad \ w_A^{T}\iota = 0 \qquad \text{(budget constraint)}, \\ & \text{(b)}\ w\geqq 0 \qquad \text{(long-only constraint)}, \\ & \text{(c)}\ \beta_P=1 \quad \Leftrightarrow \quad \beta_{AP}=0 \quad \text{(no timing)}. \end{split} \]

This completes the formulation of the relative-optimization problem. It is illustrated in more detail in the case study that follows.

2.5 Beginning of the case study

We start by importing the required packages.

Code
import pandas as pd
import numpy as np
from scipy.optimize import minimize

2.5.1 Loading and describing the data

The example investment universe for the case study comprises eight companies from the following sectors: technology, healthcare, food, pharmaceuticals, energy, and aerospace. The companies are: Costco Wholesale (COST), Cisco Systems (CSCO), IBM (IBM), Intel (INTC), Merk (MRK), Microsoft (MSFT), AT&T (T), and Exxon Mobil Corporation (XOM).

The data consist of beginning-of-month prices (“Adjusted Close”) over the 5-year period from 1.12.2004 to 1.12.2009, giving 61 time-series observations.

Code
# insert here your local directory path of the file 'Kapitel A1.xlsx'
# cd "..."
Code
frame = pd.read_excel('Kapitel A1.xlsx', 'Tabelle1', index_col=0, parse_dates=True)
Code
frame.head()
ABT BA COST CSCO IBM INTC MRK MSFT T XOM
2004-12-01 46.65 51.77 48.41 19.32 98.58 23.39 32.14 26.72 25.77 51.26
2005-01-03 45.02 50.60 47.27 18.04 93.42 22.45 28.05 26.28 23.76 51.60
2005-02-01 45.99 54.97 46.59 17.42 92.58 23.99 31.70 25.16 24.06 63.31
2005-03-01 46.62 58.46 44.18 17.89 91.38 23.23 32.37 24.17 23.69 59.60
2005-04-01 49.16 59.52 40.63 17.27 76.38 23.52 33.90 25.30 23.80 57.03

2.5.2 Case A: The active portfolio and the benchmark originate from the same investment universe

We first compute discrete monthly returns via frame.pct_change(). The risk-free rate is assumed constant at 4% per year, corresponding to a monthly rate of 0.327374%. The absolute returns are then converted into excess returns (DataFrame ex_returns). From these excess returns we estimate the vector of expected future asset returns \(\mu\) and the future variance-covariance matrix \(\Sigma\) using history-based estimation.

Code
# calculation based on discrete returns
returns = frame.pct_change().dropna()
returns_new = returns.drop(['ABT', 'BA'], 1) # we drop first two tickers

rf = (1+0.04)**(1/12)-1 # risk-free rate assumed 4% p.a.
ex_returns = returns_new - rf
means = ex_returns.mean().values*100 # non-annualised!
Sigma = ex_returns.cov().values # non-annualised!

Here the benchmark portfolio is chosen, arbitrarily, as an equally weighted portfolio of all assets. The call np.tile(A,x) generates an array containing the value A x times. We obtain the number of assets in our example universe via means.shape[0].

In what follows, we will frequently convert arrays of dimension \(N\) into \(1xN\) row vectors via np.matrix for matrix multiplication.

We obtain the time series benchmark of the benchmark excess returns \(r^´_B\) by first multiplying the asset returns row-wise by the benchmark weights Weight_1N (via ex_returns.multiply(Weight_1N)) and then summing each row.

Code
# using an equally-weighted benchmark
Weight_1N = np.tile(1.0/means.shape[0], means.shape[0])
bench_w = np.matrix(Weight_1N) # benchmark portfolio weights as row vector
benchmark = ex_returns.multiply(Weight_1N).sum(axis=1)

Estimation of further input parameters: alpha and beta

The alpha and beta parameters enter the calculations as central inputs for a relative optimization. They explain each asset’s excess return as the sum of the asset’s own autonomous return and a benchmark-dependent return component. Under the assumption of a linear return-generating process, they can be estimated simply from history by regressing the asset returns on the benchmark.

In principle, the same considerations apply to alpha and beta as to the expected returns and future risks in absolute optimization. Alpha and beta are ex ante quantities, so forecasts (estimates) of them are unavoidable. Again, bear in mind that the quality of the alpha and beta forecasts determines the active portfolio’s later performance — here, too, the forecast is where the value is created.

For the case study, we use the pragmatic approach of simple history-based estimation via univariate linear regression. Poddig et al. (2009, pp. 220-223) point to further methods specifically for forecasting alpha and beta.

To run the ordinary least squares (OLS) regression, we first import the linear_model and tools modules from the statsmodels package.

Code
import statsmodels.regression.linear_model as sm
import statsmodels.tools.tools as sm2

The matrix x2 below holds the regression’s exogenous variables. Its first column is a vector of ones, which models the regression constant (the intercept = alpha); this column is created via add_constant. The second column is the vector of benchmark returns. The regression’s y-variable is the corresponding column of the DataFrame of asset excess returns.

An OLS model is estimated via sm.OLS(y-variable, (constant, x-variables)).fit(). The estimated regression coefficients are held in the array params. We iterate the regressions with a for loop over the columns of ex_returns, again using enumerate so that the iteration index can index the result arrays alpha and beta.

Code
# calculating vectors of alphas and betas
alpha = np.zeros(ex_returns.columns.shape)
beta = np.zeros(ex_returns.columns.shape)

x1 = benchmark
x2 = sm2.add_constant(x1)

for idx, ticker in enumerate(ex_returns.columns):
    reg = sm.OLS(ex_returns[ticker], x2).fit()
    parameter = np.asarray(reg.params)
    alpha[idx] = parameter[0]
    beta[idx] = parameter[1]
df =pd.DataFrame({'Alpha': alpha, 'Beta': beta}, index=ex_returns.columns)

For better readability, we present the estimation results as a DataFrame.

Code
df
Alpha Beta
COST 0.000870 0.900610
CSCO 0.001147 1.285882
IBM 0.001738 0.798834
INTC -0.005285 1.366525
MRK 0.000367 1.158739
MSFT -0.000665 1.146965
T -0.002151 0.812048
XOM 0.003978 0.530397

For the matrix multiplication that follows, we convert the two result arrays into row vectors.

Code
alpha = np.matrix(alpha)
beta = np.matrix(beta)

We now write the optimization’s target function as the function relative_opt1. Important: this function requires that the arrays of alphas, betas, and benchmark weights have already been converted into row vectors!

From an input array of portfolio weights \(w_P\), the function computes the portfolio alpha \((w_p ^T\alpha)\), beta \((w_P^T\beta)\), the variances of the portfolio return \((w_P^T\Sigma w_P)\) and the benchmark return \((w_B^T\Sigma w_B)\), and the residual risk \((w_P^T\Sigma w_P-(w_P^T\beta)^2*w_B^T\Sigma w_B)\). It returns the target-function value multiplied by -1, since this is a maximization problem:

\(\quad TF(w) = w_p ^T\alpha-\lambda(w_P^T\Sigma w_P-(w_P^T\beta)^2*w_B^T\Sigma w_B)\)

Code
# target function: active alpha - lambda * residual risk
# alpha, beta, bench_w have to be defined prior as vectors
def relative_opt1(w, lambda0):
    port_w = np.matrix(w) # w is a row (not column!) vector
    port_alpha = (port_w*alpha.T)[0,0]
    port_beta = (port_w*beta.T)[0,0]
    port_var = (port_w * Sigma*port_w.T)[0,0]
    bench_var = (bench_w * Sigma * bench_w.T)[0,0]
    resid_var = port_var - port_beta**2*bench_var
    return -(port_alpha - lambda0 * resid_var) # -1 for minimization problem

As with the budget constraint, we implement the additional (no-timing) constraint — that the portfolio beta equals one, i.e. the active beta equals zero — via a lambda function set to zero, which we add to the tuple cons of constraint dictionaries.

Code
cons = ({'type': 'eq', 'fun': lambda x: np.sum(x) - 1},
        {'type': 'eq', 'fun': lambda x: (np.matrix(x)*beta.T)[0,0] - 1})

We set the risk-aversion coefficient to \(\lambda=-0.3705867\), use an equally weighted portfolio as the starting point for the numerical optimization, and impose holding limits (min=5%, max=40%). Calling minimize yields the following optimized portfolio weights.

Code
lambda0 = -0.3705867
bound = (0.05,0.40)
bounds = tuple(bound for asset in range(alpha.shape[1]))
res = minimize(relative_opt1, Weight_1N, args=lambda0,
                        method='SLSQP', bounds=bounds, constraints=cons,tol=1e-10)
pd.DataFrame([round(x,4) for x in res.x],index=ex_returns.columns).T 
COST CSCO IBM INTC MRK MSFT T XOM
0 0.05 0.4 0.05 0.05 0.0776 0.05 0.05 0.2724

Let us quickly check that this weight vector really satisfies the no-timing condition:

Code
# function to calculate the portfolio beta
def port_beta(w,beta):
    port_w = np.matrix(w) # w is a row (not column!) vector
    port_beta = (port_w*beta.T)[0,0]
    return port_beta


port_beta(res.x,beta)
0.9999999999038441

Evidently it does. The portfolio has an expected annualized return of:

Code
(np.matrix(means)* np.matrix(res.x).T)[0,0]*12
3.6127468232671593

2.5.3 Case B: Relative optimization with different investment universes

In the case study above, the investment universes of the active portfolio and the benchmark are identical. This, however, is an ideal case that does not always hold in practice, where the two universes often differ. Frequently, not all of the benchmark’s constituents are available for the active portfolio. In the extreme case, the benchmark is a synthetic index, and an active portfolio of individual securities not contained in the index is to be optimized against it. This raises the question of how to carry out a relative optimization when the investment universes differ.

To illustrate the problem, we now use the S&P500 index as the benchmark and optimize the active portfolio relative to it. In doing so, we deliberately proceed as if the S&P500 were a synthetic index.

Code
# benchmark is now the S&P500 index, therefore we need a new dataframe
frame1 = pd.read_excel('Kapitel A2_1.xlsx', 'Tabelle1', index_col=0, parse_dates=True)
Code
frame1.head()
COST CSCO IBM INTC MRK MSFT T XOM S&P500
2004-12-01 48.41 19.32 98.58 23.39 32.14 26.72 25.77 51.26 1211.92
2005-01-03 47.27 18.04 93.42 22.45 28.05 26.28 23.76 51.60 1181.27
2005-02-01 46.59 17.42 92.58 23.99 31.70 25.16 24.06 63.31 1203.60
2005-03-01 44.18 17.89 91.38 23.23 32.37 24.17 23.69 59.60 1180.59
2005-04-01 40.63 17.27 76.38 23.52 33.90 25.30 23.80 57.03 1156.85

The basic procedure is unchanged from above. First, the price series are converted into discrete monthly returns and then expressed as excess returns over the risk-free rate.

Code
# calculation based on discrete returns
returns = frame1.pct_change().dropna()

rf = (1+0.04)**(1/12)-1 # risk-free rate assumed 4% p.a.
ex_returns = returns - rf

# important: we use only the first 60 returns!
means = ex_returns.iloc[:60,:].mean().values*100*12 # annualised!
Sigma = ex_returns.iloc[:60,:].cov().values # non-annualised!

These form the basis for a history-based estimation of the alpha and beta parameters, obtained by univariate linear regression of each asset’s returns on the benchmark return.

Code
# calculating vectors of alphas and betas
alpha = np.zeros(ex_returns.columns.shape)
beta = np.zeros(ex_returns.columns.shape)

x1 = ex_returns['S&P500']
x2 = sm2.add_constant(x1)

for idx, ticker in enumerate(ex_returns.columns):
    reg = sm.OLS(ex_returns[ticker].iloc[:60], x2.iloc[:60]).fit()
    parameter = np.asarray(reg.params)
    alpha[idx] = parameter[0]
    beta[idx] = parameter[1]
df =pd.DataFrame({'Alpha': alpha, 'Beta': beta}, index=ex_returns.columns)
Code
df
Alpha Beta
COST 5.317110e-03 0.793253
CSCO 7.774840e-03 1.208840
IBM 6.029992e-03 0.798688
INTC 1.309308e-03 1.161773
MRK 5.708572e-03 0.916956
MSFT 4.811190e-03 0.959172
T 1.748128e-03 0.684994
XOM 6.592676e-03 0.465999
S&P500 -8.673617e-19 1.000000
Code
alpha = np.matrix(alpha)
beta = np.matrix(beta)

The relative-optimization approach is formulated and the active portfolio determined exactly as before; the only change in the case study is the use of different investment universes. A general way to handle different universes is to form, for two portfolios \(P\) and \(B\) with universes \(X\) and \(Y\), a common universe \(Z\) as the union of \(X\) and \(Y\), i.e. \(Z=X \cup Y\). The holdings of both \(P\) and \(B\) are then elements of the same universe \(Z\). Assets held in \(P\) but not in \(B\) are fixed to a weight of zero in \(B\), and, correspondingly, assets held in \(B\) but not in \(P\) are fixed to zero in \(P\).

The investment universe in the case study now has nine assets: the eight individual securities plus the S&P500 as a “virtual” investment. The history-based calculation of \(\mu\), \(\Sigma\), \(\alpha\), and \(\beta\) for these nine investments was already carried out above.

Next, the vector of benchmark weights must be set so that the first eight weights (for the individual securities) are zero and the last weight (for the S&P500) is one.

Code
# calculation of weight vector for active portfolio and benchmark
# using S&P500 as a benchmark implies that all benchmark weights (first N-1)
# elements are zero and 1 for S&P500 (last element)
bench_w = np.zeros(means.shape)
bench_w[-1]=1.0
bench_w = np.matrix(bench_w)

In addition, the vector of starting weights for the active portfolio must hold the value \(1/8\) (1/(means.shape[0]-1)) in the first eight positions and zero in the last.

Code
# vector of starting weights is equally-weighted for the first N-1 
# elements and zero for the last element
Weight_start = np.zeros(means.shape)
Weight_start[0:-1]=1/(means.shape[0]-1)
Weight_start = np.matrix(Weight_start)

The definition of the target function relative_opt1 remains unchanged, as does the tuple cons of constraints.

Note that only the first eight weights in \(w_P\) are free parameters of the optimization; the ninth weight in \(w_P\) (for the S&P500) is fixed at zero and is not optimized. The same holds for every weight in \(P\) that is explicitly fixed to zero because the corresponding investment is not part of \(P\)’s investment universe.

We impose the zero weight of the benchmark in \(w_p\) through the holding-limit tuple bounds, by setting bounds += ((0.0, 1e-10), ). This appends a further tuple of the form (0.0, 1e-10) to bounds. Important: the tuple’s upper limit must be strictly greater than its lower limit (hence the value 1e-10); this has no effect on the optimization results.

With \(\lambda=-0.1075445\), the optimization yields the following weight vector.

Code
bound = (0.05,0.40)
bounds = tuple(bound for asset in range(alpha.shape[1]-1))
# weight of the benchmark is fixed to zero; Important: lb==ub is no longer allowed!
bounds += ((0.0, 1e-10), ) 
lambda0 = -0.1075445

cons = ({'type': 'eq', 'fun': lambda x: np.sum(x) - 1},
        {'type': 'eq', 'fun': lambda x: (np.matrix(x)*beta.T)[0,0] - 1})

res1 = minimize(relative_opt1, Weight_start, args=lambda0,
                        method='SLSQP', bounds=bounds, constraints=cons,tol=1e-10)
pd.DataFrame([round(x,4) for x in res1.x],index=ex_returns.columns).T
COST CSCO IBM INTC MRK MSFT T XOM S&P500
0 0.05 0.4 0.05 0.05 0.296 0.05 0.05 0.054 0.0

A quick check that the timing condition (portfolio beta equal to one, active beta equal to zero) is satisfied.

Code
port_beta(res1.x,beta)
1.0000000000585922
Code
# portfolio variance
(np.matrix(res1.x)* Sigma* np.matrix(res1.x).T)[0,0]
0.0030267295902247117
Code
# portfolio alpha
(np.matrix(res1.x)*alpha.T)[0,0]
0.006116489277362495

2.6 Summary

In this chapter you have met the fundamental concepts of relative portfolio optimization and clearly distinguished them from classical portfolio optimization, particularly regarding the role of a benchmark. You can correctly compute active returns and active risk (the tracking error) and identify them as the central control variables of relative optimization. You are also able to formulate a relative portfolio-optimization problem with a suitable target function and typical benchmark-related constraints. You have learned to determine active portfolio weights and to analyze quantitatively how they affect risk and return relative to the benchmark. On this basis, you can implement relative portfolio optimization in Python and critically evaluate the results in the context of a benchmark-based investment strategy.

2.7 Literature and references

  • Drobetz, W. (2003). Einsatz des Black-Litterman-Verfahrens in der Asset Allocation, in: Dichtl, H., Kleeberg, J., and C. Schlenger (eds.), Handbuch Asset Allocation, Uhlenbruch Verlag: Bad Soden/Ts.
  • Grinold, R. C., Kahn, R. N. (2000). Active Portfolio Management, Quantitative Theory and Applications, 2. Auflage, New York u.a.
  • Poddig, T., Brinkmann, U., Seiler, K. (2009). Portfolio Management: Konzepte und Strategien, 2. Auflage, Uhlenbruch Verlag, Bad Soden/Ts.

Go deeper

Experience this chapter in the live seminar

In the in-house seminar Quant Portfolio Management, you will work through these methods hands-on in Python — with personal feedback, tailored case studies, and direct exchange with Prof. Dr. Thomas Mählmann.

Learn more about the seminar →