Portfoliooptimierung nach Markowitz in Python

Mit seiner im Jahr 1952 veröffentlichten Thesis legte Harry Markowitz den Grundstein der modernen Portfoliotheorie. Nebst anderen Erkenntnissen beschrieb Markowitz, dass sich das Risiko eines Portfolios mittels Diversifikation um ca. zwei Drittel verringern lässt. Die Grundlage der Diversifikation basiert auf der Korrelation, die den Zusammenhang zweier Merkmale beschreibt. Sollten bspw. zwei Wertpapiere eine hohe Korrelation aufweisen, so wird die Preisentwicklung beider Aktien fast gleich sein. Bei negativer Korrelation wäre die Preisentwicklung entsprechend entgegengesetzt. Allgemein können Korrelationen Werte zwischen +1 und -1 einnehmen.

Portfoliooptimierung in Python

Die Funktionsweise der modernen Portfoliotheorie lässt sich am besten anhand eines Beispiels aufzeigen, in dem die Portfoliooptimierung eines Aktienportfolios besprochen wird.

Insgesamt werden 6 Aktien betrachtet: Amazon, Netflix, Facebook, Alphabet, Microsoft und Apple. Das Beispiel soll zudem direkt in Python umgesetzt werden, da so jeder Leser den Code kopieren bzw. auf das eigene Portfolio anwenden kann.

#Importieren aller libraries
import pandas as pd  
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import quandl
import scipy.optimize as sco

#Daten von Quandl herunterladen
quandl.ApiConfig.api_key = "#####"
data = quandl.get(["EOD/AAPL", "EOD/MSFT", "EOD/GOOGL","EOD/AMZN","EOD/NFLX","EOD/FB"]
                  ,start_date='2015-01-01', end_date='2021-01-01',column_index =11)
data = data.rename(columns={"EOD/AAPL - Adj_Close":'AAPL',
                            "EOD/MSFT - Adj_Close":'MSFT',
                            "EOD/FB - Adj_Close":'FB',
                            "EOD/AMZN - Adj_Close":'AMZN',
                            "EOD/NFLX - Adj_Close":'NFLX',
                            "EOD/GOOGL - Adj_Close":'GOOGL'})

Nachdem die historischen Aktiendaten heruntergeladen und gespeichert wurden, sollten sie immer erst einmal geplottet werden. So kann einfach und schnell überprüft werden, ob der vorliegende Datensatz fehlerhaft ist.

Plotten der Daten

#Renditen berechnen und Performance plotten
returns = data.pct_change()
plt.plot(returns['AAPL'].cumsum(), label = "Apple")
plt.plot(returns['MSFT'].cumsum(), label = "Microsoft")
plt.plot(returns['GOOGL'].cumsum(), label = "Alphabet")
plt.plot(returns['AMZN'].cumsum(), label = "Amazon")
plt.plot(returns['NFLX'].cumsum(), label = "Netflix")
plt.plot(returns['FB'].cumsum(), label = "Facebook")
plt.xticks(size=18)
plt.yticks(size = 18)
plt.legend()

Die hier verwendeten Aktien haben in den letzten 5 Jahren allesamt eine extrem gute Rendite erwirtschaftet. Allein Netflix hat in den letzten Jahren aufgrund des rasanten Anstiegs eine durchschnittliche jährliche Rendite von fast 50 % erreicht! Gleichzeitig weißt diese Aktie allerdings auch eine Standardabweichung von über 40 % aus, was eine reine Investition in diese Aktie natürlich sehr volatil macht. Auf Grundlage der modernen Portfoliotheorie soll nun gezeigt werden, wie mittels Diversifikation bzw. gleichzeitiger Allokation in alle Aktien ein Portfolio erstellt werden kann, dass bei mäßigem Risiko eine sehr gute Rendite erwirtschaftet.

Die Schritte zum effizienten Portfolio

Grundsätzlich werden zur Berechnung eines effizienten Portfolios, also eines Portfolios welches bei gegebenem Risiko die maximale Rendite ausweist, drei Dinge benötigt: Renditen, Volatilitäten und Korrelationen. Sofern diese drei Größen gegeben sind, können mittels eines mathematischen Optimierungsproblems die Allokationen der einzelnen Aktien im Portfolio berechnet werden.

In einem ersten Schritt sollen 25.000 zufällige Portfolios simuliert werden. So sollte zu Beginn etwas klarer werden, in welchem Bereich die einzelnen Renditen und Risiken liegen.

#Einzelne Berechnungen
returns = data.pct_change() #Rendite
mean_returns = returns.mean() #Durchschnittliche Rendite
cov_matrix = returns.cov() #Kovarianzmatrix
num_portfolios = 25000 #Anzahl zu simulierender Portfolio
risk_free_rate = 0.005 #Risikofreier Zinssatz

