Exemplo n.º 1
0
    def __init__(self, ticker: str, expiration: str, queue: List[str] = None):
        """Constructor"""
        super().__init__(queue)

        self.chain = get_option_chain(ticker, expiration)
        self.calls = list(
            zip(
                self.chain.calls["strike"].tolist(),
                self.chain.calls["lastPrice"].tolist(),
            ))
        self.puts = list(
            zip(
                self.chain.puts["strike"].tolist(),
                self.chain.puts["lastPrice"].tolist(),
            ))
        self.ticker = ticker
        self.current_price = get_price(ticker)
        self.expiration = expiration
        self.options: List[Dict[str, str]] = []
        self.underlying = 0
        self.call_index_choices = range(len(self.calls))
        self.put_index_choices = range(len(self.puts))

        if session and gtff.USE_PROMPT_TOOLKIT:
            choices: dict = {c: {} for c in self.controller_choices}
            choices["pick"] = {c: {} for c in self.underlying_asset_choices}
            choices["add"] = {
                str(c): {}
                for c in list(range(max(len(self.puts), len(self.calls))))
            }
            # This menu contains dynamic choices that may change during runtime
            self.choices = choices
            self.completer = NestedCompleter.from_nested_dict(choices)
Exemplo n.º 2
0
    def call_exp(self, other_args: List[str]):
        """Process exp command"""
        parser = argparse.ArgumentParser(
            add_help=False,
            formatter_class=argparse.ArgumentDefaultsHelpFormatter,
            prog="exp",
            description="See and set expiration date",
        )
        parser.add_argument(
            "-i",
            "--index",
            dest="index",
            action="store",
            type=int,
            default=-1,
            choices=range(len(self.expiry_dates)),
            help="Select index for expiry date.",
        )
        parser.add_argument(
            "-d",
            "--date",
            dest="date",
            type=str,
            choices=self.expiry_dates + [""],
            help="Select date (YYYY-MM-DD)",
            default="",
        )

        if other_args and "-" not in other_args[0][0]:
            other_args.insert(0, "-i")
        ns_parser = parse_known_args_and_warn(parser, other_args)
        if ns_parser:
            if self.ticker:
                # Print possible expiry dates
                if ns_parser.index == -1 and not ns_parser.date:
                    console.print("\nAvailable expiry dates:")
                    for i, d in enumerate(self.expiry_dates):
                        console.print(f"   {(2 - len(str(i))) * ' '}{i}.  {d}")
                    console.print("")
                elif ns_parser.date:
                    if ns_parser.date in self.expiry_dates:
                        console.print(f"Expiration set to {ns_parser.date} \n")
                        self.selected_date = ns_parser.date
                        self.update_runtime_choices()
                    else:
                        console.print("Expiration not an option")
                else:
                    expiry_date = self.expiry_dates[ns_parser.index]
                    console.print(f"Expiration set to {expiry_date} \n")
                    self.selected_date = expiry_date
                    self.update_runtime_choices()

                if self.selected_date:
                    self.chain = yfinance_model.get_option_chain(
                        self.ticker, self.selected_date)
                    self.update_runtime_choices()
            else:
                console.print("Please load a ticker using `load <ticker>`.\n")
def test_get_option_chain(mocker, recorder):
    # FORCE SINGLE THREADING
    yf_download = yfinance_model.yf.download

    def mock_yf_download(*args, **kwargs):
        kwargs["threads"] = False
        return yf_download(*args, **kwargs)

    mocker.patch("yfinance.download", side_effect=mock_yf_download)

    result_tuple = yfinance_model.get_option_chain(
        ticker="PM",
        expiration="2022-01-07",
    )
    result_tuple = (result_tuple.calls, result_tuple.puts)

    recorder.capture_list(result_tuple)
Exemplo n.º 4
0
    def __init__(self, ticker: str, expiration: str, queue: List[str] = None):
        """Construct"""
        self.payoff_parser = argparse.ArgumentParser(add_help=False,
                                                     prog="payoff")
        self.payoff_parser.add_argument("cmd", choices=self.CHOICES)

        self.chain = get_option_chain(ticker, expiration)
        self.calls = list(
            zip(
                self.chain.calls["strike"].tolist(),
                self.chain.calls["lastPrice"].tolist(),
            ))
        self.puts = list(
            zip(
                self.chain.puts["strike"].tolist(),
                self.chain.puts["lastPrice"].tolist(),
            ))
        self.ticker = ticker
        self.current_price = get_price(ticker)
        self.expiration = expiration
        self.options: List[Dict[str, str]] = []
        self.underlying = 0

        self.call_index_choices = range(len(self.calls))
        self.put_index_choices = range(len(self.puts))

        self.completer: Union[None, NestedCompleter] = None

        if session and gtff.USE_PROMPT_TOOLKIT:

            self.choices: dict = {c: {} for c in self.CHOICES}
            self.choices["pick"] = {
                c: {}
                for c in self.underlying_asset_choices
            }
            self.choices["add"] = {
                str(c): {}
                for c in list(range(max(len(self.puts), len(self.calls))))
            }

        if queue:
            self.queue = queue
        else:
            self.queue = list()
Exemplo n.º 5
0
    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)
Exemplo n.º 6
0
def plot_volume_open_interest(
    ticker: str,
    expiry: str,
    min_sp: float,
    max_sp: float,
    min_vol: float,
    export: str = "",
    external_axes: Optional[List[plt.Axes]] = None,
):
    """Plot volume and open interest

    Parameters
    ----------
    ticker: str
        Stock ticker
    expiry: str
        Option expiration
    min_sp: float
        Min strike price
    max_sp: float
        Max strike price
    min_vol: float
        Min volume to consider
    export: str
        Format for exporting data
    external_axes : Optional[List[plt.Axes]], optional
        External axes (1 axis is expected in the list), by default None
    """

    options = yfinance_model.get_option_chain(ticker, expiry)
    calls = options.calls
    puts = options.puts
    current_price = float(yf.Ticker(ticker).info["regularMarketPrice"])

    # Process Calls Data
    df_calls = calls.pivot_table(index="strike",
                                 values=["volume", "openInterest"],
                                 aggfunc="sum").reindex()
    df_calls["strike"] = df_calls.index
    df_calls["type"] = "calls"
    df_calls["openInterest"] = df_calls["openInterest"]
    df_calls["volume"] = df_calls["volume"]
    df_calls["oi+v"] = df_calls["openInterest"] + df_calls["volume"]
    df_calls["spot"] = round(current_price, 2)

    df_puts = puts.pivot_table(index="strike",
                               values=["volume", "openInterest"],
                               aggfunc="sum").reindex()
    df_puts["strike"] = df_puts.index
    df_puts["type"] = "puts"
    df_puts["openInterest"] = df_puts["openInterest"]
    df_puts["volume"] = -df_puts["volume"]
    df_puts["openInterest"] = -df_puts["openInterest"]
    df_puts["oi+v"] = df_puts["openInterest"] + df_puts["volume"]
    df_puts["spot"] = round(current_price, 2)

    call_oi = calls.set_index("strike")["openInterest"] / 1000
    put_oi = puts.set_index("strike")["openInterest"] / 1000

    df_opt = pd.merge(call_oi, put_oi, left_index=True, right_index=True)
    df_opt = df_opt.rename(columns={
        "openInterest_x": "OI_call",
        "openInterest_y": "OI_put"
    })

    max_pain = op_helpers.calculate_max_pain(df_opt)

    if min_vol == -1 and min_sp == -1 and max_sp == -1:
        # If no argument provided, we use the percentile 50 to get 50% of upper volume data
        volume_percentile_threshold = 50
        min_vol_calls = np.percentile(df_calls["oi+v"],
                                      volume_percentile_threshold)
        min_vol_puts = np.percentile(df_puts["oi+v"],
                                     volume_percentile_threshold)

        df_calls = df_calls[df_calls["oi+v"] > min_vol_calls]
        df_puts = df_puts[df_puts["oi+v"] < min_vol_puts]

    else:
        if min_vol > -1:
            df_calls = df_calls[df_calls["oi+v"] > min_vol]
            df_puts = df_puts[df_puts["oi+v"] < -min_vol]

        if min_sp > -1:
            df_calls = df_calls[df_calls["strike"] > min_sp]
            df_puts = df_puts[df_puts["strike"] > min_sp]

        if max_sp > -1:
            df_calls = df_calls[df_calls["strike"] < max_sp]
            df_puts = df_puts[df_puts["strike"] < max_sp]

    if df_calls.empty and df_puts.empty:
        console.print(
            "The filtering applied is too strong, there is no data available for such conditions.\n"
        )
        return

    # Initialize the matplotlib figure
    if external_axes is None:
        _, ax = plt.subplots(figsize=plot_autoscale(), dpi=cfp.PLOT_DPI)
    else:
        if len(external_axes) != 1:
            logger.error("Expected list of one axis item.")
            console.print("[red]Expected list of one axis item./n[/red]")
            return
        (ax, ) = external_axes

    # make x axis symmetric
    axis_origin = max(abs(max(df_puts["oi+v"])), abs(max(df_calls["oi+v"])))
    ax.set_xlim(-axis_origin, +axis_origin)

    g = sns.barplot(
        x="oi+v",
        y="strike",
        data=df_calls,
        label="Calls: Open Interest",
        color="lightgreen",
        orient="h",
    )

    g = sns.barplot(
        x="volume",
        y="strike",
        data=df_calls,
        label="Calls: Volume",
        color="green",
        orient="h",
    )

    g = sns.barplot(
        x="oi+v",
        y="strike",
        data=df_puts,
        label="Puts: Open Interest",
        color="pink",
        orient="h",
    )

    g = sns.barplot(
        x="volume",
        y="strike",
        data=df_puts,
        label="Puts: Volume",
        color="red",
        orient="h",
    )

    # draw spot line
    s = [float(strike.get_text()) for strike in ax.get_yticklabels()]
    spot_index = bisect_left(
        s, current_price)  # find where the spot is on the graph
    spot_line = ax.axhline(spot_index, ls="--", alpha=0.3)

    # draw max pain line
    max_pain_index = bisect_left(s, max_pain)
    max_pain_line = ax.axhline(max_pain_index, ls="-", alpha=0.3, color="red")
    max_pain_line.set_linewidth(5)

    # format ticklabels without - for puts
    g.set_xticks(g.get_xticks())
    xlabels = [f"{x:,.0f}".replace("-", "") for x in g.get_xticks()]
    g.set_xticklabels(xlabels)

    ax.set_title(
        f"{ticker} volumes for {expiry} \n(open interest displayed only during market hours)",
    )
    ax.invert_yaxis()

    handles, _ = ax.get_legend_handles_labels()
    handles.append(spot_line)
    handles.append(max_pain_line)

    # create legend labels + add to graph
    labels = [
        "Calls open interest",
        "Calls volume ",
        "Puts open interest",
        "Puts volume",
        "Current stock price",
        f"Max pain = {max_pain}",
    ]

    ax.legend(handles=handles[:], labels=labels, loc="lower left")
    sns.despine(left=True, bottom=True)
    theme.style_primary_axis(ax)

    if external_axes is None:
        theme.visualize_output()

    export_data(
        export,
        os.path.dirname(os.path.abspath(__file__)),
        "voi_yf",
        options,
    )
