def view_holdings(symbol: str, num_to_show: int, export: str):
    """

    Parameters
    ----------
    symbol: str
        ETF symbol to show holdings for
    num_to_show: int
        Number of holdings to show
    export: str
        Format to export data
    """

    data = stockanalysis_model.get_etf_holdings(symbol)
    if gtff.USE_TABULATE_DF:
        print(
            tabulate(
                data[:num_to_show],
                headers=data.columns,
                tablefmt="fancy_grid",
            ),
            "\n",
        )
    else:
        print(data.head(num_to_show).to_string(), "\n")

    export_data(export, os.path.dirname(os.path.abspath(__file__)), "holdings", data)
예제 #2
0
def view_holdings(symbol: str, num_to_show: int, export: str):
    """

    Parameters
    ----------
    symbol: str
        ETF symbol to show holdings for
    num_to_show: int
        Number of holdings to show
    export: str
        Format to export data
    """

    data = stockanalysis_model.get_etf_holdings(symbol)
    print_rich_table(
        data[:num_to_show],
        headers=list(data.columns),
        title="ETF Holdings",
        show_index=True,
    )
    console.print("")

    export_data(export, os.path.dirname(os.path.abspath(__file__)), "holdings", data)
예제 #3
0
def holdings_command(etf="", num: int = 15):
    """Display ETF Holdings. [Source: StockAnalysis]"""

    if imps.DEBUG:
        logger.debug("etfs")

    holdings = stockanalysis_model.get_etf_holdings(etf.upper())
    holdings = holdings.iloc[:num]

    if holdings.empty:
        raise Exception("No company holdings found!\n")

    title = f"ETF: {etf.upper()} Holdings"

    dindex = len(holdings.index)
    if dindex > 15:
        embeds: list = []
        # Output
        i, i2, end = 0, 0, 15
        df_pg, embeds_img, images_list = [], [], []
        while i < dindex:
            df_pg = holdings.iloc[i:end]
            df_pg.append(df_pg)
            fig = imps.plot_df(
                df_pg,
                fig_size=(500, (40 + (40 * dindex))),
                col_width=[1, 2, 2],
                tbl_header=imps.PLT_TBL_HEADER,
                tbl_cells=imps.PLT_TBL_CELLS,
                font=imps.PLT_TBL_FONT,
                row_fill_color=imps.PLT_TBL_ROW_COLORS,
                paper_bgcolor="rgba(0, 0, 0, 0)",
            )
            fig.update_traces(cells=(dict(align=["center", "right"])))
            imagefile = "etf-holdings.png"
            imagefile = imps.save_image(imagefile, fig)

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

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

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

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

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

        output = {
            "view": imps.Menu,
            "title": title,
            "embed": embeds,
            "choices": choices,
            "embeds_img": embeds_img,
            "images_list": images_list,
        }
    else:
        fig = imps.plot_df(
            holdings,
            fig_size=(500, (40 + (40 * dindex))),
            col_width=[1, 2, 2],
            tbl_header=imps.PLT_TBL_HEADER,
            tbl_cells=imps.PLT_TBL_CELLS,
            font=imps.PLT_TBL_FONT,
            row_fill_color=imps.PLT_TBL_ROW_COLORS,
            paper_bgcolor="rgba(0, 0, 0, 0)",
        )
        fig.update_traces(cells=(dict(align=["center", "right"])))
        imagefile = imps.save_image("etf-holdings.png", fig)

        output = {
            "title": title,
            "imagefile": imagefile,
        }

    return output