#Annualisieren der Rendite und der Volatilität
def portfolio_annualised_performance(weights, mean_returns, cov_matrix):
    returns = np.sum(mean_returns*weights ) * 252
    std = np.sqrt(np.dot(weights.T, np.dot(cov_matrix, weights))) * np.sqrt(252)
    return std, returns

#Funktion zur Berechnung zufälliger Portfolioallokationen
def random_portfolios(num_portfolios, mean_returns, cov_matrix, risk_free_rate):
    results = np.zeros((3,num_portfolios))
    weights_record = []
    for i in range(num_portfolios):
        weights = np.random.random(6)
        weights /= np.sum(weights)
        weights_record.append(weights)
        portfolio_std_dev, portfolio_return = portfolio_annualised_performance(weights, mean_returns, cov_matrix)
        results[0,i] = portfolio_std_dev
        results[1,i] = portfolio_return
        results[2,i] = (portfolio_return - risk_free_rate) / portfolio_std_dev
    return results, weights_record

#Darstellung aller Portfolios sowie der Allokationen
def display_simulated_ef_with_random(mean_returns, cov_matrix, num_portfolios, risk_free_rate):
    results, weights = random_portfolios(num_portfolios,mean_returns, cov_matrix, risk_free_rate)
    
    max_sharpe_idx = np.argmax(results[2])
    sdp, rp = results[0,max_sharpe_idx], results[1,max_sharpe_idx]
    max_sharpe_allocation = pd.DataFrame(weights[max_sharpe_idx],index=returns.columns,columns=['allocation'])
    max_sharpe_allocation.allocation = [round(i*100,2)for i in max_sharpe_allocation.allocation]
    max_sharpe_allocation = max_sharpe_allocation.T
    
    min_vol_idx = np.argmin(results[0])
    sdp_min, rp_min = results[0,min_vol_idx], results[1,min_vol_idx]
    min_vol_allocation = pd.DataFrame(weights[min_vol_idx],index=returns.columns,columns=['allocation'])
    min_vol_allocation.allocation = [round(i*100,2)for i in min_vol_allocation.allocation]
    min_vol_allocation = min_vol_allocation.T
       
    
    plt.figure()
    plt.scatter(results[0,:],results[1,:],c=results[2,:],cmap='YlGnBu', marker='o', s=10, alpha=0.3)
    plt.colorbar()
    plt.scatter(sdp,rp,marker='*',color='r',s=500, label='Höchste Sharpe Ratio')
    plt.scatter(sdp_min,rp_min,marker='*',color='g',s=500, label='Minimale Volatilität')
    #plt.title('Simulierte Portfolio-Optimierung basierend auf dem effizieten Rand')
    plt.xlabel('Volatilität',fontsize=18)
    plt.ylabel('Rendite',fontsize=18)
    plt.xticks(fontsize=18)
    plt.yticks(fontsize=18)
    plt.legend(labelspacing=0.8)

    print("-"*80)
    print("Höchste Sharpe Ratio - Portfolio Allokation\n")
    print("Annualisierte Rendite:", round(rp,2))
    print("Annualisierte Volatilität:", round(sdp,2))
    print("\n")
    print(max_sharpe_allocation)
    print("-"*80)
    print("Minimale Volatilität - Portfolio Allokation\n")
    print("Annualisierte Rendite:", round(rp_min,2))
    print("Annualisierte Volatilität:", round(sdp_min,2))
    print("\n")
    print(min_vol_allocation)

Die Grafik bildet alle simulierten Portfolios ab, wobei zusätzlich eine Einfärbung nach der jeweiligen Höhe der Sharpe Ratio vorgenommen wurde. Zudem wurde das Portfolio mit der höchsten Sharpe Ratio und das Portfolio mit der minimalen Volatilität abgetragen. Das zuletzt genannte Portfolio wird in der Literatur auch Minimum-Varianz-Portfolio genannt. Auffälig ist die Form aller resultierenden Portfolios, da diese einen linken Rand ausbildet welcher nicht überschritten werden kann. Dieser Rand wird auch effizienter Rand genannt, da auf diesem alle Portfolios liegen, die bei gegebenem Risiko die maximale Rendite aufweisen. Es ist somit nicht möglich mit den vorliegenden Aktien ein Portfolio zu erstellne, welches über diesem effizientem Rand liegt. Um dies noch anschaulicher darzustellen, wird im nächsten Schritt der effiziente Rand berechnet und abgebildet.

