mirror of
https://github.com/OpenBB-finance/OpenBB.git
synced 2026-11-02 23:41:27 +00:00
Create holdings' performance for ETFs
This commit is contained in:
@@ -4,7 +4,7 @@ __docformat__ = "numpy"
|
||||
import argparse
|
||||
import logging
|
||||
import os
|
||||
from datetime import datetime, timedelta
|
||||
from datetime import datetime, timedelta, date
|
||||
from typing import List, Optional
|
||||
|
||||
import yfinance as yf
|
||||
@@ -50,6 +50,7 @@ class ETFController(BaseController):
|
||||
"load",
|
||||
"overview",
|
||||
"holdings",
|
||||
"holding_perf",
|
||||
"news",
|
||||
"candle",
|
||||
"weights",
|
||||
@@ -106,6 +107,7 @@ class ETFController(BaseController):
|
||||
mt.add_raw("\n")
|
||||
mt.add_cmd("overview", self.etf_name)
|
||||
mt.add_cmd("holdings", self.etf_name)
|
||||
mt.add_cmd("holding_perf", self.etf_name)
|
||||
mt.add_cmd("weights", self.etf_name)
|
||||
mt.add_cmd("news", self.etf_name)
|
||||
mt.add_cmd("candle", self.etf_name)
|
||||
@@ -638,3 +640,53 @@ class ETFController(BaseController):
|
||||
if ns_parser.sheet_name
|
||||
else None,
|
||||
)
|
||||
|
||||
@log_start_end(log=logger)
|
||||
def call_holding_perf(self, other_args: List[str]):
|
||||
print(self.etf_holdings)
|
||||
"""Process holdings performance command"""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
prog="holding_perf",
|
||||
description="Look at ETF company holdings' performance",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-s",
|
||||
"--start",
|
||||
type=valid_date,
|
||||
default=(datetime.now().date() - timedelta(days=366)),
|
||||
dest="start",
|
||||
help="The starting date (format YYYY-MM-DD) to get each holding's price"
|
||||
)
|
||||
parser.add_argument(
|
||||
"-e",
|
||||
"--end",
|
||||
type=valid_date,
|
||||
default=datetime.now().date(),
|
||||
dest="end",
|
||||
help="The ending date (format YYYY-MM-DD) to get each holding's price"
|
||||
)
|
||||
parser.add_argument(
|
||||
"-l",
|
||||
"--limit",
|
||||
type=check_positive,
|
||||
dest="limit",
|
||||
help="Number of holdings to get",
|
||||
default=20,
|
||||
)
|
||||
|
||||
if other_args and "-" not in other_args[0][0]:
|
||||
other_args.insert(0, "-l")
|
||||
|
||||
ns_parser = self.parse_known_args_and_warn(parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES, raw= True)
|
||||
if ns_parser:
|
||||
if self.etf_name:
|
||||
fmp_view.view_etf_holdings_performance(
|
||||
ticker=self.etf_name,
|
||||
start_date=ns_parser.start,
|
||||
end_date=ns_parser.end,
|
||||
limit=ns_parser.limit,
|
||||
)
|
||||
else:
|
||||
console.print("Please load a ticker using <load name>. \n")
|
||||
|
||||
@@ -3,12 +3,14 @@ __docformat__ = "numpy"
|
||||
|
||||
import json
|
||||
import logging
|
||||
from typing import Dict
|
||||
from typing import Dict, List
|
||||
from urllib.error import HTTPError
|
||||
from urllib.request import urlopen
|
||||
import pandas as pd
|
||||
|
||||
from openbb_terminal.core.session.current_user import get_current_user
|
||||
from openbb_terminal.decorators import log_start_end
|
||||
from openbb_terminal.decorators import check_api_key, log_start_end
|
||||
from openbb_terminal.helper_funcs import request
|
||||
from openbb_terminal.rich_config import console
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -47,3 +49,106 @@ def get_etf_sector_weightings(name: str) -> Dict:
|
||||
raise ValueError(data["Error Message"])
|
||||
|
||||
return data
|
||||
|
||||
@log_start_end(log=logger)
|
||||
@check_api_key(['API_KEY_FINANCIALMODELINGPREP'])
|
||||
def get_stock_price_change(ticker: str, start_date: str, end_date: str) -> float:
|
||||
"""Get stock's price percent change over specified time period.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
ticker : str
|
||||
Ticker to get information from.
|
||||
start: str
|
||||
Date from which data is fetched in format YYYY-MM-DD
|
||||
end: str
|
||||
Date from which data is fetched in format YYYY-MM-DD
|
||||
|
||||
Returns
|
||||
-------
|
||||
float
|
||||
Percent change of closing price over time period
|
||||
"""
|
||||
current_user = get_current_user()
|
||||
url = f'''https://financialmodelingprep.com/api/v3/historical-price-full/{ticker}?from={start_date}&to={end_date}&serietype=line&apikey={current_user.credentials.API_KEY_FINANCIALMODELINGPREP}'''
|
||||
|
||||
response = request(url)
|
||||
if response.status_code != 200 or "Error Message" in response.json():
|
||||
message = f"Error, Status Code: {response.status_code}."
|
||||
message = (
|
||||
message
|
||||
if "Error Message" not in response.json()
|
||||
else message + "\n" + response.json()["Error Message"] + ".\n"
|
||||
)
|
||||
console.print(message)
|
||||
return None
|
||||
data = response.json()
|
||||
|
||||
close_end = data['historical'][0]['close']
|
||||
close_start = data['historical'][-1]['close']
|
||||
pct_change = 100 * (close_end - close_start) / close_start
|
||||
|
||||
return pct_change
|
||||
|
||||
@log_start_end(log=logger)
|
||||
@check_api_key(['API_KEY_FINANCIALMODELINGPREP'])
|
||||
def get_etf_holdings(ticker: str) -> List[Dict[str,any]]:
|
||||
"""This endpoint returns all stocks held by a specific ETF.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
ticker : str
|
||||
ETF ticker.
|
||||
|
||||
Returns
|
||||
-------
|
||||
List[Dict[str,any]]
|
||||
Info for stock holdings in the ETF.
|
||||
"""
|
||||
|
||||
current_user = get_current_user()
|
||||
url = f'''https://financialmodelingprep.com/api/v3/etf-holder/{ticker}?apikey={current_user.credentials.API_KEY_FINANCIALMODELINGPREP}'''
|
||||
response = request(url)
|
||||
if response.status_code != 200 or "Error Message" in response.json():
|
||||
message = f"Error, Status Code: {response.status_code}."
|
||||
message = (
|
||||
message
|
||||
if "Error Message" not in response.json()
|
||||
else message + "\n" + response.json()["Error Message"] + ".\n"
|
||||
)
|
||||
console.print(message)
|
||||
return None
|
||||
|
||||
return response.json()
|
||||
|
||||
@log_start_end(log=logger)
|
||||
@check_api_key(['API_KEY_FINANCIALMODELINGPREP'])
|
||||
def get_holdings_pct_change(ticker: str, start_date: str, end_date: str) -> pd.DataFrame:
|
||||
"""Calculate percent change for each holding in ETF.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
ticker : str
|
||||
ETF ticker.
|
||||
|
||||
Returns
|
||||
-------
|
||||
pd.DataFrame
|
||||
Calculated percentage change for each stock in the ETF, in descending order.
|
||||
"""
|
||||
holdings = get_etf_holdings(ticker=ticker)
|
||||
data_list = []
|
||||
for stock in holdings:
|
||||
if stock['asset']:
|
||||
pct_change = get_stock_price_change(ticker=stock['asset'],
|
||||
start_date=start_date, end_date=end_date)
|
||||
data_list.append({"Ticker": stock['asset'], "Name": stock["name"],
|
||||
"Percent Change": pct_change})
|
||||
else:
|
||||
data_list.append({"Ticker": '', "Name": stock["name"],
|
||||
"Percent Change": 0})
|
||||
|
||||
df = pd.DataFrame(data_list)
|
||||
df.sort_values(by='Percent Change',ascending=False, inplace=True)
|
||||
|
||||
return df
|
||||
|
||||
@@ -6,9 +6,11 @@ import os
|
||||
from typing import Optional
|
||||
|
||||
import pandas as pd
|
||||
import seaborn as sns
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from openbb_terminal import OpenBBFigure, theme
|
||||
from openbb_terminal.decorators import log_start_end
|
||||
from openbb_terminal.decorators import check_api_key, log_start_end
|
||||
from openbb_terminal.etf import fmp_model
|
||||
from openbb_terminal.helper_funcs import (
|
||||
export_data,
|
||||
@@ -120,3 +122,39 @@ def display_etf_weightings(
|
||||
)
|
||||
|
||||
return fig.show(external=external_axes)
|
||||
|
||||
|
||||
@log_start_end(log=logger)
|
||||
@check_api_key(['API_KEY_FINANCIALMODELINGPREP'])
|
||||
def view_etf_holdings_performance(ticker: str, start_date: str, end_date: str, limit: int = 10, raw: bool = False, export: str = "", sheet_name: Optional[str] = None):
|
||||
|
||||
data = fmp_model.get_holdings_pct_change(ticker, start_date, end_date)[0:limit]
|
||||
|
||||
colors = ['g' if x > 0 else 'r' for x in data['Percent Change']]
|
||||
|
||||
sns.barplot(
|
||||
x= 'Percent Change',
|
||||
y= 'Name',
|
||||
data=data,
|
||||
palette=colors,
|
||||
)
|
||||
if raw:
|
||||
print_rich_table(
|
||||
data,
|
||||
show_index=False,
|
||||
headers=["Ticker", "Name", "Percent Change"],
|
||||
title="ETF Holdings' Performance",
|
||||
limit=limit,
|
||||
export=bool(export),
|
||||
)
|
||||
|
||||
if export:
|
||||
export_data(
|
||||
export,
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"holdings_perf",
|
||||
data,
|
||||
sheet_name,
|
||||
)
|
||||
|
||||
return plt.show()
|
||||
|
||||
1
openbb_terminal/miscellaneous/i18n/en.yml
vendored
1
openbb_terminal/miscellaneous/i18n/en.yml
vendored
@@ -702,6 +702,7 @@ en:
|
||||
etf/scr: screener ETFs overview/performance, using preset filters
|
||||
etf/overview: get overview
|
||||
etf/holdings: top company holdings
|
||||
etf/holding_perf: Performance of holdings in ETF.
|
||||
etf/weights: sector weights allocation
|
||||
etf/candle: view a candle chart for ETF
|
||||
etf/news: latest news of the company
|
||||
|
||||
Reference in New Issue
Block a user