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