def plot_view_stock(df: pd.DataFrame, symbol: str, interval: str):
    """
    Plot the loaded stock dataframe
    Parameters
    ----------
    df: Dataframe
        Dataframe of prices and volumnes
    symbol: str
        Symbol of ticker
    interval: str
        Stock data resolution for plotting purposes

    """
    df.sort_index(ascending=True, inplace=True)
    bar_colors = ["r" if x[1].Open < x[1].Close else "g" for x in df.iterrows()]

    try:
        fig, ax = plt.subplots(
            2,
            1,
            gridspec_kw={"height_ratios": [3, 1]},
            figsize=plot_autoscale(),
            dpi=cfgPlot.PLOT_DPI,
        )
    except Exception as e:
        print(e)
        print(
            "Encountered an error trying to open a chart window. Check your X server configuration."
        )
        return

    # In order to make nice Volume plot, make the bar width = interval
    if interval == "1440min":
        bar_width = timedelta(days=1)
        title_string = "Daily"
    else:
        bar_width = timedelta(minutes=int(interval.split("m")[0]))
        title_string = f"{int(interval.split('m')[0])} min"

    ax[0].yaxis.tick_right()
    if "Adj Close" in df.columns:
        ax[0].plot(df.index, df["Adj Close"], c=cfgPlot.VIEW_COLOR)
    else:
        ax[0].plot(df.index, df["Close"], c=cfgPlot.VIEW_COLOR)
    ax[0].set_xlim(df.index[0], df.index[-1])
    ax[0].set_xticks([])
    ax[0].yaxis.set_label_position("right")
    ax[0].set_ylabel("Share Price ($)")
    ax[0].grid(axis="y", color="gainsboro", linestyle="-", linewidth=0.5)

    ax[0].spines["top"].set_visible(False)
    ax[0].spines["left"].set_visible(False)
    ax[1].bar(
        df.index, df.Volume / 1_000_000, color=bar_colors, alpha=0.8, width=bar_width
    )
    ax[1].set_xlim(df.index[0], df.index[-1])
    ax[1].yaxis.tick_right()
    ax[1].yaxis.set_label_position("right")
    ax[1].set_ylabel("Volume (1M)")
    ax[1].grid(axis="y", color="gainsboro", linestyle="-", linewidth=0.5)
    ax[1].spines["top"].set_visible(False)
    ax[1].spines["left"].set_visible(False)
    ax[1].set_xlabel("Time")
    fig.suptitle(
        symbol + " " + title_string,
        size=20,
        x=0.15,
        y=0.95,
        fontfamily="serif",
        fontstyle="italic",
    )
    if gtff.USE_ION:
        plt.ion()
    fig.tight_layout(pad=2)
    plt.setp(ax[1].get_xticklabels(), rotation=20, horizontalalignment="right")

    plt.show()
    print("")
Beispiel #2
0
def display_last_contracts(
    past_transaction_days: int = 2,
    num: int = 20,
    sum_contracts: bool = False,
    export: str = "",
):
    """Last government contracts [Source: quiverquant.com]

    Parameters
    ----------
    past_transaction_days: int
        Number of days to look back
    num: int
        Number of contracts to show
    sum_contracts: bool
        Flag to show total amount of contracts given out.
    export: str
        Format to export data
    """
    df_contracts = quiverquant_model.get_government_trading("contracts")

    if df_contracts.empty:
        print("No government contracts found\n")
        return

    df_contracts.sort_values("Date", ascending=False)

    df_contracts["Date"] = pd.to_datetime(df_contracts["Date"])

    df_contracts.drop_duplicates(inplace=True)
    df = df_contracts.copy()
    df_contracts = df_contracts[
        df_contracts["Date"].isin(df_contracts["Date"].unique()[:past_transaction_days])
    ]

    df_contracts = df_contracts[["Date", "Ticker", "Amount", "Description", "Agency"]][
        :num
    ]
    if gtff.USE_TABULATE_DF:
        df_contracts["Description"] = df_contracts["Description"].apply(
            lambda x: "\n".join(textwrap.wrap(x, 50))
        )
        print(
            tabulate(
                df_contracts,
                headers=df_contracts.columns,
                tablefmt="fancy_grid",
                showindex=False,
                floatfmt=".2f",
            )
        )
    else:
        print(df_contracts.to_string(index=False))
    if sum_contracts:
        fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI)
        df["Date"] = pd.to_datetime(df["Date"]).dt.date
        df.groupby("Date").sum().div(1000).plot(kind="bar", rot=0, ax=ax)
        ax.set_ylabel("Amount ($1k)")
        ax.set_title("Total amount of government contracts given")

        if gtff.USE_ION:
            plt.ion()
        fig.tight_layout()
        plt.show()
    print("")
    export_data(export, os.path.dirname(os.path.abspath(__file__)), "lastcontracts", df)
Beispiel #3
0
def display_hist_contracts(ticker: str, raw: bool = False, export: str = ""):
    """Show historical quarterly government contracts [Source: quiverquant.com]

    Parameters
    ----------
    ticker: str
        Ticker to get congress trading data from
    raw: bool
        Flag to display raw data
    export: str
        Format to export data
    """
    df_contracts = quiverquant_model.get_government_trading(
        "quarter-contracts", ticker=ticker
    )

    if df_contracts.empty:
        print("No quarterly government contracts found\n")
        return

    amounts = df_contracts.sort_values(by=["Year", "Qtr"])["Amount"].values

    qtr = df_contracts.sort_values(by=["Year", "Qtr"])["Qtr"].values
    year = df_contracts.sort_values(by=["Year", "Qtr"])["Year"].values

    quarter_ticks = [
        f"{quarter[0]}" if quarter[1] == 1 else "" for quarter in zip(year, qtr)
    ]

    if raw:
        if gtff.USE_TABULATE_DF:
            print(
                tabulate(
                    df_contracts,
                    headers=df_contracts.columns,
                    tablefmt="fancy_grid",
                    showindex=False,
                )
            )
        else:
            print(df_contracts.to_string())

    else:
        fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI)

        ax.plot(np.arange(0, len(amounts)), amounts / 1000, "-*", lw=2, ms=15)

        ax.set_xlim([-0.5, len(amounts) - 0.5])
        ax.set_xticks(np.arange(0, len(amounts)))
        ax.set_xticklabels(quarter_ticks)
        ax.grid()
        ax.set_title(f"Historical Quarterly Government Contracts for {ticker.upper()}")
        ax.set_xlabel("Date")
        ax.set_ylabel("Amount ($1k)")
        fig.tight_layout()
        if gtff.USE_ION:
            plt.ion()

        plt.show()

    export_data(export, os.path.dirname(os.path.abspath(__file__)), "histcont")
    print("")
def plot_aroon(
    s_ticker: str,
    s_interval: str,
    df_stock: pd.DataFrame,
    length: int,
    scalar: int,
    export: str,
):
    """Plot Aroon indicator

    Parameters
    ----------
    s_ticker : str
        Ticker
    s_interval: str
        Interval of price data
    df_stock : pd.DataFrame.length
        Dataframe of prices
    length:int
        Length of window
    scalar : int
        Scalar variable
    """
    df_ta = trend_indicators_model.aroon(df_stock, length, scalar)

    fig, ax = plt.subplots(3, 1, figsize=plot_autoscale(), dpi=PLOT_DPI)
    ax0 = ax[0]
    # Daily
    if s_interval == "1440min":
        ax0.plot(df_stock.index, df_stock["Adj Close"].values, "k", lw=2)
    # Intraday
    else:
        ax0.plot(df_stock.index, df_stock["Close"].values, "k", lw=2)

    ax0.set_title(f"Aroon on {s_ticker}")
    ax0.set_xlim(df_stock.index[0], df_stock.index[-1])
    ax0.set_ylabel("Share Price ($)")
    ax0.grid(b=True, which="major", color="#666666", linestyle="-")

    ax1 = ax[1]
    ax1.plot(df_ta.index, df_ta.iloc[:, 0].values, "r", lw=2)
    ax1.plot(df_ta.index, df_ta.iloc[:, 1].values, "g", lw=2)
    ax1.set_xlim(df_stock.index[0], df_stock.index[-1])
    ax1.axhline(50, linewidth=1, color="k", ls="--")
    ax1.legend(
        [f"Aroon DOWN ({df_ta.columns[0]})", f"Aroon UP ({df_ta.columns[1]})"],
        loc="upper left",
    )
    ax1.grid(b=True, which="major", color="#666666", linestyle="-")
    ax1.set_ylim([0, 100])

    ax2 = ax[2]
    ax2.plot(df_ta.index, df_ta.iloc[:, 2].values, "b", lw=2)
    ax2.set_xlim(df_stock.index[0], df_stock.index[-1])
    ax2.set_xlabel("Time")
    ax2.legend([f"Aroon OSC ({df_ta.columns[2]})"], loc="upper left")
    ax2.grid(b=True, which="major", color="#666666", linestyle="-")
    ax2.set_ylim([-100, 100])

    if gtff.USE_ION:
        plt.ion()

    fig.tight_layout(pad=1)
    plt.show()
    plt.gcf().autofmt_xdate()

    print("")

    export_data(
        export,
        os.path.dirname(os.path.abspath(__file__)).replace("common", "stocks"),
        "aroon",
        df_ta,
    )
def topsells_command(
    gov_type="",
    past_transactions_months: int = 5,
    num: int = 10,
    raw: bool = False,
):
    """Displays most sold stocks by the congress/senate/house [quiverquant.com]"""
    # Debug user input
    if cfg.DEBUG:
        logger.debug(
            "gov-topsells %s %s %s %s",
            gov_type,
            past_transactions_months,
            num,
            raw,
        )

    possible_args = ["congress", "senate", "house"]
    if gov_type == "":
        gov_type = "congress"
    elif gov_type not in possible_args:
        raise Exception(
            "Enter a valid government argument, options are: congress, senate and house"
        )

    # Retrieve Data
    df_gov = quiverquant_model.get_government_trading(gov_type)

    # Output Data
    if df_gov.empty:
        raise Exception(f"No {gov_type} trading data found\n")

    df_gov = df_gov.sort_values("TransactionDate", ascending=False)

    start_date = datetime.now() - timedelta(days=past_transactions_months * 30)

    df_gov["TransactionDate"] = pd.to_datetime(df_gov["TransactionDate"])

    df_gov = df_gov[df_gov["TransactionDate"] > start_date].dropna()

    df_gov["Range"] = df_gov["Range"].apply(lambda x: "$5,000,001-$5,000,001"
                                            if x == ">$5,000,000" else x)

    df_gov["min"] = df_gov["Range"].apply(lambda x: x.split("-")[0].strip(
        "$").replace(",", "").strip().replace(">$", "").strip())
    df_gov["max"] = df_gov["Range"].apply(
        lambda x: x.split("-")[1].replace(",", "").strip().strip("$").replace(
            ">$", "").strip() if "-" in x else x.strip("$").replace(
                ",", "").replace(">$", "").strip())

    df_gov["lower"] = df_gov[["min", "max", "Transaction"]].apply(
        lambda x: float(x["min"])
        if x["Transaction"] == "Purchase" else -float(x["max"]),
        axis=1,
    )
    df_gov["upper"] = df_gov[["min", "max", "Transaction"]].apply(
        lambda x: float(x["max"])
        if x["Transaction"] == "Purchase" else -float(x["min"]),
        axis=1,
    )
    description = None
    df_gov = df_gov.sort_values("TransactionDate", ascending=True)
    if raw:
        df = pd.DataFrame(
            df_gov.groupby("Ticker")["upper"].sum().div(1000).sort_values(
                ascending=True).abs().head(n=num))
        description = "```" + df.to_string() + "```"
    fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI)

    df_gov.groupby("Ticker")["upper"].sum().div(1000).sort_values().abs().head(
        n=num).plot(kind="bar", rot=0, ax=ax)
    ax.set_ylabel("Amount ($1k)")
    ax.set_title(
        f"{num} most sold stocks over last {past_transactions_months} months"
        f" (upper bound) for {gov_type}")
    plt.gcf().axes[0].yaxis.get_major_formatter().set_scientific(False)
    fig.tight_layout()

    plt.savefig("gov_topsells.png")
    imagefile = "gov_topsells.png"

    imagefile = image_border(imagefile)
    return {
        "title": f"Stocks: [quiverquant.com] Top sells for {gov_type.upper()}",
        "imagefile": imagefile,
        "description": description,
    }
