import asyncio import discord import discordbot.config_discordbot as cfg from discordbot.run_discordbot import gst_bot from discordbot.reaction_helper import expiry_dates_reaction from gamestonk_terminal.config_terminal import TRADIER_TOKEN from gamestonk_terminal.stocks.options import tradier_model, yfinance_model # pylint: disable=C0412 from discordbot.stocks.options.calls import calls_command from discordbot.stocks.options.expirations import expirations_command from discordbot.stocks.options.hist import hist_command from discordbot.stocks.options.iv import iv_command from discordbot.stocks.options.oi import oi_command from discordbot.stocks.options.puts import puts_command from discordbot.stocks.options.unu import unu_command from discordbot.stocks.options.vol import vol_command from discordbot.stocks.options.opt_cmd import opt_command class OptionsCommands(discord.ext.commands.Cog): """Options menu.""" def __init__(self, bot: discord.ext.commands.Bot): self.bot = bot @discord.ext.commands.command(name="stocks.opt.iv", usage="[ticker]") async def iv(self, ctx: discord.ext.commands.Context, ticker: str = None): """Displays ticker options IV [Barchart] Parameters ----------- ticker: str ticker, """ async with ctx.typing(): await asyncio.sleep(0.2) await iv_command(ctx, ticker) @discord.ext.commands.command(name="stocks.opt.unu", usage="[ticker]") async def unu( self, ctx: discord.ext.commands.Context, num: int = None, ): """Unusual Options Parameters ----------- ticker: str ticker """ async with ctx.typing(): await asyncio.sleep(0.2) await unu_command(ctx, num) @discord.ext.commands.command(name="stocks.opt.exp", usage="[ticker]") async def expirations(self, ctx: discord.ext.commands.Context, ticker=""): """Get available expirations [yfinance] Parameters ---------- ticker: str Stock ticker """ await expirations_command(ctx, ticker) @discord.ext.commands.command( name="stocks.opt.calls", usage="[ticker] [expiration 0 - 9 (weeks out) or YYYY-MM-DD]", ) async def calls( self, ctx: discord.ext.commands.Context, ticker: str = None, expiry: str = None ): """Get call options for ticker and given expiration Parameters ---------- ticker: str Stock ticker expiry: str accepts 0-9 or YYYY-MM-DD 0 weeklies, 1+ for weeks out prompts reaction helper if empty """ async with ctx.typing(): await asyncio.sleep(0.2) if cfg.DEBUG: print(f"!stocks.opt.calls {ticker} {expiry}") func_cmd = calls_command await expiry_dates_reaction(ctx, ticker, expiry, func_cmd) @discord.ext.commands.command( name="stocks.opt.puts", usage="[ticker] [expiration 0 - 9 (weeks out) or YYYY-MM-DD]", ) async def puts( self, ctx: discord.ext.commands.Context, ticker: str = None, expiry: str = None ): """Get put options for ticker and given expiration Parameters ---------- ticker: str Stock ticker expiry: str accepts 0-9 or YYYY-MM-DD 0 weeklies, 1+ for weeks out prompts reaction helper if empty """ async with ctx.typing(): await asyncio.sleep(0.2) if cfg.DEBUG: print(f"!stocks.opt.puts {ticker} {expiry}") func_cmd = puts_command await expiry_dates_reaction(ctx, ticker, expiry, func_cmd) @discord.ext.commands.command( name="stocks.opt.oi", usage="[ticker] [expiration 0 - 9 (weeks out) or YYYY-MM-DD] [min strike] [max strike]", ) async def oi( self, ctx: discord.ext.commands.Context, ticker: str = None, expiry: str = None, min_sp: float = None, max_sp: float = None, ): """Display options open interest for ticker and given expiration [Max Pain] Parameters ----------- ticker: str ticker strike: float strike expiry: str accepts 0-9 or YYYY-MM-DD min_sp: float min strike price max_sp:float max strike price Sends a message to the discord user with the expiry dates if empty. The user can then select a reaction to trigger the selected date. """ async with ctx.typing(): await asyncio.sleep(0.2) if cfg.DEBUG: print(f"!stocks.opt.oi {ticker} {expiry} {min_sp} {max_sp}") call_arg = (min_sp, max_sp) func_cmd = oi_command await expiry_dates_reaction(ctx, ticker, expiry, func_cmd, call_arg) @discord.ext.commands.command( name="stocks.opt.hist", usage="[ticker] [strike] [c or p (call/put)] [expiration 0 - 9 (weeks out) or YYYY-MM-DD]", ) async def hist( self, ctx: discord.ext.commands.Context, ticker: str = None, strike: float = None, put="", expiry: str = None, ): """Display chart of given option historical price [Tradier] Parameters ----------- ticker: str ticker strike: float strike put: bool c for call p for put expiry: str accepts 0-9 or YYYY-MM-DD Sends a message to the discord user with the expiry dates if empty. The user can then select a reaction to trigger the selected date. """ async with ctx.typing(): await asyncio.sleep(0.2) try: if cfg.DEBUG: print(f"!stocks.opt.hist {ticker} {strike} {put} {expiry}") error = "" if TRADIER_TOKEN == "REPLACE_ME": raise Exception("Tradier Token is required") if expiry in ("c", "p"): raise Exception( 'Invalid format.\n\nUse:\n```bash\n"!stocks.opt.hist {ticker} {strike} {c/p}"```' '```bash\n"!stocks.opt {ticker} {expiration} {strike} {c/p}"```' ) if strike is None or put == "": raise Exception( 'A strike and c/p is required\n```bash\n"!stocks.opt.hist {ticker} {strike} {c/p}"```' ) if put == "p": put = bool(True) if put == "c": put = bool(False) call_arg = ( strike, put, ) func_cmd = hist_command await expiry_dates_reaction(ctx, ticker, expiry, func_cmd, call_arg) except TypeError: error = ( 'Invalid format.\n\nUse:\n```bash\n"!stocks.opt.hist {ticker} {strike} {c/p}"```' '```bash\n"!stocks.opt {ticker} {expiration} {strike} {c/p}"```' ) except Exception as e: error = str(e) finally: if error: embed = discord.Embed( title="ERROR Options: History", colour=cfg.COLOR, description=error, ) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) await ctx.send(embed=embed, delete_after=30.0) @discord.ext.commands.command( name="stocks.opt.vol", usage="[ticker] [expiration 0 - 9 (weeks out) or YYYY-MM-DD] [min strike] [max strike]", ) async def vol( self, ctx: discord.ext.commands.Context, ticker: str = None, expiry: str = None, min_sp: float = None, max_sp: float = None, ): """Display options volume for ticker and given expiration Parameters ----------- ticker: str ticker strike: float strike expiry: str accepts 0-9 or YYYY-MM-DD min_sp: float min strike price max_sp:float max strike price Sends a message to the discord user with the expiry dates if empty. The user can then select a reaction to trigger the selected date. """ async with ctx.typing(): await asyncio.sleep(0.2) if cfg.DEBUG: print(f"!stocks.opt.vol {ticker} {expiry} {min_sp} {max_sp}") call_arg = (min_sp, max_sp) func_cmd = vol_command await expiry_dates_reaction(ctx, ticker, expiry, func_cmd, call_arg) # pylint: disable=R0912 @discord.ext.commands.command(name="stocks.opt") async def opt( self, ctx: discord.ext.commands.Context, ticker="", expiration="", strike="", put="", ): """Stocks Context - Shows Options Menu Run `!help OptionsCommands` to see the list of available commands. Returns ------- Sends a message to the discord user with the commands from the stocks/options context. The user can then select a reaction to trigger a command. """ async with ctx.typing(): await asyncio.sleep(0.2) if TRADIER_TOKEN == "REPLACE_ME": dates = yfinance_model.option_expirations(ticker) else: dates = tradier_model.option_expirations(ticker) index_dates = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] if cfg.DEBUG: print(f"!stocks.opt {ticker} {expiration} {strike} {put}") if not ticker: current = 0 text = ( "```0️⃣ !stocks.opt.unu```\n" "Provide a ticker and expiration date with this menu,\n" "\ne.g.\n!stocks.opt TSLA 0-9\n!stocks.opt TSLA 2021-06-04" ) if (ticker != "") and (expiration == ""): current = 1 text = ( "```0️⃣ !stocks.opt.unu\n" f"1️⃣ !stocks.opt.exp {ticker}\n" f"2️⃣ !stocks.opt.iv {ticker}\n```" ) if expiration: current = 2 exp = int(expiration.replace("-", "")) if exp > 9 and (expiration not in dates) and (exp not in index_dates): call_arg = (strike, put) func_cmd = opt_command expiry = None await expiry_dates_reaction(ctx, ticker, expiry, func_cmd, call_arg) return if exp in index_dates: expiration = dates[int(expiration)] hist = f"7️⃣ !stocks.opt.hist {ticker} (strike*) (c/p*) {expiration}\n\n* Required" if strike and put: hist = f"7️⃣ !stocks.opt.hist {ticker} {strike} {put} {expiration}" current = 3 text = ( "```0️⃣ !stocks.opt.unu\n" f"1️⃣ !stocks.opt.exp {ticker}\n" f"2️⃣ !stocks.opt.iv {ticker}\n" f"3️⃣ !stocks.opt.calls {ticker} {expiration} \n" f"4️⃣ !stocks.opt.puts {ticker} {expiration} \n" f"5️⃣ !stocks.opt.oi {ticker} {expiration} \n" f"6️⃣ !stocks.opt.vol {ticker} {expiration} \n" f"{hist}```" ) if put == "p": put = bool(True) if put == "c": put = bool(False) title = "Stocks: Options Menu" embed = discord.Embed(title=title, description=text, colour=cfg.COLOR) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) msg = await ctx.send(embed=embed, delete_after=60.0) if current == 0: emoji_list = ["0️⃣"] if current == 1: emoji_list = ["0️⃣", "1️⃣", "2️⃣"] if current == 2: emoji_list = ["0️⃣", "1️⃣", "2️⃣", "3️⃣", "4️⃣", "5️⃣", "6️⃣"] if current == 3: emoji_list = ["0️⃣", "1️⃣", "2️⃣", "3️⃣", "4️⃣", "5️⃣", "6️⃣", "7️⃣"] for emoji in emoji_list: await msg.add_reaction(emoji) def check(reaction, user): return user == ctx.message.author and str(reaction.emoji) in emoji_list try: reaction, _ = await gst_bot.wait_for( "reaction_add", timeout=cfg.MENU_TIMEOUT, check=check ) if reaction.emoji == "0️⃣": if cfg.DEBUG: print("Reaction selected: 0") await unu_command(ctx) elif reaction.emoji == "1️⃣": if cfg.DEBUG: print("Reaction selected: 1") await expirations_command(ctx, ticker) elif reaction.emoji == "2️⃣": if cfg.DEBUG: print("Reaction selected: 2") await iv_command(ctx, ticker) elif reaction.emoji == "3️⃣": if cfg.DEBUG: print("Reaction selected: 3") await calls_command(ctx, ticker, expiration) elif reaction.emoji == "4️⃣": if cfg.DEBUG: print("Reaction selected: 4") await puts_command(ctx, ticker, expiration) elif reaction.emoji == "5️⃣": if cfg.DEBUG: print("Reaction selected: 5") await oi_command(ctx, ticker, expiration) elif reaction.emoji == "6️⃣": if cfg.DEBUG: print("Reaction selected: 6") await vol_command(ctx, ticker, expiration) elif reaction.emoji == "7️⃣": if cfg.DEBUG: print("Reaction selected: 7") strike = float(strike) await hist_command(ctx, ticker, expiration, strike, put) for emoji in emoji_list: await msg.remove_reaction(emoji, ctx.bot.user) except asyncio.TimeoutError: for emoji in emoji_list: await msg.remove_reaction(emoji, ctx.bot.user) if cfg.DEBUG: embed = discord.Embed( description="Error timeout - you snooze you lose! 😋", colour=cfg.COLOR, title="TIMEOUT Stocks: Options Menu", ).set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) await ctx.send(embed=embed, delete_after=30.0) def setup(bot: discord.ext.commands.Bot): gst_bot.add_cog(OptionsCommands(bot))