Adding type annotations and fixing up formatting.
[kiosk.git] / stock_renderer.py
1 #!/usr/bin/env python3
2
3 from typing import Dict, List, Tuple
4 import yfinance as yf
5
6 import file_writer
7 import renderer
8
9
10 class stock_quote_renderer(renderer.debuggable_abstaining_renderer):
11     """Render the stock prices page."""
12
13     def __init__(
14         self, name_to_timeout_dict: Dict[str, int], symbols: List[str]
15     ) -> None:
16         super(stock_quote_renderer, self).__init__(name_to_timeout_dict, False)
17         self.symbols = symbols
18
19     def debug_prefix(self) -> str:
20         return "stock"
21
22     @staticmethod
23     def get_ticker_name(ticker: yf.ticker.Ticker) -> str:
24         """Get friendly name of a ticker."""
25         info = ticker.get_info()
26         return info["shortName"]
27
28     @staticmethod
29     def get_price(ticker: yf.ticker.Ticker) -> float:
30         """Get most recent price of a ticker."""
31         keys = [
32             "bid",
33             "ask",
34             "regularMarketPrice",
35             "lastMarket",
36             "open",
37             "previousClose",
38         ]
39         info = ticker.get_info()
40         for key in keys:
41             if key in info and info[key] is not None and info[key] != 0.0:
42                 print(f"Price: picked {key}, ${info[key]}.")
43                 return float(info[key])
44         return None
45
46     @staticmethod
47     def get_change_and_delta(
48         ticker: yf.ticker.Ticker, price: float
49     ) -> Tuple[float, float]:
50         """Given the current price, look up opening price and compute delta."""
51         keys = [
52             "open",
53             "previousClose",
54         ]
55         info = ticker.get_info()
56         for key in keys:
57             if key in info and info[key] is not None:
58                 print(f"Change: picked {key}, ${info[key]}.")
59                 old_price = float(info[key])
60                 delta = price - old_price
61                 return (delta / old_price * 100.0, delta)
62         return (0.0, 0.0)
63
64     def periodic_render(self, key: str) -> bool:
65         """Write an up-to-date stock page."""
66         with file_writer.file_writer("stock_3_86400.html") as f:
67             f.write("<H1>Stock Quotes</H1><HR>")
68             f.write("<TABLE WIDTH=99%>")
69             symbols_finished = 0
70             for symbol in self.symbols:
71                 # print(f"--- Symbol: {symbol} ---")
72                 ticker = yf.Ticker(symbol)
73                 print(type(ticker))
74                 # print(ticker.get_info())
75                 if ticker is None:
76                     self.debug_print(f"Unknown symbol {symbol} -- ignored.")
77                     continue
78                 name = stock_quote_renderer.get_ticker_name(ticker)
79                 price = stock_quote_renderer.get_price(ticker)
80                 if price is None:
81                     self.debug_print(f"No price information for {symbol} -- skipped.")
82                     continue
83                 (percent_change, delta) = stock_quote_renderer.get_change_and_delta(
84                     ticker, price
85                 )
86                 # print(f"delta: {delta}, change: {percent_change}")
87                 cell_color = "#b00000" if percent_change < 0 else "#009000"
88                 if symbols_finished % 4 == 0:
89                     if symbols_finished > 0:
90                         f.write("</TR>")
91                         f.write("<TR>")
92                 symbols_finished += 1
93                 f.write(
94                     f"""
95 <TD WIDTH=20% HEIGHT=150 BGCOLOR="{cell_color}">
96   <!-- Container -->
97   <DIV style="position:relative;
98               height:150px;">
99     <!-- Symbol -->
100     <DIV style="position:absolute;
101                 bottom:50;
102                 right:-20;
103                 -webkit-transform:rotate(-90deg);
104                 font-size:28pt;
105                 font-family: helvetica, arial, sans-serif;
106                 font-weight:900;
107                 -webkit-text-stroke: 2px black;
108                 color: #ddd">
109       {symbol}
110     </DIV>
111     <!-- Current price, Change today and percent change today, name -->
112     <DIV style="position:absolute;
113                 left:10;
114                 top:20;
115                 font-size:23pt;
116                 font-family: helvetica, arial, sans-serif;
117                 width:70%">
118             ${price:.2f}<BR>
119             <I>({percent_change:.1f}%)</I><BR>
120             <B>${delta:.2f}</B>
121     </DIV>
122   </DIV>
123 </TD>"""
124                 )
125             f.write("</TR></TABLE>")
126         return True
127
128
129 # Test
130 # x = stock_quote_renderer({}, ["MSFT", "GOOG", "GBTC", "OPTAX", "VNQ"])
131 # x.periodic_render(None)