Exemplo n.º 7
0
def plot_plot(
    ticker: str,
    expiration: str,
    put: bool,
    x: str,
    y: str,
    custom: str,
    export: str,
    external_axes: Optional[List[plt.Axes]] = None,
) -> None:
    """Generate a graph custom graph based on user input

    Parameters
    ----------
    ticker: str
        Stock ticker
    expiration: str
        Option expiration
    min_sp: float
        Min strike price
    put: bool
        put option instead of call
    x: str
        variable to display in x axis
    y: str
        variable to display in y axis
    custom: str
        type of plot
    export: str
        type of data to export
    external_axes : Optional[List[plt.Axes]], optional
        External axes (1 axis is expected in the list), by default None
    """
    convert = {
        "ltd": "lastTradeDate",
        "s": "strike",
        "lp": "lastPrice",
        "b": "bid",
        "a": "ask",
        "c": "change",
        "pc": "percentChange",
        "v": "volume",
        "oi": "openInterest",
        "iv": "impliedVolatility",
    }
    if custom == "smile":
        x = "strike"
        y = "impliedVolatility"
    else:
        x = convert[x]
        y = convert[y]
    varis = op_helpers.opt_chain_cols
    chain = yfinance_model.get_option_chain(ticker, expiration)
    values = chain.puts if put else chain.calls

    if external_axes is None:
        _, ax = plt.subplots(figsize=plot_autoscale(), dpi=cfp.PLOT_DPI)
    else:
        if len(external_axes) != 1:
            logger.error("Expected list of one axis item.")
            console.print("[red]Expected list of one axis item./n[/red]")
            return
        (ax, ) = external_axes

    x_data = values[x]
    y_data = values[y]
    ax.plot(x_data, y_data, "--bo")
    option = "puts" if put else "calls"
    ax.set_title(
        f"{varis[y]['label']} vs. {varis[x]['label']} for {ticker} {option} on {expiration}"
    )
    ax.set_ylabel(varis[y]["label"])
    ax.set_xlabel(varis[x]["label"])
    if varis[x]["format"] == "date":
        ax.get_xaxis().set_major_formatter(mdates.DateFormatter("%Y/%m/%d"))
        ax.get_xaxis().set_major_locator(mdates.DayLocator(interval=1))
    elif varis[x]["format"]:
        ax.get_xaxis().set_major_formatter(varis[x]["format"])
    if varis[y]["format"] == "date":
        ax.get_yaxis().set_major_formatter(mdates.DateFormatter("%Y/%m/%d"))
        ax.get_yaxis().set_major_locator(mdates.DayLocator(interval=1))
    elif varis[y]["format"]:
        ax.get_yaxis().set_major_formatter(varis[y]["format"])
    theme.style_primary_axis(ax)

    if external_axes is None:
        theme.visualize_output()
    export_data(export, os.path.dirname(os.path.abspath(__file__)), "plot")
Exemplo n.º 8
0
def smile_command(
    ticker: str = None,
    expiry: str = "",
    min_sp: float = None,
    max_sp: float = None,
):
    """Options Smile"""

    # Debug
    if imps.DEBUG:
        logger.debug("opt smile %s %s %s %s", ticker, expiry, min_sp, max_sp)

    # Check for argument
    if ticker is None:
        raise Exception("Stock ticker is required")

    dates = yfinance_model.option_expirations(ticker)

    if not dates:
        raise Exception("Stock ticker is invalid")

    options = yfinance_model.get_option_chain(ticker, expiry)
    calls = options.calls.fillna(0.0)
    puts = options.puts.fillna(0.0)
    current_price = yfinance_model.get_price(ticker)

    min_strike = 0.60 * current_price
    max_strike = 1.95 * current_price

    if len(calls) > 40:
        min_strike = 0.60 * current_price
        max_strike = 1.50 * current_price

    if min_sp:
        min_strike = min_sp
    if max_sp:
        max_strike = max_sp

    fig = go.Figure()

    fig.add_trace(
        go.Scatter(
            x=calls["strike"],
            y=calls["impliedVolatility"].interpolate(method="nearest"),
            name="Calls",
            mode="lines+markers",
            marker=dict(
                color="#00ACFF",
                size=4.5,
            ),
            line=dict(color="#00ACFF", width=2, dash="dash"),
        ))

    fig.add_trace(
        go.Scatter(
            x=puts["strike"],
            y=puts["impliedVolatility"].interpolate(method="nearest"),
            name="Puts",
            mode="lines+markers",
            marker=dict(
                color="#e4003a",
                size=4.5,
            ),
            line=dict(color="#e4003a", width=2, dash="dash"),
        ))

    if imps.PLT_WATERMARK:
        fig.add_layout_image(imps.PLT_WATERMARK)
    fig.update_xaxes(
        range=[min_strike, max_strike],
        constrain="domain",
    )
    fig.update_layout(
        margin=dict(l=20, r=0, t=60, b=20),
        template=imps.PLT_SCAT_STYLE_TEMPLATE,
        font=imps.PLT_FONT,
        title=
        f"<b>Implied Volatility vs. Strike for {ticker.upper()} expiring {expiry}</b>",
        title_x=0.5,
        legend_title="",
        xaxis_title="Strike",
        yaxis_title="Implied Volatility",
        yaxis=dict(
            side="right",
            fixedrange=False,
            nticks=20,
        ),
        xaxis=dict(
            rangeslider=dict(visible=False),
            nticks=20,
        ),
        legend=dict(yanchor="top", y=0.99, xanchor="left", x=0.01),
        dragmode="pan",
    )

    imagefile = "opt_smile.png"

    # Check if interactive settings are enabled
    plt_link = ""
    if imps.INTERACTIVE:
        plt_link = imps.inter_chart(fig, imagefile, callback=False)

    fig.update_layout(
        width=800,
        height=500,
    )

    imagefile = imps.image_border(imagefile, fig=fig)

    return {
        "title":
        f"Implied Volatility vs. Stirke for {ticker.upper()} expiring {expiry}",
        "description": plt_link,
        "imagefile": imagefile,
    }
Exemplo n.º 9
0
def show_greeks(
    ticker: str,
    div_cont: float,
    expire: str,
    rf: float = None,
    opt_type: int = 1,
    mini: float = None,
    maxi: float = None,
    show_all: bool = False,
) -> None:
    """
    Shows the greeks for a given option

    Parameters
    ----------
    ticker : str
        The ticker value of the option
    div_cont : float
        The dividend continuous rate
    expire : str
        The date of expiration
    rf : float
        The risk-free rate
    opt_type : Union[1, -1]
        The option type 1 is for call and -1 is for put
    mini : float
        The minimum strike price to include in the table
    maxi : float
        The maximum strike price to include in the table
    all : bool
        Whether to show all greeks
    """

    s = yfinance_model.get_price(ticker)
    chains = yfinance_model.get_option_chain(ticker, expire)
    chain = chains.calls if opt_type == 1 else chains.puts

    if mini is None:
        mini = chain.strike.quantile(0.25)
    if maxi is None:
        maxi = chain.strike.quantile(0.75)

    chain = chain[chain["strike"] >= mini]
    chain = chain[chain["strike"] <= maxi]

    risk_free = rf if rf is not None else get_rf()
    expire_dt = datetime.strptime(expire, "%Y-%m-%d")
    dif = (expire_dt - datetime.now()).seconds / (60 * 60 * 24)

    strikes = []
    for _, row in chain.iterrows():
        vol = row["impliedVolatility"]
        opt = Option(s, row["strike"], risk_free, div_cont, dif, vol, opt_type)
        result = [
            row["strike"],
            row["impliedVolatility"],
            opt.Delta(),
            opt.Gamma(),
            opt.Vega(),
            opt.Theta(),
        ]
        if show_all:
            result += [
                opt.Rho(),
                opt.Phi(),
                opt.Charm(),
                opt.Vanna(0.01),
                opt.Vomma(0.01),
            ]
        strikes.append(result)

    columns = [
        "Strike",
        "Implied Vol",
        "Delta",
        "Gamma",
        "Vega",
        "Theta",
    ]
    if show_all:
        columns += ["Rho", "Phi", "Charm", "Vanna", "Vomma"]
    df = pd.DataFrame(strikes, columns=columns)
    print_rich_table(df,
                     headers=list(df.columns),
                     show_index=False,
                     title="Greeks")
    console.print()
    return None
