Files
OpenBB/discordbot/stocks/options/options_menu.py
teh_coderer 445ec3256c Discordbot/expiry reactions (#1114)
* added load extension for options menu

* options menu: oi, iv, unusual options

* Update opt_menu.py

* Update unu.py

if no num requested set default to 10

* Update opt_menu.py

+ !oi ticker expiry request added without inputting date format. accepts 0-9 up to 10 weeks
+ added option volume plot (works same as oi)

* Create vol.py

* Create reaction_helper.py

* Cleaned up, added historical options

Cleaned up
+ added historical options data
+ added expiry reaction helper function

* Create hist.py

* cleanup

* added min/max strike price

* cleanup

* cleanup, added min/max strike price

* Update opt_menu.py

* added yfinance if no tradier token

* Update vol.py

* Update opt_menu.py

* Create oi_tradier.py

* Update oi.py

* Update opt_menu.py

* Update oi.py

* Update opt_menu.py

* Update oi.py

* Update iv.py

* Update unu.py

* Update oi_tradier.py

* Update hist.py

* Update opt_menu.py

* Update reaction_helper.py

* Create options_menu.py

* Update run_discordbot.py

* Update vol.py

* Rename discordbot/options_menu.py to discordbot/stocks/options/options_menu.py

* Delete opt_menu.py

* Update oi.py

+ min strike/max strike

* Delete oi_tradier.py

* Update options_menu.py

* Update hist.py

* Update vol.py

* Update oi.py

* Update options_menu.py

* Update options_menu.py

* Update options_menu.py

* Linting/Pre-Commit Fixes

* linting

* Linting

* linting

* pylint fixes

* Update iv.py

* Update options_menu.py

* Update reaction_helper.py

* Improved Code

- removed 148 lines same function

* Update run_discordbot.py

* Update reaction_helper.py

* Updated Options Menu

+ reaction expiry lookup
+ added unu, iv, oi, vol ,hist

* mypy fix

* msg delete delay

* Update options_menu.py

* fix

seems bot doesn't like when last emoji is empty.
added an X when strike c/p empty

* Update opt_cmd.py

* Update options_menu.py

* better handling

* Update options_menu.py

* Update opt_cmd.py

* fix stocks.opt indention

* hist.py token/error handling

* Update hist.py

* Update options_menu.py

* Update hist.py
2022-01-01 17:26:42 +00:00

458 lines
15 KiB
Python
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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))