def aroon(l_args, s_ticker, s_interval, df_stock):
    parser = argparse.ArgumentParser(
        add_help=False,
        prog="aroon",
        description="""
            The word aroon is Sanskrit for "dawn's early light." The Aroon
            indicator attempts to show when a new trend is dawning. The indicator consists
            of two lines (Up and Down) that measure how long it has been since the highest
            high/lowest low has occurred within an n period range. \n \n When the Aroon Up is
            staying between 70 and 100 then it indicates an upward trend. When the Aroon Down
            is staying between 70 and 100 then it indicates an downward trend. A strong upward
            trend is indicated when the Aroon Up is above 70 while the Aroon Down is below 30.
            Likewise, a strong downward trend is indicated when the Aroon Down is above 70 while
            the Aroon Up is below 30. Also look for crossovers. When the Aroon Down crosses above
            the Aroon Up, it indicates a weakening of the upward trend (and vice versa).
        """,
    )

    parser.add_argument(
        "-l",
        "--length",
        action="store",
        dest="n_length",
        type=check_positive,
        default=25,
        help="length",
    )
    parser.add_argument(
        "-s",
        "--scalar",
        action="store",
        dest="n_scalar",
        type=check_positive,
        default=100,
        help="scalar",
    )
    parser.add_argument(
        "-o",
        "--offset",
        action="store",
        dest="n_offset",
        type=check_positive,
        default=0,
        help="offset",
    )

    try:
        ns_parser = parse_known_args_and_warn(parser, l_args)
        if not ns_parser:
            return

        df_ta = ta.aroon(
            high=df_stock["2. high"],
            low=df_stock["3. low"],
            length=ns_parser.n_length,
            scalar=ns_parser.n_scalar,
            offset=ns_parser.n_offset,
        ).dropna()

        plt.figure(figsize=plot_autoscale(), dpi=PLOT_DPI)
        plt.subplot(311)
        # Daily
        if s_interval == "1440min":
            plt.plot(df_stock.index, df_stock["5. adjusted close"].values, "k", lw=2)
        # Intraday
        else:
            plt.plot(df_stock.index, df_stock["4. close"].values, "k", lw=2)

        plt.title(f"Aroon on {s_ticker}")
        plt.xlim(df_stock.index[0], df_stock.index[-1])
        plt.ylabel("Share Price ($)")
        plt.grid(b=True, which="major", color="#666666", linestyle="-")
        plt.minorticks_on()
        plt.grid(b=True, which="minor", color="#999999", linestyle="-", alpha=0.2)

        plt.subplot(312)
        plt.plot(df_ta.index, df_ta.iloc[:, 0].values, "r", lw=2)
        plt.plot(df_ta.index, df_ta.iloc[:, 1].values, "g", lw=2)
        plt.xlim(df_stock.index[0], df_stock.index[-1])
        plt.axhline(50, linewidth=1, color="k", ls="--")
        plt.legend(
            [f"Aroon DOWN ({df_ta.columns[0]})", f"Aroon UP ({df_ta.columns[1]})"]
        )
        plt.grid(b=True, which="major", color="#666666", linestyle="-")
        plt.minorticks_on()
        plt.grid(b=True, which="minor", color="#999999", linestyle="-", alpha=0.2)
        plt.ylim([0, 100])

        plt.subplot(313)
        plt.plot(df_ta.index, df_ta.iloc[:, 2].values, "b", lw=2)
        plt.xlabel("Time")
        plt.legend([f"Aroon OSC ({df_ta.columns[2]})"])
        plt.grid(b=True, which="major", color="#666666", linestyle="-")
        plt.minorticks_on()
        plt.grid(b=True, which="minor", color="#999999", linestyle="-", alpha=0.2)
        plt.ylim([-100, 100])

        if gtff.USE_ION:
            plt.ion()

        plt.show()
        print("")

    except Exception as e:
        print(e)
        print("")
Beispiel #7
0
def regression(other_args: List[str], s_ticker: str, df_stock: pd.DataFrame,
               polynomial: int):
    """
    Train a regression model
    Parameters
    ----------
    other_args: List[str]
        Argparse arguments
    s_ticker: str
        Stock ticker
    df_stock: pd.DataFrame
        Dataframe of stock prices
    polynomial: int
        Order of polynomial

    """
    parser = argparse.ArgumentParser(
        add_help=False,
        prog="regression",
        description="""
            Regression attempts to model the relationship between
            two variables by fitting a linear/quadratic/cubic/other equation to
            observed data. One variable is considered to be an explanatory variable,
            and the other is considered to be a dependent variable.
        """,
    )

    parser.add_argument(
        "-i",
        "--input",
        action="store",
        dest="n_inputs",
        type=check_positive,
        default=40,
        help="number of days to use for prediction.",
    )
    parser.add_argument(
        "-d",
        "--days",
        action="store",
        dest="n_days",
        type=check_positive,
        default=5,
        help="prediction days.",
    )
    parser.add_argument(
        "-j",
        "--jumps",
        action="store",
        dest="n_jumps",
        type=check_positive,
        default=1,
        help="number of jumps in training data.",
    )
    parser.add_argument(
        "-e",
        "--end",
        action="store",
        type=valid_date,
        dest="s_end_date",
        default=None,
        help="The end date (format YYYY-MM-DD) to select - Backtesting",
    )

    if polynomial == USER_INPUT:
        parser.add_argument(
            "-p",
            "--polynomial",
            action="store",
            dest="n_polynomial",
            type=check_positive,
            required=True,
            help="polynomial associated with regression.",
        )

    try:
        ns_parser = parse_known_args_and_warn(parser, other_args)
        if not ns_parser:
            return

        # BACKTESTING
        if ns_parser.s_end_date:
            if ns_parser.s_end_date < df_stock.index[0]:
                print(
                    "Backtesting not allowed, since End Date is older than Start Date of historical data\n"
                )
                return

            if ns_parser.s_end_date < get_next_stock_market_days(
                    last_stock_day=df_stock.index[0],
                    n_next_days=ns_parser.n_inputs + ns_parser.n_days,
            )[-1]:
                print(
                    "Backtesting not allowed, since End Date is too close to Start Date to train model\n"
                )
                return

            future_index = get_next_stock_market_days(
                last_stock_day=ns_parser.s_end_date,
                n_next_days=ns_parser.n_days)

            if future_index[-1] > datetime.datetime.now():
                print(
                    "Backtesting not allowed, since End Date + Prediction days is in the future\n"
                )
                return

            df_future = df_stock[future_index[0]:future_index[-1]]
            df_stock = df_stock[:ns_parser.s_end_date]

        # Split training data
        stock_x, stock_y = splitTrain.split_train(
            df_stock["5. adjusted close"].values,
            ns_parser.n_inputs,
            ns_parser.n_days,
            ns_parser.n_jumps,
        )

        if not stock_x:
            print("Given the model parameters more training data is needed.\n")
            return

        # Machine Learning model
        if polynomial == LINEAR:
            model = linear_model.LinearRegression(n_jobs=-1)
        else:
            if polynomial == USER_INPUT:
                polynomial = ns_parser.n_polynomial
            model = pipeline.make_pipeline(
                preprocessing.PolynomialFeatures(polynomial),
                linear_model.Ridge())

        model.fit(stock_x, stock_y)
        l_predictions = model.predict(
            df_stock["5. adjusted close"].values[-ns_parser.n_inputs:].reshape(
                1, -1))[0]

        # Prediction data
        l_pred_days = get_next_stock_market_days(
            last_stock_day=df_stock["5. adjusted close"].index[-1],
            n_next_days=ns_parser.n_days,
        )
        df_pred = pd.Series(l_predictions, index=l_pred_days, name="Price")

        # Plotting
        plt.figure(figsize=plot_autoscale(), dpi=PLOT_DPI)
        plt.plot(df_stock.index, df_stock["5. adjusted close"], lw=2)
        # BACKTESTING
        if ns_parser.s_end_date:
            plt.title(
                f"BACKTESTING: Regression (polynomial {polynomial}) on {s_ticker} - {ns_parser.n_days} days prediction"
            )
        else:
            plt.title(
                f"Regression (polynomial {polynomial}) on {s_ticker} - {ns_parser.n_days} days prediction"
            )
        plt.xlim(df_stock.index[0],
                 get_next_stock_market_days(df_pred.index[-1], 1)[-1])
        plt.xlabel("Time")
        plt.ylabel("Share Price ($)")
        plt.grid(b=True, which="major", color="#666666", linestyle="-")
        plt.minorticks_on()
        plt.grid(b=True,
                 which="minor",
                 color="#999999",
                 linestyle="-",
                 alpha=0.2)
        plt.plot(
            [df_stock.index[-1], df_pred.index[0]],
            [df_stock["5. adjusted close"].values[-1], df_pred.values[0]],
            lw=1,
            c="tab:green",
            linestyle="--",
        )
        plt.plot(df_pred.index, df_pred, lw=2, c="tab:green")
        plt.axvspan(df_stock.index[-1],
                    df_pred.index[-1],
                    facecolor="tab:orange",
                    alpha=0.2)
        _, _, ymin, ymax = plt.axis()
        plt.vlines(df_stock.index[-1],
                   ymin,
                   ymax,
                   linewidth=1,
                   linestyle="--",
                   color="k")

        # BACKTESTING
        if ns_parser.s_end_date:
            plt.plot(
                df_future.index,
                df_future["5. adjusted close"],
                lw=2,
                c="tab:blue",
                ls="--",
            )
            plt.plot(
                [df_stock.index[-1], df_future.index[0]],
                [
                    df_stock["5. adjusted close"].values[-1],
                    df_future["5. adjusted close"].values[0],
                ],
                lw=1,
                c="tab:blue",
                linestyle="--",
            )

        if gtff.USE_ION:
            plt.ion()

        plt.show()

        # BACKTESTING
        if ns_parser.s_end_date:
            plt.figure(figsize=plot_autoscale(), dpi=PLOT_DPI)
            plt.subplot(211)
            plt.plot(
                df_future.index,
                df_future["5. adjusted close"],
                lw=2,
                c="tab:blue",
                ls="--",
            )
            plt.plot(df_pred.index, df_pred, lw=2, c="green")
            plt.scatter(df_future.index,
                        df_future["5. adjusted close"],
                        c="tab:blue",
                        lw=3)
            plt.plot(
                [df_stock.index[-1], df_future.index[0]],
                [
                    df_stock["5. adjusted close"].values[-1],
                    df_future["5. adjusted close"].values[0],
                ],
                lw=2,
                c="tab:blue",
                ls="--",
            )
            plt.scatter(df_pred.index, df_pred, c="green", lw=3)
            plt.plot(
                [df_stock.index[-1], df_pred.index[0]],
                [df_stock["5. adjusted close"].values[-1], df_pred.values[0]],
                lw=2,
                c="green",
                ls="--",
            )
            plt.title("BACKTESTING: Real data price versus Prediction")
            plt.xlim(df_stock.index[-1],
                     df_pred.index[-1] + datetime.timedelta(days=1))
            plt.xticks(
                [
                    df_stock.index[-1],
                    df_pred.index[-1] + datetime.timedelta(days=1)
                ],
                visible=True,
            )
            plt.ylabel("Share Price ($)")
            plt.grid(b=True, which="major", color="#666666", linestyle="-")
            plt.minorticks_on()
            plt.grid(b=True,
                     which="minor",
                     color="#999999",
                     linestyle="-",
                     alpha=0.2)
            plt.legend(["Real data", "Prediction data"])
            plt.xticks([])

            plt.subplot(212)
            plt.axhline(y=0, color="k", linestyle="--", linewidth=2)
            plt.plot(
                df_future.index,
                100 *
                (df_pred.values - df_future["5. adjusted close"].values) /
                df_future["5. adjusted close"].values,
                lw=2,
                c="red",
            )
            plt.scatter(
                df_future.index,
                100 *
                (df_pred.values - df_future["5. adjusted close"].values) /
                df_future["5. adjusted close"].values,
                c="red",
                lw=5,
            )
            plt.title(
                "BACKTESTING: Error between Real data and Prediction [%]")
            plt.plot(
                [df_stock.index[-1], df_future.index[0]],
                [
                    0,
                    100 * (df_pred.values[0] -
                           df_future["5. adjusted close"].values[0]) /
                    df_future["5. adjusted close"].values[0],
                ],
                lw=2,
                ls="--",
                c="red",
            )
            plt.xlim(df_stock.index[-1],
                     df_pred.index[-1] + datetime.timedelta(days=1))
            plt.xticks(
                [
                    df_stock.index[-1],
                    df_pred.index[-1] + datetime.timedelta(days=1)
                ],
                visible=True,
            )
            plt.xlabel("Time")
            plt.ylabel("Prediction Error (%)")
            plt.grid(b=True, which="major", color="#666666", linestyle="-")
            plt.minorticks_on()
            plt.grid(b=True,
                     which="minor",
                     color="#999999",
                     linestyle="-",
                     alpha=0.2)
            plt.legend(["Real data", "Prediction data"])

            if gtff.USE_ION:
                plt.ion()

            plt.show()

            # Refactor prediction dataframe for backtesting print
            df_pred.name = "Prediction"
            df_pred = df_pred.to_frame()
            df_pred["Real"] = df_future["5. adjusted close"]

            if gtff.USE_COLOR:

                patch_pandas_text_adjustment()

                print("Time         Real [$]  x  Prediction [$]")
                print(
                    df_pred.apply(price_prediction_backtesting_color,
                                  axis=1).to_string())
            else:
                print(df_pred[["Real", "Prediction"]].round(2).to_string())

            print("")
            print_prediction_kpis(df_pred["Real"].values,
                                  df_pred["Prediction"].values)

        else:
            # Print prediction data
            print_pretty_prediction(df_pred,
                                    df_stock["5. adjusted close"].values[-1])
        print("")

    except SystemExit:
        print("")
    except Exception as e:
        print(e)
        print("")
