示例#1
0
def display_mean_std(name: str,
                     df: pd.DataFrame,
                     target: str,
                     length: int,
                     export: str = ""):
    """View rolling spread

    Parameters
    ----------
    name : str
        Stock ticker
    df : pd.DataFrame
        Dataframe
    target : str
        Column in data to look at
    length : int
        Length of window
    export : str
        Format to export data
    """
    data = df[target]
    rolling_mean, rolling_std = rolling_model.get_rolling_avg(data, length)
    fig, axMean = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI)
    axMean.plot(
        data.index,
        data.values,
        label=name,
        linewidth=2,
        color="black",
    )
    axMean.plot(rolling_mean, linestyle="--", linewidth=3, color="blue")
    axMean.set_xlabel("Time")
    axMean.set_ylabel("Values", color="blue")
    axMean.legend(["Real values", "Rolling Mean"], loc=2)
    axMean.tick_params(axis="y", labelcolor="blue")
    axStd = axMean.twinx()
    axStd.plot(
        rolling_std,
        label="Rolling std",
        linestyle="--",
        color="green",
        linewidth=3,
        alpha=0.6,
    )
    axStd.set_ylabel("Std Deviation")
    axStd.legend(["Rolling std"], loc=1)
    axStd.set_ylabel(f"{target} standard deviation", color="green")
    axStd.tick_params(axis="y", labelcolor="green")
    axMean.set_title("Rolling mean and std with window " + str(length) +
                     " applied to " + name + target)
    axMean.set_xlim([data.index[0], data.index[-1]])
    axMean.grid(b=True, which="major", color="#666666", linestyle="-")

    if gtff.USE_ION:
        plt.ion()
    fig.tight_layout(pad=1)
    plt.gcf().autofmt_xdate()
    plt.show()
    console.print("")
    export_data(
        export,
        os.path.dirname(os.path.abspath(__file__)).replace("common", "stocks"),
        "rolling",
        rolling_mean.join(rolling_std, lsuffix="_mean", rsuffix="_sd"),
    )
示例#2
0
def display_spread(name: str,
                   df: pd.DataFrame,
                   target: str,
                   length: int,
                   export: str = ""):
    """View rolling spread

    Parameters
    ----------
    name : str
        Stock ticker
    df : pd.DataFrame
        Dataframe
    target: str
        Column in data to look at
    length : int
        Length of window
    export : str
        Format to export data
    """
    data = df[target]
    df_sd, df_var = rolling_model.get_spread(data, length)
    fig, axes = plt.subplots(3, 1, figsize=plot_autoscale(), dpi=PLOT_DPI)
    ax = axes[0]
    ax.set_title(f"{name} Spread")
    ax.plot(data.index, data.values, "fuchsia", lw=1)
    ax.set_xlim(data.index[0], data.index[-1])
    ax.set_ylabel("Value")
    ax.set_title(f"Spread of {name} {target}")
    ax.yaxis.set_label_position("right")
    ax.grid(b=True, which="major", color="#666666", linestyle="-")
    ax.minorticks_on()
    ax.grid(b=True, which="minor", color="#999999", linestyle="-", alpha=0.2)
    ax1 = axes[1]
    ax1.plot(df_sd.index, df_sd.values, "b", lw=1, label="stdev")
    ax1.set_xlim(data.index[0], data.index[-1])
    ax1.set_ylabel("Stdev")
    ax1.yaxis.set_label_position("right")
    ax1.grid(b=True, which="major", color="#666666", linestyle="-")
    ax1.minorticks_on()
    ax1.grid(b=True, which="minor", color="#999999", linestyle="-", alpha=0.2)
    ax2 = axes[2]
    ax2.plot(df_var.index, df_var.values, "g", lw=1, label="variance")
    ax2.set_xlim(data.index[0], data.index[-1])
    ax2.set_ylabel("Variance")
    ax2.yaxis.set_label_position("right")
    ax2.grid(b=True, which="major", color="#666666", linestyle="-")

    if gtff.USE_ION:
        plt.ion()

    plt.gcf().autofmt_xdate()
    fig.tight_layout(pad=1)
    plt.show()
    console.print("")
    export_data(
        export,
        os.path.dirname(os.path.abspath(__file__)).replace("common", "stocks"),
        "spread",
        df_sd.join(df_var, lsuffix="_sd", rsuffix="_var"),
    )
