def test_option_expirations_invalid_status(mocker): mock_response = requests.Response() mock_response.status_code = 400 mocker.patch(target="requests.get", new=mocker.Mock(return_value=mock_response)) result_list = tradier_model.option_expirations(ticker="PM") assert result_list == []
def test_option_expirations_json_error(mocker): mock_response = requests.Response() mock_response.status_code = 200 mocker.patch.object( target=mock_response, attribute="json", side_effect=TypeError(), ) mocker.patch(target="requests.get", new=mocker.Mock(return_value=mock_response)) result_list = tradier_model.option_expirations(ticker="PM") assert result_list == []
def __init__(self, ticker: str, queue: List[str] = None): """Constructor""" super().__init__(queue) self.ticker = ticker self.prices = pd.DataFrame(columns=["Price", "Chance"]) self.selected_date = "" self.chain = None if ticker: if TRADIER_TOKEN == "REPLACE_ME": # nosec console.print("Loaded expiry dates from Yahoo Finance") self.expiry_dates = yfinance_model.option_expirations( self.ticker) else: console.print("Loaded expiry dates from Tradier") self.expiry_dates = tradier_model.option_expirations( self.ticker) else: self.expiry_dates = [] if session and gtff.USE_PROMPT_TOOLKIT: choices: dict = {c: {} for c in self.controller_choices} choices["unu"]["-s"] = {c: {} for c in self.unu_sortby_choices} choices["pcr"] = {c: {} for c in self.pcr_length_choices} choices["disp"] = {c: {} for c in self.presets} choices["scr"] = {c: {} for c in self.presets} choices["grhist"]["-g"] = { c: {} for c in self.grhist_greeks_choices } choices["load"]["-s"] = {c: {} for c in self.load_source_choices} choices["load"]["--source"] = { c: {} for c in self.hist_source_choices } choices["load"]["-s"] = {c: {} for c in self.voi_source_choices} choices["plot"]["-x"] = {c: {} for c in self.plot_vars_choices} choices["plot"]["-y"] = {c: {} for c in self.plot_vars_choices} choices["plot"]["-c"] = {c: {} for c in self.plot_custom_choices} # This menu contains dynamic choices that may change during runtime self.choices = choices self.completer = NestedCompleter.from_nested_dict(choices)
def call_load(self, other_args: List[str]): """Process load command""" parser = argparse.ArgumentParser( add_help=False, formatter_class=argparse.ArgumentDefaultsHelpFormatter, prog="load", description="Load a ticker into option menu", ) parser.add_argument( "-t", "--ticker", action="store", dest="ticker", required="-h" not in other_args, help="Stock ticker", ) parser.add_argument( "-s", "--source", choices=self.load_source_choices, dest="source", default=None, help="Source to get option expirations from", ) if other_args and "-" not in other_args[0][0]: other_args.insert(0, "-t") ns_parser = parse_known_args_and_warn(parser, other_args) if ns_parser: self.ticker = ns_parser.ticker.upper() self.update_runtime_choices() if TRADIER_TOKEN == "REPLACE_ME" or ns_parser.source == "yf": # nosec self.expiry_dates = yfinance_model.option_expirations( self.ticker) else: self.expiry_dates = tradier_model.option_expirations( self.ticker) console.print("") if self.ticker and self.selected_date: self.chain = yfinance_model.get_option_chain( self.ticker, self.selected_date)
def test_option_expirations(recorder): result_list = tradier_model.option_expirations(ticker="AAPL") recorder.capture(result_list)
def __init__(self, ticker: str, queue: List[str] = None): """Constructor""" self.op_parser = argparse.ArgumentParser(add_help=False, prog="op") self.op_parser.add_argument( "cmd", choices=self.CHOICES, ) self.completer: Union[None, NestedCompleter] = None if session and gtff.USE_PROMPT_TOOLKIT: self.choices: dict = {c: {} for c in self.CHOICES} self.choices["unu"]["-s"] = { c: {} for c in self.unu_sortby_choices } self.choices["pcr"] = {c: {} for c in self.pcr_length_choices} self.choices["disp"] = {c: {} for c in self.presets} self.choices["scr"] = {c: {} for c in self.presets} self.choices["grhist"]["-g"] = { c: {} for c in self.grhist_greeks_choices } self.choices["load"]["-s"] = { c: {} for c in self.load_source_choices } self.choices["load"]["--source"] = { c: {} for c in self.hist_source_choices } self.choices["load"]["-s"] = { c: {} for c in self.voi_source_choices } self.choices["plot"]["-x"] = { c: {} for c in self.plot_vars_choices } self.choices["plot"]["-y"] = { c: {} for c in self.plot_vars_choices } self.choices["plot"]["-c"] = { c: {} for c in self.plot_custom_choices } self.ticker = ticker self.prices = pd.DataFrame(columns=["Price", "Chance"]) self.selected_date = "" self.chain = None if ticker: if TRADIER_TOKEN == "REPLACE_ME": print("Loaded expiry dates from Yahoo Finance") self.expiry_dates = yfinance_model.option_expirations( self.ticker) else: print("Loaded expiry dates from Tradier") self.expiry_dates = tradier_model.option_expirations( self.ticker) else: self.expiry_dates = [] if queue: self.queue = queue else: self.queue = list()
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. """ logger.info("!stocks.opt %s %s %s %s", ticker, expiration, strike, put) async with ctx.typing(): await asyncio.sleep(0.2) if TRADIER_TOKEN == "REPLACE_ME": # nosec 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 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️⃣": logger.info("Reaction selected: 0") await unu_command(ctx) elif reaction.emoji == "1️⃣": logger.info("Reaction selected: 1") await expirations_command(ctx, ticker) elif reaction.emoji == "2️⃣": logger.info("Reaction selected: 2") await iv_command(ctx, ticker) elif reaction.emoji == "3️⃣": logger.info("Reaction selected: 3") await calls_command(ctx, ticker, expiration) elif reaction.emoji == "4️⃣": logger.info("Reaction selected: 4") await puts_command(ctx, ticker, expiration) elif reaction.emoji == "5️⃣": logger.info("Reaction selected: 5") await oi_command(ctx, ticker, expiration) elif reaction.emoji == "6️⃣": logger.info("Reaction selected: 6") await vol_command(ctx, ticker, expiration) elif reaction.emoji == "7️⃣": logger.info("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)
async def expiry_dates_reaction(ctx, ticker, expiry, func_cmd, call_arg: tuple = None): if TRADIER_TOKEN == "REPLACE_ME": # nosec 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 expiry is not None: try: if expiry not in dates: exp = int(expiry.replace("-", "")) if (expiry not in dates) and (exp not in index_dates): raise Exception("Enter a valid expiration date.") if expiry in dates: if call_arg is None: await func_cmd(ctx, ticker, expiry) else: await func_cmd(ctx, ticker, expiry, *call_arg) return if exp in index_dates: expiry = dates[int(expiry)] if call_arg is None: await func_cmd(ctx, ticker, expiry) else: await func_cmd(ctx, ticker, expiry, *call_arg) return except Exception as e: embed = discord.Embed( title="ERROR Options: Expiry Date", colour=cfg.COLOR, description=e, ) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) await ctx.send(embed=embed, delete_after=10.0) return if not dates: embed = discord.Embed( title="ERROR Options", colour=cfg.COLOR, description="Enter a valid stock ticker", ) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) await ctx.send(embed=embed, delete_after=10.0) return text = ("```0️⃣ " + dates[0] + "\n" "1️⃣ " + dates[1] + "\n" "2️⃣ " + dates[2] + "\n" "3️⃣ " + dates[3] + "\n" "4️⃣ " + dates[4] + "\n" "5️⃣ " + dates[5] + "\n" "6️⃣ " + dates[6] + "\n" "7️⃣ " + dates[7] + "\n" "8️⃣ " + dates[8] + "\n" "9️⃣ " + dates[9] + "```") title = " " + ticker.upper() + " Options: Expiry Date" 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=15.0) emoji_list = [ "0️⃣", "1️⃣", "2️⃣", "3️⃣", "4️⃣", "5️⃣", "6️⃣", "7️⃣", "8️⃣", "9️⃣" ] 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) for N in range(0, 10): if reaction.emoji == emoji_list[N]: logger.info("Reaction selected: %d", N) expiry = dates[N] if call_arg is None: await func_cmd(ctx, ticker, expiry) else: await func_cmd(ctx, ticker, expiry, *call_arg) 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 " + ticker.upper() + " Options: Expiry Date", ) embed.set_author( name=cfg.AUTHOR_NAME, icon_url=cfg.AUTHOR_ICON_URL, ) await ctx.send(embed=embed, delete_after=10.0)