Beispiel #8
0
def decompose(other_args: List[str], ticker: str, stock: pd.DataFrame):
    """Decompose time series as:
        - Additive Time Series = Level + CyclicTrend + Residual + Seasonality
        - Multiplicative Time Series = Level * CyclicTrend * Residual * Seasonality

    Parameters
    ----------
    other_args : str
        Command line arguments to be processed with argparse
    ticker : str
        Ticker of the stock
    stock : pd.DataFrame
        Stock data
    """
    parser = argparse.ArgumentParser(
        add_help=False,
        prog="decompose",
        description="""
            Decompose time series as:
              - Additive Time Series = Level + CyclicTrend + Residual + Seasonality
              - Multiplicative Time Series = Level * CyclicTrend * Residual * Seasonality
        """,
    )
    parser.add_argument(
        "-m",
        "--multiplicative",
        action="store_true",
        default=False,
        dest="multiplicative",
        help="decompose using multiplicative model instead of additive",
    )

    try:
        ns_parser = parse_known_args_and_warn(parser, other_args)
        if not ns_parser:
            return

        stock = stock["Adj Close"]

        seasonal_periods = 5
        # Hodrick-Prescott filter
        # See Ravn and Uhlig: http://home.uchicago.edu/~huhlig/papers/uhlig.ravn.res.2002.pdf
        lamb = 107360000000

        fig = plt.figure(figsize=plot_autoscale(),
                         dpi=PLOT_DPI,
                         constrained_layout=True)
        spec = gridspec.GridSpec(ncols=4, nrows=5, figure=fig)

        fig.add_subplot(spec[0, :])
        plt.plot(stock)

        plt.title(ticker + " (Time-Series)")

        if ns_parser.multiplicative:
            resultMul = seasonal_decompose(stock,
                                           model="multiplicative",
                                           period=seasonal_periods)
            cycleMul, trendMul = sm.tsa.filters.hpfilter(
                resultMul.trend[resultMul.trend.notna().values], lamb=lamb)

            # Multiplicative model
            fig.add_subplot(spec[1, :4])
            plt.plot(resultMul.trend, lw=2, c="purple")
            plt.xlim([stock.index[0], stock.index[-1]])
            plt.title("Multiplicative Cyclic-Trend")

            fig.add_subplot(spec[2, 0:2])
            plt.plot(trendMul, lw=2, c="tab:blue")
            plt.xlim([stock.index[0], stock.index[-1]])
            plt.title("Multiplicative Trend component")

            fig.add_subplot(spec[2, 2:])
            plt.plot(cycleMul, lw=2, c="green")
            plt.xlim([stock.index[0], stock.index[-1]])
            plt.title("Multiplicative Cycle component")

            fig.add_subplot(spec[3, :])
            plt.plot(resultMul.seasonal, lw=2, c="orange")
            plt.xlim([stock.index[0], stock.index[-1]])
            plt.title("Multiplicative Seasonal effect")

            fig.add_subplot(spec[4, :])
            plt.plot(resultMul.resid, lw=2, c="red")
            plt.xlim([stock.index[0], stock.index[-1]])
            plt.title("Multiplicative Residuals")

        else:
            resultAdd = seasonal_decompose(stock,
                                           model="additive",
                                           period=seasonal_periods)
            cycleAdd, trendAdd = sm.tsa.filters.hpfilter(
                resultAdd.trend[resultAdd.trend.notna().values], lamb=lamb)

            # Additive model
            fig.add_subplot(spec[1, :4])
            plt.plot(resultAdd.trend, lw=2, c="purple")
            plt.xlim([stock.index[0], stock.index[-1]])
            plt.title("Additive Cyclic-Trend")

            fig.add_subplot(spec[2, 0:2])
            plt.plot(trendAdd, lw=2, c="tab:blue")
            plt.xlim([stock.index[0], stock.index[-1]])
            plt.title("Additive Trend component")

            fig.add_subplot(spec[2, 2:])
            plt.plot(cycleAdd, lw=2, c="green")
            plt.xlim([stock.index[0], stock.index[-1]])
            plt.title("Additive Cycle component")

            fig.add_subplot(spec[3, :])
            plt.plot(resultAdd.seasonal, lw=2, c="orange")
            plt.xlim([stock.index[0], stock.index[-1]])
            plt.title("Additive Seasonal effect")

            fig.add_subplot(spec[4, :])
            plt.plot(resultAdd.resid, lw=2, c="red")
            plt.xlim([stock.index[0], stock.index[-1]])
            plt.title("Additive Residuals")

        if gtff.USE_ION:
            plt.ion()

        plt.show()
        print("")

        # From  # https://otexts.com/fpp2/seasonal-strength.html

        print("Time-Series Level is " + str(round(stock.mean(), 2)))

        if ns_parser.multiplicative:
            FtMul = max(0, 1 - np.var(
                resultMul.resid)) / np.var(resultMul.trend + resultMul.resid)
            print("Strength of Trend: %.4f" % FtMul)
            FsMul = max(
                0,
                1 - np.var(resultMul.resid) /
                np.var(resultMul.seasonal + resultMul.resid),
            )
            print("Strength of Seasonality: %.4f" % FsMul)

        else:
            FtAdd = max(
                0,
                1 - np.var(resultAdd.resid) /
                np.var(resultAdd.trend + resultAdd.resid),
            )
            print("Strength of Trend: %.4f" % FtAdd)
            FsAdd = max(
                0,
                1 - np.var(resultAdd.resid) /
                np.var(resultAdd.seasonal + resultAdd.resid),
            )
            print("Strength of Seasonality: %.4f" % FsAdd)
        print("")

    except Exception as e:
        print(e, "\n")
        return
Beispiel #9
0
def hist(other_args: List[str], ticker: str, stock: pd.DataFrame,
         start: datetime):
    """Plot histogram and density

    Parameters
    ----------
    other_args : str
        Command line arguments to be processed with argparse
    ticker : str
        Ticker of the stock
    stock : pd.DataFrame
        Stock data
    """
    parser = argparse.ArgumentParser(
        add_help=False,
        prog="hist",
        description="""
            Histogram with depicted density and rug
        """,
    )

    try:
        ns_parser = parse_known_args_and_warn(parser, other_args)
        if not ns_parser:
            return

        plt.figure(figsize=plot_autoscale(), dpi=PLOT_DPI)

        stock = stock["Adj Close"]

        sns.distplot(
            stock,
            bins=35,
            color="blue",
            hist=True,
            hist_kws={"edgecolor": "black"},
            kde=True,
            kde_kws={
                "color": "black",
                "lw": 3,
                "label": "KDE"
            },
            rug=True,
            rug_kws={"edgecolor": "orange"},
        )
        plt.title(
            f"Histogram with Density of {ticker} from {start.strftime('%Y-%m-%d')}"
        )
        plt.ylabel("Density")
        plt.xlabel("Share Price")
        plt.grid(True)

        if gtff.USE_ION:
            plt.ion()

        plt.show()
        print("")

    except Exception as e:
        print(e, "\n")
        return
Beispiel #10
0
def bwm(other_args: List[str], ticker: str, stock: pd.DataFrame,
        start: datetime):
    """Box and Whisker plot monthly

    Parameters
    ----------
    other_args : str
        Command line arguments to be processed with argparse
    ticker : str
        Ticker of the stock
    stock : pd.DataFrame
        Stock data
    """
    parser = argparse.ArgumentParser(
        add_help=False,
        prog="bwm",
        description="""
            Box and Whisker plot monthly
        """,
    )

    try:
        ns_parser = parse_known_args_and_warn(parser, other_args)
        if not ns_parser:
            return

        plt.figure(figsize=plot_autoscale(), dpi=PLOT_DPI)

        stock = stock["Adj Close"]

        sns.set(style="whitegrid")
        box_plot = sns.boxplot(x=stock.index.month, y=stock)
        box_plot.set(
            xlabel="Month",
            ylabel="Share Price",
            title=
            f"Box-plot per Month of {ticker} from {start.strftime('%Y-%m-%d')}",
        )
        l_months = [
            "Jan",
            "Feb",
            "Mar",
            "Apr",
            "May",
            "Jun",
            "Jul",
            "Aug",
            "Sep",
            "Oct",
            "Nov",
            "Dec",
        ]
        l_ticks = list()
        for val in box_plot.get_xticklabels():
            l_ticks.append(l_months[int(val.get_text()) - 1])
        box_plot.set_xticklabels(l_ticks)

        if gtff.USE_ION:
            plt.ion()

        plt.show()
        print("")

    except Exception as e:
        print(e, "\n")
        return