Exemplo n.º 10
0
async def vol_command(
    ctx,
    ticker: str = None,
    expiry: str = "",
    min_sp: float = None,
    max_sp: float = None,
):
    """Options VOL"""

    try:

        # Debug
        if cfg.DEBUG:
            logger.debug("!stocks.opt.vol %s %s %s %s", ticker, expiry, min_sp, max_sp)

        # Check for argument
        if ticker is None:
            raise Exception("Stock ticker is required")

        dates = yfinance_model.option_expirations(ticker)

        if not dates:
            raise Exception("Stock ticker is invalid")

        options = yfinance_model.get_option_chain(ticker, expiry)
        current_price = yfinance_model.get_price(ticker)

        if min_sp is None:
            min_strike = 0.75 * current_price
        else:
            min_strike = min_sp

        if max_sp is None:
            max_strike = 1.25 * current_price
        else:
            max_strike = max_sp

        calls = options.calls
        puts = options.puts
        call_v = calls.set_index("strike")["volume"] / 1000
        put_v = puts.set_index("strike")["volume"] / 1000
        plt.style.use("seaborn")
        fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=cfp.PLOT_DPI)

        put_v.plot(
            x="strike",
            y="volume",
            label="Puts",
            ax=ax,
            marker="o",
            ls="-",
            c="r",
        )
        call_v.plot(
            x="strike",
            y="volume",
            label="Calls",
            ax=ax,
            marker="o",
            ls="-",
            c="g",
        )
        ax.axvline(
            current_price, lw=2, c="k", ls="--", label="Current Price", alpha=0.7
        )
        ax.grid("on")
        ax.set_xlabel("Strike Price")
        ax.set_ylabel("Volume (1k) ")
        ax.set_xlim(min_strike, max_strike)

        ax.set_title(f"Volume for {ticker.upper()} expiring {expiry}")
        plt.legend(loc=0)
        fig.tight_layout(pad=1)

        imagefile = "opt_vol.png"
        plt.savefig("opt_vol.png")
        image = discord.File(imagefile)

        if cfg.DEBUG:
            logger.debug("Image: %s", imagefile)
        title = f"Volume for {ticker.upper()} expiring {expiry}"
        embed = discord.Embed(title=title, colour=cfg.COLOR)
        embed.set_image(url="attachment://opt_vol.png")
        embed.set_author(
            name=cfg.AUTHOR_NAME,
            icon_url=cfg.AUTHOR_ICON_URL,
        )
        os.remove("opt_vol.png")

        await ctx.send(embed=embed, file=image)

    except Exception as e:
        embed = discord.Embed(
            title="ERROR Options: Volume",
            colour=cfg.COLOR,
            description=e,
        )
        embed.set_author(
            name=cfg.AUTHOR_NAME,
            icon_url=cfg.AUTHOR_ICON_URL,
        )

        await ctx.send(embed=embed)
Exemplo n.º 11
0
def plot_vol(
    ticker: str,
    expiry: str,
    min_sp: float,
    max_sp: float,
    calls_only: bool,
    puts_only: bool,
    export: str = "",
    external_axes: Optional[List[plt.Axes]] = None,
):
    """Plot volume

    Parameters
    ----------
    ticker: str
        Ticker
    expiry: str
        Expiry date for options
    min_sp: float
        Min strike to consider
    max_sp: float
        Max strike to consider
    calls_only: bool
        Show calls only
    puts_only: bool
        Show puts only
    export: str
        Format to export file
    external_axes : Optional[List[plt.Axes]], optional
        External axes (1 axis is expected in the list), by default None
    """
    options = yfinance_model.get_option_chain(ticker, expiry)
    calls = options.calls
    puts = options.puts
    current_price = float(yf.Ticker(ticker).info["regularMarketPrice"])

    if min_sp == -1:
        min_strike = 0.75 * current_price
    else:
        min_strike = min_sp

    if max_sp == -1:
        max_strike = 1.25 * current_price
    else:
        max_strike = max_sp

    if calls_only and puts_only:
        console.print("Both flags selected, please select one", "\n")
        return

    call_v = calls.set_index("strike")["volume"] / 1000
    put_v = puts.set_index("strike")["volume"] / 1000
    if external_axes is None:
        _, ax = plt.subplots(figsize=plot_autoscale(), dpi=cfp.PLOT_DPI)
    else:
        if len(external_axes) != 1:
            console.print("[red]Expected list of one axis item./n[/red]")
            return
        (ax,) = external_axes

    if not calls_only:
        put_v.plot(
            x="strike",
            y="volume",
            label="Puts",
            ax=ax,
            marker="o",
            ls="-",
        )
    if not puts_only:
        call_v.plot(
            x="strike",
            y="volume",
            label="Calls",
            ax=ax,
            marker="o",
            ls="-",
        )
    ax.axvline(current_price, lw=2, ls="--", label="Current Price", alpha=0.7)
    ax.set_xlabel("Strike Price")
    ax.set_ylabel("Volume (1k) ")
    ax.set_xlim(min_strike, max_strike)
    ax.legend()
    ax.set_title(f"Volume for {ticker.upper()} expiring {expiry}")
    theme.style_primary_axis(ax)

    if external_axes is None:
        theme.visualize_output()
    export_data(
        export,
        os.path.dirname(os.path.abspath(__file__)),
        "vol_yf",
        options,
    )
Exemplo n.º 12
0
def risk_neutral_vals(
    ticker: str,
    exp: str,
    put: bool,
    df: pd.DataFrame,
    mini: float,
    maxi: float,
    risk: float,
) -> None:
    """Prints current options prices and risk neutral values [Source: Yahoo Finance]

    Parameters
    ----------
    ticker : str
        Ticker to get expirations for
    exp : str
        Expiration to use for options
    put : bool
        Whether to use puts or calls
    df : pd.DataFrame
        Estimates for stocks prices and probabilities
    mini : float
        Minimum strike price to show
    maxi : float
        Maximum strike price to show
    risk : float
        The risk-free rate for the asset
    """
    if put:
        chain = get_option_chain(ticker, exp).puts
    else:
        chain = get_option_chain(ticker, exp).calls

    r_date = datetime.strptime(exp, "%Y-%m-%d").date()
    delta = (r_date - date.today()).days
    vals = []
    if risk is None:
        risk = get_rf()
    for _, row in chain.iterrows():
        vals.append([
            row["strike"],
            row["lastPrice"],
            op_helpers.rn_payoff(row["strike"], df, put, delta, risk),
        ])
    new_df = pd.DataFrame(vals,
                          columns=["Strike", "Last Price", "Value"],
                          dtype=float)
    new_df["Difference"] = new_df["Last Price"] - new_df["Value"]

    if mini is None:
        mini = new_df.Strike.quantile(0.25)
    if maxi is None:
        maxi = new_df.Strike.quantile(0.75)

    new_df = new_df[new_df["Strike"] >= mini]
    new_df = new_df[new_df["Strike"] <= maxi]

    print_rich_table(
        new_df,
        headers=[x.title() for x in new_df.columns],
        show_index=False,
        title="Risk Neutral Values",
    )
    console.print()
Exemplo n.º 13
0
def chain_command(
    ticker: str = None,
    expiry: str = None,
    opt_type: str = None,
    min_sp: float = None,
    max_sp: float = None,
):
    """Show calls/puts for given ticker and expiration"""

    # Debug
    if cfg.DEBUG:
        logger.debug("opt-chain %s %s %s %s %s", ticker, expiry, opt_type,
                     min_sp, max_sp)

    # Check for argument
    if not ticker:
        raise Exception("Stock ticker is required")

    dates = yfinance_model.option_expirations(ticker)

    if not dates:
        raise Exception("Stock ticker is invalid")

    options = yfinance_model.get_option_chain(ticker, str(expiry))
    calls_df = options.calls.fillna(0)
    puts_df = options.puts.fillna(0)

    column_map = {
        "openInterest": "oi",
        "volume": "vol",
        "impliedVolatility": "iv"
    }
    columns = [
        "strike",
        "bid",
        "ask",
        "volume",
        "openInterest",
        "impliedVolatility",
    ]

    if opt_type == "Calls":
        df = calls_df[columns].rename(columns=column_map)
    if opt_type == "Puts":
        df = puts_df[columns].rename(columns=column_map)

    min_strike = np.percentile(df["strike"], 1)
    max_strike = np.percentile(df["strike"], 100)

    if min_sp:
        min_strike = min_sp
    if max_sp:
        max_strike = max_sp
        if min_sp > max_sp:  # type: ignore
            min_sp, max_sp = max_strike, min_strike

    df = df[df["strike"] >= min_strike]
    df = df[df["strike"] <= max_strike]

    df["iv"] = pd.to_numeric(df["iv"].astype(float))

    formats = {"iv": "{:.2f}"}
    for col, f in formats.items():
        df[col] = df[col].map(lambda x: f.format(x))  # pylint: disable=W0640
    df.set_index("strike", inplace=True)

    title = (
        f"Stocks: {opt_type} Option Chain for {ticker.upper()} on {expiry} [yfinance]"
    )

    embeds: list = []
    # Output
    i, i2, end = 0, 0, 20
    df_pg = []
    embeds_img = []
    dindex = len(df.index)
    while i < dindex:
        df_pg = df.iloc[i:end]
        df_pg.append(df_pg)
        fig = df2img.plot_dataframe(
            df_pg,
            fig_size=(1000, (40 + (40 * 20))),
            col_width=[3, 3, 3, 3],
            tbl_cells=dict(height=35, ),
            font=dict(
                family="Consolas",
                size=20,
            ),
            template="plotly_dark",
            paper_bgcolor="rgba(0, 0, 0, 0)",
        )
        imagefile = save_image(f"opt-chain{i}.png", fig)

        uploaded_image = gst_imgur.upload_image(imagefile, title="something")
        image_link = uploaded_image.link
        embeds_img.append(f"{image_link}", )
        embeds.append(disnake.Embed(
            title=title,
            colour=cfg.COLOR,
        ), )
        i2 += 1
        i += 20
        end += 20
        os.remove(imagefile)

    # Author/Footer
    for i in range(0, i2):
        embeds[i].set_author(
            name=cfg.AUTHOR_NAME,
            url=cfg.AUTHOR_URL,
            icon_url=cfg.AUTHOR_ICON_URL,
        )
        embeds[i].set_footer(
            text=cfg.AUTHOR_NAME,
            icon_url=cfg.AUTHOR_ICON_URL,
        )

    i = 0
    for i in range(0, i2):
        embeds[i].set_image(url=embeds_img[i])

        i += 1
    embeds[0].set_footer(text=f"Page 1 of {len(embeds)}")
    choices = [
        disnake.SelectOption(label="Home", value="0", emoji="🟢"),
    ]

    return {
        "view": Menu,
        "title": title,
        "embed": embeds,
        "choices": choices,
        "embeds_img": embeds_img,
    }