예제 #4
0
    def call_load(self, other_args: List[str]):
        """Process load command"""
        parser = argparse.ArgumentParser(
            add_help=False,
            formatter_class=argparse.ArgumentDefaultsHelpFormatter,
            prog="load",
            description="Load ETF ticker to perform analysis on.",
        )
        parser.add_argument(
            "-t",
            "--ticker",
            action="store",
            dest="ticker",
            required="-h" not in other_args,
            help="ETF ticker",
        )
        parser.add_argument(
            "-s",
            "--start",
            type=valid_date,
            default=(datetime.now() -
                     timedelta(days=366)).strftime("%Y-%m-%d"),
            dest="start",
            help="The starting date (format YYYY-MM-DD) of the ETF",
        )
        parser.add_argument(
            "-e",
            "--end",
            type=valid_date,
            default=datetime.now().strftime("%Y-%m-%d"),
            dest="end",
            help="The ending date (format YYYY-MM-DD) of the ETF",
        )
        parser.add_argument(
            "-l",
            "--limit",
            type=check_positive,
            default=5,
            dest="limit",
            help="Limit of holdings to display",
        )
        if other_args and "-" not in other_args[0][0]:
            other_args.insert(0, "-t")

        ns_parser = parse_known_args_and_warn(parser, other_args)
        if ns_parser:
            df_etf_candidate = yf.download(
                ns_parser.ticker,
                start=ns_parser.start,
                end=ns_parser.end,
                progress=False,
            )
            if df_etf_candidate.empty:
                console.print("ETF ticker provided does not exist!\n")
                return

            df_etf_candidate.index.name = "date"

            self.etf_name = ns_parser.ticker.upper()
            self.etf_data = df_etf_candidate

            holdings = stockanalysis_model.get_etf_holdings(self.etf_name)
            if holdings.empty:
                console.print("No company holdings found!\n")
            else:
                self.etf_holdings = holdings.index[:ns_parser.limit].tolist()

                if "n/a" in self.etf_holdings:
                    na_tix_idx = []
                    for idx, item in enumerate(self.etf_holdings):
                        if item == "n/a":
                            na_tix_idx.append(str(idx))

                    console.print(
                        f"n/a tickers found at position {','.join(na_tix_idx)}.  Dropping these from holdings.\n"
                    )

                self.etf_holdings = list(
                    filter(lambda x: x != "n/a", self.etf_holdings))

                console.print(
                    f"Top company holdings found: {', '.join(self.etf_holdings)}\n"
                )

            console.print("")
def test_get_etf_holdings(recorder, symbol):
    result_df = stockanalysis_model.get_etf_holdings(symbol)

    assert not result_df.empty
    recorder.capture(result_df)
예제 #6
0
def get_allocation(
    data: pd.DataFrame,
    hist: pd.DataFrame,
    portfolio: pd.DataFrame,
    number: int,
    no_etf_positions: bool,
) -> pd.DataFrame:
    """Formatting the dataframe to an allocation dataframe of the positions

    Parameters
    ----------
    data : pd.DataFrame
        The dataframe of positions
    hist: pd.DataFrame
        A dataframe of the positions historical performances
    portfolio: pd.DataFrame
        Portfolio dataframe
    number: int
        Number of etf positions to be checked
    no_etf_positions: bool
        If new positions should not be added to the 'data' dataframe for etf holdings

    Returns
    ----------
    df: pd.DataFrame
        Dataframe of allocation
    """
    # Fixing dataframe formatting
    df = data["Quantity"].tail(1).transpose()
    df.columns = ["amount"]
    df = df.sort_index(axis=0, ascending=True)
    hist = hist.tail(1).transpose()
    hist = hist.reset_index(["first"])
    hist.columns = ["position", "value"]
    hist = pd.DataFrame(hist["value"])

    i = 0
    value = []
    while i < df.index.size:
        value.append(df["amount"].iloc[i] * hist["value"].iloc[i])
        i += 1
    df["value"] = value

    i = 0
    length = df.index.size
    while i < length:
        row_index = df.iloc[[i]].index[0]
        if (portfolio["Type"].loc[portfolio["Name"] == row_index].to_list()[0]
                == "etf"):  # check if position is an etf
            df = fix_etf_allocation(
                df,
                stockanalysis_model.get_etf_holdings(row_index),
                row_index,
                no_etf_positions,
                number,
            )
        if df.iloc[i].loc["value"] < 0:
            # Converting negative positions to "shorts" in order to correctly calculate allocation
            df.loc[row_index, "value"] = abs(df.loc[row_index, "value"])
            df.loc[row_index, "amount"] = abs(df.loc[row_index, "amount"])
            df.rename(index={row_index: f"short_{row_index}"}, inplace=True)
        i += 1

    total_value = df["value"].sum()
    df["pct_allocation"] = df[
        "value"] / total_value * 100  # calculate allocation
    df = df.sort_values(by="pct_allocation", ascending=False)

    return df