From d4d9e6ea5e9e9da1c4e13463eec94a13583e62e0 Mon Sep 17 00:00:00 2001 From: Lucas Leme Date: Tue, 20 Oct 2020 00:40:17 -0300 Subject: [PATCH] Fix conflicts and autopep9 format --- turingquant/metrics.py | 158 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 148 insertions(+), 10 deletions(-) diff --git a/turingquant/metrics.py b/turingquant/metrics.py index 60be00e..df89d6a 100644 --- a/turingquant/metrics.py +++ b/turingquant/metrics.py @@ -72,10 +72,12 @@ def drawdown(return_series, plot=True): wealth_index = 1000 * (1 + return_series).cumprod() previous_peaks = wealth_index.cummax() - drawdowns = pd.Series((wealth_index - previous_peaks)/previous_peaks, name='Drawdown') + drawdowns = pd.Series((wealth_index - previous_peaks) / + previous_peaks, name='Drawdown') if plot: - fig = px.line(drawdowns, x=drawdowns.index, y='Drawdown', title='Drawdown') + fig = px.line(drawdowns, x=drawdowns.index, + y='Drawdown', title='Drawdown') fig.update_xaxes(title_text='Tempo') fig.update_yaxes(title_text='Drawdown (%)') fig.show() @@ -83,7 +85,7 @@ def drawdown(return_series, plot=True): return drawdowns -def rolling_beta(returns, benchmark, window, plot=True): +def rolling_beta(returns, benchmark, window=60, plot=True): """ Plota o beta móvel para um ativo e um benchmark de referência, na forma de séries de retornos. @@ -99,7 +101,7 @@ def rolling_beta(returns, benchmark, window, plot=True): """ rolling_beta = pd.Series([beta(returns[i-window:i], benchmark[i-window:i]) - for i in range(window, len(returns))], index=returns[window:].index) + for i in range(window, len(returns))], index=returns[window:].index) if plot: fig = px.line(rolling_beta, title="Beta móvel") overall_beta = beta(returns, benchmark) @@ -170,12 +172,13 @@ def rolling_sharpe(returns, window, risk_free=0, plot=True): ]) fig.update_layout(showlegend=False) fig.update_xaxes(title_text='Tempo') - fig.update_yaxes(title_text='Sharpe móvel: ' + str(window) + ' períodos') + fig.update_yaxes(title_text='Sharpe móvel: ' + + str(window) + ' períodos') fig.show() return rolling_sharpe -def ewma_volatility(close_prices,return_type,window,plot=True): +def ewma_volatility(close_prices, return_type, window, plot=True): """ Essa função possibilita a visualização da volatilidade a partir do cálculo da EWMA e da plotagem do gráfico dessa métrica ao longo de um período. @@ -198,15 +201,149 @@ def ewma_volatility(close_prices,return_type,window,plot=True): ewma_volatility = returns.ewm(span=window).std() ewma_volatility = pd.Series.to_frame(ewma_volatility) if plot: - fig = px.line(ewma_volatility,x=ewma_volatility.index,y='Close',title='EWMA') + fig = px.line(ewma_volatility, x=ewma_volatility.index, + y='Close', title='EWMA') fig.update_xaxes(title_text='Tempo') fig.update_yaxes(title_text='EWMA') - fig.show() + fig.show() return ewma_volatility if plot == False: return ewma_volatility -def rolling_std(close_prices,return_type,window,plot=True): + +def garman_klass_volatility(high_prices, low_prices, close_prices, open_prices, window=60, time_scale=1, plot=True): + """ + Estima a volatilidade a partir dos seguintes preços: alta, baixa, abertura e fechamento + + Parâmetros: + high_prices (pd.DataFrame): série de preços de alta de uma ação + low_prices (pd.DataFrame): série de preços de baixa de uma ação + close_prices (pd.DataFrame): série de preços de fechamento de uma ação + open_prices (pd.DataFrame): série de preços de abertura de uma ação + window (int): janela das estimativa de volatilidade + time_scale (int): fator de escala da volatilidade, por padrão é 1 (diária) + plot (bool): se 'True', plota um gráfico da volatilidade móvel + + Retorna: + garman_klass_vol (pd.Series): série das estimativas de volatildade + """ + # Calculando parcelas internas da somatoria + high_low_ratio = (1 / 2) * \ + (np.log(np.divide(high_prices, low_prices))) ** 2 + + close_open_ratio = -(2 * np.log(2) - 1) * ( + np.log(np.divide(close_prices, open_prices)) ** 2 + ) + + # Somando parcelas calculadas + log_ratio = high_low_ratio + close_open_ratio.values + + # Criando dataframe para atribuir as volatilidades + garman_klass_vol = pd.Series(log_ratio, name='Garman Klass', copy=True) + + # Termo constante fora da somatoria (Considerando vol diaria) + Period_const = time_scale / window + + # Atribuindo not a number, para os valores iniciais + garman_klass_vol.iloc[:window] = np.nan + + # iteração do centro de massa da vol + for date in range(window, len(high_prices)): + garman_klass_vol.iloc[date] = np.sqrt( + Period_const * np.sum(log_ratio.iloc[date - window: date]) + ) + + if plot: + fig = px.line(garman_klass_vol, title='Garman Klass') + fig.update_xaxes(title_text='Tempo') + fig.update_yaxes(title_text='Volatilidade') + + mean_garman_klass = garman_klass_vol.mean() + fig.update_layout(shapes=[ + dict( + type='line', + xref='paper', x0=0, x1=1, + yref='y', y0=mean_garman_klass, y1=mean_garman_klass, + line=dict( + color='grey', + width=2, + dash='dash' + ) + ) + ], annotations=[ + dict( + text='Volatilidade média: %.3f' % mean_garman_klass, + xref='paper', x=0.95, + yref='y', y=1.1 * mean_garman_klass, + xanchor='left' + ) + ]) + + fig.show() + + return garman_klass_vol + + +def parkinson_volatility(high_prices, low_prices, window=60, time_scale=1, plot=True): + """ + Estimando a volatilidade a partir dos preços de Alta e de Baixa + + Parâmetros: + high (pd.DataFrame): série de preços de alta de uma ação + low (pd.DataFrame): série de preços de baixa de uma ação + window (int): janela das estimativa de volatilidade + time_scale (int): fator de escala da volatilidade, por padrão é 1 (diária) + plot (bool): se 'True', plota um gráfico da volatilidade móvel + + Retorna: + garman_klass_vol (pd.Series): série das estimativas de volatildade + + """ + + log_ratio = np.log(np.divide(high_prices, low_prices)) ** 2 + + parkinson_vol = pd.Series(log_ratio, name='Parkinson', copy=True) + + Period_const = time_scale / (4 * window * np.log(2)) + + parkinson_vol.iloc[:window] = np.nan + + for date in range(window, len(high_prices)): + parkinson_vol.iloc[date] = np.sqrt( + Period_const * np.sum(log_ratio.iloc[date - window: date]) + ) + + if plot: + fig = px.line(parkinson_vol, title='Número de Parkinson') + fig.update_xaxes(title_text='Tempo') + fig.update_yaxes(title_text='Volatilidade') + + mean_parkinson = parkinson_vol.mean() + fig.update_layout(shapes=[ + dict( + type='line', + xref='paper', x0=0, x1=1, + yref='y', y0=mean_parkinson, y1=mean_parkinson, + line=dict( + color='grey', + width=2, + dash='dash' + ) + ) + ], annotations=[ + dict( + text='Volatilidade média: %.3f' % mean_parkinson, + xref='paper', x=0.95, + yref='y', y=1.1 * mean_parkinson, + xanchor='left' + ) + ]) + fig.show() + + return parkinson_vol + + +def rolling_std(close_prices, return_type, window, plot=True): """ Essa função possibilita a visualização da volatilidade a partir do cálculo da desvio padrão móvel e da plotagem do gráfico dessa métrica ao longo de um período. @@ -229,7 +366,8 @@ def rolling_std(close_prices,return_type,window,plot=True): rolling_std = returns.rolling(window).std() rolling_std = pd.Series.to_frame(rolling_std) if plot: - fig = px.line(rolling_std, x=rolling_std.index, y='Close',title='Desvio Padrão Móvel') + fig = px.line(rolling_std, x=rolling_std.index, + y='Close', title='Desvio Padrão Móvel') fig.update_xaxes(title_text='Tempo') fig.update_yaxes(title_text='Desvio padrão móvel') fig.show()