Exemplo n.º 14
0
def plot_oi(
    ticker: str,
    expiry: str,
    min_sp: float,
    max_sp: float,
    calls_only: bool,
    puts_only: bool,
    export: str = "",
    external_axes: Optional[List[plt.Axes]] = None,
):
    """Plot open interest

    Parameters
    ----------
    ticker: str
        Ticker
    expiry: str
        Expiry date for options
    min_sp: float
        Min strike to consider
    max_sp: float
        Max strike to consider
    calls_only: bool
        Show calls only
    puts_only: bool
        Show puts only
    export: str
        Format to export file
    external_axes : Optional[List[plt.Axes]], optional
        External axes (1 axis is expected in the list), by default None
    """
    options = yfinance_model.get_option_chain(ticker, expiry)
    export_data(
        export,
        os.path.dirname(os.path.abspath(__file__)),
        "oi_yf",
        options,
    )
    calls = options.calls
    puts = options.puts
    current_price = float(yf.Ticker(ticker).info["regularMarketPrice"])

    if min_sp == -1:
        min_strike = 0.75 * current_price
    else:
        min_strike = min_sp

    if max_sp == -1:
        max_strike = 1.25 * current_price
    else:
        max_strike = max_sp

    if calls_only and puts_only:
        console.print("Both flags selected, please select one", "\n")
        return

    call_oi = calls.set_index("strike")["openInterest"] / 1000
    put_oi = puts.set_index("strike")["openInterest"] / 1000

    df_opt = pd.merge(call_oi, put_oi, left_index=True, right_index=True)
    df_opt = df_opt.rename(columns={
        "openInterest_x": "OI_call",
        "openInterest_y": "OI_put"
    })

    max_pain = op_helpers.calculate_max_pain(df_opt)
    if external_axes is None:
        _, ax = plt.subplots(figsize=plot_autoscale(), dpi=cfp.PLOT_DPI)
    else:
        if len(external_axes) != 1:
            logger.error("Expected list of one axis item.")
            console.print("[red]Expected list of one axis item./n[/red]")
            return
        (ax, ) = external_axes

    if not calls_only:
        put_oi.plot(
            x="strike",
            y="openInterest",
            label="Puts",
            ax=ax,
            marker="o",
            ls="-",
        )
    if not puts_only:
        call_oi.plot(
            x="strike",
            y="openInterest",
            label="Calls",
            ax=ax,
            marker="o",
            ls="-",
        )
    ax.axvline(current_price, lw=2, ls="--", label="Current Price", alpha=0.7)
    ax.axvline(max_pain, lw=3, label=f"Max Pain: {max_pain}", alpha=0.7)
    ax.set_xlabel("Strike Price")
    ax.set_ylabel("Open Interest (1k) ")
    ax.set_xlim(min_strike, max_strike)
    ax.legend()
    ax.set_title(f"Open Interest for {ticker.upper()} expiring {expiry}")

    theme.style_primary_axis(ax)

    if external_axes is None:
        theme.visualize_output()
Exemplo n.º 15
0
def plot_vol(
    ticker: str,
    expiry: str,
    min_sp: float,
    max_sp: float,
    calls_only: bool,
    puts_only: bool,
    export: str = "",
):
    """Plot volume

    Parameters
    ----------
    ticker: str
        Ticker
    expiry: str
        Expiry date for options
    min_sp: float
        Min strike to consider
    max_sp: float
        Max strike to consider
    calls_only: bool
        Show calls only
    puts_only: bool
        Show puts only
    export: str
        Format to export file
    """
    options = yfinance_model.get_option_chain(ticker, expiry)
    calls = options.calls
    puts = options.puts
    current_price = float(yf.Ticker(ticker).info["regularMarketPrice"])

    if min_sp == -1:
        min_strike = 0.75 * current_price
    else:
        min_strike = min_sp

    if max_sp == -1:
        max_strike = 1.25 * current_price
    else:
        max_strike = max_sp

    if calls_only and puts_only:
        console.print("Both flags selected, please select one", "\n")
        return

    call_v = calls.set_index("strike")["volume"] / 1000
    put_v = puts.set_index("strike")["volume"] / 1000
    plt.style.use("classic")
    fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=cfp.PLOT_DPI)

    if not calls_only:
        put_v.plot(
            x="strike",
            y="volume",
            label="Puts",
            ax=ax,
            marker="o",
            ls="-",
            c="r",
        )
    if not puts_only:
        call_v.plot(
            x="strike",
            y="volume",
            label="Calls",
            ax=ax,
            marker="o",
            ls="-",
            c="g",
        )
    ax.axvline(current_price,
               lw=2,
               c="k",
               ls="--",
               label="Current Price",
               alpha=0.7)
    ax.grid("on")
    ax.set_xlabel("Strike Price")
    ax.set_ylabel("Volume (1k) ")
    ax.set_xlim(min_strike, max_strike)

    if gtff.USE_ION:
        plt.ion()

    ax.set_title(f"Volume for {ticker.upper()} expiring {expiry}")
    plt.legend(loc=0)
    fig.tight_layout(pad=1)

    plt.show()
    export_data(
        export,
        os.path.dirname(os.path.abspath(__file__)),
        "vol_yf",
        options,
    )
    console.print("")
Exemplo n.º 16
0
async def vol_command(
    ctx,
    ticker: str = None,
    expiry: str = "",
    min_sp: float = None,
    max_sp: float = None,
):
    """Options VOL"""

    try:

        # Debug
        if cfg.DEBUG:
            print(f"opt-vol {ticker} {expiry} {min_sp} {max_sp}")

        # Check for argument
        if ticker is None:
            raise Exception("Stock ticker is required")

        dates = yfinance_model.option_expirations(ticker)

        if not dates:
            raise Exception("Stock ticker is invalid")

        options = yfinance_model.get_option_chain(ticker, expiry)
        current_price = yfinance_model.get_price(ticker)

        if min_sp is None:
            min_strike = 0.75 * current_price
        else:
            min_strike = min_sp

        if max_sp is None:
            max_strike = 1.25 * current_price
        else:
            max_strike = max_sp

        calls = options.calls
        puts = options.puts
        call_v = calls.set_index("strike")["volume"] / 1000
        put_v = puts.set_index("strike")["volume"] / 1000

        df_opt = pd.merge(put_v, call_v, left_index=True, right_index=True)
        dmax = df_opt.values.max()

        fig = go.Figure()
        fig.add_trace(
            go.Scatter(
                x=call_v.index,
                y=call_v.values,
                name="Calls",
                mode="lines+markers",
                line=dict(color="green", width=3),
            ))
        fig.add_trace(
            go.Scatter(
                x=put_v.index,
                y=put_v.values,
                name="Puts",
                mode="lines+markers",
                line=dict(color="red", width=3),
            ))
        fig.add_trace(
            go.Scatter(
                x=[current_price, current_price],
                y=[0, dmax],
                mode="lines",
                line=dict(color="gold", width=2),
                name="Current Price",
            ))
        fig.update_xaxes(
            range=[min_strike, max_strike],
            constrain="domain",
        )
        fig.update_layout(
            margin=dict(l=0, r=0, t=60, b=20),
            template=cfg.PLT_SCAT_STYLE_TEMPLATE,
            title=f"Volume for {ticker.upper()} expiring {expiry}",
            title_x=0.5,
            legend_title="",
            xaxis_title="Strike",
            yaxis_title="Volume (1k)",
            xaxis=dict(rangeslider=dict(visible=False), ),
            legend=dict(yanchor="top", y=0.99, xanchor="left", x=0.01),
            dragmode="pan",
        )
        config = dict({"scrollZoom": True})
        imagefile = "opt_vol.png"
        fig.write_image(imagefile)

        # Check if interactive settings are enabled
        plt_link = ""
        if cfg.INTERACTIVE:
            html_ran = random.randint(69, 69420)
            fig.write_html(f"in/vol_{html_ran}.html", config=config)
            plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/vol_{html_ran}.html)"

        img = Image.open(imagefile)
        im_bg = Image.open(cfg.IMG_BG)
        h = img.height + 240
        w = img.width + 520

        # Paste fig onto background img and autocrop background
        img = img.resize((w, h), Image.ANTIALIAS)
        x1 = int(0.5 * im_bg.size[0]) - int(0.5 * img.size[0])
        y1 = int(0.5 * im_bg.size[1]) - int(0.5 * img.size[1])
        x2 = int(0.5 * im_bg.size[0]) + int(0.5 * img.size[0])
        y2 = int(0.5 * im_bg.size[1]) + int(0.5 * img.size[1])
        img = img.convert("RGB")
        im_bg.paste(img, box=(x1 - 5, y1, x2 - 5, y2))
        im_bg.save(imagefile, "PNG", quality=100)
        image = Image.open(imagefile)
        image = autocrop_image(image, 0)
        image.save(imagefile, "PNG", quality=100)

        image = discord.File(imagefile)

        if cfg.DEBUG:
            print(f"Image: {imagefile}")
        title = f"Volume for {ticker.upper()} expiring {expiry}"
        embed = discord.Embed(title=title,
                              description=plt_link,
                              colour=cfg.COLOR)
        embed.set_image(url=f"attachment://{imagefile}")
        embed.set_author(
            name=cfg.AUTHOR_NAME,
            icon_url=cfg.AUTHOR_ICON_URL,
        )
        os.remove(imagefile)

        await ctx.send(embed=embed, file=image)

    except Exception as e:
        embed = discord.Embed(
            title="ERROR Options: Volume",
            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=30.0)