Beispiel #11
0
def rolling(other_args: List[str], ticker: str, stock: pd.DataFrame):
    """Rolling mean and std deviation

    Parameters
    ----------
    other_args : str
        Command line arguments to be processed with argparse
    ticker : str
        Ticker of the stock
    stock : pd.DataFrame
        Stock data
    """
    parser = argparse.ArgumentParser(
        add_help=False,
        prog="rolling",
        description="""
            Rolling mean and std deviation
        """,
    )
    parser.add_argument(
        "-w",
        "--window",
        dest="rolling_window",
        type=check_positive,
        default=10,
        help="rolling window",
    )

    try:
        ns_parser = parse_known_args_and_warn(parser, other_args)
        if not ns_parser:
            return

        stock = stock["Adj Close"]

        rolling_mean = stock.rolling(ns_parser.rolling_window,
                                     center=True,
                                     min_periods=1).mean()
        rolling_std = stock.rolling(ns_parser.rolling_window,
                                    center=True,
                                    min_periods=1).std()

        _, axMean = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI)

        axMean.plot(stock.index,
                    stock.values,
                    label=ticker,
                    linewidth=2,
                    color="black")
        axMean.plot(rolling_mean, linestyle="--", linewidth=3, color="blue")
        axMean.set_xlabel("Time")
        axMean.set_ylabel("Share Price", 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)
        axStd.set_ylabel("Std Deviation")
        axStd.legend(["Rolling std"], loc=1)
        axStd.set_ylabel("Share Price standard deviation", color="green")
        axStd.tick_params(axis="y", labelcolor="green")
        axMean.set_title("Rolling mean and std with window " +
                         str(ns_parser.rolling_window) + " applied to " +
                         ticker)
        plt.xlim([stock.index[0], stock.index[-1]])
        plt.grid(b=True, which="major", color="#666666", linestyle="-")
        plt.minorticks_on()
        plt.grid(b=True,
                 which="minor",
                 color="#999999",
                 linestyle="-",
                 alpha=0.2)

        if gtff.USE_ION:
            plt.ion()

        plt.show()
        print("")

    except Exception as e:
        print(e, "\n")
        return
Beispiel #12
0
def cumulative_distribution_function(other_args: List[str], ticker: str,
                                     stock: pd.DataFrame, start: datetime):
    """Plot cumulative distribution function

    Parameters
    ----------
    other_args : str
        Command line arguments to be processed with argparse
    ticker : str
        Ticker of the stock
    stock : pd.DataFrame
        Stock data
    """
    parser = argparse.ArgumentParser(
        add_help=False,
        prog="cdf",
        description="""
            Cumulative distribution function
        """,
    )

    try:
        ns_parser = parse_known_args_and_warn(parser, other_args)
        if not ns_parser:
            return

        plt.figure(figsize=plot_autoscale(), dpi=PLOT_DPI)

        stock = stock["Adj Close"]

        cdf = stock.value_counts().sort_index().div(len(stock)).cumsum()
        cdf.plot(lw=2)
        plt.title(
            f"Cumulative Distribution Function of {ticker} from {start.strftime('%Y-%m-%d')}"
        )
        plt.ylabel("Probability")
        plt.xlabel("Share Price")
        minVal = stock.values.min()
        q25 = np.quantile(stock.values, 0.25)
        medianVal = np.quantile(stock.values, 0.5)
        q75 = np.quantile(stock.values, 0.75)
        data = [
            (minVal, q25),
            (0.25, 0.25),
            "r",
            (q25, q25),
            (0, 0.25),
            "r",
            (minVal, medianVal),
            (0.5, 0.5),
            "r",
            (medianVal, medianVal),
            (0, 0.5),
            "r",
            (minVal, q75),
            (0.75, 0.75),
            "r",
            (q75, q75),
            (0, 0.75),
            "r",
        ]
        plt.plot(*data, ls="--")
        plt.text(minVal + (q25 - minVal) / 2,
                 0.27,
                 "Q1",
                 color="r",
                 fontweight="bold")
        plt.text(
            minVal + (medianVal - minVal) / 2,
            0.52,
            "Median",
            color="r",
            fontweight="bold",
        )
        plt.text(minVal + (q75 - minVal) / 2,
                 0.77,
                 "Q3",
                 color="r",
                 fontweight="bold")
        plt.xlim(cdf.index[0], cdf.index[-1])
        plt.grid(True)

        if gtff.USE_ION:
            plt.ion()

        plt.show()
        print("")

    except Exception as e:
        print(e, "\n")
        return
def exponential_smoothing(other_args: List[str], s_ticker: str,
                          df_stock: pd.DataFrame):
    """
    Perform exponential smoothing forecasting
    Parameters
    ----------
    other_args: List[str]
        Argparse arguments
    s_ticker: str
        Loaded ticker
    df_stock: pd.DataFrame
        Loaded stock dataframe

    """
    parser = argparse.ArgumentParser(
        add_help=False,
        prog="ets",
        description="""
            Exponential Smoothing, see https://otexts.com/fpp2/taxonomy.html

            Trend='N',  Seasonal='N': Simple Exponential Smoothing
            Trend='N',  Seasonal='A': Exponential Smoothing
            Trend='N',  Seasonal='M': Exponential Smoothing
            Trend='A',  Seasonal='N': Holt’s linear method
            Trend='A',  Seasonal='A': Additive Holt-Winters’ method
            Trend='A',  Seasonal='M': Multiplicative Holt-Winters’ method
            Trend='Ad', Seasonal='N': Additive damped trend method
            Trend='Ad', Seasonal='A': Exponential Smoothing
            Trend='Ad', Seasonal='M': Holt-Winters’ damped method
            Trend component: N: None, A: Additive, Ad: Additive Damped
            Seasonality component: N: None, A: Additive, M: Multiplicative
        """,
    )

    parser.add_argument(
        "-d",
        "--days",
        action="store",
        dest="n_days",
        type=check_positive,
        default=5,
        help="prediction days.",
    )
    parser.add_argument(
        "-t",
        "--trend",
        action="store",
        dest="trend",
        type=check_valid_trend,
        default="N",
        help="Trend component: N: None, A: Additive, Ad: Additive Damped.",
    )
    parser.add_argument(
        "-s",
        "--seasonal",
        action="store",
        dest="seasonal",
        type=check_valid_seasonal,
        default="N",
        help="Seasonality component: N: None, A: Additive, M: Multiplicative.",
    )
    parser.add_argument(
        "-p",
        "--periods",
        action="store",
        dest="seasonal_periods",
        type=check_positive,
        default=5,
        help="Seasonal periods.",
    )
    parser.add_argument(
        "-e",
        "--end",
        action="store",
        type=valid_date,
        dest="s_end_date",
        default=None,
        help="The end date (format YYYY-MM-DD) to select - Backtesting",
    )

    try:
        ns_parser = parse_known_args_and_warn(parser, other_args)
        if not ns_parser:
            return

        # BACKTESTING
        if ns_parser.s_end_date:

            if ns_parser.s_end_date < df_stock.index[0]:
                print(
                    "Backtesting not allowed, since End Date is older than Start Date of historical data\n"
                )
                return

            if (ns_parser.s_end_date < get_next_stock_market_days(
                    last_stock_day=df_stock.index[0],
                    n_next_days=5 + ns_parser.n_days)[-1]):
                print(
                    "Backtesting not allowed, since End Date is too close to Start Date to train model\n"
                )
                return

            future_index = get_next_stock_market_days(
                last_stock_day=ns_parser.s_end_date,
                n_next_days=ns_parser.n_days)

            if future_index[-1] > datetime.datetime.now():
                print(
                    "Backtesting not allowed, since End Date + Prediction days is in the future\n"
                )
                return

            df_future = df_stock[future_index[0]:future_index[-1]]
            df_stock = df_stock[:ns_parser.s_end_date]

        # Get ETS model
        model, title = get_exponential_smoothing_model(
            df_stock["Adj Close"].values,
            ns_parser.trend,
            ns_parser.seasonal,
            ns_parser.seasonal_periods,
        )

        if model.mle_retvals.success:
            forecast = [
                i if i > 0 else 0 for i in model.forecast(ns_parser.n_days)
            ]

            l_pred_days = get_next_stock_market_days(
                last_stock_day=df_stock["Adj Close"].index[-1],
                n_next_days=ns_parser.n_days,
            )
            df_pred = pd.Series(forecast, index=l_pred_days, name="Price")

            if ~np.isnan(forecast).any():

                print(f"\n{title}")
                print("\nFit model parameters:")
                for key, value in model.params.items():
                    print(f"{key} {' '*(18-len(key))}: {value}")

                print("\nAssess fit model:")
                print(f"AIC: {round(model.aic, 2)}")
                print(f"BIC: {round(model.bic, 2)}")
                print(f"SSE: {round(model.sse, 2)}\n")

                # Plotting
                plt.figure(figsize=plot_autoscale(), dpi=PLOT_DPI)
                plt.plot(df_stock.index, df_stock["Adj Close"], lw=2)
                # BACKTESTING
                if ns_parser.s_end_date:
                    plt.title(f"BACKTESTING: {title} on {s_ticker}")
                else:
                    plt.title(f"{title} on {s_ticker}")

                plt.xlim(
                    df_stock.index[0],
                    get_next_stock_market_days(df_pred.index[-1], 1)[-1],
                )
                plt.xlabel("Time")
                plt.ylabel("Share Price ($)")
                plt.grid(b=True, which="major", color="#666666", linestyle="-")
                plt.minorticks_on()
                plt.grid(b=True,
                         which="minor",
                         color="#999999",
                         linestyle="-",
                         alpha=0.2)
                plt.plot(
                    [df_stock.index[-1], df_pred.index[0]],
                    [df_stock["Adj Close"].values[-1], df_pred.values[0]],
                    lw=1,
                    c="tab:green",
                    linestyle="--",
                )
                plt.plot(df_pred.index, df_pred, lw=2, c="tab:green")
                plt.axvspan(
                    df_stock.index[-1],
                    df_pred.index[-1],
                    facecolor="tab:orange",
                    alpha=0.2,
                )
                _, _, ymin, ymax = plt.axis()
                plt.vlines(
                    df_stock.index[-1],
                    ymin,
                    ymax,
                    linewidth=1,
                    linestyle="--",
                    color="k",
                )

                # BACKTESTING
                if ns_parser.s_end_date:
                    plt.plot(
                        df_future.index,
                        df_future["Adj Close"],
                        lw=2,
                        c="tab:blue",
                        ls="--",
                    )
                    plt.plot(
                        [df_stock.index[-1], df_future.index[0]],
                        [
                            df_stock["Adj Close"].values[-1],
                            df_future["Adj Close"].values[0],
                        ],
                        lw=1,
                        c="tab:blue",
                        linestyle="--",
                    )

                if gtff.USE_ION:
                    plt.ion()

                plt.show()

                # BACKTESTING
                if ns_parser.s_end_date:
                    plt.figure(figsize=plot_autoscale(), dpi=PLOT_DPI)
                    plt.subplot(211)
                    plt.plot(
                        df_future.index,
                        df_future["Adj Close"],
                        lw=2,
                        c="tab:blue",
                        ls="--",
                    )
                    plt.plot(df_pred.index, df_pred, lw=2, c="green")
                    plt.scatter(
                        df_future.index,
                        df_future["Adj Close"],
                        c="tab:blue",
                        lw=3,
                    )
                    plt.plot(
                        [df_stock.index[-1], df_future.index[0]],
                        [
                            df_stock["Adj Close"].values[-1],
                            df_future["Adj Close"].values[0],
                        ],
                        lw=2,
                        c="tab:blue",
                        ls="--",
                    )
                    plt.scatter(df_pred.index, df_pred, c="green", lw=3)
                    plt.plot(
                        [df_stock.index[-1], df_pred.index[0]],
                        [df_stock["Adj Close"].values[-1], df_pred.values[0]],
                        lw=2,
                        c="green",
                        ls="--",
                    )
                    plt.title("BACKTESTING: Real data price versus Prediction")
                    plt.xlim(
                        df_stock.index[-1],
                        df_pred.index[-1] + datetime.timedelta(days=1),
                    )
                    plt.ylabel("Share Price ($)")
                    plt.grid(b=True,
                             which="major",
                             color="#666666",
                             linestyle="-")
                    plt.minorticks_on()
                    plt.grid(b=True,
                             which="minor",
                             color="#999999",
                             linestyle="-",
                             alpha=0.2)
                    plt.legend(["Real data", "Prediction data"])
                    plt.xticks([])

                    plt.subplot(212)
                    plt.axhline(y=0, color="k", linestyle="--", linewidth=2)
                    plt.plot(
                        df_future.index,
                        100 *
                        (df_pred.values - df_future["Adj Close"].values) /
                        df_future["Adj Close"].values,
                        lw=2,
                        c="red",
                    )
                    plt.scatter(
                        df_future.index,
                        100 *
                        (df_pred.values - df_future["Adj Close"].values) /
                        df_future["Adj Close"].values,
                        c="red",
                        lw=5,
                    )
                    plt.title(
                        "BACKTESTING: Error between Real data and Prediction [%]"
                    )
                    plt.plot(
                        [df_stock.index[-1], df_future.index[0]],
                        [
                            0,
                            100 * (df_pred.values[0] -
                                   df_future["Adj Close"].values[0]) /
                            df_future["Adj Close"].values[0],
                        ],
                        lw=2,
                        ls="--",
                        c="red",
                    )
                    plt.xlim(
                        df_stock.index[-1],
                        df_pred.index[-1] + datetime.timedelta(days=1),
                    )
                    plt.xlabel("Time")
                    plt.ylabel("Prediction Error (%)")
                    plt.grid(b=True,
                             which="major",
                             color="#666666",
                             linestyle="-")
                    plt.minorticks_on()
                    plt.grid(b=True,
                             which="minor",
                             color="#999999",
                             linestyle="-",
                             alpha=0.2)
                    plt.legend(["Real data", "Prediction data"])

                    if gtff.USE_ION:
                        plt.ion()

                    plt.show()

                    # Refactor prediction dataframe for backtesting print
                    df_pred.name = "Prediction"
                    df_pred = df_pred.to_frame()
                    df_pred["Real"] = df_future["Adj Close"]

                    if gtff.USE_COLOR:

                        patch_pandas_text_adjustment()

                        print("Time         Real [$]  x  Prediction [$]")
                        print(
                            df_pred.apply(price_prediction_backtesting_color,
                                          axis=1).to_string())
                    else:
                        print(df_pred[["Real",
                                       "Prediction"]].round(2).to_string())

                    print("")
                    print_prediction_kpis(df_pred["Real"].values,
                                          df_pred["Prediction"].values)

                else:
                    # Print prediction data
                    print_pretty_prediction(df_pred,
                                            df_stock["Adj Close"].values[-1])
                print("")

            else:
                print(
                    "RuntimeWarning: invalid value encountered in double_scalars."
                )
        else:
            print("ConvergenceWarning: Optimization failed to converge.")

    except Exception as e:
        print(e)
        print("")
        df = df / 1_000
        denomination = "[$ Thousands]"
    else:
        denomination = ""

    if raw:
        print_rich_table(
            df.fillna("-"),
            headers=list(df.columns),
            show_index=True,
            title=f"{item_name} {denomination}",
        )
    else:
        # This plot has 1 axis
        if external_axes is None:
            _, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI)
        else:
            if len(external_axes) != 1:
                console.print("[red]Expected list of one axis item./n[/red]")
                return stocks_data, company_tickers
            (ax, ) = external_axes

        for company in df.columns:
            ax.plot(df[company], ls="-", marker="o", label=company)

        ax.set_title(f"{item_name} {denomination}")
        ax.legend()
        theme.style_primary_axis(ax)

        if external_axes is None:
            theme.visualize_output()