Die Berechnung basiert hierbei auf einer Optimierungsfunktion, welche für jedes ausgewiesene Risiko die Portfolioallokation mit der höchsten Rendite berechnet. Die genauen mathematischen Formeln bzw. detaillierte Berechnungen sollen in diesem Artikel nicht gezeigt werden. Der interessierte Leser findet hier mehr Informationen zu den genauen mathematischen Berechnungen.

#Optimierungsfunktion
def portfolio_volatility(weights, mean_returns, cov_matrix):
    return portfolio_annualised_performance(weights, mean_returns, cov_matrix)[0]

def min_variance(mean_returns, cov_matrix):
    num_assets = len(mean_returns)
    args = (mean_returns, cov_matrix)
    constraints = ({'type': 'eq', 'fun': lambda x: np.sum(x) - 1})
    bound = (0.0,1.0)
    bounds = tuple(bound for asset in range(num_assets))

    result = sco.minimize(portfolio_volatility, num_assets*[1./num_assets,], args=args,
                        method='SLSQP', bounds=bounds, constraints=constraints)

    return result

#Berechnung des effizienten Rands
def efficient_return(mean_returns, cov_matrix, target):
    num_assets = len(mean_returns)
    args = (mean_returns, cov_matrix)

    def portfolio_return(weights):
        return portfolio_annualised_performance(weights, mean_returns, cov_matrix)[1]

    constraints = ({'type': 'eq', 'fun': lambda x: portfolio_return(x) - target},
                   {'type': 'eq', 'fun': lambda x: np.sum(x) - 1})
    bounds = tuple((0,1) for asset in range(num_assets))
    result = sco.minimize(portfolio_volatility, num_assets*[1./num_assets,], args=args, method='SLSQP', bounds=bounds, constraints=constraints)
    return result


def efficient_frontier(mean_returns, cov_matrix, returns_range):
    efficients = []
    for ret in returns_range:
        efficients.append(efficient_return(mean_returns, cov_matrix, ret))


#Berechnung bzw. Darstellung des effizienten Rands
def display_calculated_ef_with_random(mean_returns, cov_matrix, num_portfolios, risk_free_rate):
    results, _ = random_portfolios(num_portfolios,mean_returns, cov_matrix, risk_free_rate)
    
    max_sharpe = max_sharpe_ratio(mean_returns, cov_matrix, risk_free_rate)
    sdp, rp = portfolio_annualised_performance(max_sharpe['x'], mean_returns, cov_matrix)
    max_sharpe_allocation = pd.DataFrame(max_sharpe.x,index=data.columns,columns=['allocation'])
    max_sharpe_allocation.allocation = [round(i*100,2)for i in max_sharpe_allocation.allocation]
    max_sharpe_allocation = max_sharpe_allocation.T
    max_sharpe_allocation

    min_vol = min_variance(mean_returns, cov_matrix)
    sdp_min, rp_min = portfolio_annualised_performance(min_vol['x'], mean_returns, cov_matrix)
    min_vol_allocation = pd.DataFrame(min_vol.x,index=data.columns,columns=['allocation'])
    min_vol_allocation.allocation = [round(i*100,2)for i in min_vol_allocation.allocation]
    min_vol_allocation = min_vol_allocation.T
    
    plt.figure()
    plt.scatter(results[0,:],results[1,:],c=results[2,:],cmap='YlGnBu', marker='o', s=10, alpha=0.3)
    plt.colorbar()
    plt.scatter(sdp,rp,marker='*',color='r',s=500, label='Höchste Sharpe Ratio')
    plt.scatter(sdp_min,rp_min,marker='*',color='g',s=500, label='Minimale Volatilität')

    target = np.linspace(rp_min, 0.42, 50)
    efficient_portfolios = efficient_frontier(mean_returns, cov_matrix, target)
    plt.plot([p['fun'] for p in efficient_portfolios], target, linestyle='-.', color='black', label='Effizienter Rand')
    plt.xlabel('Volatilität')
    plt.ylabel('Rendite')
    plt.legend(labelspacing=0.8)
    
    print("Höchste Sharpe Ratio - Portfolio Allokation\n")
    print("Annualisierte Rendite:", round(rp,2))
    print("Annualisierte Volatilität:", round(sdp,2))
    print("\n")
    print(max_sharpe_allocation)
    
    print("-"*80)
    print("Minimale Volatilität - Portfolio Allokation\n")
    print("Annualisierte Rendite:", round(rp_min,2))
    print("Annualisierte Volatilität:", round(sdp_min,2))
    print("\n")
    print(min_vol_allocation)
    return efficients

