mirror of
https://github.com/OpenBB-finance/OpenBB.git
synced 2026-10-31 14:31:20 +00:00
208 lines
6.2 KiB
Python
208 lines
6.2 KiB
Python
"""FinancialModelingPrep model"""
|
|
__docformat__ = "numpy"
|
|
|
|
import json
|
|
import logging
|
|
from typing import Any, 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 check_api_key, log_start_end
|
|
from openbb_terminal.helper_funcs import request
|
|
from openbb_terminal.rich_config import console, optional_rich_track
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# pylint: disable=consider-using-with
|
|
|
|
|
|
@log_start_end(log=logger)
|
|
def get_etf_sector_weightings(name: str) -> Dict:
|
|
"""Return sector weightings allocation of ETF. [Source: FinancialModelingPrep]
|
|
|
|
Parameters
|
|
----------
|
|
name: str
|
|
ETF name
|
|
|
|
Returns
|
|
-------
|
|
Dict[str, Any]
|
|
Dictionary with sector weightings allocation
|
|
"""
|
|
try:
|
|
response = urlopen( # noqa: S310
|
|
"https://financialmodelingprep.com/api/v3/etf-sector-weightings/"
|
|
f"{name}?apikey={get_current_user().credentials.API_KEY_FINANCIALMODELINGPREP}"
|
|
)
|
|
data = json.loads(response.read().decode("utf-8"))
|
|
except HTTPError:
|
|
console.print(
|
|
"This endpoint is only for premium members. Please visit the subscription page to upgrade the "
|
|
"plan (Starter or higher) at https://financialmodelingprep.com/developer/docs/pricing"
|
|
)
|
|
return dict()
|
|
|
|
if "Error Message" in data:
|
|
raise ValueError(data["Error Message"])
|
|
|
|
return data
|
|
|
|
|
|
@log_start_end(log=logger)
|
|
@check_api_key(["API_KEY_FINANCIALMODELINGPREP"])
|
|
def get_stock_price_change(
|
|
tickers: List[str], start_date: str, end_date: str
|
|
) -> Dict[str, float]:
|
|
"""Get stock's price percent change over specified time period.
|
|
|
|
Parameters
|
|
----------
|
|
tickers : List[str]
|
|
Ticker(s) to get information for.
|
|
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
|
|
-------
|
|
Dict[str, float]
|
|
Percent change of closing price over time period, or dictionary of ticker, change pairs.
|
|
"""
|
|
tickers_tracker = optional_rich_track(
|
|
tickers, False, "Gathering stock prices", len(tickers)
|
|
)
|
|
current_user = get_current_user()
|
|
data_aggregate = dict()
|
|
|
|
for tick in tickers_tracker:
|
|
tickers_req = str(tick) + ","
|
|
for _ in range(4):
|
|
try:
|
|
_tick = next(tickers_tracker)
|
|
tickers_req += _tick + ","
|
|
except StopIteration:
|
|
break
|
|
|
|
url = f"""https://financialmodelingprep.com/api/v3/historical-price-full/{tickers_req}?\
|
|
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 dict()
|
|
|
|
data = response.json()
|
|
stock_list = data
|
|
if "historicalStockList" in data:
|
|
stock_list = data["historicalStockList"]
|
|
|
|
for stock in stock_list:
|
|
close_end = stock["historical"][0]["close"]
|
|
close_start = stock["historical"][-1]["close"]
|
|
pct_change = 100 * (close_end - close_start) / close_start
|
|
data_aggregate[stock["symbol"]] = pct_change
|
|
|
|
return data_aggregate
|
|
|
|
|
|
@log_start_end(log=logger)
|
|
@check_api_key(["API_KEY_FINANCIALMODELINGPREP"])
|
|
def get_etf_holdings(ticker: str, limit: int = 10) -> List[Dict[str, Any]]:
|
|
"""This endpoint returns all stocks held by a specific ETF.
|
|
|
|
Parameters
|
|
----------
|
|
ticker : str
|
|
ETF ticker.
|
|
limit: int
|
|
Limit amount of stocks to return. FMP returns data
|
|
by descending weighting.
|
|
|
|
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 []
|
|
|
|
return response.json()[0:limit]
|
|
|
|
|
|
@log_start_end(log=logger)
|
|
@check_api_key(["API_KEY_FINANCIALMODELINGPREP"])
|
|
def get_holdings_pct_change(
|
|
ticker: str,
|
|
start_date: str,
|
|
end_date: str,
|
|
limit: int = 10,
|
|
) -> pd.DataFrame:
|
|
"""Calculate percent change for each holding in ETF.
|
|
|
|
Parameters
|
|
----------
|
|
ticker : str
|
|
ETF ticker.
|
|
limit: int
|
|
Limit amount of stocks to return. FMP returns data
|
|
by descending weighting.
|
|
|
|
Returns
|
|
-------
|
|
pd.DataFrame
|
|
Calculated percentage change for each stock in the ETF, in descending order.
|
|
"""
|
|
|
|
df = pd.DataFrame(columns=["Ticker", "Name", "Percent Change"], data=[])
|
|
holdings = get_etf_holdings(ticker, limit)
|
|
tickers = []
|
|
for stock in holdings:
|
|
tickers.append(stock.get("asset", " "))
|
|
|
|
pct_changes = get_stock_price_change(tickers, start_date, end_date)
|
|
|
|
for stock in holdings:
|
|
pct_change = pct_changes.get(stock["asset"], 0)
|
|
if pct_change == 0:
|
|
console.print(
|
|
f"""Percent change not found for: {stock["asset"]}: {stock["name"]}"""
|
|
)
|
|
new_df = pd.DataFrame(
|
|
{
|
|
"Ticker": stock["asset"],
|
|
"Name": stock["name"],
|
|
"Percent Change": pct_changes.get(stock["asset"], 0),
|
|
},
|
|
index=[0],
|
|
)
|
|
|
|
df = pd.concat([df, new_df], ignore_index=True)
|
|
|
|
sorted_df = df.sort_values(by="Percent Change", ascending=False, inplace=False)
|
|
|
|
return sorted_df
|