def fibonacci_retracement(
    df_stock: pd.DataFrame,
    period: int = 120,
    start_date: Any = None,
    end_date: Any = None,
    s_ticker: str = "",
    export: str = "",
):
    """Calculate fibonacci retracement levels

    Parameters
    ----------
    df_stock: pd.DataFrame
        Stock data
    period: int
        Days to lookback
    start_date: Any
        User picked date for starting retracement
    end_date: Any
        User picked date for ending retracement
    s_ticker:str
        Stock ticker
    export: str
        Format to export data
    """
    (
        df_fib,
        min_date,
        max_date,
        min_pr,
        max_pr,
    ) = custom_indicators_model.calculate_fib_levels(df_stock, period,
                                                     start_date, end_date)

    levels = df_fib.Price
    fig, ax = plt.subplots(figsize=(plot_autoscale()), dpi=cfp.PLOT_DPI)

    ax.plot(df_stock["Adj Close"], "b")
    ax.plot([min_date, max_date], [min_pr, max_pr], c="k")

    for i in levels:
        ax.axhline(y=i, c="g", alpha=0.5)

    for i in range(5):
        ax.fill_between(df_stock.index, levels[i], levels[i + 1], alpha=0.6)

    ax.set_ylabel("Price")
    ax.set_title(f"Fibonacci Support for {s_ticker.upper()}")
    ax.set_xlim(df_stock.index[0], df_stock.index[-1])

    ax1 = ax.twinx()
    ax1.set_ylim(ax.get_ylim())
    ax1.set_yticks(levels)
    ax1.set_yticklabels([0, 0.235, 0.382, 0.5, 0.618, 1])

    plt.gcf().autofmt_xdate()
    fig.tight_layout(pad=1)

    if gtff.USE_ION:
        plt.ion()
    plt.show()

    print_rich_table(
        df_fib,
        headers=["Fib Level", "Price"],
        show_index=False,
        title="Fibonacci retractment levels",
    )
    console.print("")

    export_data(
        export,
        os.path.dirname(os.path.abspath(__file__)).replace("common", "stocks"),
        "fib",
        df_fib,
    )
Beispiel #16
0
def acf(other_args: List[str], ticker: str, stock: pd.DataFrame,
        start: datetime):
    """Auto-Correlation and Partial Auto-Correlation Functions for diff and diff diff stock data

    Parameters
    ----------
    other_args : str
        Command line arguments to be processed with argparse
    ticker : str
        Ticker of the stock
    stock : pd.DataFrame
        Stock data
    """
    parser = argparse.ArgumentParser(
        add_help=False,
        prog="acf",
        description="""
            Auto-Correlation and Partial Auto-Correlation Functions for diff and diff diff stock data
        """,
    )
    parser.add_argument(
        "-l",
        "--lags",
        dest="lags",
        type=check_positive,
        default=15,
        help="maximum lags to display in plots",
    )

    try:
        ns_parser = parse_known_args_and_warn(parser, other_args)
        if not ns_parser:
            return

        print(stock.head())
        stock = stock["Adj Close"]

        fig = plt.figure(figsize=plot_autoscale(),
                         dpi=PLOT_DPI,
                         constrained_layout=True)
        spec = gridspec.GridSpec(ncols=2, nrows=2, figure=fig)

        # Diff Auto-correlation function for original time series
        ax_acf = fig.add_subplot(spec[0, 0])
        sm.graphics.tsa.plot_acf(np.diff(np.diff(stock.values)),
                                 lags=ns_parser.lags,
                                 ax=ax_acf)
        plt.title(
            f"Diff({ticker}) Auto-Correlation from {start.strftime('%Y-%m-%d')}"
        )
        # Diff Partial auto-correlation function for original time series
        ax_pacf = fig.add_subplot(spec[0, 1])
        sm.graphics.tsa.plot_pacf(np.diff(np.diff(stock.values)),
                                  lags=ns_parser.lags,
                                  ax=ax_pacf)
        plt.title(
            f"Diff({ticker}) Partial Auto-Correlation from {start.strftime('%Y-%m-%d')}"
        )

        # Diff Diff Auto-correlation function for original time series
        ax_acf = fig.add_subplot(spec[1, 0])
        sm.graphics.tsa.plot_acf(np.diff(np.diff(stock.values)),
                                 lags=ns_parser.lags,
                                 ax=ax_acf)
        plt.title(
            f"Diff(Diff({ticker})) Auto-Correlation from {start.strftime('%Y-%m-%d')}"
        )
        # Diff Diff Partial auto-correlation function for original time series
        ax_pacf = fig.add_subplot(spec[1, 1])
        sm.graphics.tsa.plot_pacf(np.diff(np.diff(stock.values)),
                                  lags=ns_parser.lags,
                                  ax=ax_pacf)
        plt.title(
            f"Diff(Diff({ticker})) Partial Auto-Correlation from {start.strftime('%Y-%m-%d')}"
        )

        if gtff.USE_ION:
            plt.ion()

        plt.show()
        print("")

    except Exception as e:
        print(e, "\n")
        return
def display_big_mac_index(
    country_codes: List[str],
    raw: bool = False,
    export: str = "",
    external_axes: Optional[List[plt.Axes]] = None,
):
    """Display Big Mac Index for given countries

    Parameters
    ----------
    country_codes : List[str]
        List of country codes to get for
    raw : bool, optional
        Flag to display raw data, by default False
    export : str, optional
        Format data, by default ""
    external_axes : Optional[List[plt.Axes]], optional
        External axes (3 axes are expected in the list), by default None
    """
    df_cols = ["Date"]
    df_cols.extend(country_codes)
    big_mac = pd.DataFrame(columns=df_cols)
    for country in country_codes:
        df1 = nasdaq_model.get_big_mac_index(country)
        if not df1.empty:
            big_mac[country] = df1["dollar_price"]
            big_mac["Date"] = df1["Date"]
    big_mac.set_index("Date", inplace=True)

    if not big_mac.empty:
        if external_axes is None:
            _, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI)

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

        big_mac.plot(ax=ax, marker="o")
        ax.legend()
        ax.set_title("Big Mac Index (USD)")
        ax.set_ylabel("Price of Big Mac in USD")
        theme.style_primary_axis(ax)
        if external_axes is None:
            theme.visualize_output()

        if raw:
            print_rich_table(
                big_mac, headers=list(big_mac.columns), title="Big Mac Index"
            )
            console.print("")

        export_data(
            export, os.path.dirname(os.path.abspath(__file__)), "bigmac", big_mac
        )
        console.print("")
    else:
        logger.error("Unable to get big mac data")
        console.print("[red]Unable to get big mac data[/red]\n")
