Aufbau effizienter Fracht- und LKW-Aktienportfolios in Python

In früheren Beiträgen habe ich gezeigt, wie Aktienkursdaten mit pandas_datareader in Python heruntergeladen werden können – direkt aus Python.

In diesem Beitrag werde ich einen Algorithmus vorstellen mit dem Sie ein effizientes Portfolio basierend auf einer Reihe von Aktien erstellen können, die Sie möglicherweise in Betracht ziehen. Der Algorithmus ermittelt den optimalen Anteil jeder Aktie in Ihrem Portfolio auf der Grundlage des Risikos, das Sie sicher eingehen.

Genauer gesagt werde ich eine Methode zur Visualisierung der effizienten Grenze von Portfolios vorstellen, die aus einem bestimmten Satz von Aktien bestehen. In diesem Beispiel werde ich mit den folgenden 8 Aktien arbeiten, die alle Speditionen sind:

  • Yamato Holdings (YATRY)
  • Knight-Swift Transportation Holdings (KNX)
  • BEST (BEST)
  • YRC Worldwide (YRCW)
  • Schneider National (SNDR)
  • Old Dominion Freight Line (ODFL)
  • Arc Best (ARCB)
  • Werner Enterprises (WERN)

Das Risiko wird als Standardabweichung der historischen Renditen gemessen. Die Rendite wird als durchschnittliche historische tägliche Aktienrendite (unter Verwendung der Schlusskurse) gemessen.

Ich beginne mit dem Import einiger relevanter Module in Python:

# relevante Module importieren
import pandas as pd
import numpy as np
import pandas_datareader.data as web
import datetime
import matplotlib.pyplot as plt
import statistics as stat
import random as rnd
from matplotlib.ticker import StrMethodFormatter

Ich möchte historische Aktienkursdaten für die letzten 6 Monate sammeln. Im Folgenden gebe ich das Start- und Enddatum des relevanten Zeitraums an, um Daten zu sammeln von:

# Geben Sie das relevante Start- und Enddatum für den Erfassungszeitraum der Aktienkursdaten an
start_date = datetime.datetime(2020,4,1)
end_date = datetime.datetime(2020,9,30)

Als Nächstes definiere ich eine Hilfsfunktion die einen von Yahoo Finance über pandas_datareader gesammelten Aktienkursdatenrahmen aufnimmt und in tägliche Renditen transformiert:

# Funktion definieren, die eine Liste der täglichen Rückgaben zurückgibt
def returns(df):
    prices = df["Close"]
    returns = [0 if i == 0 else (prices[i]-prices[i-1])/(prices[i-1]) for i in range(0,len(prices))]
    return(returns)

Jetzt definiere ich eine weitere Hilfsfunktion. Diese Funktion nimmt eine Liste von Aktien-Ticks auf und berechnet deren durchschnittliche tägliche Rendite sowie die dazugehörige Standardabweichung. Diese Funktion verwendet pandas_datareader um Aktienkursdaten von Yahoo abzufragen:

# Funktion definieren, die einen Datenrahmen mit Standardabweichungen und durchschnittlichen täglichen Erträgen erstellen kann
def analyzeStocks(tickersArr,start_date,end_date):
    # Leere Datenrahmenvorlage erstellen
    index = ["ticker","return","stdev"]
    muArr = []
    sigmaArr = []
    # Durchlaufe alle Ticker
    for i in range(0,len(tickersArr)):
        # Ticker zur Tabelle hinzufügen
        tick = tickersArr[i]
        # Aktienkursdaten abrufen
        data = web.DataReader(tickersArr[i],"yahoo",start_date,end_date)
        # Berechne die durchschnittliche tägliche Rendite
        muArr.append(stat.mean(returns(data)))
        # Standardabweichung berechnen
        sigmaArr.append(stat.stdev(returns(data)))
    # einen Datenrahmen zurückgeben
    return(pd.DataFrame(np.array([tickersArr, muArr, sigmaArr]),index=index,columns=tickersArr))

In diesem Beitrag möchte ich die folgenden Ticker-Zeichen analysieren:

tickersArr = ["YATRY","KNX","BEST","YRCW","SNDR","ODFL","ARCB","WERN"]

Mit den obigen Tickern führe ich analyseStocks aus – um Aktienkursdaten abzurufen und die durchschnittliche Rendite und Standardabweichung zu berechnen.

