"""
.. module:: quote
:synopsis: individual ticker.
.. moduleauthor:: Tianning Li <ltianningli@gmail.com>
"""
from datetime import datetime, date
import json
import pandas as pd
import requests
from finvizfinance.util import (
web_scrap,
image_scrap,
number_covert,
headers,
format_datetime,
)
QUOTE_URL = "https://finviz.com/quote.ashx?t={ticker}"
NUM_COL = [
"P/E",
"EPS (ttm)",
"Insider Own",
"Shs Outstand",
"Market Cap",
"Forward P/E",
"EPS nest Y",
"Insider ",
]
[docs]
class Quote:
"""quote
Getting current price of the ticker
"""
[docs]
def get_current(self, ticker):
"""Getting current price of the ticker.
Returns:
price(float): price of the ticker
"""
soup = web_scrap("https://finviz.com/request_quote.ashx?t={}".format(ticker))
return soup.text
[docs]
class finvizfinance:
"""finvizfinance
Getting information from the individual ticker.
Args:
ticker(str): ticker string
verbose(int): choice of visual the progress. 1 for visualize progress.
"""
def __init__(
self,
ticker,
verbose=0,
):
"""initiate module"""
self.ticker = ticker
self.flag = False
self.quote_url = QUOTE_URL.format(ticker=ticker)
self.soup = web_scrap(self.quote_url)
if self._checkexist(verbose):
self.flag = True
self.info = {}
def _checkexist(self, verbose):
try:
if "not found" in self.soup.find("td", class_="body-text").text:
print("Ticker not found.")
return False
except:
if verbose == 1:
print("Ticker exists.")
return True
[docs]
def ticker_charts(
self, timeframe="daily", charttype="advanced", out_dir="", urlonly=False
):
"""Download ticker charts.
Args:
timeframe(str): choice of timeframe (daily, weekly, monthly).
charttype(str): choice of type of chart (candle, line, advanced).
out_dir(str): output image directory. default none.
urlonly (bool): choice of downloading charts, default: downloading chart
Returns:
charturl(str): url for the chart
"""
if timeframe not in ["daily", "weekly", "monthly"]:
raise ValueError("Invalid timeframe '{}'".format(timeframe))
if charttype not in ["candle", "line", "advanced"]:
raise ValueError("Invalid chart type '{}'".format(charttype))
url_type = "c"
url_ta = "0"
if charttype == "line":
url_type = "l"
elif (
charttype == "advanced" and timeframe != "weekly" and timeframe != "monthly"
):
url_ta = "1"
url_timeframe = "d"
if timeframe == "weekly":
url_timeframe = "w"
elif timeframe == "monthly":
url_timeframe = "m"
chart_url = "https://finviz.com/chart.ashx?t={ticker}&ty={type}&ta={ta}&p={timeframe}".format(
ticker=self.ticker, type=url_type, ta=url_ta, timeframe=url_timeframe
)
if not urlonly:
image_scrap(chart_url, self.ticker, out_dir)
return chart_url
[docs]
def ticker_fundament(self, raw=True, output_format="dict"):
"""Get ticker fundament.
Args:
raw(boolean): if True, the data is raw.
output_format(str): choice of output format (dict, series).
Returns:
fundament(dict): ticker fundament.
"""
if output_format not in ["dict", "series"]:
raise ValueError(
"Invalid output format '{}'. Possible choice: {}".format(
output_format, ["dict", "series"]
)
)
fundament_info = {}
fundament_info["Company"] = self.soup.find(
"h2", class_="quote-header_ticker-wrapper_company"
).text.strip()
quote_links = self.soup.find("div", class_="quote-links")
links = quote_links.find_all("a")
fundament_info["Sector"] = links[0].text
fundament_info["Industry"] = links[1].text
fundament_info["Country"] = links[2].text
fundament_info["Exchange"] = links[3].text
fundament_table = self.soup.find("table", class_="snapshot-table2")
rows = fundament_table.find_all("tr")
for row in rows:
cols = row.find_all("td")
cols = [i.text for i in cols]
fundament_info = self._parse_column(cols, raw, fundament_info)
self.info["fundament"] = fundament_info
if output_format == "dict":
return fundament_info
return pd.DataFrame.from_dict(fundament_info, orient="index", columns=["Stat"])
def _parse_column(self, cols, raw, fundament_info):
header = ""
for i, value in enumerate(cols):
if i % 2 == 0:
header = value
else:
if header == "Volatility":
fundament_info = self._parse_volatility(
header, fundament_info, value, raw
)
elif header == "52W Range":
fundament_info = self._parse_52w_range(
header, fundament_info, value, raw
)
elif header == "Optionable" or header == "Shortable":
if raw:
fundament_info[header] = value
elif value == "Yes":
fundament_info[header] = True
else:
fundament_info[header] = False
else:
# Handle EPS Next Y keys with two different values
if header == "EPS next Y" and header in fundament_info.keys():
header += " Percentage"
if raw:
fundament_info[header] = value
else:
try:
fundament_info[header] = number_covert(value)
except ValueError:
fundament_info[header] = value
return fundament_info
def _parse_52w_range(self, header, fundament_info, value, raw):
info_header = ["52W Range From", "52W Range To"]
info_value = [0, 2]
self._parse_value(header, fundament_info, value, raw, info_header, info_value)
return fundament_info
def _parse_volatility(self, header, fundament_info, value, raw):
info_header = ["Volatility W", "Volatility M"]
info_value = [0, 1]
self._parse_value(header, fundament_info, value, raw, info_header, info_value)
return fundament_info
def _parse_value(self, header, fundament_info, value, raw, info_header, info_value):
try:
value = value.split()
if raw:
for i, value_index in enumerate(info_value):
fundament_info[info_header[i]] = value[value_index]
else:
for i, value_index in enumerate(info_value):
fundament_info[info_header[i]] = number_covert(value[value_index])
except:
fundament_info[header] = value
return fundament_info
[docs]
def ticker_description(self):
"""Get ticker description.
Returns:
description(str): ticker description.
"""
return self.soup.find("td", class_="fullview-profile").text
[docs]
def ticker_outer_ratings(self):
"""Get outer ratings table.
Returns:
df(pandas.DataFrame): outer ratings table
"""
fullview_ratings_outer = self.soup.find("table", class_="js-table-ratings")
frame = []
try:
rows = fullview_ratings_outer.find_all(
"td", class_="fullview-ratings-inner"
)
if len(rows) == 0:
rows = fullview_ratings_outer.find_all("tr")[1:]
for row in rows:
each_row = row.find("tr")
if not each_row:
each_row = row
cols = each_row.find_all("td")
rating_date = cols[0].text
if rating_date.lower().startswith("today"):
rating_date = date.today()
else:
rating_date = datetime.strptime(rating_date, "%b-%d-%y")
status = cols[1].text
outer = cols[2].text
rating = cols[3].text
price = cols[4].text
info_dict = {
"Date": rating_date,
"Status": status,
"Outer": outer,
"Rating": rating,
"Price": price,
}
frame.append(info_dict)
df = pd.DataFrame(frame)
self.info["ratings_outer"] = df
return df
except AttributeError:
return None
[docs]
def ticker_news(self):
"""Get news information table.
Returns:
df(pandas.DataFrame): news information table
"""
fullview_news_outer = self.soup.find("table", class_="fullview-news-outer")
rows = fullview_news_outer.find_all("tr")
frame = []
last_date = ""
for row in rows:
try:
cols = row.find_all("td")
news_date = cols[0].text
title = cols[1].a.text
link = cols[1].a["href"]
news_time = news_date.split()
if len(news_time) == 2:
last_date = news_time[0]
news_time = " ".join(news_time)
else:
news_time = last_date + " " + news_time[0]
news_time = format_datetime(news_time)
info_dict = {"Date": news_time, "Title": title, "Link": link}
frame.append(info_dict)
except AttributeError:
pass
df = pd.DataFrame(frame)
self.info["news"] = df
return df
[docs]
def ticker_inside_trader(self):
"""Get insider information table.
Returns:
df(pandas.DataFrame): insider information table
"""
inside_trader = self.soup.find("table", class_="body-table")
rows = inside_trader.find_all("tr")
table_header = [i.text for i in rows[0].find_all("th")]
table_header += ["SEC Form 4 Link", "Insider_id"]
frame = []
rows = rows[1:]
num_col = ["Cost", "#Shares", "Value ($)", "#Shares Total"]
num_col_index = [table_header.index(i) for i in table_header if i in num_col]
for row in rows:
cols = row.find_all("td")
info_dict = {}
for i, col in enumerate(cols):
if i not in num_col_index:
info_dict[table_header[i]] = col.text
else:
info_dict[table_header[i]] = number_covert(col.text)
info_dict["SEC Form 4 Link"] = cols[-1].find("a").attrs["href"]
info_dict["Insider_id"] = cols[0].a["href"].split("oc=")[1].split("&tc=")[0]
frame.append(info_dict)
df = pd.DataFrame(frame)
self.info["inside trader"] = df
return df
[docs]
def ticker_signal(self):
"""Get all the trading signals from finviz.
Returns:
ticker_signals(list): get all the ticker signals as list.
"""
from finvizfinance.screener.ticker import Ticker
fticker = Ticker()
signals = [
"Top Gainers",
"Top Losers",
"New High",
"New Low",
"Most Volatile",
"Most Active",
"Unusual Volume",
"Overbought",
"Oversold",
"Downgrades",
"Upgrades",
"Earnings Before",
"Earnings After",
"Recent Insider Buying",
"Recent Insider Selling",
"Major News",
"Horizontal S/R",
"TL Resistance",
"TL Support",
"Wedge Up",
"Wedge Down",
"Triangle Ascending",
"Triangle Descending",
"Wedge",
"Channel Up",
"Channel Down",
"Channel",
"Double Top",
"Double Bottom",
"Multiple Top",
"Multiple Bottom",
"Head & Shoulders",
"Head & Shoulders Inverse",
]
ticker_signal = []
for signal in signals:
try:
fticker.set_filter(signal=signal, ticker=self.ticker.upper())
if fticker.screener_view(verbose=0) == [self.ticker.upper()]:
ticker_signal.append(signal)
except:
pass
return ticker_signal
[docs]
def ticker_full_info(self):
"""Get all the ticker information.
Returns:
df(pandas.DataFrame): insider information table
"""
self.ticker_fundament()
self.ticker_outer_ratings()
self.ticker_news()
self.ticker_inside_trader()
return self.info
[docs]
class Statements:
"""
Getting statements of ticker
"""
[docs]
def get_statements(self, ticker, statement="I", timeframe="A"):
"""Getting statements of ticker.
Args:
ticker(str): ticker string
statement(str): I(Income Statement), B(Balace Sheet), C(Cash Flow)
timeframe(str): A(Annual), Q(Quarter)
Returns:
df(pandas.DataFrame): statements table
"""
url = "https://finviz.com/api/statement.ashx?t={ticker}&s={statement}{timeframe}".format(
ticker=ticker, statement=statement, timeframe=timeframe
)
try:
website = requests.get(url, headers=headers)
website.raise_for_status()
response = json.loads(website.content)
df = pd.DataFrame.from_dict(response["data"], orient="index")
return df
except requests.exceptions.HTTPError as err:
raise Exception(err)