Exemplo n.º 17
0
def chain_command(
    ticker: str = None,
    expiry: str = None,
    opt_type: str = None,
    min_sp: float = None,
    max_sp: float = None,
):
    """Show calls/puts for given ticker and expiration"""

    # Debug
    if imps.DEBUG:
        logger.debug("opt chain %s %s %s %s %s", ticker, expiry, opt_type,
                     min_sp, max_sp)

    # Check for argument
    if not ticker:
        raise Exception("Stock ticker is required")

    dates = yfinance_model.option_expirations(ticker)

    if not dates:
        raise Exception("Stock ticker is invalid")

    options = yfinance_model.get_option_chain(ticker, str(expiry))
    calls_df = options.calls.fillna(0)
    puts_df = options.puts.fillna(0)

    column_map = {
        "openInterest": "oi",
        "volume": "vol",
        "impliedVolatility": "iv"
    }
    columns = [
        "strike",
        "bid",
        "ask",
        "volume",
        "openInterest",
        "impliedVolatility",
    ]

    if opt_type == "Calls":
        df = calls_df[columns].rename(columns=column_map)
    if opt_type == "Puts":
        df = puts_df[columns].rename(columns=column_map)

    min_strike = np.percentile(df["strike"], 1)
    max_strike = np.percentile(df["strike"], 100)

    if min_sp:
        min_strike = min_sp
    if max_sp:
        max_strike = max_sp
        if min_sp > max_sp:  # type: ignore
            min_sp, max_sp = max_strike, min_strike

    df = df[df["strike"] >= min_strike]
    df = df[df["strike"] <= max_strike]

    df["iv"] = pd.to_numeric(df["iv"].astype(float))

    formats = {"iv": "{:.2f}"}
    for col, f in formats.items():
        df[col] = df[col].map(lambda x: f.format(x))  # pylint: disable=W0640
    df.columns = df.columns.str.capitalize()
    df.set_index("Strike", inplace=True)

    title = (
        f"Stocks: {opt_type} Option Chain for {ticker.upper()} on\n{expiry} [yfinance]"
    )

    embeds: list = []
    # Output
    i, i2, end = 0, 0, 20
    df_pg, embeds_img, images_list = [], [], []
    while i < len(df.index):
        df_pg = df.iloc[i:end]
        df_pg.append(df_pg)
        fig = imps.plot_df(
            df_pg,
            fig_size=(570, (40 + (40 * 20))),
            col_width=[3.1, 3.1, 3.1, 3.5],
            tbl_header=imps.PLT_TBL_HEADER,
            tbl_cells=imps.PLT_TBL_CELLS,
            font=imps.PLT_TBL_FONT,
            row_fill_color=imps.PLT_TBL_ROW_COLORS,
            paper_bgcolor="rgba(0, 0, 0, 0)",
        )
        fig.update_traces(cells=(dict(align=["center", "right"])))
        imagefile = "opt-chain.png"
        imagefile = imps.save_image(imagefile, fig)

        if imps.IMAGES_URL or not imps.IMG_HOST_ACTIVE:
            image_link = imps.multi_image(imagefile)
            images_list.append(imagefile)
        else:
            image_link = imps.multi_image(imagefile)

        embeds_img.append(f"{image_link}", )
        embeds.append(disnake.Embed(
            title=title,
            colour=imps.COLOR,
        ), )
        i2 += 1
        i += 20
        end += 20

    # Author/Footer
    for i in range(0, i2):
        embeds[i].set_author(
            name=imps.AUTHOR_NAME,
            url=imps.AUTHOR_URL,
            icon_url=imps.AUTHOR_ICON_URL,
        )
        embeds[i].set_footer(
            text=imps.AUTHOR_NAME,
            icon_url=imps.AUTHOR_ICON_URL,
        )

    i = 0
    for i in range(0, i2):
        embeds[i].set_image(url=embeds_img[i])

        i += 1
    embeds[0].set_footer(text=f"Page 1 of {len(embeds)}")
    choices = [
        disnake.SelectOption(label="Home", value="0", emoji="🟢"),
    ]

    return {
        "view": imps.Menu,
        "title": title,
        "embed": embeds,
        "choices": choices,
        "embeds_img": embeds_img,
        "images_list": images_list,
    }
Exemplo n.º 18
0
async def overview_command(
    ctx,
    ticker: str = None,
    expiry: str = None,
    min_sp: float = None,
    max_sp: float = None,
):
    """Options Overview"""

    try:

        # Debug
        startTime2 = time.time()
        if cfg.DEBUG:
            print(f"!stocks.opt.iv {ticker} {expiry} {min_sp} {max_sp}")

        # Check for argument
        if ticker is None:
            raise Exception("Stock ticker is required")

        # Get options info/dates, Look for logo_url
        df = get_options_info(ticker)  # Barchart Options IV Overview

        dates = yfinance_model.option_expirations(ticker)  # Expiration dates
        tup = f"{ticker.upper()}"
        url = yf.Ticker(tup).info["logo_url"]
        url += "?raw=true" if url else ""

        if not dates:
            raise Exception("Stock ticker is invalid")

        options = yfinance_model.get_option_chain(ticker, str(expiry))
        calls = options.calls
        puts = options.puts
        current_price = yfinance_model.get_price(ticker)

        min_strike2 = np.percentile(calls["strike"], 1)
        max_strike2 = np.percentile(calls["strike"], 100)
        min_strike = 0.75 * current_price
        max_strike = 1.95 * current_price

        if len(calls) > 40:
            min_strike = 0.75 * current_price
            max_strike = 1.25 * current_price

        if min_sp:
            min_strike = min_sp
            min_strike2 = min_sp
        if max_sp:
            max_strike = max_sp
            max_strike2 = max_sp
            if min_sp > max_sp:  # type: ignore
                min_sp, max_sp = max_strike2, min_strike2

        call_oi = calls.set_index("strike")["openInterest"] / 1000
        put_oi = puts.set_index("strike")["openInterest"] / 1000

        df_opt = pd.merge(call_oi, put_oi, left_index=True, right_index=True)
        df_opt = df_opt.rename(columns={
            "openInterest_x": "OI_call",
            "openInterest_y": "OI_put"
        })

        max_pain = op_helpers.calculate_max_pain(df_opt)
        fig = go.Figure()

        dmax = df_opt[["OI_call", "OI_put"]].values.max()
        dmin = df_opt[["OI_call", "OI_put"]].values.min()
        fig.add_trace(
            go.Scatter(
                x=df_opt.index,
                y=df_opt["OI_call"],
                name="Calls",
                mode="lines+markers",
                line=dict(color="green", width=3),
            ))

        fig.add_trace(
            go.Scatter(
                x=df_opt.index,
                y=df_opt["OI_put"],
                name="Puts",
                mode="lines+markers",
                line=dict(color="red", width=3),
            ))
        fig.add_trace(
            go.Scatter(
                x=[current_price, current_price],
                y=[dmin, dmax],
                mode="lines",
                line=dict(color="gold", width=2),
                name="Current Price",
            ))
        fig.add_trace(
            go.Scatter(
                x=[max_pain, max_pain],
                y=[dmin, dmax],
                mode="lines",
                line=dict(color="grey", width=3, dash="dash"),
                name=f"Max Pain: {max_pain}",
            ))
        fig.update_xaxes(
            range=[min_strike, max_strike],
            constrain="domain",
        )
        fig.update_layout(
            margin=dict(l=0, r=0, t=60, b=20),
            template=cfg.PLT_SCAT_STYLE_TEMPLATE,
            title=f"Open Interest for {ticker.upper()} expiring {expiry}",
            title_x=0.5,
            legend_title="",
            xaxis_title="Strike",
            yaxis_title="Open Interest (1k)",
            xaxis=dict(rangeslider=dict(visible=False), ),
            legend=dict(yanchor="top", y=0.99, xanchor="left", x=0.01),
            dragmode="pan",
        )
        config = dict({"scrollZoom": True})
        imagefile = "opt_oi.png"
        fig.write_image(imagefile)

        plt_link = ""
        if cfg.INTERACTIVE:
            html_ran = random.randint(69, 69420)
            fig.write_html(f"in/oi_{html_ran}.html", config=config)
            plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/oi_{html_ran}.html)"

        img = Image.open(imagefile)
        im_bg = Image.open(cfg.IMG_BG)

        h = img.height + 240
        w = img.width + 520

        img = img.resize((w, h), Image.ANTIALIAS)
        x1 = int(0.5 * im_bg.size[0]) - int(0.5 * img.size[0])
        y1 = int(0.5 * im_bg.size[1]) - int(0.5 * img.size[1])
        x2 = int(0.5 * im_bg.size[0]) + int(0.5 * img.size[0])
        y2 = int(0.5 * im_bg.size[1]) + int(0.5 * img.size[1])
        img = img.convert("RGB")
        im_bg.paste(img, box=(x1 - 5, y1, x2 - 5, y2))
        im_bg.save(imagefile, "PNG", quality=100)
        image = Image.open(imagefile)
        image = autocrop_image(image, 0)
        image.save(imagefile, "PNG", quality=100)

        uploaded_image_oi = gst_imgur.upload_image(imagefile,
                                                   title="something")
        image_link_oi = uploaded_image_oi.link

        column_map = {
            "openInterest": "oi",
            "volume": "vol",
            "impliedVolatility": "iv"
        }
        columns = [
            "strike",
            "bid",
            "ask",
            "volume",
            "openInterest",
            "impliedVolatility",
        ]

        calls_df = calls[columns].rename(columns=column_map)
        puts_df = puts[columns].rename(columns=column_map)

        calls_df = calls_df[calls_df["strike"] >= min_strike2]
        calls_df = calls_df[calls_df["strike"] <= max_strike2]
        puts_df = puts_df[puts_df["strike"] >= min_strike2]
        puts_df = puts_df[puts_df["strike"] <= max_strike2]

        calls_df["iv"] = pd.to_numeric(calls_df["iv"].astype(float))
        puts_df["iv"] = pd.to_numeric(puts_df["iv"].astype(float))

        formats = {"iv": "{:.2f}"}
        for col, f in formats.items():
            calls_df[col] = calls_df[col].map(lambda x: f.format(x)  # pylint: disable=W0640
                                              )
            puts_df[col] = puts_df[col].map(lambda x: f.format(x)  # pylint: disable=W0640
                                            )

        calls_df.set_index("strike", inplace=True)
        puts_df.set_index("strike", inplace=True)

        if "-" in df.iloc[0, 1]:
            iv = f"```diff\n-             {df.iloc[0, 1]}\n```"
        else:
            iv = f"```yaml\n              {df.iloc[0, 1]}\n```"

        pfix, sfix = f"{ticker.upper()} ", f" expiring {expiry}"
        if expiry == dates[0]:
            pfix = f"{ticker.upper()} Weekly "
            sfix = ""

        embeds = [
            disnake.Embed(
                title=f"{ticker.upper()} Overview",
                color=cfg.COLOR,
            ),
            disnake.Embed(
                title=f"{pfix}Open Interest{sfix}",
                description=plt_link,
                colour=cfg.COLOR,
            ),
        ]
        choices = [
            disnake.SelectOption(label=f"{ticker.upper()} Overview",
                                 value="0",
                                 emoji="🟢"),
            disnake.SelectOption(label=f"{pfix}Open Interest{sfix}",
                                 value="1",
                                 emoji="🟢"),
        ]
        embeds_img = []

        i, i2, end = 0, 0, 20
        df_calls = []
        dindex = len(calls_df.index)
        while i <= dindex:
            df_calls = calls_df.iloc[i:end]
            df_calls.append(df_calls)
            figp = df2img.plot_dataframe(
                df_calls,
                fig_size=(1000, (40 + (40 * 20))),
                col_width=[3, 3, 3, 3],
                tbl_cells=cfg.PLT_TBL_CELLS,
                font=cfg.PLT_TBL_FONT,
                template=cfg.PLT_TBL_STYLE_TEMPLATE,
                paper_bgcolor="rgba(0, 0, 0, 0)",
            )
            imagefile = f"opt-calls{i}.png"

            df2img.save_dataframe(fig=figp, filename=imagefile)
            image = Image.open(imagefile)
            image = autocrop_image(image, 0)
            image.save(imagefile, "PNG", quality=100)
            uploaded_image = gst_imgur.upload_image(imagefile,
                                                    title="something")
            image_link = uploaded_image.link
            embeds_img.append(f"{image_link}", )
            embeds.append(
                disnake.Embed(
                    title=f"{pfix}Calls{sfix}",
                    colour=cfg.COLOR,
                ), )
            i2 += 1
            i += 20
            end += 20
            os.remove(imagefile)

        # Add Calls page field
        i, page, puts_page = 2, 0, 3
        i3 = i2 + 2
        choices.append(
            disnake.SelectOption(label="Calls Page 1", value="2",
                                 emoji="🟢"), )
        for i in range(2, i3):
            page += 1
            puts_page += 1

            embeds[i].add_field(name=f"Calls Page {page}",
                                value="_ _",
                                inline=True)

        # Puts Pages
        i, end = 0, 20
        df_puts = []

        dindex = len(puts_df.index)
        while i <= dindex:
            df_puts = puts_df.iloc[i:end]
            df_puts.append(df_puts)
            figp = df2img.plot_dataframe(
                df_puts,
                fig_size=(1000, (40 + (40 * 20))),
                col_width=[3, 3, 3, 3],
                tbl_cells=cfg.PLT_TBL_CELLS,
                font=cfg.PLT_TBL_FONT,
                template=cfg.PLT_TBL_STYLE_TEMPLATE,
                paper_bgcolor="rgba(0, 0, 0, 0)",
            )
            imagefile = f"opt-puts{i}.png"

            df2img.save_dataframe(fig=figp, filename=imagefile)
            image = Image.open(imagefile)
            image = autocrop_image(image, 0)
            image.save(imagefile, "PNG", quality=100)
            uploaded_image = gst_imgur.upload_image(imagefile,
                                                    title="something")
            image_link = uploaded_image.link
            embeds_img.append(f"{image_link}", )
            embeds.append(
                disnake.Embed(
                    title=f"{pfix}Puts{sfix}",
                    colour=cfg.COLOR,
                ), )
            i2 += 1
            i += 20
            end += 20
            os.remove(imagefile)

        # Add Puts page field
        i, page = 0, 0
        puts_page -= 1
        i2 += 2
        choices.append(
            disnake.SelectOption(label="Puts Page 1",
                                 value=f"{puts_page}",
                                 emoji="🟢"), )
        for i in range(puts_page, i2):
            page += 1
            embeds[i].add_field(name=f"Puts Page {page}",
                                value="_ _",
                                inline=True)

        # Author/Footer
        for i in range(0, i2):
            embeds[i].set_author(
                name=cfg.AUTHOR_NAME,
                url=cfg.AUTHOR_URL,
                icon_url=cfg.AUTHOR_ICON_URL,
            )
            embeds[i].set_footer(
                text=cfg.AUTHOR_NAME,
                icon_url=cfg.AUTHOR_ICON_URL,
            )

        # Set images to Pages
        i = 0
        img_i = 0
        embeds[1].set_image(url=image_link_oi)
        for i in range(2, i2):
            embeds[i].set_image(url=embeds_img[img_i])
            img_i += 1
            i += 1

        if url:
            embeds[0].set_thumbnail(url=f"{url}")
        else:
            embeds[0].set_thumbnail(url=cfg.AUTHOR_ICON_URL)

        # Overview Section
        embeds[0].add_field(name=f"{df.iloc[0, 0]}", value=iv, inline=False)

        embeds[0].add_field(name=f"•{df.iloc[1, 0]}",
                            value=f"```css\n{df.iloc[1, 1]}\n```",
                            inline=True)
        for N in range(2, 6):
            embeds[0].add_field(
                name=f"_ _ _ _ _ _ _ _ _ _ •{df.iloc[N, 0]}",
                value=f"```css\n{df.iloc[N, 1]}\n```",
                inline=True,
            )

        embeds[0].add_field(name="_ _", value="_ _", inline=False)
        for N in range(6, 8):
            embeds[0].add_field(
                name=f"_ _ _ _ _ _ _ _ _ _ •{df.iloc[N, 0]}",
                value=f"```css\n{df.iloc[N, 1]}\n```",
                inline=True,
            )

        embeds[0].add_field(name="_ _", value="_ _", inline=False)
        for N in range(8, 10):
            embeds[0].add_field(
                name=f"_ _ _ _ _ _ _ _ _ _ •{df.iloc[N, 0]}",
                value=f"```css\n{df.iloc[N, 1]}\n```",
                inline=True,
            )

        embeds[0].add_field(name="_ _", value="_ _", inline=False)
        for N in range(10, 12):
            embeds[0].add_field(
                name=f"_ _ _ _ _ _ _ _ _ _ •{df.iloc[N, 0]}",
                value=f"```css\n{df.iloc[N, 1]}\n```",
                inline=True,
            )

        embeds[0].set_footer(text=f"Page 1 of {len(embeds)}")
        executionTime2 = time.time() - startTime2
        print(
            f"\n> {__name__} is finished: time in seconds: {executionTime2}\n")
        await ctx.send(embed=embeds[0], view=Menu(embeds, choices))

    except Exception as e:
        embed = disnake.Embed(
            title="ERROR Options: Overview",
            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=30.0)
