Construindo portfólios de estoque de frete e caminhões eficientes em Python

Em posts anteriores, demonstrei como você pode consultar dados de preços de ações com, por exemplo, pandas_datareader em Python.

Neste post, apresentarei um algoritmo com o qual você pode construir um portfólio eficiente com base em qualquer conjunto de ações que você esteja considerando. O algoritmo determinará a parcela ideal de cada ação em seu portfólio, com base no nível de risco que você se sente confiante em assumir.

Mais precisamente, apresentarei um método para visualizar a fronteira eficiente de carteiras compostas por um conjunto específico de ações. Neste exemplo, trabalharei com as 8 ações a seguir, que são todas empresas de transporte rodoviário:

  • Yamato Holdings (YATRY)
  • Knight-Swift Transportation Holdings (KNX)
  • MELHOR (MELHOR)
  • YRC Mundial (YRCW)
  • Schneider National (SNDR)
  • Old Dominion Freight Line (ODFL)
  • Melhor Arco (ARCB)
  • Empresas Werner (WERN)

O risco é medido como desvio padrão dos retornos históricos. O retorno é medido como o retorno médio histórico diário das ações (usando os preços de fechamento).

Começo importando alguns módulos relevantes em Python:

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

Quero coletar dados históricos de preços de ações dos 6 meses anteriores. Abaixo, especifico as datas de início e término do período relevante para coletar dados de:

start_date = datetime.datetime(2020,4,1)
end_date = datetime.datetime(2020,9,30)

Em seguida, defino uma função auxiliar que receberá um quadro de dados de preço de ações coletado do Yahoo Finance via pandas_datareader e o traduzirá em retornos diários:

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)

Agora defino outra função auxiliar que receberá uma lista de ticks de ações e calculará seu retorno médio diário e seu desvio padrão de retornos diários para algum período específico. Esta função usa pandas_datareader para consultar dados de preços de ações do Yahoo:

def analyzeStocks(tickersArr,start_date,end_date):
    index = ["ticker","return","stdev"]
    muArr = []
    sigmaArr = []
    for i in range(0,len(tickersArr)):
        tick = tickersArr[i]
        data = web.DataReader(tickersArr[i],"yahoo",start_date,end_date)
        muArr.append(stat.mean(returns(data)))
        sigmaArr.append(stat.stdev(returns(data)))
    return(pd.DataFrame(np.array([tickersArr, muArr, sigmaArr]),index=index,columns=tickersArr))

Neste post quero analisar os ticks abaixo:

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

Usando os tickers acima, executo analyzeStocks para extrair dados de preços de ações e calcular o avg. retorno diário e desvio padrão dos retornos diários:

base_df = analyzeStocks(tickersArr,start_date,end_date)
base_df
YATRYKNXMELHORYRCWSNDRODFLARCBWERN
relógioYATRYKNXMELHORYRCWSNDRODFLARCBWERN
Retorna0,0046537435231962980,0023175179239564793-0,00341243394859026650,0111591997557838490,0024620517170550630,0033492593161784590,0058616868290849180,0017903742321965712
stdev0,023584636993742740,021140916591625140,0313978411557502770,094552762399063540,0193725719356334160,0233054617384102940,0372340691779706750,02237976138155402

Usando matplotlib, faço um gráfico de dispersão simples do retorno histórico versus desempenho de volatilidade das ações individuais:

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

Agora, defino uma função de construção de portfólio. A função criará um número definido de carteiras com pesos atribuídos aleatoriamente por ação. O retorno esperado e o desvio padrão dos retornos diários resultantes disso são retornados na forma de um data frame Pandas:

def portfolioBuilder(n,tickersArr,start_date,end_date):
    muArr = []
    sigmaArr = []
    dailyreturnsArr = []
    weightedreturnsArr = []
    portfoliodailyreturnsArr = []
    for i in range(0,len(tickersArr)):
        data = web.DataReader(tickersArr[i],"yahoo",start_date,end_date)
        dailyreturnsArr.append(returns(data))
    for i in range(0,n):
        portfoliodailyreturnsArr = []
        weightsArr = [rnd.uniform(0,1) for i in range(0,len(tickersArr))]
        nweightsArr = [i/sum(weightsArr) for i in weightsArr]
        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)
        muArr.append(stat.mean(portfoliodailyreturnsArr))
        sigmaArr.append(stat.stdev(portfoliodailyreturnsArr))
    return([sigmaArr,muArr])

Eu aplico a função de construção de portfólio aos tickers e ploto o resultado usando para 500.000 portfólios aleatórios usando 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')

Este gráfico permite validar se sua escolha atual de pesos para suas ações de escolha são atualmente eficientes ou não. Portfólios eficientes seriam localizados ao longo da linha superior do gráfico de dispersão.

Leave a Reply

Deixe um comentário

O seu endereço de e-mail não será publicado.

Esse site utiliza o Akismet para reduzir spam. Aprenda como seus dados de comentários são processados.

Close

Meta