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
YATRY | KNX | MELHOR | YRCW | SNDR | ODFL | ARCB | WERN | |
---|---|---|---|---|---|---|---|---|
relógio | YATRY | KNX | MELHOR | YRCW | SNDR | ODFL | ARCB | WERN |
Retorna | 0,004653743523196298 | 0,0023175179239564793 | -0,0034124339485902665 | 0,011159199755783849 | 0,002462051717055063 | 0,003349259316178459 | 0,005861686829084918 | 0,0017903742321965712 |
stdev | 0,02358463699374274 | 0,02114091659162514 | 0,031397841155750277 | 0,09455276239906354 | 0,019372571935633416 | 0,023305461738410294 | 0,037234069177970675 | 0,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.
Cientista de dados com foco em simulação, otimização e modelagem em R, SQL, VBA e Python
Leave a Reply