def show_ef(stocks: List[str], other_args: List[str]):
    """Display efficient frontier

    Parameters
    ----------
    stocks : List[str]
        List of the stocks to be included in the weights
    other_args : List[str]
        argparse other args
    """

    parser = argparse.ArgumentParser(
        add_help=False,
        prog="ef",
        description="""This function plots random portfolios based
                                     on their risk and returns and shows the efficient frontier.""",
    )

    parser.add_argument(
        "-p",
        "--period",
        default="3mo",
        dest="period",
        help="period to get yfinance data from",
        choices=period_choices,
    )
    parser.add_argument(
        "-n",
        "--number-portfolios",
        default=300,
        type=check_non_negative,
        dest="n_port",
        help="number of portfolios to simulate",
    )

    try:
        if other_args:
            if "-" not in other_args[0]:
                other_args.insert(0, "-n")
        ns_parser = parse_known_args_and_warn(parser, other_args)
        if not ns_parser:
            return
        if len(stocks) < 2:
            print("Please have at least 2 loaded tickers to calculate weights.\n")
            return

        stock_prices = process_stocks(stocks, ns_parser.period)
        mu = expected_returns.mean_historical_return(stock_prices)
        S = risk_models.sample_cov(stock_prices)
        ef = EfficientFrontier(mu, S)
        _, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI)

        # Generate random portfolios
        n_samples = ns_parser.n_port
        w = np.random.dirichlet(np.ones(len(mu)), n_samples)
        rets = w.dot(mu)
        stds = np.sqrt(np.diag(w @ S @ w.T))
        sharpes = rets / stds
        ax.scatter(stds, rets, marker=".", c=sharpes, cmap="viridis_r")

        plotting.plot_efficient_frontier(ef, ax=ax, show_assets=True)
        # Find the tangency portfolio
        ef.max_sharpe()
        ret_sharpe, std_sharpe, _ = ef.portfolio_performance()
        ax.scatter(std_sharpe, ret_sharpe, marker="*", s=100, c="r", label="Max Sharpe")

        ax.set_title(f"Efficient Frontier simulating {ns_parser.n_port} portfolios")
        ax.legend()
        plt.tight_layout()
        plt.grid(b=True, which="major", color="#666666", linestyle="-")
        plt.minorticks_on()
        plt.grid(b=True, which="minor", color="#999999", linestyle="-", alpha=0.2)

        if gtff.USE_ION:
            plt.ion()

        plt.show()
        print("")

    except Exception as e:
        print(e)
        print("")
def historical(
    other_args: List[str],
    df_stock: pd.DataFrame,
    ticker: str,
    start: datetime,
    interval: str,
    similar: List[str],
):
    """Display historical data from Yahoo Finance

    Parameters
    ----------
    other_args : List[str]
        Command line arguments to be processed with argparse
    df_stock : pd.DataFrame
        Stock data
    ticker : str
        Ticker symbol
    start : datetime
        Time start
    interval : str
        Time interval
    similar : List[str]
        List of similar tickers
    """
    parser = argparse.ArgumentParser(
        add_help=False,
        formatter_class=argparse.ArgumentDefaultsHelpFormatter,
        prog="historical",
        description=
        """Historical price comparison between similar companies [Source: Yahoo Finance]
        """,
    )
    parser.add_argument(
        "-s",
        "--similar",
        dest="l_similar",
        type=lambda s: [str(item) for item in s.split(",")],
        default=[],
        help="similar companies to compare with.",
    )
    parser.add_argument(
        "-a",
        "--also",
        dest="l_also",
        type=lambda s: [str(item) for item in s.split(",")],
        default=[],
        help="apart from loaded similar companies also compare with.",
    )
    parser.add_argument(
        "-t",
        "--type",
        action="store",
        dest="type_candle",
        type=check_one_of_ohlca,
        default="a",  # in case it's adjusted close
        help=(
            "type of candles: o-open, h-high, l-low, c-close, a-adjusted close."
        ),
    )

    try:
        if interval != "1440min":
            print(
                "Intraday historical data analysis comparison is not yet available."
            )
            # Alpha Vantage only supports 5 calls per minute, we need another API to get intraday data
        else:
            ns_parser = parse_known_args_and_warn(parser, other_args)
            if not ns_parser:
                return

            if ns_parser.l_similar:
                similar = ns_parser.l_similar

            similar += ns_parser.l_also

            plt.figure(figsize=plot_autoscale(), dpi=PLOT_DPI)
            plt.title(f"Similar companies to {ticker}")
            df_stock = yf.download(ticker,
                                   start=start,
                                   progress=False,
                                   threads=False)
            plt.plot(df_stock.index,
                     df_stock[d_candle_types[ns_parser.type_candle]].values)
            # plt.plot(df_stock.index, df_stock["Adj Close"].values, lw=2)
            l_min = [df_stock.index[0]]
            l_leg = [ticker]

            l_stocks = similar[:]

            while l_stocks:
                l_parsed_stocks = []
                for symbol in l_stocks:
                    try:
                        df_similar_stock = yf.download(symbol,
                                                       start=start,
                                                       progress=False,
                                                       threads=False)
                        if not df_similar_stock.empty:
                            plt.plot(
                                df_similar_stock.index,
                                df_similar_stock[d_candle_types[
                                    ns_parser.type_candle]].values,
                            )
                            l_min.append(df_similar_stock.index[0])
                            l_leg.append(symbol)

                        l_parsed_stocks.append(symbol)
                    except Exception as e:
                        print("")
                        print(e)
                        print(
                            "Disregard previous error, which is due to API Rate limits from Yahoo Finance."
                        )
                        print(
                            f"Because we like '{symbol}', and we won't leave without getting data from it."
                        )

                for parsed_stock in l_parsed_stocks:
                    l_stocks.remove(parsed_stock)

            plt.xlabel("Time")
            plt.ylabel("Share Price ($)")
            plt.legend(l_leg)
            plt.grid(b=True, which="major", color="#666666", linestyle="-")
            plt.minorticks_on()
            plt.grid(b=True,
                     which="minor",
                     color="#999999",
                     linestyle="-",
                     alpha=0.2)
            # ensures that the historical data starts from same datapoint
            plt.xlim([max(l_min), df_stock.index[-1]])
            plt.show()
        print("")

    except SystemExit:
        print("Similar companies need to be provided", "\n")
    except Exception as e:
        print(e, "\n")
Beispiel #20
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 = tradier_model.get_option_chains(ticker, expiry)
    current_price = tradier_model.last_price(ticker)

    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

    calls = options[options.option_type == "call"][["strike", "open_interest"]]
    puts = options[options.option_type == "put"][["strike", "open_interest"]]
    call_oi = calls.set_index("strike")["open_interest"] / 1000
    put_oi = puts.set_index("strike")["open_interest"] / 1000

    df_opt = pd.merge(call_oi, put_oi, left_index=True, right_index=True)
    df_opt = df_opt.rename(
        columns={"open_interest_x": "OI_call", "open_interest_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:
            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="open_interest",
            label="Puts",
            ax=ax,
            marker="o",
            ls="-",
        )
    if not puts_only:
        call_oi.plot(
            x="strike",
            y="open_interest",
            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.set_title(f"Open Interest 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__)),
        "oi_tr",
        options,
    )
def plot_adx(
    s_ticker: str,
    s_interval: str,
    df_stock: pd.DataFrame,
    length: int,
    scalar: int,
    drift: int,
    export: str,
):
    """Plot ADX indicator

    Parameters
    ----------
    s_ticker : str
        Ticker
    s_interval : str
        Interval for data
    df_stock : pd.DataFrame
        Dataframe of prices
    length : int
        Length of window
    scalar : int
        Scalar variable
    drift : int
        Drift variable
    export: str
        Format to export data
    """
    df_ta = trend_indicators_model.adx(s_interval, df_stock, length, scalar,
                                       drift)

    fig, ax = plt.subplots(2, 1, figsize=plot_autoscale(), dpi=PLOT_DPI)
    ax0 = ax[0]
    ax0.plot(df_stock.index, df_stock["Close"].values, "k", lw=2)
    ax0.set_title(f"Average Directional Movement Index (ADX) on {s_ticker}")
    ax0.set_xlim(df_stock.index[0], df_stock.index[-1])
    ax0.set_ylabel("Share Price ($)")
    ax0.grid(b=True, which="major", color="#666666", linestyle="-")

    ax1 = ax[1]
    ax1.plot(df_ta.index, df_ta.iloc[:, 0].values, "b", lw=2)
    ax1.plot(df_ta.index, df_ta.iloc[:, 1].values, "g", lw=1)
    ax1.plot(df_ta.index, df_ta.iloc[:, 2].values, "r", lw=1)
    ax1.set_xlim(df_stock.index[0], df_stock.index[-1])
    ax1.axhline(25, linewidth=3, color="k", ls="--")
    ax1.legend(
        [
            f"ADX ({df_ta.columns[0]})",
            f"+DI ({df_ta.columns[1]})",
            f"- DI ({df_ta.columns[2]})",
        ],
        loc="upper left",
    )
    ax1.set_xlabel("Time")
    ax1.grid(b=True, which="major", color="#666666", linestyle="-")

    ax1.set_ylim([0, 100])

    if gtff.USE_ION:
        plt.ion()
    fig.tight_layout()
    plt.gcf().autofmt_xdate()

    plt.show()
    print("")

    export_data(
        export,
        os.path.dirname(os.path.abspath(__file__)).replace("common", "stocks"),
        "adx",
        df_ta,
    )
Beispiel #22
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 = tradier_model.get_option_chains(ticker, expiry)
    current_price = tradier_model.last_price(ticker)

    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

    calls = options[options.option_type == "call"][["strike", "volume"]]
    puts = options[options.option_type == "put"][["strike", "volume"]]
    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:
        ax = external_axes[0]

    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)
    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_tr",
        options,
    )
    console.print("")
Beispiel #23
0
def display_correlation_interest(
    ticker: str,
    df_data: pd.DataFrame,
    words: List[str],
    export: str = "",
    external_axes: Optional[List[plt.Axes]] = None,
):
    """Plot interest over time of words/sentences versus stock price. [Source: Google]

    Parameters
    ----------
    ticker : str
        Ticker to check price
    df_data : pd.DataFrame
        Data dataframe
    words : List[str]
        Words to check for interest for
    export: str
        Format to export data
    external_axes : Optional[List[plt.Axes]], optional
        External axes (1 axis is expected in the list), by default None
    """

    # This plot has 1 axis
    if external_axes is None:
        _, ax = plt.subplots(
            figsize=plot_autoscale(),
            dpi=PLOT_DPI,
            nrows=2,
            ncols=1,
            sharex=True,
            gridspec_kw={"height_ratios": [1, 2]},
        )
    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[0].set_title(
        f"{ticker.upper()} stock price and interest over time on {','.join(words)}"
    )
    ax[0].plot(
        df_data.index,
        df_data["Adj Close"].values,
        c="#FCED00",
    )
    ax[0].set_ylabel("Stock Price")
    ax[0].set_xlim(df_data.index[0], df_data.index[-1])

    colors = theme.get_colors()[1:]
    for idx, word in enumerate(words):
        df_interest = google_model.get_mentions(word)
        ax[1].plot(df_interest.index,
                   df_interest[word],
                   "-",
                   color=colors[idx])

    ax[1].set_ylabel("Interest [%]")
    ax[1].set_xlim(df_data.index[0], df_data.index[-1])
    ax[1].legend(words)
    theme.style_primary_axis(ax[0])
    theme.style_primary_axis(ax[1])

    if external_axes is None:
        theme.visualize_output()

    export_data(export, os.path.dirname(os.path.abspath(__file__)), "interest",
                df_interest)
