1
0
mirror of https://gitlab.com/simple-stock-bots/simple-telegram-stock-bot.git synced 2025-06-16 15:06:53 +00:00

updated all docstrings

This commit is contained in:
MisterBiggs 2021-07-02 12:41:36 -07:00
parent acc0ebdd28
commit 43976e873a
8 changed files with 114 additions and 359 deletions

View File

@ -30,7 +30,7 @@ class IEX_Symbol:
Parameters Parameters
---------- ----------
IEX_TOKEN : str IEX_TOKEN : str
IEX Token IEX API Token
""" """
try: try:
self.IEX_TOKEN = os.environ["IEX"] self.IEX_TOKEN = os.environ["IEX"]
@ -47,12 +47,28 @@ class IEX_Symbol:
schedule.every().day.do(self.clear_charts) schedule.every().day.do(self.clear_charts)
def clear_charts(self) -> None: 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 = {} self.charts = {}
def get_symbol_list( def get_symbol_list(
self, return_df=False self, return_df=False
) -> Optional[Tuple[pd.DataFrame, datetime]]: ) -> 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( reg_symbols = r.get(
f"https://cloud.iexapis.com/stable/ref-data/symbols?token={self.IEX_TOKEN}", f"https://cloud.iexapis.com/stable/ref-data/symbols?token={self.IEX_TOKEN}",
@ -145,18 +161,16 @@ class IEX_Symbol:
return symbol_list return symbol_list
def price_reply(self, symbol: Stock) -> str: 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 Parameters
---------- ----------
symbols : list symbol : Stock
List of stock symbols.
Returns Returns
------- -------
Dict[str, str] str
Each symbol passed in is a key with its value being a human readable Formatted markdown
markdown formatted string of the symbols price and movement.
""" """
IEXurl = f"https://cloud.iexapis.com/stable/stock/{symbol.id}/quote?token={self.IEX_TOKEN}" IEXurl = f"https://cloud.iexapis.com/stable/stock/{symbol.id}/quote?token={self.IEX_TOKEN}"
@ -223,13 +237,12 @@ class IEX_Symbol:
Parameters Parameters
---------- ----------
symbols : list symbol : Stock
List of stock symbols.
Returns Returns
------- -------
Dict[str, str] str
Each symbol passed in is a key with its value being a human readable formatted string of the symbols div dates. Formatted markdown
""" """
if symbol.symbol.upper() in self.otc_list: if symbol.symbol.upper() in self.otc_list:
return "OTC stocks do not currently support any commands." 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." return f"${symbol.id.upper()} either doesn't exist or pays no dividend."
def news_reply(self, symbol: Stock) -> str: def news_reply(self, symbol: Stock) -> str:
"""Gets recent english news on stock symbols. """Gets most recent, english, non-paywalled news
Parameters Parameters
---------- ----------
symbols : list symbol : Stock
List of stock symbols.
Returns Returns
------- -------
Dict[str, str] str
Each symbol passed in is a key with its value being a human readable markdown formatted string of the symbols news. Formatted markdown
""" """
if symbol.symbol.upper() in self.otc_list: if symbol.symbol.upper() in self.otc_list:
return "OTC stocks do not currently support any commands." 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]) return f"News for **{symbol.id.upper()}**:\n" + "\n".join(line[:5])
def info_reply(self, symbol: Stock) -> str: def info_reply(self, symbol: Stock) -> str:
"""Gets information on stock symbols. """Gets description for Stock
Parameters Parameters
---------- ----------
symbols : List[str] symbol : Stock
List of stock symbols.
Returns Returns
------- -------
Dict[str, str] str
Each symbol passed in is a key with its value being a human readable formatted string of the symbols information. Formatted text
""" """
if symbol.symbol.upper() in self.otc_list: if symbol.symbol.upper() in self.otc_list:
return "OTC stocks do not currently support any commands." 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." return f"No information found for: {symbol}\nEither today is boring or the symbol does not exist."
def stat_reply(self, symbol: Stock) -> str: def stat_reply(self, symbol: Stock) -> str:
"""Gets key statistics for each symbol in the list """Key statistics on a Stock
Parameters Parameters
---------- ----------
symbols : List[str] symbol : Stock
List of stock symbols
Returns Returns
------- -------
Dict[str, str] str
Each symbol passed in is a key with its value being a human readable formatted string of the symbols statistics. Formatted markdown
""" """
if symbol.symbol.upper() in self.otc_list: if symbol.symbol.upper() in self.otc_list:
return "OTC stocks do not currently support any commands." return "OTC stocks do not currently support any commands."
@ -503,7 +513,7 @@ class IEX_Symbol:
Returns Returns
------- -------
list[str] list[str]
list of $ID: NAME list of $ID: NAME, CHANGE%
""" """
stocks = r.get( stocks = r.get(

View File

@ -25,6 +25,8 @@ class Symbol:
class Stock(Symbol): class Stock(Symbol):
"""Stock Market Object. Gets data from IEX Cloud"""
def __init__(self, symbol: str) -> None: def __init__(self, symbol: str) -> None:
self.symbol = symbol self.symbol = symbol
self.id = symbol self.id = symbol
@ -36,6 +38,8 @@ coins = r.get("https://api.coingecko.com/api/v3/coins/list").json()
class Coin(Symbol): class Coin(Symbol):
"""Cryptocurrency Object. Gets data from CoinGecko."""
@functools.cache @functools.cache
def __init__(self, symbol: str) -> None: def __init__(self, symbol: str) -> None:
self.symbol = symbol self.symbol = symbol

View File

@ -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) 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. 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. 📰 news - $[symbol] News about the symbol. 📰
stat - $[symbol] Key statistics about the symbol. 🔢 stat - $[symbol] Key statistics about the symbol. 🔢
dividend - $[symbol] Dividend info 📅 dividend - $[symbol] Dividend info 📅
intra - $[symbol] Plot since the last market open. 📈
trending - Trending Stocks and Cryptos. 💬 trending - Trending Stocks and Cryptos. 💬
intra - $[symbol] Plot since the last market open. 📈
chart - $[chart] Plot of the past month. 📊 chart - $[chart] Plot of the past month. 📊
""" # Not used by the bot but for updaing commands with BotFather """ # Not used by the bot but for updaing commands with BotFather

57
bot.py
View File

@ -53,7 +53,7 @@ info("Bot script started.")
def start(update: Update, context: CallbackContext): 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}") info(f"Start command ran by {update.message.chat.username}")
update.message.reply_text( update.message.reply_text(
text=t.help_text, text=t.help_text,
@ -63,7 +63,7 @@ def start(update: Update, context: CallbackContext):
def help(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}") info(f"Help command ran by {update.message.chat.username}")
update.message.reply_text( update.message.reply_text(
text=t.help_text, text=t.help_text,
@ -73,7 +73,7 @@ def help(update: Update, context: CallbackContext):
def license(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}") info(f"License command ran by {update.message.chat.username}")
update.message.reply_text( update.message.reply_text(
text=t.license, text=t.license,
@ -83,6 +83,7 @@ def license(update: Update, context: CallbackContext):
def status(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}") warning(f"Status command ran by {update.message.chat.username}")
bot_resp = datetime.datetime.now(update.message.date.tzinfo) - update.message.date 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): def donate(update: Update, context: CallbackContext):
"""Sets up donation."""
info(f"Donate command ran by {update.message.chat.username}") info(f"Donate command ran by {update.message.chat.username}")
chat_id = update.message.chat_id chat_id = update.message.chat_id
@ -133,6 +135,7 @@ def donate(update: Update, context: CallbackContext):
def precheckout_callback(update: Update, context: CallbackContext): def precheckout_callback(update: Update, context: CallbackContext):
"""Approves donation"""
info(f"precheckout_callback queried") info(f"precheckout_callback queried")
query = update.pre_checkout_query query = update.pre_checkout_query
@ -146,6 +149,7 @@ def precheckout_callback(update: Update, context: CallbackContext):
def successful_payment_callback(update: Update, context: CallbackContext): def successful_payment_callback(update: Update, context: CallbackContext):
"""Thanks user for donation"""
info(f"Successful payment!") info(f"Successful payment!")
update.message.reply_text( update.message.reply_text(
"Thank you for your donation! It goes a long way to keeping the bot free!" "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): def dividend(update: Update, context: CallbackContext):
""" """/dividend or /div command and then finds dividend info on that symbol."""
waits for /dividend or /div command and then finds dividend info on that symbol.
"""
info(f"Dividend command ran by {update.message.chat.username}") info(f"Dividend command ran by {update.message.chat.username}")
message = update.message.text message = update.message.text
chat_id = update.message.chat_id chat_id = update.message.chat_id
@ -203,9 +205,7 @@ def dividend(update: Update, context: CallbackContext):
def news(update: Update, context: CallbackContext): def news(update: Update, context: CallbackContext):
""" """/news command then finds news info on that symbol."""
waits for /news command and then finds news info on that symbol.
"""
info(f"News command ran by {update.message.chat.username}") info(f"News command ran by {update.message.chat.username}")
message = update.message.text message = update.message.text
chat_id = update.message.chat_id chat_id = update.message.chat_id
@ -230,9 +230,7 @@ def news(update: Update, context: CallbackContext):
def information(update: Update, context: CallbackContext): def information(update: Update, context: CallbackContext):
""" """/info command then finds info on that symbol."""
waits for /info command and then finds info on that symbol.
"""
info(f"Information command ran by {update.message.chat.username}") info(f"Information command ran by {update.message.chat.username}")
message = update.message.text message = update.message.text
chat_id = update.message.chat_id chat_id = update.message.chat_id
@ -257,6 +255,10 @@ def information(update: Update, context: CallbackContext):
def search(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}") info(f"Search command ran by {update.message.chat.username}")
message = update.message.text.replace("/search ", "") message = update.message.text.replace("/search ", "")
chat_id = update.message.chat_id chat_id = update.message.chat_id
@ -281,8 +283,8 @@ def search(update: Update, context: CallbackContext):
def intra(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}") 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 message = update.message.text
chat_id = update.message.chat_id chat_id = update.message.chat_id
@ -337,8 +339,8 @@ def intra(update: Update, context: CallbackContext):
def chart(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}") 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 message = update.message.text
chat_id = update.message.chat_id chat_id = update.message.chat_id
@ -390,9 +392,7 @@ def chart(update: Update, context: CallbackContext):
def stat(update: Update, context: CallbackContext): def stat(update: Update, context: CallbackContext):
""" """returns key statistics on symbol"""
https://iexcloud.io/docs/api/#key-stats
"""
info(f"Stat command ran by {update.message.chat.username}") info(f"Stat command ran by {update.message.chat.username}")
message = update.message.text message = update.message.text
chat_id = update.message.chat_id chat_id = update.message.chat_id
@ -417,9 +417,7 @@ def stat(update: Update, context: CallbackContext):
def cap(update: Update, context: CallbackContext): def cap(update: Update, context: CallbackContext):
""" """returns market cap for symbol"""
Market Cap Information
"""
info(f"Cap command ran by {update.message.chat.username}") info(f"Cap command ran by {update.message.chat.username}")
message = update.message.text message = update.message.text
chat_id = update.message.chat_id chat_id = update.message.chat_id
@ -444,9 +442,7 @@ def cap(update: Update, context: CallbackContext):
def trending(update: Update, context: CallbackContext): def trending(update: Update, context: CallbackContext):
""" """returns currently trending symbols and how much they've moved in the past trading day."""
Trending Symbols
"""
info(f"Trending command ran by {update.message.chat.username}") info(f"Trending command ran by {update.message.chat.username}")
chat_id = update.message.chat_id chat_id = update.message.chat_id
@ -462,8 +458,8 @@ def trending(update: Update, context: CallbackContext):
def inline_query(update: Update, context: CallbackContext): def inline_query(update: Update, context: CallbackContext):
""" """
Handles inline query. Handles inline query. Searches by looking if query is contained
Does a fuzzy search on input and returns stocks that are close. in the symbol and returns matches in alphabetical order.
""" """
info(f"Inline command ran by {update.message.chat.username}") info(f"Inline command ran by {update.message.chat.username}")
info(f"Query: {update.inline_query.query}") 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): def rand_pick(update: Update, context: CallbackContext):
"""For the gamblers. Returns a random symbol to buy and a sell date"""
info( info(
f"Someone is gambling! Random_pick command ran by {update.message.chat.username}" 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("cap", cap))
dp.add_handler(CommandHandler("trending", trending)) dp.add_handler(CommandHandler("trending", trending))
dp.add_handler(CommandHandler("search", search)) 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("random", rand_pick))
dp.add_handler(CommandHandler("donate", donate)) dp.add_handler(CommandHandler("donate", donate))
dp.add_handler(CommandHandler("status", status)) 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 # on noncommand i.e message - echo the message on Telegram
dp.add_handler(MessageHandler(Filters.text, symbol_detect)) dp.add_handler(MessageHandler(Filters.text, symbol_detect))

View File

@ -220,18 +220,18 @@ class cg_Crypto:
return pd.DataFrame() return pd.DataFrame()
def stat_reply(self, symbol: Coin) -> str: 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 Parameters
---------- ----------
symbols : List[str] symbol : Coin
List of coin symbols
Returns Returns
------- -------
Dict[str, str] str
Each symbol passed in is a key with its value being a human readable formatted string of the symbols statistics. Preformatted markdown.
""" """
response = r.get( response = r.get(
f"https://api.coingecko.com/api/v3/coins/{symbol.id}?localization=false", f"https://api.coingecko.com/api/v3/coins/{symbol.id}?localization=false",
timeout=5, timeout=5,
@ -253,17 +253,16 @@ class cg_Crypto:
return f"{symbol.symbol} returned an error." return f"{symbol.symbol} returned an error."
def cap_reply(self, coin: Coin) -> str: def cap_reply(self, coin: Coin) -> str:
"""Gets market Cap for each symbol in the list """Gets market cap for Coin
Parameters Parameters
---------- ----------
symbols : List[str] coin : Coin
List of coin symbols
Returns Returns
------- -------
Dict[str, str] str
Each symbol passed in is a key with its value being a human readable formatted string of the symbols markey cap. Preformatted markdown.
""" """
response = r.get( response = r.get(
f"https://api.coingecko.com/api/v3/simple/price?ids={coin.id}&vs_currencies={self.vs_currency}&include_market_cap=true", 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 return message
def info_reply(self, symbol: Coin) -> str: def info_reply(self, symbol: Coin) -> str:
"""Gets information on stock symbols. """Gets coin description
Parameters Parameters
---------- ----------
symbols : List[str] symbol : Coin
List of stock symbols.
Returns Returns
------- -------
Dict[str, str] str
Each symbol passed in is a key with its value being a human readable formatted string of the symbols information. Preformatted markdown.
""" """
response = r.get( response = r.get(
@ -322,7 +320,7 @@ class cg_Crypto:
Returns Returns
------- -------
list[str] list[str]
list of $$ID: NAME list of $$ID: NAME, CHANGE%
""" """
coins = r.get( coins = r.get(
@ -352,6 +350,17 @@ class cg_Crypto:
return trending return trending
def batch_price(self, coins: list[Coin]) -> list[str]: 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]) query = ",".join([c.id for c in coins])
prices = r.get( prices = r.get(

Binary file not shown.

View File

@ -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": "<div>\n<style scoped>\n .dataframe tbody tr th:only-of-type {\n vertical-align: middle;\n }\n\n .dataframe tbody tr th {\n vertical-align: top;\n }\n\n .dataframe thead th {\n text-align: right;\n }\n</style>\n<table border=\"1\" class=\"dataframe\">\n <thead>\n <tr style=\"text-align: right;\">\n <th></th>\n <th>id</th>\n <th>symbol</th>\n <th>name</th>\n <th>description</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <th>0</th>\n <td>A</td>\n <td>A</td>\n <td>Agilent Technologies Inc.</td>\n <td>$A: Agilent Technologies Inc.</td>\n </tr>\n <tr>\n <th>1</th>\n <td>AA</td>\n <td>AA</td>\n <td>Alcoa Corp</td>\n <td>$AA: Alcoa Corp</td>\n </tr>\n <tr>\n <th>2</th>\n <td>AAA</td>\n <td>AAA</td>\n <td>Listed Funds Trust - AAF First Priority CLO Bo...</td>\n <td>$AAA: Listed Funds Trust - AAF First Priority ...</td>\n </tr>\n <tr>\n <th>3</th>\n <td>AAAU</td>\n <td>AAAU</td>\n <td>Goldman Sachs Physical Gold ETF Shares - Goldm...</td>\n <td>$AAAU: Goldman Sachs Physical Gold ETF Shares ...</td>\n </tr>\n <tr>\n <th>4</th>\n <td>AAC</td>\n <td>AAC</td>\n <td>Ares Acquisition Corporation - Class A</td>\n <td>$AAC: Ares Acquisition Corporation - Class A</td>\n </tr>\n <tr>\n <th>...</th>\n <td>...</td>\n <td>...</td>\n <td>...</td>\n <td>...</td>\n </tr>\n <tr>\n <th>6565</th>\n <td>zyro</td>\n <td>zyro</td>\n <td>Zyro</td>\n <td>$$zyro: Zyro</td>\n </tr>\n <tr>\n <th>6566</th>\n <td>zytara-dollar</td>\n <td>zusd</td>\n <td>Zytara Dollar</td>\n <td>$$zusd: Zytara Dollar</td>\n </tr>\n <tr>\n <th>6567</th>\n <td>zyx</td>\n <td>zyx</td>\n <td>ZYX</td>\n <td>$$zyx: ZYX</td>\n </tr>\n <tr>\n <th>6568</th>\n <td>zzz-finance</td>\n <td>zzz</td>\n <td>zzz.finance</td>\n <td>$$zzz: zzz.finance</td>\n </tr>\n <tr>\n <th>6569</th>\n <td>zzz-finance-v2</td>\n <td>zzzv2</td>\n <td>zzz.finance v2</td>\n <td>$$zzzv2: zzz.finance v2</td>\n </tr>\n </tbody>\n</table>\n<p>16946 rows × 4 columns</p>\n</div>"
},
"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<ipython-input-92-559f57361529>\u001b[0m in \u001b[0;36m<module>\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<ipython-input-79-2cfb15c9428a>\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": []
}
]
}

View File

@ -27,7 +27,6 @@ class Router:
def find_symbols(self, text: str) -> list[Symbol]: def find_symbols(self, text: str) -> list[Symbol]:
"""Finds stock tickers starting with a dollar sign, and cryptocurrencies with two dollar signs """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. in a blob of text and returns them in a list.
Only returns each match once. Example: Whats the price of $tsla?
Parameters Parameters
---------- ----------
@ -91,7 +90,8 @@ class Router:
Returns Returns
------- -------
list[tuple[str, str]] 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]) df = pd.concat([self.stock.symbol_list, self.crypto.symbol_list])
@ -183,7 +183,8 @@ class Router:
Returns Returns
------- -------
Dict[str, str] 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 = [] replies = []
for symbol in symbols: for symbol in symbols:
@ -207,7 +208,8 @@ class Router:
Returns Returns
------- -------
Dict[str, str] 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 = [] replies = []
@ -235,7 +237,8 @@ class Router:
Returns Returns
------- -------
Dict[str, str] 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 = [] replies = []
@ -260,7 +263,8 @@ class Router:
Returns Returns
------- -------
pd.DataFrame 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): if isinstance(symbol, Stock):
@ -283,7 +287,8 @@ class Router:
Returns Returns
------- -------
pd.DataFrame 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): if isinstance(symbol, Stock):
return self.stock.chart_reply(symbol) return self.stock.chart_reply(symbol)
@ -304,7 +309,8 @@ class Router:
Returns Returns
------- -------
Dict[str, str] 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 = [] replies = []
@ -329,7 +335,8 @@ class Router:
Returns Returns
------- -------
Dict[str, str] 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 = [] replies = []