diff --git a/IEX_Symbol.py b/IEX_Symbol.py index 7c00c01..2fdae3a 100644 --- a/IEX_Symbol.py +++ b/IEX_Symbol.py @@ -30,7 +30,7 @@ class IEX_Symbol: Parameters ---------- IEX_TOKEN : str - IEX Token + IEX API Token """ try: self.IEX_TOKEN = os.environ["IEX"] @@ -47,12 +47,28 @@ class IEX_Symbol: schedule.every().day.do(self.clear_charts) def clear_charts(self) -> None: - """Clears cache of chart data.""" + """ + Clears cache of chart data. + Charts are cached so that only 1 API call per 24 hours is needed since the + chart data is expensive and a large download. + """ self.charts = {} def get_symbol_list( self, return_df=False ) -> Optional[Tuple[pd.DataFrame, datetime]]: + """Gets list of all symbols supported by IEX + + Parameters + ---------- + return_df : bool, optional + return the dataframe of all stock symbols, by default False + + Returns + ------- + Optional[Tuple[pd.DataFrame, datetime]] + If `return_df` is set to `True` returns a dataframe, otherwise returns `None`. + """ reg_symbols = r.get( f"https://cloud.iexapis.com/stable/ref-data/symbols?token={self.IEX_TOKEN}", @@ -145,18 +161,16 @@ class IEX_Symbol: return symbol_list def price_reply(self, symbol: Stock) -> str: - """Returns current market price or after hours if its available for a given stock symbol. + """Returns price movement of Stock for the last market day, or after hours. Parameters ---------- - symbols : list - List of stock symbols. + symbol : Stock Returns ------- - Dict[str, str] - Each symbol passed in is a key with its value being a human readable - markdown formatted string of the symbols price and movement. + str + Formatted markdown """ IEXurl = f"https://cloud.iexapis.com/stable/stock/{symbol.id}/quote?token={self.IEX_TOKEN}" @@ -223,13 +237,12 @@ class IEX_Symbol: Parameters ---------- - symbols : list - List of stock symbols. + symbol : Stock Returns ------- - Dict[str, str] - Each symbol passed in is a key with its value being a human readable formatted string of the symbols div dates. + str + Formatted markdown """ if symbol.symbol.upper() in self.otc_list: return "OTC stocks do not currently support any commands." @@ -284,17 +297,16 @@ class IEX_Symbol: return f"${symbol.id.upper()} either doesn't exist or pays no dividend." def news_reply(self, symbol: Stock) -> str: - """Gets recent english news on stock symbols. + """Gets most recent, english, non-paywalled news Parameters ---------- - symbols : list - List of stock symbols. + symbol : Stock Returns ------- - Dict[str, str] - Each symbol passed in is a key with its value being a human readable markdown formatted string of the symbols news. + str + Formatted markdown """ if symbol.symbol.upper() in self.otc_list: return "OTC stocks do not currently support any commands." @@ -323,17 +335,16 @@ class IEX_Symbol: return f"News for **{symbol.id.upper()}**:\n" + "\n".join(line[:5]) def info_reply(self, symbol: Stock) -> str: - """Gets information on stock symbols. + """Gets description for Stock Parameters ---------- - symbols : List[str] - List of stock symbols. + symbol : Stock Returns ------- - Dict[str, str] - Each symbol passed in is a key with its value being a human readable formatted string of the symbols information. + str + Formatted text """ if symbol.symbol.upper() in self.otc_list: return "OTC stocks do not currently support any commands." @@ -354,17 +365,16 @@ class IEX_Symbol: return f"No information found for: {symbol}\nEither today is boring or the symbol does not exist." def stat_reply(self, symbol: Stock) -> str: - """Gets key statistics for each symbol in the list + """Key statistics on a Stock Parameters ---------- - symbols : List[str] - List of stock symbols + symbol : Stock Returns ------- - Dict[str, str] - Each symbol passed in is a key with its value being a human readable formatted string of the symbols statistics. + str + Formatted markdown """ if symbol.symbol.upper() in self.otc_list: return "OTC stocks do not currently support any commands." @@ -503,7 +513,7 @@ class IEX_Symbol: Returns ------- list[str] - list of $ID: NAME + list of $ID: NAME, CHANGE% """ stocks = r.get( diff --git a/Symbol.py b/Symbol.py index 3e204c6..8c702a1 100644 --- a/Symbol.py +++ b/Symbol.py @@ -25,6 +25,8 @@ class Symbol: class Stock(Symbol): + """Stock Market Object. Gets data from IEX Cloud""" + def __init__(self, symbol: str) -> None: self.symbol = symbol self.id = symbol @@ -36,6 +38,8 @@ coins = r.get("https://api.coingecko.com/api/v3/coins/list").json() class Coin(Symbol): + """Cryptocurrency Object. Gets data from CoinGecko.""" + @functools.cache def __init__(self, symbol: str) -> None: self.symbol = symbol diff --git a/T_info.py b/T_info.py index 66d07ef..f87446c 100644 --- a/T_info.py +++ b/T_info.py @@ -21,7 +21,7 @@ Keep up with the latest news for the bot in its Telegram Channel: https://t.me/s Full documentation on using and running your own stock bot can be found [on the bots website.](https://simple-stock-bots.gitlab.io/site) -The bot detects _"Symbols"_ using either one or two dollar signs before the symbol. One dollar sign is for a stock market ticker, while two is for a cryptocurrency coin. `/chart $$eth` would return a chart of the past month of data for Ethereum, while `/dividend $psec` returns dividend information for Prospect Capital stock. +The bot detects _"Symbols"_ using either one `$` or two `$$` dollar signs before the symbol. One dollar sign is for a stock market ticker, while two is for a cryptocurrency coin. `/chart $$eth` would return a chart of the past month of data for Ethereum, while `/dividend $psec` returns dividend information for Prospect Capital stock. Simply calling a symbol in any message that the bot can see will also return the price. So a message like: `I wonder if $$btc will go to the Moon now that $tsla accepts it as payment` would return the current price for both Bitcoin and Tesla. @@ -67,7 +67,7 @@ info - $[symbol] General information about the symbol. â„šī¸ news - $[symbol] News about the symbol. 📰 stat - $[symbol] Key statistics about the symbol. đŸ”ĸ dividend - $[symbol] Dividend info 📅 -intra - $[symbol] Plot since the last market open. 📈 trending - Trending Stocks and Cryptos. đŸ’Ŧ +intra - $[symbol] Plot since the last market open. 📈 chart - $[chart] Plot of the past month. 📊 """ # Not used by the bot but for updaing commands with BotFather diff --git a/bot.py b/bot.py index 8f2f934..4fe6e9b 100644 --- a/bot.py +++ b/bot.py @@ -53,7 +53,7 @@ info("Bot script started.") def start(update: Update, context: CallbackContext): - """Send a message when the command /start is issued.""" + """Send help text when the command /start is issued.""" info(f"Start command ran by {update.message.chat.username}") update.message.reply_text( text=t.help_text, @@ -63,7 +63,7 @@ def start(update: Update, context: CallbackContext): def help(update: Update, context: CallbackContext): - """Send link to docs when the command /help is issued.""" + """Send help text when the command /help is issued.""" info(f"Help command ran by {update.message.chat.username}") update.message.reply_text( text=t.help_text, @@ -73,7 +73,7 @@ def help(update: Update, context: CallbackContext): def license(update: Update, context: CallbackContext): - """Return bots license agreement""" + """Send bots license when the /license command is issued.""" info(f"License command ran by {update.message.chat.username}") update.message.reply_text( text=t.license, @@ -83,6 +83,7 @@ def license(update: Update, context: CallbackContext): def status(update: Update, context: CallbackContext): + """Gather status of bot and dependant services and return important status updates.""" warning(f"Status command ran by {update.message.chat.username}") bot_resp = datetime.datetime.now(update.message.date.tzinfo) - update.message.date @@ -96,6 +97,7 @@ def status(update: Update, context: CallbackContext): def donate(update: Update, context: CallbackContext): + """Sets up donation.""" info(f"Donate command ran by {update.message.chat.username}") chat_id = update.message.chat_id @@ -133,6 +135,7 @@ def donate(update: Update, context: CallbackContext): def precheckout_callback(update: Update, context: CallbackContext): + """Approves donation""" info(f"precheckout_callback queried") query = update.pre_checkout_query @@ -146,6 +149,7 @@ def precheckout_callback(update: Update, context: CallbackContext): def successful_payment_callback(update: Update, context: CallbackContext): + """Thanks user for donation""" info(f"Successful payment!") update.message.reply_text( "Thank you for your donation! It goes a long way to keeping the bot free!" @@ -177,9 +181,7 @@ def symbol_detect(update: Update, context: CallbackContext): def dividend(update: Update, context: CallbackContext): - """ - waits for /dividend or /div command and then finds dividend info on that symbol. - """ + """/dividend or /div command and then finds dividend info on that symbol.""" info(f"Dividend command ran by {update.message.chat.username}") message = update.message.text chat_id = update.message.chat_id @@ -203,9 +205,7 @@ def dividend(update: Update, context: CallbackContext): def news(update: Update, context: CallbackContext): - """ - waits for /news command and then finds news info on that symbol. - """ + """/news command then finds news info on that symbol.""" info(f"News command ran by {update.message.chat.username}") message = update.message.text chat_id = update.message.chat_id @@ -230,9 +230,7 @@ def news(update: Update, context: CallbackContext): def information(update: Update, context: CallbackContext): - """ - waits for /info command and then finds info on that symbol. - """ + """/info command then finds info on that symbol.""" info(f"Information command ran by {update.message.chat.username}") message = update.message.text chat_id = update.message.chat_id @@ -257,6 +255,10 @@ def information(update: Update, context: CallbackContext): def search(update: Update, context: CallbackContext): + """ + Uses fuzzy search on full list of stocks and crypto names + and descriptions then returns the top matches in order. + """ info(f"Search command ran by {update.message.chat.username}") message = update.message.text.replace("/search ", "") chat_id = update.message.chat_id @@ -281,8 +283,8 @@ def search(update: Update, context: CallbackContext): def intra(update: Update, context: CallbackContext): + """returns a chart of intraday data for a symbol""" info(f"Intra command ran by {update.message.chat.username}") - # TODO: Document usage of this command. https://iexcloud.io/docs/api/#historical-prices message = update.message.text chat_id = update.message.chat_id @@ -337,8 +339,8 @@ def intra(update: Update, context: CallbackContext): def chart(update: Update, context: CallbackContext): + """returns a chart of the past month of data for a symbol""" info(f"Chart command ran by {update.message.chat.username}") - # TODO: Document usage of this command. https://iexcloud.io/docs/api/#historical-prices message = update.message.text chat_id = update.message.chat_id @@ -390,9 +392,7 @@ def chart(update: Update, context: CallbackContext): def stat(update: Update, context: CallbackContext): - """ - https://iexcloud.io/docs/api/#key-stats - """ + """returns key statistics on symbol""" info(f"Stat command ran by {update.message.chat.username}") message = update.message.text chat_id = update.message.chat_id @@ -417,9 +417,7 @@ def stat(update: Update, context: CallbackContext): def cap(update: Update, context: CallbackContext): - """ - Market Cap Information - """ + """returns market cap for symbol""" info(f"Cap command ran by {update.message.chat.username}") message = update.message.text chat_id = update.message.chat_id @@ -444,9 +442,7 @@ def cap(update: Update, context: CallbackContext): def trending(update: Update, context: CallbackContext): - """ - Trending Symbols - """ + """returns currently trending symbols and how much they've moved in the past trading day.""" info(f"Trending command ran by {update.message.chat.username}") chat_id = update.message.chat_id @@ -462,8 +458,8 @@ def trending(update: Update, context: CallbackContext): def inline_query(update: Update, context: CallbackContext): """ - Handles inline query. - Does a fuzzy search on input and returns stocks that are close. + Handles inline query. Searches by looking if query is contained + in the symbol and returns matches in alphabetical order. """ info(f"Inline command ran by {update.message.chat.username}") info(f"Query: {update.inline_query.query}") @@ -496,6 +492,7 @@ def inline_query(update: Update, context: CallbackContext): def rand_pick(update: Update, context: CallbackContext): + """For the gamblers. Returns a random symbol to buy and a sell date""" info( f"Someone is gambling! Random_pick command ran by {update.message.chat.username}" ) @@ -558,13 +555,17 @@ def main(): dp.add_handler(CommandHandler("cap", cap)) dp.add_handler(CommandHandler("trending", trending)) dp.add_handler(CommandHandler("search", search)) - dp.add_handler(CommandHandler("intraday", intra)) - dp.add_handler(CommandHandler("intra", intra, run_async=True)) - dp.add_handler(CommandHandler("chart", chart, run_async=True)) dp.add_handler(CommandHandler("random", rand_pick)) dp.add_handler(CommandHandler("donate", donate)) dp.add_handler(CommandHandler("status", status)) + # Charting can be slow so they run async. + dp.add_handler(CommandHandler("intra", intra, run_async=True)) + dp.add_handler(CommandHandler("intraday", intra, run_async=True)) + dp.add_handler(CommandHandler("day", intra, run_async=True)) + dp.add_handler(CommandHandler("chart", chart, run_async=True)) + dp.add_handler(CommandHandler("month", chart, run_async=True)) + # on noncommand i.e message - echo the message on Telegram dp.add_handler(MessageHandler(Filters.text, symbol_detect)) diff --git a/cg_Crypto.py b/cg_Crypto.py index 0d59104..c71962e 100644 --- a/cg_Crypto.py +++ b/cg_Crypto.py @@ -220,18 +220,18 @@ class cg_Crypto: return pd.DataFrame() def stat_reply(self, symbol: Coin) -> str: - """Gets key statistics for each symbol in the list + """Gathers key statistics on coin. Mostly just CoinGecko scores. Parameters ---------- - symbols : List[str] - List of coin symbols + symbol : Coin Returns ------- - Dict[str, str] - Each symbol passed in is a key with its value being a human readable formatted string of the symbols statistics. + str + Preformatted markdown. """ + response = r.get( f"https://api.coingecko.com/api/v3/coins/{symbol.id}?localization=false", timeout=5, @@ -253,17 +253,16 @@ class cg_Crypto: return f"{symbol.symbol} returned an error." def cap_reply(self, coin: Coin) -> str: - """Gets market Cap for each symbol in the list + """Gets market cap for Coin Parameters ---------- - symbols : List[str] - List of coin symbols + coin : Coin Returns ------- - Dict[str, str] - Each symbol passed in is a key with its value being a human readable formatted string of the symbols markey cap. + str + Preformatted markdown. """ response = r.get( f"https://api.coingecko.com/api/v3/simple/price?ids={coin.id}&vs_currencies={self.vs_currency}&include_market_cap=true", @@ -290,17 +289,16 @@ class cg_Crypto: return message def info_reply(self, symbol: Coin) -> str: - """Gets information on stock symbols. + """Gets coin description Parameters ---------- - symbols : List[str] - List of stock symbols. + symbol : Coin Returns ------- - Dict[str, str] - Each symbol passed in is a key with its value being a human readable formatted string of the symbols information. + str + Preformatted markdown. """ response = r.get( @@ -322,7 +320,7 @@ class cg_Crypto: Returns ------- list[str] - list of $$ID: NAME + list of $$ID: NAME, CHANGE% """ coins = r.get( @@ -352,6 +350,17 @@ class cg_Crypto: return trending def batch_price(self, coins: list[Coin]) -> list[str]: + """Gets price of a list of coins all in one API call + + Parameters + ---------- + coins : list[Coin] + + Returns + ------- + list[str] + returns preformatted list of strings detailing price movement of each coin passed in. + """ query = ",".join([c.id for c in coins]) prices = r.get( diff --git a/search_index.pickle b/search_index.pickle deleted file mode 100644 index 26e0883..0000000 Binary files a/search_index.pickle and /dev/null differ diff --git a/searchindexer.ipynb b/searchindexer.ipynb deleted file mode 100644 index 66d422e..0000000 --- a/searchindexer.ipynb +++ /dev/null @@ -1,276 +0,0 @@ -{ - "metadata": { - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.0-final" - }, - "orig_nbformat": 2, - "kernelspec": { - "name": "python3", - "display_name": "Python 3.9.0 64-bit", - "metadata": { - "interpreter": { - "hash": "36cf16204b8548560b1c020c4e8fb5b57f0e4c58016f52f2d4be01e192833930" - } - } - } - }, - "nbformat": 4, - "nbformat_minor": 2, - "cells": [ - { - "cell_type": "code", - "execution_count": 62, - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Requirement already satisfied: tqdm in /home/anson/.local/lib/python3.8/site-packages (4.59.0)\n" - ] - } - ], - "source": [ - "!pip install tqdm" - ] - }, - { - "cell_type": "code", - "execution_count": 64, - "metadata": {}, - "outputs": [], - "source": [ - "import requests as r\n", - "import pandas as pd\n", - "from fuzzywuzzy import fuzz\n", - "from functools import cache\n", - "from tqdm import tqdm" - ] - }, - { - "cell_type": "code", - "execution_count": 49, - "metadata": {}, - "outputs": [], - "source": [ - " def stocks():\n", - "\n", - " raw_symbols = r.get(\n", - " f\"https://cloud.iexapis.com/stable/ref-data/symbols?token=WOOOPS\"\n", - " ).json()\n", - " symbols = pd.DataFrame(data=raw_symbols)\n", - "\n", - " symbols[\"description\"] = \"$\" + symbols[\"symbol\"] + \": \" + symbols[\"name\"]\n", - " symbols[\"id\"] = symbols[\"symbol\"]\n", - "\n", - " symbols = symbols[[\"id\", \"symbol\", \"name\", \"description\"]]\n", - "\n", - " return symbols\n", - "\n", - "\n", - "\n", - " def coins():\n", - "\n", - " raw_symbols = r.get(\"https://api.coingecko.com/api/v3/coins/list\").json()\n", - " symbols = pd.DataFrame(data=raw_symbols)\n", - "\n", - " symbols[\"description\"] = \"$$\" + symbols[\"symbol\"] + \": \" + symbols[\"name\"]\n", - " symbols = symbols[[\"id\", \"symbol\", \"name\", \"description\"]]\n", - "\n", - " return symbols" - ] - }, - { - "cell_type": "code", - "execution_count": 51, - "metadata": {}, - "outputs": [ - { - "output_type": "execute_result", - "data": { - "text/plain": [ - " id symbol \\\n", - "0 A A \n", - "1 AA AA \n", - "2 AAA AAA \n", - "3 AAAU AAAU \n", - "4 AAC AAC \n", - "... ... ... \n", - "6565 zyro zyro \n", - "6566 zytara-dollar zusd \n", - "6567 zyx zyx \n", - "6568 zzz-finance zzz \n", - "6569 zzz-finance-v2 zzzv2 \n", - "\n", - " name \\\n", - "0 Agilent Technologies Inc. \n", - "1 Alcoa Corp \n", - "2 Listed Funds Trust - AAF First Priority CLO Bo... \n", - "3 Goldman Sachs Physical Gold ETF Shares - Goldm... \n", - "4 Ares Acquisition Corporation - Class A \n", - "... ... \n", - "6565 Zyro \n", - "6566 Zytara Dollar \n", - "6567 ZYX \n", - "6568 zzz.finance \n", - "6569 zzz.finance v2 \n", - "\n", - " description \n", - "0 $A: Agilent Technologies Inc. \n", - "1 $AA: Alcoa Corp \n", - "2 $AAA: Listed Funds Trust - AAF First Priority ... \n", - "3 $AAAU: Goldman Sachs Physical Gold ETF Shares ... \n", - "4 $AAC: Ares Acquisition Corporation - Class A \n", - "... ... \n", - "6565 $$zyro: Zyro \n", - "6566 $$zusd: Zytara Dollar \n", - "6567 $$zyx: ZYX \n", - "6568 $$zzz: zzz.finance \n", - "6569 $$zzzv2: zzz.finance v2 \n", - "\n", - "[16946 rows x 4 columns]" - ], - "text/html": "
\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
idsymbolnamedescription
0AAAgilent Technologies Inc.$A: Agilent Technologies Inc.
1AAAAAlcoa Corp$AA: Alcoa Corp
2AAAAAAListed Funds Trust - AAF First Priority CLO Bo...$AAA: Listed Funds Trust - AAF First Priority ...
3AAAUAAAUGoldman Sachs Physical Gold ETF Shares - Goldm...$AAAU: Goldman Sachs Physical Gold ETF Shares ...
4AACAACAres Acquisition Corporation - Class A$AAC: Ares Acquisition Corporation - Class A
...............
6565zyrozyroZyro$$zyro: Zyro
6566zytara-dollarzusdZytara Dollar$$zusd: Zytara Dollar
6567zyxzyxZYX$$zyx: ZYX
6568zzz-financezzzzzz.finance$$zzz: zzz.finance
6569zzz-finance-v2zzzv2zzz.finance v2$$zzzv2: zzz.finance v2
\n