base_df = analyzeStocks(tickersArr,start_date,end_date)
base_df
YATRYKNXBESTYRCWSNDRODFLARCBWERN
tickerYATRYKNXBESTYRCWSNDRODFLARCBWERN
return0.0046537435231962980.0023175179239564793-0.00341243394859026650.0111591997557838490.0024620517170550630.0033492593161784590.0058616868290849180.0017903742321965712
stdev0.023584636993742740.021140916591625140.0313978411557502770.094552762399063540.0193725719356334160.0233054617384102940.0372340691779706750.02237976138155402

Mit Matplotlib mache ich ein einfaches Streudiagramm der historischen Rendite gegenüber der historischen Volatilität:

plt.figure(figsize=(15,8))
muArr = [float(i) for i in base_df.iloc[1,]]
sigmaArr = [float(i) for i in base_df.iloc[2,]]
sharpeArr = [muArr[i]/sigmaArr[i] for i in range(0,len(muArr))]
plt.scatter(sigmaArr,muArr,c=sharpeArr,cmap="plasma")
plt.title("Historical avg. returns vs. standard deviations [single stocks]",size=22)
plt.xlabel("Standard deviation",size=14)
plt.ylabel("Avg. daily return",size=14)
Text(0, 0.5, 'Avg. daily return')

Jetzt definiere ich eine Portfolioaufbaufunktion. Die Funktion erstellt eine definierte Anzahl von Portfolios mit zufällig zugewiesenen Aktiengewichtungen. Die daraus resultierende erwartete Rendite und Standardabweichung wird in Form eines Pandas-Datenrahmens zurückgegeben:

# Funktion zum Erstellen einer definierten Anzahl von Portfolios definieren
def portfolioBuilder(n,tickersArr,start_date,end_date):
    muArr = []
    sigmaArr = []
    dailyreturnsArr = []
    weightedreturnsArr = []
    portfoliodailyreturnsArr = []
    # tägliche Renditen ausfüllen
    for i in range(0,len(tickersArr)):
        data = web.DataReader(tickersArr[i],"yahoo",start_date,end_date)
        dailyreturnsArr.append(returns(data))
    # n verschiedene Portfolios erstellen
    for i in range(0,n):
        # tägliche Portfolio-Liste zurücksetzen
        portfoliodailyreturnsArr = []
        # Portfoliogewicht erstellen
        weightsArr = [rnd.uniform(0,1) for i in range(0,len(tickersArr))]
        nweightsArr = [i/sum(weightsArr) for i in weightsArr]
        # Gewicht die täglichen Renditen
        for j in range(0,len(dailyreturnsArr[0])):
            temp = 0
            for k in range(0,len(tickersArr)):
                temp = temp + float(dailyreturnsArr[k][j])*float(nweightsArr[k])
            portfoliodailyreturnsArr.append(temp)
        # Berechnen und Anhängen der täglichen gewichteten Portfoliorenditen
        muArr.append(stat.mean(portfoliodailyreturnsArr))
        # Berechnen und Anhängen der Standardabweichung der täglichen Renditen des gewichteten Portfolios
        sigmaArr.append(stat.stdev(portfoliodailyreturnsArr))
    # erwartete Renditen und Standardabweichung für die erstellten Portfolios zurückgeben
    return([sigmaArr,muArr])

Ich wende die Funktion an um 500000 zufällige Portfolios basierend auf den angegebenen Ticker-Symbolen zu erzeugen. Die Visualisierung erfolgt mittels Matplotlib.pyplot:

portfoliosArr = portfolioBuilder(500000,tickersArr,start_date,end_date)
plt.figure(figsize=(15,8))
muArr = [float(portfoliosArr[1][i]) for i in range(0,len(portfoliosArr[1]))]
sigmaArr = [float(portfoliosArr[0][i]) for i in range(0,len(portfoliosArr[0]))]
sharpeArr = [muArr[i]/sigmaArr[i] for i in range(0,len(muArr))]
plt.scatter(sigmaArr,muArr,c=sharpeArr,cmap="plasma")
plt.title("Historical avg. returns vs. standard deviations [single stocks]",size=22)
plt.colorbar(label="Sharpe ratio")
plt.xlabel("Standard deviation",size=14)
plt.ylabel("Avg. daily returns",size=14)
Text(0, 0.5, 'Avg. daily returns')

Mit dieser Graphik kann ein Analyst überprüfen ob dessen aktuelle Auswahl an Aktiengewichtungen für die ausgewählten Aktien derzeit effizient ist. Effiziente Portfolios befinden sich entlang der oberen Linie in dem Streudiagramm.

You May Also Like

Leave a Reply

Leave a Reply

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.