Exemplo n.º 19
0
def show_parity(ticker: str, exp: str, put: bool, ask: bool, mini: float,
                maxi: float, export: str) -> None:
    """Prints options and whether they are under or over priced [Source: Yahoo Finance]

    Parameters
    ----------
    ticker : str
        Ticker to get expirations for
    exp : str
        Expiration to use for options
    put : bool
        Whether to use puts or calls
    ask : bool
        Whether to use ask or lastPrice
    mini : float
        Minimum strike price to show
    maxi : float
        Maximum strike price to show
    export : str
        Export data
    """
    r_date = datetime.strptime(exp, "%Y-%m-%d").date()
    delta = (r_date - date.today()).days
    rate = ((1 + get_rf())**(delta / 365)) - 1
    stock = get_price(ticker)

    div_info = yfinance_model.get_dividend(ticker)
    div_dts = div_info.index.values.tolist()

    if div_dts:
        last_div = pd.to_datetime(div_dts[-1])

        if len(div_dts) > 3:
            avg_div = np.mean(div_info.to_list()[-4:])
        else:
            avg_div = np.mean(div_info.to_list())

        next_div = last_div + timedelta(days=91)
        dividends = []
        while next_div < datetime.strptime(exp, "%Y-%m-%d"):
            day_dif = (next_div - datetime.now()).days
            dividends.append((avg_div, day_dif))
            next_div += timedelta(days=91)
        div_pvs = [x[0] / ((1 + get_rf())**(x[1] / 365)) for x in dividends]
        pv_dividend = sum(div_pvs)
    else:
        pv_dividend = 0

    chain = get_option_chain(ticker, exp)
    name = "ask" if ask else "lastPrice"
    o_type = "put" if put else "call"

    calls = chain.calls[["strike", name]].copy()
    calls = calls.rename(columns={name: "callPrice"})
    puts = chain.puts[["strike", name]].copy()
    puts = puts.rename(columns={name: "putPrice"})

    opts = pd.merge(calls, puts, on="strike")
    opts = opts.dropna()
    opts = opts.loc[opts["callPrice"] * opts["putPrice"] != 0]

    opts["callParity"] = (opts["putPrice"] + stock -
                          (opts["strike"] / (1 + rate)) - pv_dividend)
    opts["putParity"] = ((opts["strike"] / (1 + rate)) + opts["callPrice"] -
                         stock + pv_dividend)

    diff = o_type + " Difference"
    opts[diff] = opts[o_type + "Price"] - opts[o_type + "Parity"]
    opts["distance"] = abs(stock - opts["strike"])
    filtered = opts.copy()

    if mini is None:
        mini = filtered.strike.quantile(0.25)
    if maxi is None:
        maxi = filtered.strike.quantile(0.75)

    filtered = filtered.loc[filtered["strike"] >= mini]
    filtered = filtered.loc[filtered["strike"] <= maxi]

    show = filtered[["strike", diff]].copy()

    if ask:
        console.print(
            "Warning: Options with no current ask price not shown.\n")

    print_rich_table(
        show,
        headers=[x.title() for x in show.columns],
        show_index=False,
        title="Warning: Low volume options may be difficult to trade.",
    )

    export_data(
        export,
        os.path.dirname(os.path.abspath(__file__)),
        "parity",
        show,
    )
    console.print()