Beispiel #24
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
    """
    current_price = tradier_model.last_price(ticker)
    options = tradier_model.get_option_chains(ticker, expiry)

    calls = options[options.option_type == "call"][
        ["strike", "volume", "open_interest"]
    ]
    puts = options[options.option_type == "put"][["strike", "volume", "open_interest"]]

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

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

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

    df_opt = pd.merge(call_oi, put_oi, left_index=True, right_index=True)
    df_opt = df_opt.rename(
        columns={"open_interest_x": "OI_call", "open_interest_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:
        ax = external_axes[0]

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

    _ = ax.legend()
    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_tr",
        options,
    )
Beispiel #25
0
def display_government_sells(
    gov_type: str,
    past_transactions_months: int = 6,
    num: int = 10,
    raw: bool = False,
    export: str = "",
):
    """Top buy government trading [Source: quiverquant.com]

    Parameters
    ----------
    gov_type: str
        Type of government data between: congress, senate and house
    past_transactions_months: int
        Number of months to get trading for
    num: int
        Number of tickers to show
    raw: bool
        Display raw data
    export: str
        Format to export data
    """
    df_gov = quiverquant_model.get_government_trading(gov_type)

    if df_gov.empty:
        print(f"No {gov_type} trading data found\n")
        return

    df_gov = df_gov.sort_values("TransactionDate", ascending=False)

    start_date = datetime.now() - timedelta(days=past_transactions_months * 30)

    df_gov["TransactionDate"] = pd.to_datetime(df_gov["TransactionDate"])

    df_gov = df_gov[df_gov["TransactionDate"] > start_date].dropna()
    df_gov["Range"] = df_gov["Range"].apply(
        lambda x: "$5,000,001-$5,000,001" if x == ">$5,000,000" else x
    )
    df_gov["min"] = df_gov["Range"].apply(
        lambda x: x.split("-")[0]
        .strip("$")
        .replace(",", "")
        .strip()
        .replace(">$", "")
        .strip()
    )
    df_gov["max"] = df_gov["Range"].apply(
        lambda x: x.split("-")[1]
        .replace(",", "")
        .strip()
        .strip("$")
        .replace(">$", "")
        .strip()
        if "-" in x
        else x.strip("$").replace(",", "").replace(">$", "").strip()
    )

    df_gov["lower"] = df_gov[["min", "max", "Transaction"]].apply(
        lambda x: float(x["min"])
        if x["Transaction"] == "Purchase"
        else -float(x["max"]),
        axis=1,
    )
    df_gov["upper"] = df_gov[["min", "max", "Transaction"]].apply(
        lambda x: float(x["max"])
        if x["Transaction"] == "Purchase"
        else -float(x["min"]),
        axis=1,
    )

    df_gov = df_gov.sort_values("TransactionDate", ascending=True)
    if raw:
        df = pd.DataFrame(
            df_gov.groupby("Ticker")["upper"]
            .sum()
            .div(1000)
            .sort_values(ascending=True)
            .abs()
            .head(n=num)
        )
        if gtff.USE_TABULATE_DF:
            print(
                tabulate(
                    df, headers=["Amount ($1k)"], tablefmt="fancy_grid", showindex=True
                )
            )
        else:
            print(df.to_string())
    fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI)

    df_gov.groupby("Ticker")["upper"].sum().div(1000).sort_values().abs().head(
        n=num
    ).plot(kind="bar", rot=0, ax=ax)
    ax.set_ylabel("Amount ($1k)")
    ax.set_title(
        f"{num} most sold stocks over last {past_transactions_months} months"
        f" (upper bound) for {gov_type}"
    )
    plt.gcf().axes[0].yaxis.get_major_formatter().set_scientific(False)
    if gtff.USE_ION:
        plt.ion()
    fig.tight_layout()
    plt.show()
    print("")
    export_data(export, os.path.dirname(os.path.abspath(__file__)), "topsells", df_gov)
Beispiel #26
0
def display_historical(
    ticker: str,
    expiry: str,
    strike: float,
    put: bool,
    raw: bool,
    chain_id: str,
    export: str = "",
    external_axes: Optional[List[plt.Axes]] = None,
):
    """Plot historical option prices

    Parameters
    ----------
    ticker: str
        Stock ticker
    expiry: str
        Expiry date of option
    strike: float
        Option strike price
    put: bool
        Is this a put option?
    raw: bool
        Print raw data
    chain_id: str
        OCC option symbol
    export: str
        Format of export file
    external_axes : Optional[List[plt.Axes]], optional
        External axes (1 axis is expected in the list), by default None
    """

    df_hist = tradier_model.get_historical_options(
        ticker, expiry, strike, put, chain_id
    )

    if raw:
        print_rich_table(
            df_hist,
            headers=[x.title() for x in df_hist.columns],
            title="Historical Option Prices",
        )

    op_type = ["call", "put"][put]

    candle_chart_kwargs = {
        "type": "candle",
        "style": theme.mpf_style,
        "volume": True,
        "xrotation": theme.xticks_rotation,
        "scale_padding": {"left": 0.3, "right": 1.2, "top": 0.8, "bottom": 0.8},
        "update_width_config": {
            "candle_linewidth": 0.6,
            "candle_width": 0.8,
            "volume_linewidth": 0.8,
            "volume_width": 0.8,
        },
    }
    if external_axes is None:
        candle_chart_kwargs["returnfig"] = True
        candle_chart_kwargs["figratio"] = (10, 7)
        candle_chart_kwargs["figscale"] = 1.10
        candle_chart_kwargs["figsize"] = plot_autoscale()
        fig, _ = mpf.plot(df_hist, **candle_chart_kwargs)
        fig.suptitle(
            f"Historical {strike} {op_type.title()}",
            x=0.055,
            y=0.965,
            horizontalalignment="left",
        )
        theme.visualize_output(force_tight_layout=False)
    else:
        if len(external_axes) != 2:
            console.print("[red]Expected list of 2 axis items./n[/red]")
            return
        (ax1, ax2) = external_axes
        candle_chart_kwargs["ax"] = ax1
        candle_chart_kwargs["volume"] = ax2
        mpf.plot(df_hist, **candle_chart_kwargs)

    console.print()

    if export:
        export_data(
            export,
            os.path.dirname(os.path.abspath(__file__)),
            "hist",
            df_hist,
        )
Beispiel #27
0
def display_qtr_contracts(analysis: str, num: int, raw: bool = False, export: str = ""):
    """Quarterly contracts [Source: quiverquant.com]

    Parameters
    ----------
    analysis: str
        Analysis to perform.  Either 'total', 'upmom' 'downmom'
    num: int
        Number to show
    raw: bool
        Flag to display raw data
    export: str
        Format to export data
    """
    df_contracts = quiverquant_model.get_government_trading("quarter-contracts")

    if df_contracts.empty:
        print("No quarterly government contracts found\n")
        return

    tickers = quiverquant_model.analyze_qtr_contracts(analysis, num)
    if analysis in ("upmom", "downmom"):
        if raw:
            if gtff.USE_TABULATE_DF:
                print(
                    tabulate(
                        pd.DataFrame(tickers.values),
                        headers=["tickers"],
                        tablefmt="fancy_grid",
                        showindex=False,
                    )
                )
            else:
                print(tickers.to_string())
        else:
            fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI)
            max_amount = 0
            quarter_ticks = []
            for symbol in tickers:
                amounts = (
                    df_contracts[df_contracts["Ticker"] == symbol]
                    .sort_values(by=["Year", "Qtr"])["Amount"]
                    .values
                )

                qtr = (
                    df_contracts[df_contracts["Ticker"] == symbol]
                    .sort_values(by=["Year", "Qtr"])["Qtr"]
                    .values
                )
                year = (
                    df_contracts[df_contracts["Ticker"] == symbol]
                    .sort_values(by=["Year", "Qtr"])["Year"]
                    .values
                )

                ax.plot(
                    np.arange(0, len(amounts)), amounts / 1_000_000, "-*", lw=2, ms=15
                )

                if len(amounts) > max_amount:
                    max_amount = len(amounts)
                    quarter_ticks = [
                        f"{quarter[0]} - Q{quarter[1]} " for quarter in zip(year, qtr)
                    ]

            ax.set_xlim([-0.5, max_amount - 0.5])
            ax.set_xticks(np.arange(0, max_amount))
            ax.set_xticklabels(quarter_ticks)
            ax.grid()
            ax.legend(tickers)
            titles = {
                "upmom": "Highest increasing quarterly Government Contracts",
                "downmom": "Highest decreasing quarterly Government Contracts",
            }
            ax.set_title(titles[analysis])
            ax.set_xlabel("Date")
            ax.set_ylabel("Amount ($1M)")
            fig.tight_layout()
            if gtff.USE_ION:
                plt.ion()

            plt.show()

    elif analysis == "total":
        if gtff.USE_TABULATE_DF:
            print(
                tabulate(
                    tickers, headers=["Total"], tablefmt="fancy_grid", floatfmt=".2e"
                )
            )
        else:
            print(tickers.to_string())

    export_data(
        export, os.path.dirname(os.path.abspath(__file__)), "qtrcontracts", df_contracts
    )
    print("")
Beispiel #28
0
async def donchian_command(ctx,
                           ticker="",
                           upper_length="25",
                           lower_length="100",
                           start="",
                           end=""):
    """Displays chart with donchian channel [Yahoo Finance]"""

    try:

        # Debug
        if cfg.DEBUG:
            print(
                f"!stocks.ta.donchian {ticker} {upper_length} {lower_length} {start} {end}"
            )

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

        if start == "":
            start = datetime.now() - timedelta(days=365)
        else:
            start = datetime.strptime(start, cfg.DATE_FORMAT)

        if end == "":
            end = datetime.now()
        else:
            end = datetime.strptime(end, cfg.DATE_FORMAT)

        if not upper_length.lstrip("-").isnumeric():
            raise Exception("Number has to be an integer")
        upper_length = float(upper_length)
        if not lower_length.lstrip("-").isnumeric():
            raise Exception("Number has to be an integer")
        lower_length = float(lower_length)

        ticker = ticker.upper()
        df_stock = discordbot.helpers.load(ticker, start)
        if df_stock.empty:
            raise Exception("Stock ticker is invalid")

        # Retrieve Data
        df_stock = df_stock.loc[(df_stock.index >= start)
                                & (df_stock.index < end)]

        df_ta = volatility_model.donchian(df_stock, upper_length, lower_length)

        # Output Data
        fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI)
        ax.plot(df_stock.index, df_stock["Adj Close"].values, color="k", lw=3)
        ax.plot(df_ta.index,
                df_ta.iloc[:, 0].values,
                "b",
                lw=1.5,
                label="upper")
        ax.plot(df_ta.index, df_ta.iloc[:, 1].values, "b", lw=1.5, ls="--")
        ax.plot(df_ta.index,
                df_ta.iloc[:, 2].values,
                "b",
                lw=1.5,
                label="lower")
        ax.set_title(f"{ticker} donchian")
        ax.set_xlim(df_stock.index[0], df_stock.index[-1])
        ax.set_xlabel("Time")
        ax.set_ylabel("Price ($)")

        ax.legend(
            [ticker, df_ta.columns[0], df_ta.columns[1], df_ta.columns[2]])
        ax.fill_between(
            df_ta.index,
            df_ta.iloc[:, 0].values,
            df_ta.iloc[:, 2].values,
            alpha=0.1,
            color="b",
        )
        ax.grid(b=True, which="major", color="#666666", linestyle="-")

        plt.gcf().autofmt_xdate()
        fig.tight_layout(pad=1)

        plt.legend()

        plt.savefig("ta_donchian.png")
        uploaded_image = gst_imgur.upload_image("ta_donchian.png",
                                                title="something")
        image_link = uploaded_image.link
        if cfg.DEBUG:
            print(f"Image URL: {image_link}")
        title = "Stocks: Donchian-Channels " + ticker
        embed = discord.Embed(title=title, colour=cfg.COLOR)
        embed.set_author(
            name=cfg.AUTHOR_NAME,
            icon_url=cfg.AUTHOR_ICON_URL,
        )
        embed.set_image(url=image_link)
        os.remove("ta_donchian.png")

        await ctx.send(embed=embed)

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

        await ctx.send(embed=embed)
def plot_pattern_recognition(
    ticker: str,
    resolution: str,
    export: str,
    external_axes: Optional[List[plt.Axes]] = None,
):
    """Plot pattern recognition signal

    Parameters
    ----------
    ticker : str
        Ticker to display pattern recognition on top of the data
    resolution : str
        Resolution of data to get pattern recognition from
    export: str
        Format of export file
    external_axes : Optional[List[plt.Axes]], optional
        External axes (1 axis is expected in the list), by default None
    """

    pattern = finnhub_model.get_pattern_recognition(ticker, resolution)

    export_data(
        export,
        os.path.dirname(os.path.abspath(__file__)),
        "pr",
        pattern,
    )

    l_segments = []
    for i in pattern:
        a_part = ("", "")
        if "aprice" in pattern[i]:
            if pattern[i]["aprice"] != 0 and not math.isnan(
                    pattern[i]["aprice"]):
                a_part = (
                    datetime.utcfromtimestamp(
                        pattern[i]["atime"]).strftime("%Y-%m-%d"),
                    pattern[i]["aprice"],
                )

        b_part = ("", "")
        if "bprice" in pattern[i]:
            if pattern[i]["bprice"] != 0 and not math.isnan(
                    pattern[i]["bprice"]):
                b_part = (
                    datetime.utcfromtimestamp(
                        pattern[i]["btime"]).strftime("%Y-%m-%d"),
                    pattern[i]["bprice"],
                )

        c_part = ("", "")
        if "cprice" in pattern[i]:
            if pattern[i]["cprice"] != 0 and not math.isnan(
                    pattern[i]["cprice"]):
                c_part = (
                    datetime.utcfromtimestamp(
                        pattern[i]["ctime"]).strftime("%Y-%m-%d"),
                    pattern[i]["cprice"],
                )

        d_part = ("", "")
        if "dprice" in pattern[i]:
            if pattern[i]["dprice"] != 0 and not math.isnan(
                    pattern[i]["dprice"]):
                d_part = (
                    datetime.utcfromtimestamp(
                        pattern[i]["dtime"]).strftime("%Y-%m-%d"),
                    pattern[i]["dprice"],
                )

        segment = (a_part, b_part, c_part, d_part)

        l_segment = list(segment)
        while ("", "") in l_segment:
            l_segment.remove(("", ""))
        segm = tuple(l_segment)

        l_segments.append(segm)

    start_time = 999999999999
    for i in pattern:
        if pattern[i]["atime"] < start_time:
            start_time = pattern[i]["atime"]

    df_stock = yf.download(
        ticker,
        start=datetime.utcfromtimestamp(start_time).strftime("%Y-%m-%d"),
        progress=False,
    )
    df_stock["date_id"] = (df_stock.index.date -
                           df_stock.index.date.min()).astype("timedelta64[D]")
    df_stock["date_id"] = df_stock["date_id"].dt.days + 1

    df_stock["OC_High"] = df_stock[["Open", "Close"]].max(axis=1)
    df_stock["OC_Low"] = df_stock[["Open", "Close"]].min(axis=1)

    candle_chart_kwargs = {
        "type": "candle",
        "style": theme.mpf_style,
        "volume": False,
        "alines": l_segments,
        "xrotation": theme.xticks_rotation,
        "scale_padding": {
            "left": 0.3,
            "right": 1,
            "top": 0.8,
            "bottom": 0.8
        },
        "update_width_config": {
            "candle_linewidth": 0.6,
            "candle_width": 0.8,
            "volume_linewidth": 0.8,
            "volume_width": 0.8,
        },
        "warn_too_much_data": 10000,
    }
    # This plot has 2 axes
    if not external_axes:
        candle_chart_kwargs["returnfig"] = True
        candle_chart_kwargs["figratio"] = (10, 7)
        candle_chart_kwargs["figscale"] = 1.10
        candle_chart_kwargs["figsize"] = plot_autoscale()
        (fig, ax) = mpf.plot(df_stock, **candle_chart_kwargs)
        fig.suptitle(
            f"\n{ticker}",
            x=0.055,
            y=0.965,
            horizontalalignment="left",
        )

        theme.visualize_output(force_tight_layout=False)

    else:
        if len(external_axes) != 1:
            logger.error("Expected list of one axis item.")
            console.print("[red]Expected list of 1 axis items./n[/red]")
            return
        (ax, ) = external_axes
        candle_chart_kwargs["ax"] = ax
        mpf.plot(df_stock, **candle_chart_kwargs)

    for ix in range(len(pattern.columns)):
        console.print(
            f"From {l_segments[ix][0][0]} to {l_segments[ix][-1][0]}")
        console.print(
            f"Pattern: {pattern[0]['patternname']} ({pattern[0]['patterntype']})",
            "\n")
def candle(s_ticker: str, other_args: List[str]):
    """Shows candle plot of loaded ticker

    Parameters
    ----------
    s_ticker: str
        Ticker to display
    other_args: str
        Argparse arguments
    """
    parser = argparse.ArgumentParser(
        add_help=False,
        formatter_class=argparse.ArgumentDefaultsHelpFormatter,
        prog="candle",
        description="Displays candle chart of loaded ticker",
    )
    parser.add_argument(
        "-s",
        "--start_date",
        dest="s_start",
        type=valid_date,
        default=(datetime.now() - timedelta(days=366)).strftime("%Y-%m-%d"),
        help="Start date for candle data",
    )
    parser.add_argument(
        "--plotly",
        dest="plotly",
        action="store_true",
        default=False,
        help="Flag to show interactive plot using plotly.",
    )
    # TODO: Add option to customize plot even further

    try:
        ns_parser = parse_known_args_and_warn(parser, other_args)
        if not ns_parser:
            return
        if not s_ticker:
            print("No ticker loaded.  First use `load {ticker}`", "\n")
            return

        df_stock = trend.load_ticker(s_ticker, ns_parser.s_start)
        df_stock["ma20"] = df_stock["Close"].rolling(20).mean().fillna(method="bfill")
        df_stock["ma50"] = df_stock["Close"].rolling(50).mean().fillna(method="bfill")

        df_stock = trend.find_trendline(df_stock, "OC_High", "high")
        df_stock = trend.find_trendline(df_stock, "OC_Low", "low")
        if not ns_parser.plotly:
            mc = mpf.make_marketcolors(
                up="green",
                down="red",
                edge="black",
                wick="black",
                volume="in",
                ohlc="i",
            )

            s = mpf.make_mpf_style(marketcolors=mc, gridstyle=":", y_on_right=True)

            ap0 = []

            if "OC_High_trend" in df_stock.columns:
                ap0.append(
                    mpf.make_addplot(df_stock["OC_High_trend"], color="g"),
                )

            if "OC_Low_trend" in df_stock.columns:
                ap0.append(
                    mpf.make_addplot(df_stock["OC_Low_trend"], color="b"),
                )

            if gtff.USE_ION:
                plt.ion()

            mpf.plot(
                df_stock,
                type="candle",
                mav=(20, 50),
                volume=True,
                title=f"\n{s_ticker} - Starting {ns_parser.s_start.strftime('%Y-%m-%d')}",
                addplot=ap0,
                xrotation=10,
                style=s,
                figratio=(10, 7),
                figscale=1.10,
                figsize=(plot_autoscale()),
                update_width_config=dict(
                    candle_linewidth=1.0, candle_width=0.8, volume_linewidth=1.0
                ),
            )
        else:
            fig = make_subplots(
                rows=2,
                cols=1,
                shared_xaxes=True,
                vertical_spacing=0.06,
                subplot_titles=(f"{s_ticker}", "Volume"),
                row_width=[0.2, 0.7],
            )
            fig.add_trace(
                go.Candlestick(
                    x=df_stock.index,
                    open=df_stock.Open,
                    high=df_stock.High,
                    low=df_stock.Low,
                    close=df_stock.Close,
                    name="OHLC",
                ),
                row=1,
                col=1,
            )
            fig.add_trace(
                go.Scatter(
                    x=df_stock.index,
                    y=df_stock["ma20"],
                    name="MA20",
                    mode="lines",
                    line=go.scatter.Line(color="royalblue"),
                ),
                row=1,
                col=1,
            )
            fig.add_trace(
                go.Scatter(
                    x=df_stock.index,
                    y=df_stock["ma50"],
                    name="MA50",
                    mode="lines",
                    line=go.scatter.Line(color="black"),
                ),
                row=1,
                col=1,
            )

            if "OC_High_trend" in df_stock.columns:
                fig.add_trace(
                    go.Scatter(
                        x=df_stock.index,
                        y=df_stock["OC_High_trend"],
                        name="High Trend",
                        mode="lines",
                        line=go.scatter.Line(color="red"),
                    ),
                    row=1,
                    col=1,
                )
            if "OC_Low_trend" in df_stock.columns:
                fig.add_trace(
                    go.Scatter(
                        x=df_stock.index,
                        y=df_stock["OC_Low_trend"],
                        name="Low Trend",
                        mode="lines",
                        line=go.scatter.Line(color="green"),
                    ),
                    row=1,
                    col=1,
                )
            fig.add_trace(
                go.Bar(
                    x=df_stock.index,
                    y=df_stock.Volume,
                    name="Volume",
                    marker_color="black",
                ),
                row=2,
                col=1,
            )
            fig.update_layout(
                yaxis_title="Stock Price ($)",
                xaxis=dict(
                    rangeselector=dict(
                        buttons=list(
                            [
                                dict(
                                    count=1,
                                    label="1m",
                                    step="month",
                                    stepmode="backward",
                                ),
                                dict(
                                    count=3,
                                    label="3m",
                                    step="month",
                                    stepmode="backward",
                                ),
                                dict(
                                    count=1, label="YTD", step="year", stepmode="todate"
                                ),
                                dict(
                                    count=1,
                                    label="1y",
                                    step="year",
                                    stepmode="backward",
                                ),
                                dict(step="all"),
                            ]
                        )
                    ),
                    rangeslider=dict(visible=False),
                    type="date",
                ),
            )

            fig.show()
        print("")

    except Exception as e:
        print(e, "\n")