16946 rows × 4 columns

\n
" - }, - "metadata": {}, - "execution_count": 51 - } - ], - "source": [ - "df = pd.concat([stocks(), coins()])\n", - "df" - ] - }, - { - "cell_type": "code", - "execution_count": 79, - "metadata": {}, - "outputs": [], - "source": [ - " def search_symbols(search: str):\n", - " \"\"\"Performs a fuzzy search to find stock symbols closest to a search term.\n", - "\n", - " Parameters\n", - " ----------\n", - " search : str\n", - " String used to search, could be a company name or something close to the companies stock ticker.\n", - "\n", - " Returns\n", - " -------\n", - " List[tuple[str, str]]\n", - " A list tuples of every stock sorted in order of how well they match. Each tuple contains: (Symbol, Issue Name).\n", - " \"\"\"\n", - "\n", - " try:\n", - " if search_index[search]: return search_index[search]\n", - " except KeyError:\n", - " pass\n", - "\n", - "\n", - "\n", - " search = search.lower()\n", - "\n", - " df[\"Match\"] = df.apply(\n", - " lambda x: fuzz.ratio(search, f\"{x['symbol']}\".lower()),\n", - " axis=1,\n", - " )\n", - "\n", - " df.sort_values(by=\"Match\", ascending=False, inplace=True)\n", - " if df[\"Match\"].head().sum() < 300:\n", - " df[\"Match\"] = df.apply(\n", - " lambda x: fuzz.partial_ratio(search, x[\"name\"].lower()),\n", - " axis=1,\n", - " )\n", - "\n", - " df.sort_values(by=\"Match\", ascending=False, inplace=True)\n", - "\n", - " symbols = df.head(20)\n", - " symbol_list = list(zip(list(symbols[\"symbol\"]), list(symbols[\"description\"])))\n", - " \n", - " return symbol_list" - ] - }, - { - "cell_type": "code", - "execution_count": 91, - "metadata": {}, - "outputs": [], - "source": [ - "search_list = df['id'].to_list() + df['description'].to_list()\n", - "search_index = {}" - ] - }, - { - "cell_type": "code", - "execution_count": 92, - "metadata": { - "tags": [] - }, - "outputs": [ - { - "output_type": "stream", - "name": "stderr", - "text": [ - " 5%|▍ | 1545/33892 [06:51<2:23:40, 3.75it/s]\n" - ] - }, - { - "output_type": "error", - "ename": "KeyboardInterrupt", - "evalue": "", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mKeyboardInterrupt\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mi\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mtqdm\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0msearch_list\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0msearch_index\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mi\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0msearch_symbols\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mi\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[0;32m\u001b[0m in \u001b[0;36msearch_symbols\u001b[0;34m(search)\u001b[0m\n\u001b[1;32m 22\u001b[0m \u001b[0msearch\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0msearch\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlower\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 23\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 24\u001b[0;31m df[\"Match\"] = df.apply(\n\u001b[0m\u001b[1;32m 25\u001b[0m \u001b[0;32mlambda\u001b[0m \u001b[0mx\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mfuzz\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mratio\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0msearch\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34mf\"{x['symbol']}\"\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlower\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 26\u001b[0m \u001b[0maxis\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/.local/lib/python3.9/site-packages/pandas/core/frame.py\u001b[0m in \u001b[0;36mapply\u001b[0;34m(self, func, axis, raw, result_type, args, **kwds)\u001b[0m\n\u001b[1;32m 7763\u001b[0m \u001b[0mkwds\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mkwds\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 7764\u001b[0m )\n\u001b[0;32m-> 7765\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mop\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_result\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 7766\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 7767\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mapplymap\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfunc\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mna_action\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mOptional\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mstr\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m->\u001b[0m \u001b[0mDataFrame\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/.local/lib/python3.9/site-packages/pandas/core/apply.py\u001b[0m in \u001b[0;36mget_result\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 183\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mapply_raw\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 184\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 185\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mapply_standard\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 186\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 187\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mapply_empty_result\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/.local/lib/python3.9/site-packages/pandas/core/apply.py\u001b[0m in \u001b[0;36mapply_standard\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 274\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 275\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mapply_standard\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 276\u001b[0;31m \u001b[0mresults\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mres_index\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mapply_series_generator\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 277\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 278\u001b[0m \u001b[0;31m# wrap results\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/.local/lib/python3.9/site-packages/pandas/core/apply.py\u001b[0m in \u001b[0;36mapply_series_generator\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 286\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 287\u001b[0m \u001b[0;32mwith\u001b[0m \u001b[0moption_context\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"mode.chained_assignment\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 288\u001b[0;31m \u001b[0;32mfor\u001b[0m \u001b[0mi\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mv\u001b[0m \u001b[0;32min\u001b[0m \u001b[0menumerate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mseries_gen\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 289\u001b[0m \u001b[0;31m# ignore SettingWithCopy here in case the user mutates\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 290\u001b[0m \u001b[0mresults\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mi\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mf\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mv\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/.local/lib/python3.9/site-packages/pandas/core/apply.py\u001b[0m in \u001b[0;36mseries_generator\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 408\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0marr\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mname\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mzip\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mvalues\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mindex\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 409\u001b[0m \u001b[0;31m# GH#35462 re-pin mgr in case setitem changed it\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 410\u001b[0;31m \u001b[0mser\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_mgr\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mmgr\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 411\u001b[0m \u001b[0mblk\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mvalues\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0marr\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 412\u001b[0m \u001b[0mser\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mname\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mname\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/.local/lib/python3.9/site-packages/pandas/core/generic.py\u001b[0m in \u001b[0;36m__setattr__\u001b[0;34m(self, name, value)\u001b[0m\n\u001b[1;32m 5473\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 5474\u001b[0m \u001b[0mobject\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__getattribute__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mname\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 5475\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mobject\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__setattr__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mname\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mvalue\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 5476\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0mAttributeError\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 5477\u001b[0m \u001b[0;32mpass\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mKeyboardInterrupt\u001b[0m: " - ] - } - ], - "source": [ - "\n", - "for i in tqdm(search_list):\n", - " search_index[i] = search_symbols(i)" - ] - }, - { - "cell_type": "code", - "execution_count": 89, - "metadata": {}, - "outputs": [], - "source": [ - "import pickle\n", - "\n", - "\n", - "\n", - "with open('search_index.pickle', 'wb') as handle:\n", - " pickle.dump(search_index, handle, protocol=pickle.HIGHEST_PROTOCOL)\n", - "\n", - "# with open('filename.pickle', 'rb') as handle:\n", - "# b = pickle.load(handle)\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ] -} diff --git a/symbol_router.py b/symbol_router.py index 87d6667..5144e72 100644 --- a/symbol_router.py +++ b/symbol_router.py @@ -27,7 +27,6 @@ class Router: def find_symbols(self, text: str) -> list[Symbol]: """Finds stock tickers starting with a dollar sign, and cryptocurrencies with two dollar signs in a blob of text and returns them in a list. - Only returns each match once. Example: Whats the price of $tsla? Parameters ---------- @@ -91,7 +90,8 @@ class Router: Returns ------- list[tuple[str, str]] - A list tuples of every stock sorted in order of how well they match. Each tuple contains: (Symbol, Issue Name). + A list tuples of every stock sorted in order of how well they match. + Each tuple contains: (Symbol, Issue Name). """ df = pd.concat([self.stock.symbol_list, self.crypto.symbol_list]) @@ -183,7 +183,8 @@ class Router: Returns ------- Dict[str, str] - Each symbol passed in is a key with its value being a human readable formatted string of the symbols div dates. + Each symbol passed in is a key with its value being a human readable + formatted string of the symbols div dates. """ replies = [] for symbol in symbols: @@ -207,7 +208,8 @@ class Router: Returns ------- Dict[str, str] - Each symbol passed in is a key with its value being a human readable markdown formatted string of the symbols news. + Each symbol passed in is a key with its value being a human + readable markdown formatted string of the symbols news. """ replies = [] @@ -235,7 +237,8 @@ class Router: Returns ------- Dict[str, str] - Each symbol passed in is a key with its value being a human readable formatted string of the symbols information. + Each symbol passed in is a key with its value being a human readable formatted + string of the symbols information. """ replies = [] @@ -260,7 +263,8 @@ class Router: Returns ------- pd.DataFrame - Returns a timeseries dataframe with high, low, and volume data if its available. Otherwise returns empty pd.DataFrame. + Returns a timeseries dataframe with high, low, and volume data if its available. + Otherwise returns empty pd.DataFrame. """ if isinstance(symbol, Stock): @@ -283,7 +287,8 @@ class Router: Returns ------- pd.DataFrame - Returns a timeseries dataframe with high, low, and volume data if its available. Otherwise returns empty pd.DataFrame. + Returns a timeseries dataframe with high, low, and volume data if its available. + Otherwise returns empty pd.DataFrame. """ if isinstance(symbol, Stock): return self.stock.chart_reply(symbol) @@ -304,7 +309,8 @@ class Router: Returns ------- Dict[str, str] - Each symbol passed in is a key with its value being a human readable formatted string of the symbols statistics. + Each symbol passed in is a key with its value being a human readable + formatted string of the symbols statistics. """ replies = [] @@ -329,7 +335,8 @@ class Router: Returns ------- Dict[str, str] - Each symbol passed in is a key with its value being a human readable formatted string of the symbols market cap. + Each symbol passed in is a key with its value being a human readable + formatted string of the symbols market cap. """ replies = []