Add mutual funds to sdk (#4610)

* add mutual funds to sdk

* add docstring returns

* mypy

* remove avanza

* add historical chart

* split funcs

* revert po controller

* revert forecast sdk controller

* revert forecsat

* revert changes to forecast

* redo po

* remove .md files

* pylint

* why these md gone?

* pagesize to limit

* missed this one

* country default US

* default on load not on search

---------

Co-authored-by: James Maslek <jmaslek11@gmail.com>
This commit is contained in:
montezdesousa
2023-03-29 20:28:09 +01:00
committed by GitHub
parent 226f57a622
commit e57747cb95
10 changed files with 313 additions and 113 deletions

View File

@@ -101,9 +101,11 @@ def has_content(submission_) -> bool:
class RedditResponses:
tickers: dict = {}
authors: List[str] = []
def __init__(self):
self.tickers: dict = {}
self.authors: List[str] = []
pass
def gather(self, praw_api, responses) -> int:
count = 0

View File

@@ -7,9 +7,10 @@ from .crypto_sdk_model import CryptoRoot
from .econometrics_sdk_model import EconometricsRoot
from .economy_sdk_model import EconomyRoot
from .etf_sdk_model import EtfRoot
from .fixedincome_sdk_model import FixedincomeRoot
from .forecast_sdk_model import ForecastRoot
from .fixedincome_sdk_model import FixedincomeRoot
from .forex_sdk_model import ForexRoot
from .funds_sdk_model import FundsRoot
from .futures_sdk_model import FuturesRoot
from .keys_sdk_model import KeysRoot
from .portfolio_sdk_model import PortfolioRoot

View File

@@ -0,0 +1,35 @@
# ######### THIS FILE IS AUTO GENERATED - ANY CHANGES WILL BE VOID ######### #
# flake8: noqa
# pylint: disable=C0301,R0902,R0903
from openbb_terminal.core.sdk.sdk_helpers import Category
import openbb_terminal.core.sdk.sdk_init as lib
class FundsRoot(Category):
"""Mutual Funds Module
Attributes:
`carbon`: Search mstarpy for carbon metrics\n
`exclusion`: Search mstarpy exclusion policy in esgData\n
`historical`: Get historical fund, category, index price\n
`historical_chart`: Display historical fund, category, index price\n
`holdings`: Search mstarpy for holdings\n
`load`: Search mstarpy for matching funds\n
`search`: Search mstarpy for matching funds\n
`sector`: Get fund, category, index sector breakdown\n
`sector_chart`: Display fund, category, index sector breakdown\n
"""
_location_path = "funds"
def __init__(self):
super().__init__()
self.carbon = lib.funds_mstarpy_model.load_carbon_metrics
self.exclusion = lib.funds_mstarpy_model.load_exclusion_policy
self.historical = lib.funds_mstarpy_model.get_historical
self.historical_chart = lib.funds_mstarpy_view.display_historical
self.holdings = lib.funds_mstarpy_model.load_holdings
self.load = lib.funds_mstarpy_model.load_funds
self.search = lib.funds_mstarpy_model.search_funds
self.sector = lib.funds_mstarpy_model.get_sector
self.sector_chart = lib.funds_mstarpy_view.display_sector

View File

@@ -257,6 +257,12 @@ from openbb_terminal.forex import (
sdk_helpers as forex_sdk_helpers,
)
# Funds
from openbb_terminal.mutual_funds import (
mstarpy_model as funds_mstarpy_model,
mstarpy_view as funds_mstarpy_view,
)
# Futures
from openbb_terminal.futures import (
yfinance_model as futures_yfinance_model,

View File

@@ -267,6 +267,13 @@ forex.oanda.positionbook,forex_oanda_model.positionbook_plot_data_request,forex_
forex.oanda.price,forex_oanda_model.fx_price_request,forex_oanda_view.get_fx_price
forex.oanda.summary,forex_oanda_model.account_summary_request,forex_oanda_view.get_account_summary
forex.quote,forex_sdk_helpers.quote,
funds.load,funds_mstarpy_model.load_funds,
funds.search,funds_mstarpy_model.search_funds,
funds.holdings,funds_mstarpy_model.load_holdings,
funds.exclusion,funds_mstarpy_model.load_exclusion_policy,
funds.carbon,funds_mstarpy_model.load_carbon_metrics,
funds.historical,funds_mstarpy_model.get_historical,funds_mstarpy_view.display_historical
funds.sector,funds_mstarpy_model.get_sector,funds_mstarpy_view.display_sector
futures.curve,futures_yfinance_model.get_curve_futures,futures_yfinance_view.display_curve
futures.historical,futures_sdk_model.get_historical,futures_yfinance_view.display_historical
futures.search,futures_yfinance_model.get_search_futures,
1 trail model view
267 forex.oanda.price forex_oanda_model.fx_price_request forex_oanda_view.get_fx_price
268 forex.oanda.summary forex_oanda_model.account_summary_request forex_oanda_view.get_account_summary
269 forex.quote forex_sdk_helpers.quote
270 funds.load funds_mstarpy_model.load_funds
271 funds.search funds_mstarpy_model.search_funds
272 funds.holdings funds_mstarpy_model.load_holdings
273 funds.exclusion funds_mstarpy_model.load_exclusion_policy
274 funds.carbon funds_mstarpy_model.load_carbon_metrics
275 funds.historical funds_mstarpy_model.get_historical funds_mstarpy_view.display_historical
276 funds.sector funds_mstarpy_model.get_sector funds_mstarpy_view.display_sector
277 futures.curve futures_yfinance_model.get_curve_futures futures_yfinance_view.display_curve
278 futures.historical futures_sdk_model.get_historical futures_yfinance_view.display_historical
279 futures.search futures_yfinance_model.get_search_futures

View File

@@ -15,13 +15,18 @@ logger = logging.getLogger(__name__)
@log_start_end(log=logger)
def get_data(isin: str):
def get_data(isin: str) -> dict:
"""Gets the data from Avanza
Parameters
----------
isin: str
ISIN of the fund
Returns
-------
dict
Fund data
"""
ava_fund = pd.read_csv(
os.path.join("openbb_terminal", "mutual_funds", "avanza_fund_ID.csv"),

View File

@@ -4,10 +4,100 @@ import mstarpy
import pandas as pd
from openbb_terminal.decorators import log_start_end
from openbb_terminal.rich_config import console
logger = logging.getLogger(__name__)
def get_historical(
loaded_funds: mstarpy.Funds,
start_date: str,
end_date: str,
comparison: str = "",
) -> pd.DataFrame:
"""Get historical fund, category, index price
Parameters
----------
loaded_funds: mstarpy.Funds
class mstarpy.Funds instantiated with selected funds
start_date: str
start date of the historical data
end_date: str
end date of the historical data
comparison: str
can be index, category, both
Returns
-------
pd.DataFrame
Dataframe containing historical data
Examples
--------
>>> from openbb_terminal.sdk import openbb
>>> f = openbb.funds.load("Vanguard", "US")
>>> openbb.funds.historical(f, "2020-01-01", "2020-12-31")
"""
try:
start_date_dt = pd.to_datetime(start_date)
end_date_dt = pd.to_datetime(end_date)
if not comparison:
data = loaded_funds.nav(start_date_dt, end_date_dt, frequency="daily")
df = pd.DataFrame(data).set_index("date")
df.index = pd.to_datetime(df.index)
else:
comparison_list = {
"index": [
"fund",
"index",
],
"category": ["fund", "category"],
"both": ["fund", "index", "category"],
}
data = loaded_funds.historicalData()
df_dict = {}
for x in comparison_list[comparison]:
df_dict[x] = pd.DataFrame(data["graphData"][x]).set_index("date")
df = pd.concat(
list(df_dict.values())[:], axis=1, keys=list(df_dict.keys())[:]
)
df.index = pd.to_datetime(df.index)
df = df.loc[(df.index >= start_date_dt) & (df.index <= end_date_dt)]
df = (df.pct_change().fillna(0) + 1).cumprod() * 100
df.columns = [col[0] for col in df.columns]
except Exception as e:
console.print(f"Error: {e}")
return pd.DataFrame()
return df
@log_start_end(log=logger)
def get_sector(loaded_funds: mstarpy.Funds, asset_type: str = "equity"):
"""Get fund, category, index sector breakdown
Parameters
----------
loaded_funds: mstarpy.funds
class mstarpy.Funds instantiated with selected funds
asset_type: str
can be equity or fixed income
Returns
-------
pd.DataFrame
Dataframe containing sector breakdown
"""
key = "EQUITY" if asset_type == "equity" else "FIXEDINCOME"
d = loaded_funds.sector()[key]
if d:
return pd.DataFrame(d)
return pd.DataFrame()
@log_start_end(log=logger)
def load_carbon_metrics(loaded_funds: mstarpy.Funds) -> pd.DataFrame:
"""Search mstarpy for carbon metrics
@@ -19,7 +109,14 @@ def load_carbon_metrics(loaded_funds: mstarpy.Funds) -> pd.DataFrame:
Returns
-------
pd.DataFrame of carbon metrics
pd.DataFrame
Dataframe containing carbon metrics
Examples
--------
>>> from openbb_terminal.sdk import openbb
>>> f = openbb.funds.load("Vanguard", "US")
>>> openbb.funds.carbon(f)
"""
carbonMetrics = loaded_funds.carbonMetrics()
return pd.Series(carbonMetrics, name="carbonMetrics").reset_index()
@@ -36,7 +133,14 @@ def load_exclusion_policy(loaded_funds: mstarpy.Funds) -> pd.DataFrame:
Returns
-------
pd.DataFrame of exclusion policy
pd.DataFrame
Dataframe containing exclusion policy
Examples
--------
>>> from openbb_terminal.sdk import openbb
>>> f = openbb.funds.load("Vanguard", "US")
>>> openbb.funds.exclusion(f)
"""
esgData = loaded_funds.esgData()
if "sustainabilityIntentionality" in esgData:
@@ -49,7 +153,7 @@ def load_exclusion_policy(loaded_funds: mstarpy.Funds) -> pd.DataFrame:
@log_start_end(log=logger)
def load_funds(
term: str = "",
country: str = "",
country: str = "US",
) -> mstarpy.Funds:
"""Search mstarpy for matching funds
@@ -62,7 +166,13 @@ def load_funds(
Returns
-------
mstarpy.Funds
mstarpy.Funds
class mstarpy.Funds instantiated with selected funds
Examples
--------
>>> from openbb_terminal.sdk import openbb
>>> f = openbb.funds.load("Vanguard", "US")
"""
return mstarpy.Funds(term, country)
@@ -83,7 +193,14 @@ def load_holdings(
Returns
-------
pd.DataFrame of funds holdings
pd.DataFrame
Dataframe containing holdings
Examples
--------
>>> from openbb_terminal.sdk import openbb
>>> f = openbb.funds.load("Vanguard", "US")
>>> openbb.funds.holdings(f)
"""
holdings = loaded_funds.holdings(holding_type)
if holdings.empty:
@@ -95,7 +212,7 @@ def load_holdings(
def search_funds(
term: str = "",
country: str = "",
pageSize=10,
limit=10,
) -> pd.DataFrame:
"""Search mstarpy for matching funds
@@ -107,18 +224,23 @@ def search_funds(
list of field who will be displayed
country : str
country where the funds is hosted
pageSize : int
limit : int
length of results to display
Returns
-------
pd.DataFrame
Dataframe containing matches
Examples
--------
>>> from openbb_terminal.sdk import openbb
>>> openbb.funds.search("Vanguard", "US")
"""
field = ["SecId", "TenforeId", "LegalName"]
try:
return pd.DataFrame(
mstarpy.search_funds(term, field, country=country, pageSize=pageSize)
mstarpy.search_funds(term, field, country=country, pageSize=limit)
)
except RuntimeError as e:
logger.exception(str(e))

View File

@@ -2,7 +2,6 @@ import logging
from typing import Union
import mstarpy
import numpy as np
import pandas as pd
from openbb_terminal import OpenBBFigure
@@ -23,7 +22,6 @@ def display_carbon_metrics(loaded_funds: mstarpy.Funds):
----------
loaded_funds: mstarpy.Funds
class mstarpy.Funds instantiated with selected funds
"""
carbonMetrics = mstarpy_model.load_carbon_metrics(loaded_funds)
@@ -42,7 +40,6 @@ def display_exclusion_policy(loaded_funds: mstarpy.Funds):
----------
loaded_funds: mstarpy.Funds
class mstarpy.Funds instantiated with selected funds
"""
exclusion_policy = mstarpy_model.load_exclusion_policy(loaded_funds)
@@ -75,61 +72,56 @@ def display_historical(
type of comparison, can be index, category, both
external_axes : bool, optional
Whether to return the figure object or not, by default False
Examples
--------
>>> from openbb_terminal.sdk import openbb
>>> f = openbb.funds.load("Vanguard", "US")
>>> openbb.funds.historical_chart(f)
"""
title = f"Performance of {loaded_funds.name}"
start_date_dt = pd.to_datetime(start_date)
end_date_dt = pd.to_datetime(end_date)
df = mstarpy_model.get_historical(
loaded_funds, start_date_dt, end_date_dt, comparison
)
if df.empty:
return None
if not comparison:
data = loaded_funds.nav(start_date, end_date, frequency="daily")
fig = OpenBBFigure(xaxis_title="Date", yaxis_title="Nav").set_title(title)
df = pd.DataFrame(data)
df["date"] = pd.to_datetime(df["date"])
fig.add_scatter(
x=df.date,
x=df.index,
y=df.nav,
name=f"{loaded_funds.name}",
mode="lines",
)
else:
data = loaded_funds.historicalData()
comparison_list = {
"index": [
"fund",
"index",
],
"category": ["fund", "category"],
"both": ["fund", "index", "category"],
}
fig = OpenBBFigure(xaxis_title="Date", yaxis_title="Performance (Base 100)")
for x in comparison_list[comparison]:
df = pd.DataFrame(data["graphData"][x])
df["date"] = pd.to_datetime(df["date"])
df = df.loc[(df["date"] >= start_date) & (df["date"] <= end_date)]
df["pct"] = (df["value"] / df["value"].shift(1) - 1).fillna(0)
df["base_100"] = 100 * np.cumprod(1 + df["pct"])
if x == "fund":
data = loaded_funds.historicalData()
for col in df.columns:
if col == "fund":
label = f"funds : {loaded_funds.name}"
else:
key = f"{x}Name"
key = f"{col}Name"
if key in data:
label = f"{x} : {data[key]}"
label = f"{col} : {data[key]}"
title += f" vs {label},"
else:
label = x
label = col
fig.add_scatter(
x=df.date,
y=df.base_100,
x=df.index,
y=df[col],
name=f"{label}",
mode="lines",
)
fig.set_title(title.rstrip(","), wrap=True, wrap_width=70)
fig.update_layout(margin=dict(t=65))
fig.set_title(title.rstrip(","), wrap=True, wrap_width=70)
fig.update_layout(margin=dict(t=65))
return fig.show(external=external_axes)
@@ -142,10 +134,8 @@ def display_holdings(loaded_funds: mstarpy.Funds, holding_type: str = "all"):
----------
loaded_funds: mstarpy.Funds
class mstarpy.Funds instantiated with selected funds
holding_type : str
type of holdings, can be all, equity, bond, other
"""
holdings = mstarpy_model.load_holdings(loaded_funds, holding_type)
@@ -175,10 +165,13 @@ def display_load(
----------
term : str
String that will be searched for
country: str
Country to filter on
Returns
-------
mstarpy.Funds
class mstarpy.Funds instantiated with selected funds
"""
iso_country = mapping_country[country] if country else ""
funds = mstarpy_model.load_funds(term, country=iso_country)
@@ -206,7 +199,7 @@ def display_search(
Number to show
"""
iso_country = mapping_country[country] if country else ""
searches = mstarpy_model.search_funds(term, country=iso_country, pageSize=limit)
searches = mstarpy_model.search_funds(term, country=iso_country, limit=limit)
if searches.empty:
console.print("No matches found.")
return
@@ -238,41 +231,54 @@ def display_sector(
can be equity or fixed income
external_axes : bool, optional
Whether to return the figure object or not, by default False
Returns
-------
fig : plotly.graph_objects.Figure
Plotly figure object
Examples
--------
>>> from openbb_terminal.sdk import openbb
>>> f = openbb.funds.load("Vanguard", "US")
>>> openbb.funds.sector_chart(f)
"""
key = "EQUITY" if asset_type == "equity" else "FIXEDINCOME"
d = loaded_funds.sector()[key]
fig = OpenBBFigure()
df = mstarpy_model.get_sector(loaded_funds, asset_type)
width = -0.3
if not df.empty:
fig = OpenBBFigure()
title = "Sector breakdown of"
for x in ["fund", "index", "category"]:
name = d[f"{x}Name"]
data = d[f"{x}Portfolio"]
width = -0.3
p_date = data["portfolioDate"]
portfolio_date = p_date[:10] if p_date else ""
data.pop("portfolioDate")
title = "Sector breakdown of"
for x in ["fund", "index", "category"]:
name = df[f"{x}Name"].iloc[0]
data = df[f"{x}Portfolio"].to_dict()
labels = list(data.keys())
values = list(data.values())
p_date = data["portfolioDate"]
portfolio_date = p_date[:10] if p_date else ""
data.pop("portfolioDate")
# if all values are 0, skip
values = [0 if v is None else v for v in values]
if sum(values) == 0:
continue
labels = list(data.keys())
values = list(data.values())
fig.add_bar(
x=labels,
y=values,
name=f"{x} : {name} - {portfolio_date}",
)
width += 0.3 # the width of the bars
# if all values are 0, skip
values = [0 if v is None else v for v in values]
if sum(values) == 0:
continue
title += f" {name},"
fig.add_bar(
x=labels,
y=values,
name=f"{x} : {name} - {portfolio_date}",
)
width += 0.3 # the width of the bars
fig.update_layout(margin=dict(t=45), xaxis=dict(tickangle=-15))
fig.set_title(title.rstrip(","), wrap=True, wrap_width=60)
title += f" {name},"
return fig.show(external=external_axes)
fig.update_layout(margin=dict(t=45), xaxis=dict(tickangle=-15))
fig.set_title(title.rstrip(","), wrap=True, wrap_width=60)
return fig.show(external=external_axes)
return None

View File

@@ -212,38 +212,6 @@ class OpenBBSDK:
return ctrl.EtfController()
@property
def fixedincome(self):
"""Fixedincome Submodule
Attributes:
`ameribor`: Obtain data for American Interbank Offered Rate (AMERIBOR)\n
`cp`: Obtain Commercial Paper data\n
`dwpcr`: Obtain data for the Discount Window Primary Credit Rate.\n
`ecb`: Obtain data for ECB interest rates.\n
`ecbycrv`: Gets euro area yield curve data from ECB.\n
`estr`: Obtain data for Euro Short-Term Rate (ESTR)\n
`fed`: Obtain data for Effective Federal Funds Rate.\n
`ffrmc`: Get data for Selected Treasury Constant Maturity Minus Federal Funds Rate\n
`hqm`: The HQM yield curve represents the high quality corporate bond market, i.e.,\n
`icebofa`: Get data for ICE BofA US Corporate Bond Indices.\n
`icespread`: Get data for ICE BofA US Corporate Bond Spreads\n
`iorb`: Obtain data for Interest Rate on Reserve Balances.\n
`moody`: Get data for Moody Corporate Bond Index\n
`projection`: Obtain data for the Federal Reserve's projection of the federal funds rate.\n
`sofr`: Obtain data for Secured Overnight Financing Rate (SOFR)\n
`sonia`: Obtain data for Sterling Overnight Index Average (SONIA)\n
`spot`: The spot rate for any maturity is the yield on a bond that provides\n
`tbffr`: Get data for Selected Treasury Bill Minus Federal Funds Rate.\n
`tmc`: Get data for 10-Year Treasury Constant Maturity Minus Selected Treasury Constant Maturity.\n
`treasury`: Gets interest rates data from selected countries (3 month and 10 year)\n
`usrates`: Plot various treasury rates from the United States\n
`ycrv`: Gets yield curve data from FRED.\n
`ycrv_chart`: Display yield curve based on US Treasury rates for a specified date.\n
"""
return model.FixedincomeRoot()
@property
def forecast(self):
"""Forecasting Submodule
@@ -313,6 +281,38 @@ class OpenBBSDK:
return model.ForecastRoot()
@property
def fixedincome(self):
"""Fixedincome Submodule
Attributes:
`ameribor`: Obtain data for American Interbank Offered Rate (AMERIBOR)\n
`cp`: Obtain Commercial Paper data\n
`dwpcr`: Obtain data for the Discount Window Primary Credit Rate.\n
`ecb`: Obtain data for ECB interest rates.\n
`ecbycrv`: Gets euro area yield curve data from ECB.\n
`estr`: Obtain data for Euro Short-Term Rate (ESTR)\n
`fed`: Obtain data for Effective Federal Funds Rate.\n
`ffrmc`: Get data for Selected Treasury Constant Maturity Minus Federal Funds Rate\n
`hqm`: The HQM yield curve represents the high quality corporate bond market, i.e.,\n
`icebofa`: Get data for ICE BofA US Corporate Bond Indices.\n
`icespread`: Get data for ICE BofA US Corporate Bond Spreads\n
`iorb`: Obtain data for Interest Rate on Reserve Balances.\n
`moody`: Get data for Moody Corporate Bond Index\n
`projection`: Obtain data for the Federal Reserve's projection of the federal funds rate.\n
`sofr`: Obtain data for Secured Overnight Financing Rate (SOFR)\n
`sonia`: Obtain data for Sterling Overnight Index Average (SONIA)\n
`spot`: The spot rate for any maturity is the yield on a bond that provides\n
`tbffr`: Get data for Selected Treasury Bill Minus Federal Funds Rate.\n
`tmc`: Get data for 10-Year Treasury Constant Maturity Minus Selected Treasury Constant Maturity.\n
`treasury`: Gets interest rates data from selected countries (3 month and 10 year)\n
`usrates`: Plot various treasury rates from the United States\n
`ycrv`: Gets yield curve data from FRED.\n
`ycrv_chart`: Display yield curve based on US Treasury rates for a specified date.\n
"""
return model.FixedincomeRoot()
@property
def forex(self):
"""Forex Submodule
@@ -330,6 +330,24 @@ class OpenBBSDK:
return ctrl.ForexController()
@property
def funds(self):
"""Mutual Funds Submodule
Attributes:
`carbon`: Search mstarpy for carbon metrics\n
`exclusion`: Search mstarpy exclusion policy in esgData\n
`historical`: Get historical fund, category, index price\n
`historical_chart`: Display historical fund, category, index price\n
`holdings`: Search mstarpy for holdings\n
`load`: Search mstarpy for matching funds\n
`search`: Search mstarpy for matching funds\n
`sector`: Get fund, category, index sector breakdown\n
`sector_chart`: Display fund, category, index sector breakdown\n
"""
return model.FundsRoot()
@property
def futures(self):
"""Futures Submodule

View File

@@ -29,15 +29,13 @@ def test_load_funds(term, country):
@pytest.mark.record_http
@pytest.mark.parametrize(
"term, country, pageSize, expected_columns",
"term, country, limit, expected_columns",
[
("Vanguard", "US", 1, ["SecId", "TenforeId", "LegalName"]),
],
)
def test_search_funds(record, term, country, pageSize, expected_columns):
searched_funds = mstarpy_model.search_funds(
term=term, country=country, pageSize=pageSize
)
def test_search_funds(record, term, country, limit, expected_columns):
searched_funds = mstarpy_model.search_funds(term=term, country=country, limit=limit)
record.add_verify(obj=searched_funds)
assert searched_funds is not None