Auf dem eingezeichnetem effizienten Rand liegen nun alle Portfolios, die bei gegebenem Risiko die maximale Rendite ausweisen. Zudem wurde wieder das Portfolio mit der höchsten Sharpe Ratio und das Minimum-Varianz-Portfolio eingezeichnet. Um die einzelnen Aktien nun besser einordnen zu können bzw. um zu sehen, welche Vorteile ein Portfolio gegenüber der Investition in eine einzelne Aktie hätte, werden in der folgenden Grafik neben dem effizientem Rand noch die 6 Einzelaktien abgetragen.

Es ist nun deutlich zu sehen, dass es einem Privatanleger durch die Erstellung eines Portfolios möglich wird, gleichzeitig das Risiko zu senken bzw. die Rendite zu erhöhen. Selbstverständlich hätte eine Investition in Netflix alleine die beste Performance mit sich gebracht, doch vorherzusagen, welche Aktie solch eine Entwicklung hinlegt ist wohl ein Ding der Unmöglichkeit. Zugegebenermaßen wurden in diesem Artikel auch nicht die besten Aktien zur Darstellung eines Diversifkationseffektes gewählt. Alle Aktien sind dem Bereich Technologie zuzuordnen und somit auch entsprechend hoch korreliert. Bei Hinzunahme verschiedenster Sektoren, die im optimalen Fall negativ miteinander korreliert sind, lässt sich ein sehr guter Diversifikationseffekt beobachten.

 

Effizienter Rand = Heiliger Gral?

Kurze Antwort: Nein! Die Portfoliooptimierung nach Markowitz ist ein spannendes Konstrukt und so gut wie jeder Wirtschaftsstudent lernt die notwendigen mathematischen Prinzipien während des Studiums. Doch in der Praxis mag sich diese Art der Optimierung eher nicht eignen. Wieso? Ganz einfach. Es wird auf Basis historischer Daten optimiert, allerdings weiß mittlerweile jeder Privatanleger, dass historische Entwicklungen kein Indikator für künftige darstellen. Im vorliegenden berechneten Fall würde beispielsweise das Portfolio, welches die höchste Sharpe Ratio ausweist, folgendermaßen aussehen:

  1. Amazon – 55 %
  2. Netflix – 21 %
  3. Apple – 19 %
  4. Microsoft – 5 %
  5. Facebook – 0 %
  6. Google – 0 %

Von allen 6 Aktien werden in der Optimierung allein schon zwei überhaupt nicht berücksichtigt. Zudem ist die Hälfte des Portfolios in Amazon allokiert. Rückwärts betrachtet hätte diese Allokation auch definitiv Sinn gemacht, da Amazon historisch gesehen die höchste Sharpe Ratio ausgewiesen hat. Doch wird dies in den nächsten Jahren ebenso sein? Wohl eher fraglich.

So gibt es auch in der akademischen Literatur wie bspw.

  1. Michaud, R. (1989). “The Markowitz Optimization Enigma: Is Optimization Optimal?”
  2. Chopra, Vijay K., and William T. Ziemba. (1993). “The Effect of Errors in Means, Variances, and Covariances on Optimal Portfolio Choice.”

kritische Stimmen zur praktischen Anwendung. Kurz gesagt: zur Erstellung eines optimalen Portfolios werden Schätzungen bezüglich künftiger Renditen, Risiken und Korrelationen benötigt. Hierzu wird im Normalfall auf historische Daten zurückgegriffen. Diese jedoch enthalten sehr oft einen erheblichen Schätzfehler, auf welchem nun die Optimierung durchgeführt wird.

Fazit

Markowitz hat mittels der modernen Portfoliotheorie die Grundlagen der Kapitalmarkttheorie wie wir sie heute kennen gelegt. Die dahinterstehende Theorie wird an jeder Universität gelehrt und bildet die Grundlage fortgeschrittener Kapitalmarktmodelle wie bspw. dem CAPM. Die Praxistauglichkeit jedoch gilt es zu hinterfragen, da diese auch in akademischen Kreise immer wieder kritisiert wird.

Festzuhalten ist jedoch, dass Diversifikation wohl berechtigterweise als einziger “Free Lunch” an der Börse zu sehen ist und somit das eigene Portfolio auch genügend diversifiziert werden sollte.

 

 

Inhalte werden geladen

Dieser Beitrag hat 2 Kommentare

  1. Patrick

    Wow, vielen Dank für den gesamten Code! Bin gerade selber dabei mir einiges in Python beizubringen und das hilft wirklich 🙂

    1. Peter

      Hey Patrick, vielen Dank!
      Ich hatte zu Beginn die Befürchtung, dass das am zu viel Code sein könnte.
      Doch wenn es hilft, dann freue ich mich natürlich.

Schreibe einen Kommentar