mirror of
https://github.com/OpenBB-finance/OpenBB.git
synced 2026-10-29 13:32:08 +00:00
* 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
458 lines
15 KiB
Python
458 lines
15 KiB
Python
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))
|