diff --git a/docs/api/modules/stockindex.rst b/docs/api/modules/stockindex.rst new file mode 100644 index 0000000..6cc01a8 --- /dev/null +++ b/docs/api/modules/stockindex.rst @@ -0,0 +1,56 @@ +:mod:`StockIndex` --- DJIA and NASDAQ Quotes +============================================ + +This module provides quotes for the DJIA and NASDAQ indexes. It requires a free API key from +https://financialmodelingprep.com/ + + +Commands +-------- + +.. cmdoption:: .djia + + Display the DJIA index + +.. cmdoption:: .nasdaq + + Display the NASDAQ index + + +Config +------ + +.. code-block:: json + + { + "apikey": "xxxxxxxxxxxxx", + "cache_update_interval": 600, + "cache_update_timeout": 10, + "warning_thresh": 1800 + } + +.. cmdoption:: apikey + + API ley obtained from https://financialmodelingprep.com/ + +.. cmdoption:: cache_update_interval + + How many seconds between fetching new index quotes from the API. + +.. cmdoption:: cache_update_timeout + + Maximum seconds to wait on the HTTP request sent to the API + +.. cmdoption:: warning_thresh + + A warning will be shown that the quote is out-of-date if the last successful fetch was longer ago than this + setting's number of seconds. + + +Class Reference +--------------- + +.. automodule:: pyircbot.modules.StockIndex + :members: + :undoc-members: + :show-inheritance: diff --git a/pyircbot/modules/StockIndex.py b/pyircbot/modules/StockIndex.py new file mode 100644 index 0000000..38755c1 --- /dev/null +++ b/pyircbot/modules/StockIndex.py @@ -0,0 +1,81 @@ +from pyircbot.modulebase import ModuleBase, command +from pyircbot.modules.ModInfo import info +from threading import Thread +from time import sleep, time +import requests +import traceback + + +API_URL = "https://financialmodelingprep.com/api/v3/quotes/index?apikey={apikey}" + + +def bits(is_gain): + if is_gain: + return ("\x0303", "⬆", "+", ) + return ("\x0304", "⬇", "-", ) + + +class StockIndex(ModuleBase): + def __init__(self, bot, moduleName): + super().__init__(bot, moduleName) + self.session = requests.session() + self.updater = None + self.running = True + self.last_update = 0 + self.start_cache_updater() + + def start_cache_updater(self): + self.updater = Thread(target=self.cache_updater, daemon=True) + self.updater.start() + + def ondisable(self): + self.running = False + self.updater.join() + + def cache_updater(self): + while self.running: + try: + self.update_cache() + except: + traceback.print_exc() + delay = self.config.get("cache_update_interval", 600) + while self.running and delay > 0: + delay -= 1 + sleep(1) + + def update_cache(self): + data = self.session.get(API_URL.format(**self.config), + timeout=self.config.get("cache_update_timeout", 10)).json() + self.cache = {item["symbol"]: item for item in data} + self.last_update = time() + + @info("djia", "get the current value of the DJIA", cmds=["djia"]) + @command("djia", allow_private=True) + def cmd_djia(self, message, command): + self.send_quote("^DJI", "DJIA", message.replyto) + + @info("nasdaq", "get the current value of the NASDAQ composite index", cmds=["nasdaq"]) + @command("nasdaq", allow_private=True) + def cmd_nasdaq(self, message, command): + self.send_quote("^IXIC", "NASDAQ", message.replyto) + + def send_quote(self, key, symbol, to): + index = self.cache[key] + is_gain = index["price"] >= index["previousClose"] + color, arrow, plusmin = bits(is_gain) + + change = float(index["price"]) - float(index["previousClose"]) + percentchange = float(change) / float(index["previousClose"]) * 100 + + warn_thresh = self.config.get("warning_thresh", 1800) + + warning = "" if time() - self.last_update < warn_thresh else " \x030(quote is out-of-date)" + + self.bot.act_PRIVMSG(to, "{}{} ${:.2f} {}{:.2f} ({:.2f}%){}{}".format(color, + symbol, + index["price"], + plusmin, + change, + percentchange, + arrow, + warning))