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