X-Git-Url: https://wannabe.guru.org/gitweb/?a=blobdiff_plain;f=stock_renderer.py;h=cfd2277a62d54c18a3eb27bef8212657e17c6a1e;hb=7eae23537dcc61565a24d5c957d4325b7337b63a;hp=f8491e6a9eb73b8ba06ea35ffd1eb96f5ca0b5c2;hpb=75b27cc68871343681f01e3f5b04cae84b1b7b2a;p=kiosk.git diff --git a/stock_renderer.py b/stock_renderer.py index f8491e6..cfd2277 100644 --- a/stock_renderer.py +++ b/stock_renderer.py @@ -1,129 +1,108 @@ -from bs4 import BeautifulSoup -from threading import Thread -import datetime +#!/usr/bin/env python3 + +from typing import Dict, List, Optional, Tuple +import yfinance as yf # type: ignore + import file_writer -import json -import re import renderer -import random -import secrets -import time -import urllib.request, urllib.error, urllib.parse + class stock_quote_renderer(renderer.debuggable_abstaining_renderer): - # format exchange:symbol - def __init__(self, name_to_timeout_dict, symbols): + """Render the stock prices page.""" + + def __init__( + self, + name_to_timeout_dict: Dict[str, int], + symbols: List[str], + display_subs: Dict[str, str] = None, + ) -> None: super(stock_quote_renderer, self).__init__(name_to_timeout_dict, False) self.symbols = symbols - self.prefix = "https://www.alphavantage.co/query?" - self.thread = None + self.display_subs = display_subs - def debug_prefix(self): + def debug_prefix(self) -> str: return "stock" - def get_random_key(self): - return random.choice(secrets.alphavantage_keys) - - def periodic_render(self, key): - now = datetime.datetime.now() - if (now.hour < (9 - 3) or - now.hour >= (17 - 3) or - datetime.datetime.today().weekday() > 4): - self.debug_print("The stock market is closed so not re-rendering") - return True - - if (self.thread is None or not self.thread.is_alive()): - self.debug_print("Spinning up a background thread...") - self.thread = Thread(target = self.thread_internal_render, args=()) - self.thread.start() - return True - - def thread_internal_render(self): - symbols_finished = 0 - f = file_writer.file_writer('stock_3_86400.html') - f.write("

Stock Quotes


") - f.write("") - for symbol in self.symbols: -# print "---------- Working on %s\n" % symbol - - # https://www.alphavantage.co/query?function=TIME_SERIES_INTRADAY&symbol=MSFT&interval=5min&apikey= - - # https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol=MSFT&apikey= - - attempts = 0 - cooked = "" - while True: - key = self.get_random_key() - url = self.prefix + "function=GLOBAL_QUOTE&symbol=%s&apikey=%s" % (symbol, key) - raw = urllib.request.urlopen(url).read() - cooked = json.loads(raw) - if 'Global Quote' not in cooked: -# print "%s\n" % cooked - print("Failure %d, sleep %d sec...\n" % (attempts + 1, - 2 ** attempts)) - time.sleep(2 ** attempts) - attempts += 1 - if attempts > 10: # we'll wait up to 512 seconds per symbol - break - else: - break - - # These fuckers... - if 'Global Quote' not in cooked: - print("Can't get data for symbol %s: %s\n" % ( - symbol, raw)) - continue - cooked = cooked['Global Quote'] - - # { - # u'Global Quote': - # { - # u'01. symbol': u'MSFT', - # u'02. open': u'151.2900', - # u'03. high': u'151.8900', - # u'04. low': u'150.7650', - # u'05. price': u'151.1300', - # u'06. volume': u'16443559', - # u'07. latest trading day': u'2019-12-10', - # u'08. previous close': u'151.3600', - # u'09. change': u'-0.2300' - # u'10. change percent': u'-0.1520%', - # } - # } - - price = "?????" - if '05. price' in cooked: - price = cooked['05. price'] - price = price[:-2] - - percent_change = "?????" - if '10. change percent' in cooked: - percent_change = cooked['10. change percent'] - if not '-' in percent_change: - percent_change = "+" + percent_change - - change = "?????" - cell_color = "#bbbbbb" - if '09. change' in cooked: - change = cooked['09. change'] - if "-" in change: - cell_color = "#b00000" - else: - cell_color = "#009000" - change = change[:-2] - - if symbols_finished % 4 == 0: - if (symbols_finished > 0): - f.write("") - f.write("") - symbols_finished += 1 - - f.write(""" -""" + ) + f.write("
+ @staticmethod + def get_ticker_name(ticker: yf.ticker.Ticker) -> str: + """Get friendly name of a ticker.""" + info = ticker.get_info() + if "shortName" in info: + return info["shortName"] + return ticker + + @staticmethod + def get_price(ticker: yf.ticker.Ticker) -> Optional[float]: + """Get most recent price of a ticker.""" + keys = [ + "bid", + "ask", + "regularMarketPrice", + "lastMarket", + "open", + "previousClose", + ] + info = ticker.get_info() + for key in keys: + if key in info and info[key] is not None and info[key] != 0.0: + print(f"Price: picked {key}, ${info[key]}.") + return float(info[key]) + return None + + @staticmethod + def get_change_and_delta( + ticker: yf.ticker.Ticker, price: float + ) -> Tuple[float, float]: + """Given the current price, look up opening price and compute delta.""" + keys = [ + "previousClose", + "open", + ] + info = ticker.get_info() + for key in keys: + if key in info and info[key] is not None: + print(f"Change: picked {key}, ${info[key]}.") + old_price = float(info[key]) + delta = price - old_price + return (delta / old_price * 100.0, delta) + return (0.0, 0.0) + + def periodic_render(self, key: str) -> bool: + """Write an up-to-date stock page.""" + with file_writer.file_writer("stock_3_86400.html") as f: + f.write("

Stock Quotes


") + f.write("") + symbols_finished = 0 + for symbol in self.symbols: + ticker = yf.Ticker(symbol) + # print(ticker.get_info()) + if ticker is None: + self.debug_print(f"Unknown symbol {symbol} -- ignored.") + continue + name = stock_quote_renderer.get_ticker_name(ticker) + price = stock_quote_renderer.get_price(ticker) + if price is None: + self.debug_print(f"No price information for {symbol} -- skipped.") + continue + (percent_change, delta) = stock_quote_renderer.get_change_and_delta( + ticker, price + ) + # print(f"delta: {delta}, change: {percent_change}") + cell_color = "#b00000" if percent_change < 0 else "#009000" + if symbols_finished % 4 == 0: + if symbols_finished > 0: + f.write("") + f.write("") + symbols_finished += 1 + if self.display_subs is not None and symbol in self.display_subs: + symbol = self.display_subs[symbol] + f.write( + f""" +""" % (cell_color, - symbol, - price, - percent_change, - change)) - f.write("
- +
- %s + {symbol}
- +
- $%s
- (%s)
- $%s + width:70%"> + ${price:.2f}
+ ({percent_change:.1f}%)
+ ${delta:.2f}
-
") - f.close() +
") return True -#x = stock_quote_renderer({}, ["MSFT", "GOOG", "GOOGL", "OPTAX", "VNQ"]) -#x.periodic_render(None) +# Test +#x = stock_quote_renderer({}, ["MSFT", "GOOG", "BTC-USD", "OPTAX", "GC=F", "VNQ"], { "BTC-USD": "BTC", "GC=F": "GOLD" }) #x.periodic_render(None)