Exemplo n.º 20
0
def plot_oi(
    ticker: str,
    expiry: str,
    min_sp: float,
    max_sp: float,
    calls_only: bool,
    puts_only: bool,
    export: str = "",
):
    """Plot open interest

    Parameters
    ----------
    ticker: str
        Ticker
    expiry: str
        Expiry date for options
    min_sp: float
        Min strike to consider
    max_sp: float
        Max strike to consider
    calls_only: bool
        Show calls only
    puts_only: bool
        Show puts only
    export: str
        Format to export file
    """
    options = yfinance_model.get_option_chain(ticker, expiry)
    export_data(
        export,
        os.path.dirname(os.path.abspath(__file__)),
        "oi_yf",
        options,
    )
    calls = options.calls
    puts = options.puts
    current_price = float(yf.Ticker(ticker).info["regularMarketPrice"])

    if min_sp == -1:
        min_strike = 0.75 * current_price
    else:
        min_strike = min_sp

    if max_sp == -1:
        max_strike = 1.25 * current_price
    else:
        max_strike = max_sp

    if calls_only and puts_only:
        print("Both flags selected, please select one", "\n")
        return

    call_oi = calls.set_index("strike")["openInterest"] / 1000
    put_oi = puts.set_index("strike")["openInterest"] / 1000

    df_opt = pd.merge(call_oi, put_oi, left_index=True, right_index=True)
    df_opt = df_opt.rename(columns={
        "openInterest_x": "OI_call",
        "openInterest_y": "OI_put"
    })

    max_pain = op_helpers.calculate_max_pain(df_opt)
    plt.style.use("classic")
    fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=cfp.PLOT_DPI)

    if not calls_only:
        put_oi.plot(
            x="strike",
            y="openInterest",
            label="Puts",
            ax=ax,
            marker="o",
            ls="-",
            c="r",
        )
    if not puts_only:
        call_oi.plot(
            x="strike",
            y="openInterest",
            label="Calls",
            ax=ax,
            marker="o",
            ls="-",
            c="g",
        )
    ax.axvline(current_price,
               lw=2,
               c="k",
               ls="--",
               label="Current Price",
               alpha=0.7)
    ax.axvline(max_pain, lw=3, c="k", label=f"Max Pain: {max_pain}", alpha=0.7)
    ax.grid("on")
    ax.set_xlabel("Strike Price")
    ax.set_ylabel("Open Interest (1k) ")
    ax.set_xlim(min_strike, max_strike)

    if gtff.USE_ION:
        plt.ion()

    ax.set_title(f"Open Interest for {ticker.upper()} expiring {expiry}")
    plt.legend(loc=0)
    fig.tight_layout(pad=1)

    plt.show()
    print("")
Exemplo n.º 21
0
async def oi_command(
    ctx,
    ticker: str = None,
    expiry: str = "",
    min_sp: float = None,
    max_sp: float = None,
):
    """Options OI"""

    try:

        # Debug
        print(f"!stocks.opt.oi {ticker} {expiry} {min_sp} {max_sp}")

        # Check for argument
        if ticker is None:
            raise Exception("Stock ticker is required")

        dates = yfinance_model.option_expirations(ticker)

        if not dates:
            raise Exception("Stock ticker is invalid")

        options = yfinance_model.get_option_chain(ticker, expiry)
        calls = options.calls
        puts = options.puts
        current_price = yfinance_model.get_price(ticker)

        min_strike = 0.75 * current_price
        max_strike = 1.95 * current_price

        if len(calls) > 40:
            min_strike = 0.75 * current_price
            max_strike = 1.25 * current_price

        if min_sp:
            min_strike = min_sp
        if max_sp:
            max_strike = max_sp

        call_oi = calls.set_index("strike")["openInterest"] / 1000
        put_oi = puts.set_index("strike")["openInterest"] / 1000

        df_opt = pd.merge(call_oi, put_oi, left_index=True, right_index=True)
        df_opt = df_opt.rename(
            columns={"openInterest_x": "OI_call", "openInterest_y": "OI_put"}
        )

        max_pain = op_helpers.calculate_max_pain(df_opt)
        fig = go.Figure()

        dmax = df_opt[["OI_call", "OI_put"]].values.max()
        dmin = df_opt[["OI_call", "OI_put"]].values.min()
        fig.add_trace(
            go.Scatter(
                x=df_opt.index,
                y=df_opt["OI_call"],
                name="Calls",
                mode="lines+markers",
                line=dict(color="green", width=3),
            )
        )

        fig.add_trace(
            go.Scatter(
                x=df_opt.index,
                y=df_opt["OI_put"],
                name="Puts",
                mode="lines+markers",
                line=dict(color="red", width=3),
            )
        )
        fig.add_trace(
            go.Scatter(
                x=[current_price, current_price],
                y=[dmin, dmax],
                mode="lines",
                line=dict(color="gold", width=2),
                name="Current Price",
            )
        )
        fig.add_trace(
            go.Scatter(
                x=[max_pain, max_pain],
                y=[dmin, dmax],
                mode="lines",
                line=dict(color="grey", width=3, dash="dash"),
                name=f"Max Pain: {max_pain}",
            )
        )
        fig.update_xaxes(
            range=[min_strike, max_strike],
            constrain="domain",
        )
        fig.update_layout(
            margin=dict(l=0, r=0, t=60, b=20),
            template=cfg.PLT_SCAT_STYLE_TEMPLATE,
            title=f"Open Interest for {ticker.upper()} expiring {expiry}",
            title_x=0.5,
            legend_title="",
            xaxis_title="Strike",
            yaxis_title="Open Interest (1k)",
            xaxis=dict(
                rangeslider=dict(visible=False),
            ),
            legend=dict(yanchor="top", y=0.99, xanchor="left", x=0.01),
            dragmode="pan",
        )
        config = dict({"scrollZoom": True})
        imagefile = "opt_oi.png"

        # Check if interactive settings are enabled
        plt_link = ""
        if cfg.INTERACTIVE:
            html_ran = random.randint(69, 69420)
            fig.write_html(f"in/cci_{html_ran}.html", config=config)
            plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/cci_{html_ran}.html)"

        fig.update_layout(
            width=800,
            height=500,
        )
        fig.write_image(imagefile)

        img = Image.open(imagefile)
        im_bg = Image.open(cfg.IMG_BG)
        h = img.height + 240
        w = img.width + 520

        img = img.resize((w, h), Image.ANTIALIAS)
        x1 = int(0.5 * im_bg.size[0]) - int(0.5 * img.size[0])
        y1 = int(0.5 * im_bg.size[1]) - int(0.5 * img.size[1])
        x2 = int(0.5 * im_bg.size[0]) + int(0.5 * img.size[0])
        y2 = int(0.5 * im_bg.size[1]) + int(0.5 * img.size[1])
        img = img.convert("RGB")
        im_bg.paste(img, box=(x1 - 5, y1, x2 - 5, y2))
        im_bg.save(imagefile, "PNG", quality=100)
        image = Image.open(imagefile)
        image = autocrop_image(image, 0)
        image.save(imagefile, "PNG", quality=100)

        image = disnake.File(imagefile)

        if cfg.DEBUG:
            print(f"Image URL: {imagefile}")
        title = f"Open Interest for {ticker.upper()} expiring {expiry}"
        embed = disnake.Embed(title=title, description=plt_link, colour=cfg.COLOR)
        embed.set_image(url=f"attachment://{imagefile}")
        embed.set_author(
            name=cfg.AUTHOR_NAME,
            icon_url=cfg.AUTHOR_ICON_URL,
        )
        os.remove(imagefile)

        await ctx.send(embed=embed, file=image)

    except Exception as e:
        embed = disnake.Embed(
            title="ERROR Options: Open Interest",
            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=30.0)
Exemplo n.º 22
0
def plot_plot(ticker: str, expiration: str, put: bool, x: str, y: str,
              custom: str, export: str) -> None:
    """Generate a graph custom graph based on user input

    Parameters
    ----------
    ticker: str
        Stock ticker
    expiration: str
        Option expiration
    min_sp: float
        Min strike price
    put: bool
        put option instead of call
    x: str
        variable to display in x axis
    y: str
        variable to display in y axis
    custom: str
        type of plot
    export: str
        type of data to export
    """
    convert = {
        "ltd": "lastTradeDate",
        "s": "strike",
        "lp": "lastPrice",
        "b": "bid",
        "a": "ask",
        "c": "change",
        "pc": "percentChange",
        "v": "volume",
        "oi": "openInterest",
        "iv": "impliedVolatility",
    }

    x = convert[x]
    y = convert[y]
    varis = op_helpers.opt_chain_cols
    chain = yfinance_model.get_option_chain(ticker, expiration)
    values = chain.puts if put else chain.calls
    _, ax = plt.subplots()
    if custom == "smile":
        x = "strike"
        y = "impliedVolatility"
    x_data = values[x]
    y_data = values[y]
    ax.plot(x_data, y_data, "--bo")
    word = "puts" if put else "calls"
    ax.set_title(
        f"{varis[y]['label']} vs. {varis[x]['label']} for {ticker} {word} on {expiration}"
    )
    ax.set_ylabel(varis[y]["label"])
    ax.set_xlabel(varis[x]["label"])
    if varis[x]["format"] == "date":
        plt.gca().xaxis.set_major_formatter(mdates.DateFormatter("%Y/%m/%d"))
        plt.gca().xaxis.set_major_locator(mdates.DayLocator(interval=1))
        plt.gcf().autofmt_xdate()
    elif varis[x]["format"]:
        ax.xaxis.set_major_formatter(varis[x]["format"])
    if varis[y]["format"] == "date":
        plt.gca().yaxis.set_major_formatter(mdates.DateFormatter("%Y/%m/%d"))
        plt.gca().yaxis.set_major_locator(mdates.DayLocator(interval=1))
    elif varis[y]["format"]:
        ax.yaxis.set_major_formatter(varis[y]["format"])
    if gtff.USE_ION:
        plt.ion()
    plt.show()
    export_data(export, os.path.dirname(os.path.abspath(__file__)), "plot")
    print("")