示例#3
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()
示例#4
0
def display_luna_circ_supply_change(
    days: int,
    export: str,
    supply_type: str = LUNA_CIR_SUPPLY_CHANGE,
    limit: int = 5,
    external_axes: Optional[List[plt.Axes]] = None,
):
    """Display Luna circulating supply stats

    Parameters
    ----------
    days: int
        Number of days
    supply_type: str
        Supply type to unpack json
    export: str
        Export type
    limit: int
        Number of results display on the terminal
        Default: 5
    external_axes : Optional[List[plt.Axes]], optional
        External axes (1 axis is expected in the list), by default None
    Returns
        None
    -------
    """

    df = smartstake_model.get_luna_supply_stats(supply_type, days)

    if df.empty:
        return

    # This plot has 1 axis
    if not external_axes:
        _, ax = plt.subplots(figsize=plot_autoscale(), dpi=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

    ax.plot(
        df.index,
        df["circulatingSupplyInMil"],
        c="black",
        label="Circulating Supply",
    )
    ax.plot(
        df.index,
        df["liquidCircSupplyInMil"],
        c="red",
        label="Liquid Circulating Supply",
    )
    ax.plot(df.index,
            df["stakeFromCircSupplyInMil"],
            c="green",
            label="Stake of Supply")
    ax.plot(
        df.index,
        df["recentTotalLunaBurntInMil"],
        c="blue",
        label="Supply Reduction (Luna Burnt)",
    )

    ax.grid()
    ax.set_ylabel("Millions")
    ax.set_xlabel("Time")
    ax.set_title("Luna Circulating Supply Changes (In Millions)")
    ax.set_xlim(df.index[0], df.index[-1])
    ax.legend(loc="best")

    theme.style_primary_axis(ax)

    if external_axes is None:
        theme.visualize_output()

    RAW_COLS = [
        "circulatingSupplyInMil",
        "liquidCircSupplyInMil",
        "circSupplyChangeInMil",
        "recentTotalLunaBurntInMil",
    ]

    export_data(
        export,
        os.path.dirname(os.path.abspath(__file__)),
        "lcsc",
        df[RAW_COLS],
    )

    df.index = df.index.strftime("%Y-%m-%d")
    df = df.sort_index(ascending=False)

    print_rich_table(
        df[RAW_COLS].head(limit),
        headers=[
            "Circ Supply",
            "Liquid Circ Supply",
            "Supply Change",
            "Supply Reduction (Luna Burnt)",
        ],
        show_index=True,
        index_name="Time",
        title="Luna Circulating Supply Changes (in Millions)",
    )
示例#5
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()
示例#6
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")
示例#7
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,
    )
