def get_country(ticker): country = "NA" data = yf.utils.get_json(f"https://finance.yahoo.com/quote/{ticker}") if "summaryProfile" in data: country = data["summaryProfile"]["country"] if country not in financedatabase_model.get_countries(): similar_cmd = difflib.get_close_matches( country, financedatabase_model.get_countries(), n=1, cutoff=0.7, ) if similar_cmd: country = similar_cmd[0] return country
def call_country(self, other_args: List[str]): """Process country command""" parser = argparse.ArgumentParser( add_help=False, formatter_class=argparse.ArgumentDefaultsHelpFormatter, prog="country", description= "See existing countries, or set country if arg specified", ) parser.add_argument( "-n", "--name", type=str, dest="name", nargs="+", help="country to select", ) if other_args and "-" not in other_args[0][0]: other_args.insert(0, "-n") ns_parser = parse_known_args_and_warn(parser, other_args) if ns_parser: possible_countries = financedatabase_model.get_countries( industry=self.industry, sector=self.sector) if ns_parser.name: if " ".join(ns_parser.name) in possible_countries: self.country = " ".join(ns_parser.name) self.update_runtime_choices() else: console.print( f"Country '{' '.join(ns_parser.name)}' does not exist." ) similar_cmd = difflib.get_close_matches( " ".join(ns_parser.name), possible_countries, n=1, cutoff=0.75, ) if similar_cmd: console.print(f"Replacing by '{similar_cmd[0]}'") self.country = similar_cmd[0] self.update_runtime_choices() else: similar_cmd = difflib.get_close_matches( " ".join(ns_parser.name), possible_countries, n=1, cutoff=0.5, ) if similar_cmd: console.print(f"Did you mean '{similar_cmd[0]}'?") else: for country in possible_countries: console.print(country) self.stocks_data = {} console.print("")
def update_runtime_choices(self): """Update runtime choices""" if session and gtff.USE_PROMPT_TOOLKIT: self.choices["industry"] = { i: None for i in financedatabase_model.get_industries( country=self.country, sector=self.sector) } self.choices["sector"] = { s: None for s in financedatabase_model.get_sectors( industry=self.industry, country=self.country) } self.choices["country"] = { c: None for c in financedatabase_model.get_countries( industry=self.industry, sector=self.sector) } self.completer = NestedCompleter.from_nested_dict(self.choices)
def call_load(self, other_args: List[str]): """Process load command""" parser = argparse.ArgumentParser( add_help=False, formatter_class=argparse.ArgumentDefaultsHelpFormatter, prog="load", description= "Load stock ticker and alter the industry, sector, country and market cap " "accordingly for this ticker.", ) parser.add_argument( "-t", "--ticker", action="store", dest="ticker", required="-h" not in other_args, help="Stock ticker", ) if other_args and "-" not in other_args[0][0]: other_args.insert(0, "-t") ns_parser = parse_known_args_and_warn(parser, other_args) if ns_parser: df_stock_candidate = stocks_helper.load(ns_parser.ticker, ) if not df_stock_candidate.empty: if "." in ns_parser.ticker: self.ticker = ns_parser.ticker.upper().split(".")[0] else: self.ticker = ns_parser.ticker.upper() data = yf.utils.get_json( f"https://finance.yahoo.com/quote/{self.ticker}") if "summaryProfile" in data: self.country = data["summaryProfile"]["country"] if self.country not in financedatabase_model.get_countries( ): similar_cmd = difflib.get_close_matches( self.country, financedatabase_model.get_countries(), n=1, cutoff=0.7, ) if similar_cmd: self.country = similar_cmd[0] self.sector = data["summaryProfile"]["sector"] if self.sector not in financedatabase_model.get_sectors(): similar_cmd = difflib.get_close_matches( self.sector, financedatabase_model.get_sectors(), n=1, cutoff=0.7, ) if similar_cmd: self.sector = similar_cmd[0] self.industry = data["summaryProfile"]["industry"] if self.industry not in financedatabase_model.get_industries( ): similar_cmd = difflib.get_close_matches( self.industry, financedatabase_model.get_industries(), n=1, cutoff=0.7, ) if similar_cmd: self.industry = similar_cmd[0] if "price" in data: mktcap = data["price"]["marketCap"] if mktcap < 2_000_000_000: self.mktcap = "Small" elif mktcap > 10_000_000_000: self.mktcap = "Large" else: self.mktcap = "Mid" self.stocks_data = {} self.update_runtime_choices()
def __init__( self, ticker: str, queue: List[str] = None, ): """Constructor""" super().__init__(queue) self.country = "United States" self.sector = "Financial Services" self.industry = "Financial Data & Stock Exchanges" self.mktcap = "Large" self.exclude_exchanges = True self.period = "Annual" self.ticker = ticker self.stocks_data: dict = {} self.tickers: List = list() self.currency: str = "" if ticker: data = yf.utils.get_json( f"https://finance.yahoo.com/quote/{ticker}") if "summaryProfile" in data: self.country = data["summaryProfile"]["country"] if self.country not in financedatabase_model.get_countries(): similar_cmd = difflib.get_close_matches( self.country, financedatabase_model.get_countries(), n=1, cutoff=0.7, ) if similar_cmd: self.country = similar_cmd[0] self.sector = data["summaryProfile"]["sector"] if self.sector not in financedatabase_model.get_sectors(): similar_cmd = difflib.get_close_matches( self.sector, financedatabase_model.get_sectors(), n=1, cutoff=0.7, ) if similar_cmd: self.sector = similar_cmd[0] self.industry = data["summaryProfile"]["industry"] if self.industry not in financedatabase_model.get_industries(): similar_cmd = difflib.get_close_matches( self.industry, financedatabase_model.get_industries(), n=1, cutoff=0.7, ) if similar_cmd: self.industry = similar_cmd[0] if "price" in data: mktcap = data["price"]["marketCap"] if mktcap < 2_000_000_000: self.mktcap = "Small" elif mktcap > 10_000_000_000: self.mktcap = "Large" else: self.mktcap = "Mid" if session and gtff.USE_PROMPT_TOOLKIT: choices: dict = {c: {} for c in self.controller_choices} choices["mktcap"] = {c: None for c in self.mktcap_choices} choices["period"] = {c: None for c in self.period_choices} choices["clear"] = {c: None for c in self.clear_choices} choices["metric"] = {c: None for c in self.metric_choices} # This menu contains dynamic choices that may change during runtime self.choices = choices self.completer = NestedCompleter.from_nested_dict(choices)
def test_get_countries(industry, recorder, sector): result = financedatabase_model.get_countries(industry=industry, sector=sector) recorder.capture(result)
def call_load(self, other_args: List[str]): """Process load command""" parser = argparse.ArgumentParser( add_help=False, formatter_class=argparse.ArgumentDefaultsHelpFormatter, prog="load", description= "Load stock ticker to perform analysis on. When the data source" + " is 'yf', an Indian ticker can be loaded by using '.NS' at the end," + " e.g. 'SBIN.NS'. See available market in" + " https://help.yahoo.com/kb/exchanges-data-providers-yahoo-finance-sln2310.html.", ) parser.add_argument( "-t", "--ticker", action="store", dest="ticker", required="-h" not in other_args, help="Stock ticker", ) if other_args and "-" not in other_args[0][0]: other_args.insert(0, "-t") ns_parser = parse_known_args_and_warn(parser, other_args) if ns_parser: df_stock_candidate = stocks_helper.load(ns_parser.ticker, ) if not df_stock_candidate.empty: if "." in ns_parser.ticker: self.ticker = ns_parser.ticker.upper().split(".")[0] else: self.ticker = ns_parser.ticker.upper() data = yf.utils.get_json( f"https://finance.yahoo.com/quote/{self.ticker}") if "summaryProfile" in data: self.country = data["summaryProfile"]["country"] if self.country not in financedatabase_model.get_countries( ): similar_cmd = difflib.get_close_matches( self.country, financedatabase_model.get_countries(), n=1, cutoff=0.7, ) if similar_cmd: self.country = similar_cmd[0] self.sector = data["summaryProfile"]["sector"] if self.sector not in financedatabase_model.get_sectors(): similar_cmd = difflib.get_close_matches( self.sector, financedatabase_model.get_sectors(), n=1, cutoff=0.7, ) if similar_cmd: self.sector = similar_cmd[0] self.industry = data["summaryProfile"]["industry"] if self.industry not in financedatabase_model.get_industries( ): similar_cmd = difflib.get_close_matches( self.industry, financedatabase_model.get_industries(), n=1, cutoff=0.7, ) if similar_cmd: self.industry = similar_cmd[0] if "price" in data: mktcap = data["price"]["marketCap"] if mktcap < 2_000_000_000: self.mktcap = "Small" elif mktcap > 10_000_000_000: self.mktcap = "Large" else: self.mktcap = "Mid" self.stocks_data = {} self.update_runtime_choices()
def __init__( self, ticker: str, queue: List[str] = None, ): """Constructor""" self.sia_parser = argparse.ArgumentParser(add_help=False, prog="sia") self.sia_parser.add_argument( "cmd", choices=self.CHOICES, ) self.completer: Union[None, NestedCompleter] = None if session and gtff.USE_PROMPT_TOOLKIT: self.choices: dict = {c: {} for c in self.CHOICES} self.choices["mktcap"] = {c: None for c in self.mktcap_choices} self.choices["clear"] = {c: None for c in self.clear_choices} self.choices["metric"] = {c: None for c in self.metric_choices} if queue: self.queue = queue else: self.queue = list() self.country = "United States" self.sector = "Financial Services" self.industry = "Financial Data & Stock Exchanges" self.mktcap = "Large" self.exclude_exhanges = True self.ticker = ticker self.stocks_data: dict = {} self.tickers: List = list() if ticker: data = yf.utils.get_json( f"https://finance.yahoo.com/quote/{ticker}") if "summaryProfile" in data: self.country = data["summaryProfile"]["country"] if self.country not in financedatabase_model.get_countries(): similar_cmd = difflib.get_close_matches( self.country, financedatabase_model.get_countries(), n=1, cutoff=0.7, ) if similar_cmd: self.country = similar_cmd[0] self.sector = data["summaryProfile"]["sector"] if self.sector not in financedatabase_model.get_sectors(): similar_cmd = difflib.get_close_matches( self.sector, financedatabase_model.get_sectors(), n=1, cutoff=0.7, ) if similar_cmd: self.sector = similar_cmd[0] self.industry = data["summaryProfile"]["industry"] if self.industry not in financedatabase_model.get_industries(): similar_cmd = difflib.get_close_matches( self.industry, financedatabase_model.get_industries(), n=1, cutoff=0.7, ) if similar_cmd: self.industry = similar_cmd[0] if "price" in data: mktcap = data["price"]["marketCap"] if mktcap < 2_000_000_000: self.mktcap = "Small" elif mktcap > 10_000_000_000: self.mktcap = "Large" else: self.mktcap = "Mid"
def menu( ticker: str, queue: List[str] = None, ): """Sector and Industry Analysis Menu""" sia_controller = SectorIndustryAnalysisController(ticker, queue) an_input = "HELP_ME" while True: # There is a command in the queue if sia_controller.queue and len(sia_controller.queue) > 0: # If the command is quitting the menu we want to return in here if sia_controller.queue[0] in ("q", "..", "quit"): print("") if len(sia_controller.queue) > 1: return sia_controller.queue[1:] return [] # Consume 1 element from the queue an_input = sia_controller.queue[0] sia_controller.queue = sia_controller.queue[1:] # Print the current location because this was an instruction and we want user to know what was the action if an_input and an_input.split( " ")[0] in sia_controller.CHOICES_COMMANDS: print(f"{get_flair()} /stocks/sia/ $ {an_input}") # Get input command from user else: # Display help menu when entering on this menu from a level above if an_input == "HELP_ME": sia_controller.print_help() # Get input from user using auto-completion if session and gtff.USE_PROMPT_TOOLKIT and sia_controller.choices: sia_controller.choices["industry"] = { i: None for i in financedatabase_model.get_industries( country=sia_controller.country, sector=sia_controller.sector) } sia_controller.choices["sector"] = { s: None for s in financedatabase_model.get_sectors( industry=sia_controller.industry, country=sia_controller.country) } sia_controller.choices["country"] = { c: None for c in financedatabase_model.get_countries( industry=sia_controller.industry, sector=sia_controller.sector) } completer = NestedCompleter.from_nested_dict( sia_controller.choices) an_input = session.prompt( f"{get_flair()} /stocks/sia/ $ ", completer=completer, search_ignore_case=True, ) # Get input from user without auto-completion else: an_input = input(f"{get_flair()} /stocks/sia/ $ ") try: # Process the input command sia_controller.queue = sia_controller.switch(an_input) except SystemExit: print( f"\nThe command '{an_input}' doesn't exist on the /stocks/sia menu.", end="", ) similar_cmd = difflib.get_close_matches( an_input.split(" ")[0] if " " in an_input else an_input, sia_controller.CHOICES, n=1, cutoff=0.7, ) if similar_cmd: if " " in an_input: candidate_input = ( f"{similar_cmd[0]} {' '.join(an_input.split(' ')[1:])}" ) if candidate_input == an_input: an_input = "" sia_controller.queue = [] print("\n") continue an_input = candidate_input else: an_input = similar_cmd[0] print(f" Replacing by '{an_input}'.") sia_controller.queue.insert(0, an_input) else: print("\n")
def metric_command( finance_key: str, finance_metric: str, ticker: str = "", ): """Display financials bars comparing sectors, industry, analysis, countries, market cap and excluding exchanges. Parameters ---------- finance_key: str Select finance key from Yahoo Finance(e.g. financialData, defaultKeyStatistics, summaryProfile) finance_metric: str Select finance metric from Yahoo Finance (e.g. operatingCashflow, revenueGrowth, ebitda, freeCashflow) ticker: str Company ticker to use as the basis. """ logger.info("metrics") exclude_exchanges: bool = True limit: int = 10 ticker = ticker.lower() if ticker: data = yfinance.utils.get_json(f"https://finance.yahoo.com/quote/{ticker}") if "summaryProfile" in data: country = data["summaryProfile"]["country"] if country not in financedatabase_model.get_countries(): similar_cmd = difflib.get_close_matches( country, financedatabase_model.get_countries(), n=1, cutoff=0.7, ) if similar_cmd: country = similar_cmd[0] sector = data["summaryProfile"]["sector"] if sector not in financedatabase_model.get_sectors(): similar_cmd = difflib.get_close_matches( sector, financedatabase_model.get_sectors(), n=1, cutoff=0.7, ) if similar_cmd: sector = similar_cmd[0] industry = data["summaryProfile"]["industry"] if industry not in financedatabase_model.get_industries(): similar_cmd = difflib.get_close_matches( industry, financedatabase_model.get_industries(), n=1, cutoff=0.7, ) if similar_cmd: industry = similar_cmd[0] if "price" in data: mktcap = data["price"]["marketCap"] if mktcap < 2_000_000_000: mktcap = "Small" elif mktcap > 10_000_000_000: mktcap = "Large" else: mktcap = "Mid" stocks_data = financedatabase_model.get_stocks_data( country, sector, industry, mktcap, exclude_exchanges ) metric_data = {} for symbol in list(stocks_data.keys()): if finance_key in stocks_data[symbol] and "quoteType" in stocks_data[symbol]: stock_name = stocks_data[symbol]["quoteType"]["longName"] metric = ( stocks_data[symbol][finance_key][finance_metric] if stocks_data[symbol][finance_key] is not None and finance_metric in stocks_data[symbol][finance_key] else None ) if metric and stock_name: metric_data[stock_name] = (metric, symbol) if len(metric_data) > 1: metric_data = dict( OrderedDict( sorted(metric_data.items(), key=lambda t: t[1][0], reverse=True) ) ) company_names = list() company_metrics = list() company_tickers = list() for name, metric in metric_data.items(): company_names.append(name) company_metrics.append(metric[0]) company_tickers.append(metric[1]) company_name = np.array(company_names)[:limit] company_metric = np.array(company_metrics)[:limit] company_ticker = np.array(company_tickers)[:limit] magnitude = 0 while max(company_metric) > 1_000 or abs(min(company_metric)) > 1_000: company_metric = np.divide(company_metric, 1_000) magnitude += 1 # check if the value is a percentage if (magnitude == 0) and all(company_metric >= 0) and all(company_metric <= 1): unit = "%" company_metric = company_metric * 100 else: unit = " KMBTP"[magnitude] colors = [ "#ffed00", "#ef7d00", "#e4003a", "#c13246", "#822661", "#48277c", "#005ca9", "#00aaff", "#9b30d9", "#af005f", "#5f00af", "#af87ff", ] fig = go.Figure() i = 0 for name, metric, tickers in zip( company_name[::-1], company_metric[::-1], company_ticker[::-1] ): if len(name.split(" ")) > 6 and len(name) > 40: name = ( f'{" ".join(name.split(" ")[:4])}\n{" ".join(name.split(" ")[4:])}' ) df_name = [] df_metric = [] df_tickers = [] df_name.append(name) df_metric.append(metric) df_tickers.append(tickers) fig.add_trace( go.Bar( name=tickers, y=[name], x=[metric], orientation="h", marker=dict( color=colors[i], line=dict(color="rgb(248, 248, 249)", width=1), ), ), ) i += 1 metric_title = ( "".join( " " + char if char.isupper() else char.strip() for char in finance_metric ) .strip() .capitalize() ) benchmark = np.median(company_metric) if unit != " ": units = f" [{unit}] " else: units = " " title = f"{metric_title.capitalize()}{units}with benchmark of {benchmark:.2f} {unit}<br>" title += mktcap + " cap companies " if mktcap else "Companies " if industry: title += f"in {industry} industry<br>" elif sector: title += f"in {sector} sector<br>" if country: title += f"in {country}" title += " " if (industry or sector) else "<br>" title += ( "(excl. data from international exchanges)" if exclude_exchanges else "(incl. data from international exchanges)" ) fig.add_vline( x=benchmark, fillcolor="grey", opacity=1, layer="below", line_width=3, line=dict(color="grey", dash="dash"), ) if imps.PLT_WATERMARK: fig.add_layout_image(imps.PLT_WATERMARK) fig.update_layout( margin=dict(l=40, r=0, t=100, b=20), template=imps.PLT_CANDLE_STYLE_TEMPLATE, title=title, colorway=colors, font=imps.PLT_FONT, legend={"traceorder": "reversed"}, ) imagefile = "sia_metrics.png" # Check if interactive settings are enabled plt_link = "" if imps.INTERACTIVE: plt_link = imps.inter_chart(fig, imagefile, callback=False) fig.update_layout( width=800, height=500, ) imagefile = imps.image_border(imagefile, fig=fig) return { "title": "Stocks - Sector and Industry Analysis", "description": plt_link, "imagefile": imagefile, }