Exemplo n.º 23
0
def vol_command(
    ticker: str = None,
    expiry: str = "",
    min_sp: float = None,
    max_sp: float = None,
):
    """Options VOL"""

    # Debug
    if cfg.DEBUG:
        logger.debug("opt-vol %s %s %s %s", ticker, expiry, min_sp, max_sp)

    # Check for argument
    if ticker is None:
        raise Exception("Stock ticker is required")

    dates = yfinance_model.option_expirations(ticker)

    if not dates:
        raise Exception("Stock ticker is invalid")

    options = yfinance_model.get_option_chain(ticker, expiry)
    current_price = yfinance_model.get_price(ticker)

    if min_sp is None:
        min_strike = 0.75 * current_price
    else:
        min_strike = min_sp

    if max_sp is None:
        max_strike = 1.25 * current_price
    else:
        max_strike = max_sp

    calls = options.calls
    puts = options.puts
    call_v = calls.set_index("strike")["volume"] / 1000
    put_v = puts.set_index("strike")["volume"] / 1000

    df_opt = pd.merge(put_v, call_v, left_index=True, right_index=True)
    dmax = df_opt.values.max()

    fig = go.Figure()
    fig.add_trace(
        go.Scatter(
            x=call_v.index,
            y=call_v.values,
            name="Calls",
            mode="lines+markers",
            line=dict(color="green", width=3),
        ))
    fig.add_trace(
        go.Scatter(
            x=put_v.index,
            y=put_v.values,
            name="Puts",
            mode="lines+markers",
            line=dict(color="red", width=3),
        ))
    fig.add_trace(
        go.Scatter(
            x=[current_price, current_price],
            y=[0, dmax],
            mode="lines",
            line=dict(color="gold", width=2),
            name="Current Price",
        ))
    fig.update_xaxes(
        range=[min_strike, max_strike],
        constrain="domain",
    )
    fig.update_layout(
        margin=dict(l=0, r=0, t=60, b=20),
        template=cfg.PLT_SCAT_STYLE_TEMPLATE,
        title=f"Volume for {ticker.upper()} expiring {expiry}",
        title_x=0.5,
        legend_title="",
        xaxis_title="Strike",
        yaxis_title="Volume (1k)",
        xaxis=dict(rangeslider=dict(visible=False), ),
        legend=dict(yanchor="top", y=0.99, xanchor="left", x=0.01),
        dragmode="pan",
    )
    config = dict({"scrollZoom": True})
    imagefile = "opt_vol.png"

    # Check if interactive settings are enabled
    plt_link = ""
    if cfg.INTERACTIVE:
        html_ran = helpers.uuid_get()
        fig.write_html(f"in/vol_{html_ran}.html", config=config)
        plt_link = f"[Interactive]({cfg.INTERACTIVE_URL}/vol_{html_ran}.html)"

    fig.update_layout(
        width=800,
        height=500,
    )

    imagefile = helpers.image_border(imagefile, fig=fig)

    return {
        "title": f"Volume for {ticker.upper()} expiring {expiry}",
        "description": plt_link,
        "imagefile": imagefile,
    }
Exemplo n.º 24
0
def get_historical_greeks(
    ticker: str, expiry: str, chain_id: str, strike: float, put: bool
) -> pd.DataFrame:
    """Get histoical option greeks

    Parameters
    ----------
    ticker: str
        Stock ticker
    expiry: str
        Option expiration date
    chain_id: str
        OCC option symbol.  Overwrites other inputs
    strike: float
        Strike price to look for
    put: bool
        Is this a put option?

    Returns
    -------
    df: pd.DataFrame
        Dataframe containing historical greeks
    """
    if not chain_id:
        options = yfinance_model.get_option_chain(ticker, expiry)

        if put:
            options = options.puts
        else:
            options = options.calls

        chain_id = options.loc[options.strike == strike, "contractSymbol"].values[0]

    r = requests.get(f"https://api.syncretism.io/ops/historical/{chain_id}")

    if r.status_code != 200:
        console.print("Error in request.")
        return pd.DataFrame()

    history = r.json()

    iv, delta, gamma, theta, rho, vega, premium, price, time = (
        [],
        [],
        [],
        [],
        [],
        [],
        [],
        [],
        [],
    )

    for entry in history:
        time.append(pd.to_datetime(entry["timestamp"], unit="s"))
        iv.append(entry["impliedVolatility"])
        gamma.append(entry["gamma"])
        delta.append(entry["delta"])
        theta.append(entry["theta"])
        rho.append(entry["rho"])
        vega.append(entry["vega"])
        premium.append(entry["premium"])
        price.append(entry["regularMarketPrice"])

    data = {
        "iv": iv,
        "gamma": gamma,
        "delta": delta,
        "theta": theta,
        "rho": rho,
        "vega": vega,
        "premium": premium,
        "price": price,
    }

    df = pd.DataFrame(data, index=time)
    return df
Exemplo n.º 25
0
def options_data(
    ticker: str = None,
    expiry: str = None,
    min_sp: float = None,
    max_sp: float = None,
):

    # Debug
    if imps.DEBUG:
        logger.debug("opt overview %s %s %s %s", ticker, expiry, min_sp,
                     max_sp)

    # Check for argument
    if ticker is None:
        raise Exception("Stock ticker is required")

    # Get options info/dates, Look for logo_url
    if "^" not in ticker:
        df_bcinfo = get_options_info(ticker)  # Barchart Options IV Overview
        df_bcinfo = df_bcinfo.fillna("")
        df_bcinfo = df_bcinfo.set_axis(
            [
                " ",
                "",
            ],
            axis="columns",
        )
        df_bcinfo[""] = df_bcinfo[""].str.lstrip()
    else:
        df_bcinfo = ""

    dates = yfinance_model.option_expirations(ticker)  # Expiration dates
    tup = f"{ticker.upper()}"
    url = yf.Ticker(tup).info["logo_url"]
    url += "?raw=true" if url else ""

    if not dates:
        raise Exception("Stock ticker is invalid")

    options = yfinance_model.get_option_chain(ticker, str(expiry))
    calls = options.calls.fillna(0)
    puts = options.puts.fillna(0)

    current_price = yfinance_model.get_price(ticker)

    min_strike2 = np.percentile(calls["strike"], 1)
    max_strike2 = np.percentile(calls["strike"], 100)
    min_strike = 0.75 * current_price
    max_strike = 1.95 * current_price

    if len(calls) > 40:
        min_strike = 0.75 * current_price
        max_strike = 1.25 * current_price

    if min_sp:
        min_strike = min_sp
        min_strike2 = min_sp
    if max_sp:
        max_strike = max_sp
        max_strike2 = max_sp
        if min_sp > max_sp:  # type: ignore
            min_sp, max_sp = max_strike2, min_strike2

    call_oi = calls.set_index("strike")["openInterest"] / 1000
    put_oi = puts.set_index("strike")["openInterest"] / 1000

    df_opt = pd.merge(call_oi, put_oi, left_index=True, right_index=True)
    df_opt = df_opt.rename(columns={
        "openInterest_x": "OI_call",
        "openInterest_y": "OI_put"
    })

    max_pain = op_helpers.calculate_max_pain(df_opt)
    data = [
        ticker,
        url,
        expiry,
        dates,
        df_bcinfo,
        calls,
        puts,
        df_opt,
        current_price,
        min_strike,
        max_strike,
        min_strike2,
        max_strike2,
        max_pain,
    ]
    return data
Exemplo n.º 26
0
async def oi_command(
    ctx,
    ticker: str = None,
    expiry: str = "",
    min_sp: float = None,
    max_sp: float = None,
):
    """Options OI"""

    try:
        # Debug
        if cfg.DEBUG:
            logger.debug("!stocks.opt.oi %s %s %s %s", ticker, expiry, min_sp,
                         max_sp)

        # Check for argument
        if ticker is None:
            raise Exception("Stock ticker is required")

        dates = yfinance_model.option_expirations(ticker)

        if not dates:
            raise Exception("Stock ticker is invalid")

        options = yfinance_model.get_option_chain(ticker, expiry)
        calls = options.calls
        puts = options.puts
        current_price = yfinance_model.get_price(ticker)

        if min_sp is None:
            min_strike = 0.75 * current_price
        else:
            min_strike = min_sp

        if max_sp is None:
            max_strike = 1.25 * current_price
        else:
            max_strike = max_sp

        call_oi = calls.set_index("strike")["openInterest"] / 1000
        put_oi = puts.set_index("strike")["openInterest"] / 1000

        df_opt = pd.merge(call_oi, put_oi, left_index=True, right_index=True)
        df_opt = df_opt.rename(columns={
            "openInterest_x": "OI_call",
            "openInterest_y": "OI_put"
        })

        max_pain = op_helpers.calculate_max_pain(df_opt)
        plt.style.use("seaborn")
        fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI)

        put_oi.plot(
            x="strike",
            y="openInterest",
            label="Puts",
            ax=ax,
            marker="o",
            ls="-",
            c="r",
        )
        call_oi.plot(
            x="strike",
            y="openInterest",
            label="Calls",
            ax=ax,
            marker="o",
            ls="-",
            c="g",
        )
        ax.axvline(current_price,
                   lw=2,
                   c="k",
                   ls="--",
                   label="Current Price",
                   alpha=0.4)
        ax.axvline(max_pain,
                   lw=3,
                   c="k",
                   label=f"Max Pain: {max_pain}",
                   alpha=0.4)
        ax.grid("on")
        ax.set_xlabel("Strike Price")
        ax.set_ylabel("Open Interest (1k) ")
        ax.set_xlim(min_strike, max_strike)

        ax.set_title(f"Open Interest for {ticker.upper()} expiring {expiry}")
        plt.legend(loc=0)
        fig.tight_layout()

        imagefile = "opt_oi.png"
        plt.savefig("opt_oi.png")
        image = discord.File(imagefile)

        if cfg.DEBUG:
            logger.debug("Image %s", imagefile)
        title = f"Open Interest for {ticker.upper()} expiring {expiry}"
        embed = discord.Embed(title=title, colour=cfg.COLOR)
        embed.set_image(url="attachment://opt_oi.png")
        embed.set_author(
            name=cfg.AUTHOR_NAME,
            icon_url=cfg.AUTHOR_ICON_URL,
        )
        os.remove("opt_oi.png")

        await ctx.send(embed=embed, file=image)

    except Exception as e:
        embed = discord.Embed(
            title="ERROR Options: Open Interest",
            colour=cfg.COLOR,
            description=e,
        )
        embed.set_author(
            name=cfg.AUTHOR_NAME,
            icon_url=cfg.AUTHOR_ICON_URL,
        )

        await ctx.send(embed=embed)