示例#8
0
def display_fred_series(
    d_series: Dict[str, Dict[str, str]],
    start_date: str,
    raw: bool = False,
    export: str = "",
    limit: int = 10,
    external_axes: Optional[List[plt.Axes]] = None,
):
    """Display (multiple) series from https://fred.stlouisfed.org. [Source: FRED]

    Parameters
    ----------
    series : str
        FRED Series ID from https://fred.stlouisfed.org. For multiple series use: series1,series2,series3
    start_date : str
        Starting date (YYYY-MM-DD) of data
    raw : bool
        Output only raw data
    export : str
        Export data to csv,json,xlsx or png,jpg,pdf,svg file
    limit: int
        Number of raw data rows to show
    external_axes : Optional[List[plt.Axes]], optional
        External axes (3 axes are expected in the list), by default None
    """
    series_ids = list(d_series.keys())
    data = pd.DataFrame()

    for s_id in series_ids:
        data = pd.concat(
            [
                data,
                pd.DataFrame(fred_model.get_series_data(s_id, start_date),
                             columns=[s_id]),
            ],
            axis=1,
        )
    data = data.dropna()
    # Try to get everything onto the same 0-10 scale.
    # To do so, think in scientific notation.  Divide the data by whatever the E would be
    if external_axes is None:
        _, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI)

    else:
        if len(external_axes) != 3:
            console.print("[red]Expected list of 3 axis items./n[/red]")
            return
        (ax, ) = external_axes

    if len(series_ids) == 1:
        s_id = series_ids[0]
        sub_dict: Dict = d_series[s_id]
        title = f"{sub_dict['title']} ({sub_dict['units']})"
        ax.plot(data.index, data, label="\n".join(textwrap.wrap(title, 80)))
    else:
        for s_id, sub_dict in d_series.items():
            data_to_plot = data[s_id].dropna()
            exponent = int(np.log10(data_to_plot.max()))
            data_to_plot /= 10**exponent
            multiplier = f"x {format_units(10**exponent)}" if exponent > 0 else ""
            title = f"{sub_dict['title']} ({sub_dict['units']}) {'['+multiplier+']' if multiplier else ''}"
            ax.plot(
                data_to_plot.index,
                data_to_plot,
                label="\n".join(textwrap.wrap(title, 80))
                if len(series_ids) < 5 else title,
            )

    ax.legend(prop={"size": 10}, bbox_to_anchor=(0, 1), loc="lower left")
    if data.empty:
        console.print("[red]No data[/red]\n")
    else:
        ax.set_xlim(data.index[0], data.index[-1])
        theme.style_primary_axis(ax)
        if external_axes is None:
            theme.visualize_output()

        data.index = [x.strftime("%Y-%m-%d") for x in data.index]
        if raw:
            print_rich_table(
                data.tail(limit),
                headers=list(data.columns),
                show_index=True,
                index_name="Date",
            )
            console.print("")
        export_data(
            export,
            os.path.dirname(os.path.abspath(__file__)),
            "plot",
            data,
        )
def upcoming_earning_release_dates(num_pages: int, num_earnings: int,
                                   export: str):
    """Displays upcoming earnings release dates

    Parameters
    ----------
    num_pages: int
        Number of pages to scrap
    num_earnings: int
        Number of upcoming earnings release dates
    export : str
        Export dataframe data to csv,json,xlsx file
    """
    # TODO: Check why there are repeated companies
    # TODO: Create a similar command that returns not only upcoming, but antecipated earnings
    # i.e. companies where expectation on their returns are high

    df_earnings = seeking_alpha_model.get_next_earnings(num_pages)

    pd.set_option("display.max_colwidth", None)
    if export:
        l_earnings = []
        l_earnings_dates = []

    for n_days, earning_date in enumerate(df_earnings.index.unique()):
        if n_days > (num_earnings - 1):
            break

        # TODO: Potentially extract Market Cap for each Ticker, and sort
        # by Market Cap. Then cut the number of tickers shown to 10 with
        # bigger market cap. Didier attempted this with yfinance, but
        # the computational time involved wasn't worth pursuing that solution.

        df_earn = df_earnings[earning_date == df_earnings.index][[
            "Ticker", "Name"
        ]].dropna()

        if export:
            l_earnings_dates.append(earning_date.date())
            l_earnings.append(df_earn)

        df_earn.index = df_earn["Ticker"].values
        df_earn.drop(columns=["Ticker"], inplace=True)

        print_rich_table(
            df_earn,
            show_index=True,
            headers=[f"Earnings on {earning_date.date()}"],
            title="Upcoming Earnings Releases",
        )

    if export:
        for i, _ in enumerate(l_earnings):
            l_earnings[i].reset_index(drop=True, inplace=True)
        df_data = pd.concat(l_earnings, axis=1, ignore_index=True)
        df_data.columns = l_earnings_dates

        export_data(
            export,
            os.path.dirname(os.path.abspath(__file__)),
            "upcoming",
            df_data,
        )