Source code for xquantipy.stocks.ticker

import yfinance as yf
import pandas as pd
import xquantipy.constants as constants
import copy
import plotly.graph_objects as go
import numpy as np
import statsmodels.api as sm
import requests
import json


[docs] class Ticker(object): """ A class to represent a stock object ... Attributes: stock : str stock ticker name period : str period selected for the data default: "10Y" data : Dataframe timeseries daily data of the stock fundamentals : dict -> DISCONTINUED DUE TO YAHOO FINANCE fundamental data of the stock Methods: get_adj_close(self) returns a adj close dataframe for the ticker show_adj_close(self) plot adj close for the ticker get_beta(self) gets the beta value of the ticker object get_alpha(Self, index = constants.BENCHMARK_INDEX, risk_free_rate=constants.RISK_FREE_RATE) gets the alpha value of the ticker object show_moving_average(self, period = [constants.MOVING_AVERAGE_PERIOD]) get the moving average of the particular stock analysis objects show_moving_average_convergence_divergence(self, fastperiod=12, slowperiod=26, signalperiod=9) plot the moving average convergence divergence (MACD) of the particular stock analysis objects show_parabolic_sar(self, af=0.02, max_af=0.2) plot the Parabolic SAR of the particular stock analysis objects show_bollinger_bands(self, period=constants.MOVING_AVERAGE_PERIOD) plot the bollinger band of the particular stock analysis objects """ def __init__(self, ticker, period = constants.PERIOD): assert type(ticker) == str, "Error: ticker argument must be a string" assert type(period) == str, "Error: period argument must be a string" self.stock = ticker self.period = period data = yf.download(ticker, period=period) data.reset_index(inplace=True) data['daily_return'] = (data['Adj Close'] - data['Adj Close'].shift(1)) / data['Adj Close'].shift(1) data['cum_return'] = (1+data['daily_return']).cumprod()-1 self.data = data url = constants.BASE_OPTIONS_URL + str(self.stock) response = requests.get(url=url, headers=constants.HEADERS) if response.status_code == 200: data = json.loads(response.content) self.fundamentals = data['optionChain']['result'][0]['quote'] else: print('Error fetching fundamental data:', response.status_code) self.fundamentals = {}
[docs] def get_adj_close(self): """ Summary: A method to get only the adj close column which is renamed to the self.stock name Return: adj_close : dataframe return value which represents the dataframe with adj close column """ adj_close = copy.deepcopy(self.data[['Date', 'Adj Close']]) adj_close.rename(columns={'Adj Close': str(self.stock)}, inplace=True) return adj_close
[docs] def show_adj_close(self): """ Summary: A method to plot adj close column which is renamed to the self.stock name Return: fig : module return value which represents the matplotlib figure with adj close column """ df = self.get_adj_close() fig = go.Figure() fig.add_trace(go.Scatter(x=df['Date'], y=df[self.stock], mode='lines', name='Closing Price')) fig.update_layout( title=f'{self.stock} Stock Price', xaxis_title='Date', yaxis_title=('Adj Close ' + str(self.stock)), showlegend=True, template='plotly_dark', ) return fig
[docs] def get_beta(self, index = constants.BENCHMARK_INDEX): """ Summary: A method to calculate the beta value of the stock this value measures the expected move in a stock relative to movements in the overall market Return: beta : float return value which represents the beta of the stock """ index_data = yf.download(index, period=self.period) index_data.reset_index(inplace=True) stock_returns = self.data['Adj Close'].pct_change().dropna() market_returns = index_data['Adj Close'].pct_change().dropna() data = pd.DataFrame({'Stock': stock_returns, 'Market': market_returns}).dropna() X = sm.add_constant(data['Market']) # Add a constant for the intercept Y = data['Stock'] model = sm.OLS(Y, X).fit() beta = model.params['Market'] return round(beta, 2)
[docs] def get_alpha(self, index = constants.BENCHMARK_INDEX, risk_free_rate=constants.RISK_FREE_RATE): """ Summary: A method to calculate the alpha value of the stock which is a measure to find how a stock is beating a benchmark Parameters: index : str a string for the bench mark index default: ^GSPC risk_free_rate : float value of the risk free return value default: 0.05 i.e. 5% Return: alpha : float return value which represents the alpha of the stock """ assert type(index) == str, "Error: index argument must be string" assert type(risk_free_rate) == float, "Error: risk_free_rate argument must be float" index_data = yf.download(index, period=self.period) index_data['daily_return'] = (index_data['Adj Close'] - index_data['Adj Close'].shift(1)) / index_data['Adj Close'].shift(1) index_data['cum_return'] = (1+index_data['daily_return']).cumprod()-1 index_start_value = index_data['Adj Close'].iloc[0] index_end_value = index_data['Adj Close'].iloc[-1] stock_start_value = self.data['Adj Close'].iloc[0] stock_end_value = self.data['Adj Close'].iloc[-1] # Need to change to Actual return instead of simple return simple_return_index = (index_end_value - index_start_value)/index_start_value simple_return_stock = (stock_end_value - stock_start_value)/stock_start_value alpha = simple_return_stock - (risk_free_rate + self.get_beta()*(simple_return_index - risk_free_rate)) return float(alpha)
[docs] def get_moving_average(self, type='simple', period = [constants.MOVING_AVERAGE_PERIOD]): """ Summary: A method to get the moving average of the particular stock analysis objects Parameters: type : str can be simple or exponential moving average period : list a list of period to which moving average is calculated Returns: df : Dataframe a Dataframe of the stock with moving average """ df = self.get_adj_close() if type == 'simple': for i in period: df[str('MA_' + str(i))] = df[self.stock].rolling(window=i).mean() elif type == 'exponential': for i in period: df[str('EMA_' + str(i))] = df[self.stock].ewm(span=i, adjust=False).mean() else: raise Exception("type should be simple or exponential") return df
[docs] def show_moving_average(self, type='simple', period = [constants.MOVING_AVERAGE_PERIOD]): """ Summary: A method to plot the moving comparison of the particular stock analysis objects Parameters: type : str can be simple or exponential moving average period : list a list of period to which moving average is calculated Return: fig : matplotlib a figure object represents moving average """ df = self.get_moving_average(type, period) columns = list(df.columns) columns.pop(0) columns.pop(0) fig = go.Figure() fig.add_trace(go.Scatter(x=df['Date'], y=df[self.stock], mode='lines', name='Closing Price')) for i in columns: fig.add_trace(go.Scatter(x=df['Date'], y=df[i], mode='lines', name=f'{i}')) fig.update_layout( title=f'{self.stock} Stock Price with {period}-Day {type} Moving Average', xaxis_title='Date', yaxis_title='Price', showlegend=True, template='plotly_dark',) return fig
[docs] def show_moving_average_convergence_divergence(self, fastperiod=12, slowperiod=26, signalperiod=9): """ Summary: A method to plot the moving average convergence divergence (MACD) of the particular stock analysis objects Parameters: fastperiod : int fast period for the calculation slowperiod : int slow period for the calculation signalperiod : int signal period for the calculation Return: fig : matplotlib a figure object represents moving average convergence divergence """ df = self.get_adj_close() EMAfast = df[self.stock].ewm(span=fastperiod, min_periods=fastperiod).mean() EMAslow = df[self.stock].ewm(span=slowperiod, min_periods=slowperiod).mean() MACD = EMAfast - EMAslow signal = MACD.ewm(span=signalperiod, min_periods=signalperiod).mean() MACDhist = MACD - signal fig = go.Figure() fig.add_trace(go.Scatter(x=df['Date'],y=MACD,mode='lines', name='MACD')) fig.add_trace(go.Scatter(x=df['Date'],y=signal,mode='lines', name='Signal')) fig.add_trace(go.Bar(x=df['Date'],y=signal, name='MACD Histogram')) fig.update_layout( title="MACD", xaxis_title="Date", yaxis_title="MACD", template='plotly_dark', ) return fig
[docs] def show_parabolic_sar(self, af=0.02, max_af=0.2): """ Summary: A method to plot the Parabolic SAR of the particular stock analysis objects Parameters: af : int acceleration factor for the calculation max_af : int max acceleration factor for the calculation Return: fig : matplotlib a figure object represents parabolic SAR """ psar_values = [] initial_psar = self.data.High[0] trend = 'up' af = 0.02 new_psar = initial_psar extreme_point = self.data.High[0] for i in range(1, len(self.data)): current_close = self.data.Close[i] current_high = self.data.High[i] current_low = self.data.Low[i] if trend == 'up': if current_high > extreme_point: extreme_point = current_high af = min(af + 0.02, max_af) else: new_psar = initial_psar + af * (extreme_point - initial_psar) new_psar = max(new_psar, min(current_high, current_low)) if current_low < new_psar: trend = 'down' initial_psar = current_low af = 0.02 extreme_point = current_low else: initial_psar = new_psar else: if current_low < extreme_point: extreme_point = current_low af = min(af + 0.02, max_af) else: new_psar = initial_psar - af * (initial_psar - extreme_point) new_psar = max(new_psar, min(current_high, current_low)) if current_high > new_psar: trend = 'up' initial_psar = current_high af = 0.02 extreme_point = current_high else: initial_psar = new_psar psar_values.append(new_psar) fig = go.Figure() fig.add_trace(go.Scatter(x=self.data.Date, y=psar_values, name="PSAR")) fig.add_trace(go.Scatter(x=self.data.Date, y=self.data.Close, name="Close Price")) fig.update_layout( title="PSAR and Close Price", xaxis_title="Date", yaxis_title="Price", template='plotly_dark', ) return fig
[docs] def show_bollinger_bands(self, period=constants.MOVING_AVERAGE_PERIOD): """ Summary: A method to plot the bollinger band of the particular stock analysis objects Parameters: period : int period for the calculation Return: fig : matplotlib a figure object represents bollinger band """ moving_average = self.data['Close'].rolling(window=period).mean() standard_deviation = self.data['Close'].rolling(window=period).std() upper_band = moving_average + 2 * standard_deviation lower_band = moving_average - 2 * standard_deviation fig = go.Figure() fig.add_trace(go.Scatter(x=self.data['Date'], y=self.data['Close'], name='Close Price')) fig.add_trace(go.Scatter(x=self.data['Date'], y=upper_band, name='Upper Bollinger Band')) fig.add_trace(go.Scatter(x=self.data['Date'], y=lower_band, name='Lower Bollinger Band')) fig.update_layout( title='Bollinger Bands of a Stock', xaxis_title='Date', yaxis_title='Price', template='plotly_dark', ) return fig
def __str__(self): start_date = str(self.data['Date'].iloc[0])[:10] end_date = str(self.data['Date'].iloc[-1])[:10] return str(self.stock).upper() + " [" + start_date + " - " + end_date + "]"