Merge branch 'develop' into feature/reference-generator-package-builder

This commit is contained in:
Pratyush Shukla
2024-03-12 00:38:13 +05:30
committed by GitHub
21 changed files with 1504 additions and 219 deletions

6
.github/labeler.yml vendored
View File

@@ -23,4 +23,8 @@ labels:
- label: "excel"
files:
- "website/content/excel/.*"
- "website/content/excel/.*"
- label: "breaking_change"
files:
- "openbb_platform/core/openbb_core/provider/standard_models/.*"

View File

@@ -8,6 +8,9 @@ categories:
- title: 🐛 OpenBB Platform Bug Fixes
labels:
- 'bug'
- title: 🚨 OpenBB Platform Breaking Changes
labels:
- 'breaking_change'
- title: 📚 OpenBB Documentation Changes
labels:
- 'docs'

View File

@@ -51,6 +51,7 @@ def update_extension_dependencies(path: Path):
def bump_version(path: Path):
"""Bump the version of the package"""
print(f"\n> {path.parent.stem}") # noqa: T201
subprocess.run(CMD + VERSION_BUMP_CMD, cwd=path.parent, check=True) # noqa: S603
@@ -77,7 +78,9 @@ def publish(dry_run: bool = False, core: bool = False, extensions: bool = False)
# Publish (if not dry run)
if not dry_run:
subprocess.run(
CMD + PUBLISH_CMD, cwd=path.parent, check=True # noqa: S603
CMD + PUBLISH_CMD, # noqa: S603
cwd=path.parent,
check=True, # noqa: S603
)
except Exception as e:
print(f"Error publishing {path.parent}:\n{e}") # noqa: T201

View File

@@ -4,9 +4,9 @@
[![LatestRelease](https://badge.fury.io/py/openbb.svg)](https://github.com/OpenBB-finance/OpenBBTerminal)
| OpenBB is committed to build the future of investment research by focusing on an open source infrastructure accessible to everyone, everywhere. |
|:--:|
| ![OpenBBLogo](https://user-images.githubusercontent.com/25267873/218899768-1f0964b8-326c-4f35-af6f-ea0946ac970b.png) |
| Check our website at [openbb.co](www.openbb.co) |
| :---------------------------------------------------------------------------------------------------------------------------------------------: |
| ![OpenBBLogo](https://user-images.githubusercontent.com/25267873/218899768-1f0964b8-326c-4f35-af6f-ea0946ac970b.png) |
| Check our website at [openbb.co](www.openbb.co) |
## Overview
@@ -22,21 +22,19 @@ pip install openbb
This will install the following data providers:
| Extension Name | Description | Installation Command | Minimum Subscription Type Required |
|----------------|-------------|----------------------|------------------------------------|
| openbb-benzinga | [Benzinga](https://www.benzinga.com/apis/en-ca/) data connector | pip install openbb-benzinga | Paid |
| openbb-biztoc | [Biztoc](https://api.biztoc.com/#biztoc-default) News data connector | pip install openbb-biztoc | Free |
| openbb-ecb | [ECB](https://data.ecb.europa.eu/) data connector | pip install openbb-ecb | Free |
| openbb-finra | [FINRA](https://www.finra.org/finra-data) data connector | pip install openbb-finra | Free |
| openbb-finviz | [Finviz](https://finviz.com) data connector | pip install openbb-finra | Free |
| openbb-fmp | [FMP](https://site.financialmodelingprep.com/developer/) data connector | pip install openbb-fmp | Free |
| openbb-fred | [FRED](https://fred.stlouisfed.org/) data connector | pip install openbb-fred | Free |
| openbb-intrinio | [Intrinio](https://intrinio.com/pricing) data connector | pip install openbb-intrinio | Paid |
| openbb-oecd | [OECD](https://data.oecd.org/) data connector | pip install openbb-oecd | Free |
| openbb-polygon | [Polygon](https://polygon.io/) data connector | pip install openbb-polygon | Free |
| openbb-sec | [SEC](https://www.sec.gov/edgar/sec-api-documentation) data connector | pip install openbb-sec | Free |
| openbb-tiingo | [Tiingo](https://www.tiingo.com/about/pricing) data connector | pip install openbb-tiingo | Free |
| openbb-tradingeconomics | [TradingEconomics](https://tradingeconomics.com/api) data connector | pip install openbb-tradingeconomics | Paid |
| Extension Name | Description | Installation Command | Minimum Subscription Type Required |
| ----------------------- | ------------------------------------------------------------------------- | ----------------------------------- | ---------------------------------- |
| openbb-benzinga | [Benzinga](https://www.benzinga.com/apis/en-ca/) data connector | pip install openbb-benzinga | Paid |
| openbb-federal-reserve | [FederalReserve](https://www.federalreserve.gov/data.html) data connector | pip install openbb-federal-reserve | Free |
| openbb-fmp | [FMP](https://site.financialmodelingprep.com/developer/) data connector | pip install openbb-fmp | Free |
| openbb-fred | [FRED](https://fred.stlouisfed.org/) data connector | pip install openbb-fred | Free |
| openbb-intrinio | [Intrinio](https://intrinio.com/pricing) data connector | pip install openbb-intrinio | Paid |
| openbb-oecd | [OECD](https://data.oecd.org/) data connector | pip install openbb-oecd | Free |
| openbb-polygon | [Polygon](https://polygon.io/) data connector | pip install openbb-polygon | Free |
| openbb-sec | [SEC](https://www.sec.gov/edgar/sec-api-documentation) data connector | pip install openbb-sec | Free |
| openbb-tiingo | [Tiingo](https://www.tiingo.com/about/pricing) data connector | pip install openbb-tiingo | Free |
| openbb-tradingeconomics | [TradingEconomics](https://tradingeconomics.com/api) data connector | pip install openbb-tradingeconomics | Paid |
| openbb-yahoo-finance | [Yahoo Finance](https://finance.yahoo.com/) data connector | pip install openbb-yfinance | Free |
To install extensions that expand the core functionalities specify the extension name or use `all` to install all.

View File

@@ -1,6 +1,6 @@
"""Key Metrics Standard Model."""
from typing import List, Literal, Optional, Set, Union
from typing import Literal, Optional
from pydantic import Field, field_validator
@@ -48,11 +48,3 @@ class KeyMetricsData(Data):
pe_ratio: Optional[float] = Field(
default=None, description="Price-to-earnings ratio (P/E ratio)"
)
@field_validator("symbol", mode="before", check_fields=False)
@classmethod
def to_upper(cls, v: Union[str, List[str], Set[str]]):
"""Convert field to uppercase."""
if isinstance(v, str):
return v.upper()
return ",".join([symbol.upper() for symbol in list(v)]) if v else None

View File

@@ -346,12 +346,6 @@ def residual_autocorrelation(
'obb.econometrics.cointegration(data=stock_data, columns=["open", "close"])',
],
),
APIEx(
parameters={
"columns": ["open", "close"],
"data": APIEx.mock_data("timeseries"),
}
),
],
)
def cointegration(

View File

@@ -189,7 +189,7 @@ def test_etf_search(params, headers):
(
{
"provider": "tradier",
"symbol": "AAPL,MSFT",
"symbol": "SPY",
"start_date": "2023-01-01",
"end_date": "2023-06-06",
"interval": "1M",
@@ -199,7 +199,7 @@ def test_etf_search(params, headers):
(
{
"provider": "tradier",
"symbol": "AAPL,MSFT",
"symbol": "SPY,DJIA",
"start_date": None,
"end_date": None,
"interval": "15m",

View File

@@ -187,7 +187,7 @@ def test_etf_search(params, obb):
(
{
"provider": "tradier",
"symbol": "AAPL,MSFT",
"symbol": "SPY",
"start_date": "2023-01-01",
"end_date": "2023-06-06",
"interval": "1M",
@@ -197,7 +197,7 @@ def test_etf_search(params, obb):
(
{
"provider": "tradier",
"symbol": "AAPL,MSFT",
"symbol": "SPY,DJIA",
"start_date": None,
"end_date": None,
"interval": "15m",

View File

@@ -206,7 +206,13 @@ def test_quantitative_unitroot_test(params, data_type):
"params, data_type",
[
(
{"data": "", "target": "close", "rfr": "", "window": "", "index": "date"},
{
"data": "",
"target": "close",
"rfr": "",
"window": "100",
"index": "date",
},
"equity",
),
(
@@ -214,7 +220,7 @@ def test_quantitative_unitroot_test(params, data_type):
"data": "",
"target": "high",
"rfr": "0.5",
"window": "250",
"window": "150",
"index": "date",
},
"crypto",
@@ -243,7 +249,7 @@ def test_quantitative_performance_sharpe_ratio(params, data_type):
"data": "",
"target": "close",
"target_return": "",
"window": "",
"window": "100",
"adjusted": "",
"index": "date",
},

View File

@@ -327,7 +327,6 @@ def test_quantitative_summary(params, data_type, obb):
"data": "",
"target": "close",
"window": "10",
"quantile_pct": "",
"index": "date",
},
"equity",
@@ -337,7 +336,6 @@ def test_quantitative_summary(params, data_type, obb):
"data": "",
"target": "high",
"window": "50",
"quantile_pct": "0.6",
"index": "date",
},
"crypto",
@@ -363,7 +361,6 @@ def test_quantitative_rolling_stdev(params, data_type, obb):
"data": "",
"target": "close",
"window": "10",
"quantile_pct": "",
"index": "date",
},
"equity",
@@ -373,7 +370,6 @@ def test_quantitative_rolling_stdev(params, data_type, obb):
"data": "",
"target": "high",
"window": "50",
"quantile_pct": "0.6",
"index": "date",
},
"crypto",
@@ -399,7 +395,6 @@ def test_quantitative_rolling_mean(params, data_type, obb):
"data": "",
"target": "close",
"window": "10",
"quantile_pct": "",
"index": "date",
},
"equity",
@@ -409,7 +404,6 @@ def test_quantitative_rolling_mean(params, data_type, obb):
"data": "",
"target": "high",
"window": "50",
"quantile_pct": "0.6",
"index": "date",
},
"crypto",

View File

@@ -931,7 +931,6 @@ def donchian(
"ichimoku_data = obb.technical.ichimoku(data=stock_data.results, conversion=9, base=26, lookahead=False)",
],
),
APIEx(parameters={"data": APIEx.mock_data("timeseries")}),
],
)
def ichimoku(
@@ -973,6 +972,7 @@ def ichimoku(
OBBject[List[Data]]
The calculated data.
"""
validate_data(data, [conversion, base, lagging])
df = basemodel_to_df(data, index=index)
df_target = get_target_columns(df, ["high", "low", "close"])
df_ichimoku, df_span = df_target.ta.ichimoku(

View File

@@ -1,6 +1,6 @@
"""Custom pytest configuration for the extensions."""
from typing import Dict, List
from typing import Any, Dict, List, Tuple
import pytest
from openbb_core.app.router import CommandMap
@@ -13,7 +13,7 @@ commands = list(cm.map.keys())
# ruff: noqa: SIM114
def parametrize(argnames: str, argvalues: List[Dict], **kwargs):
def parametrize(argnames: str, argvalues: List[Tuple[Any, ...]], **kwargs):
"""Custom parametrize decorator that filters test cases based on the environment."""
routers, providers, obbject_ext = list_openbb_extensions()

View File

@@ -2412,7 +2412,7 @@ class ROUTER_equity_fundamental(Container):
symbol: Annotated[
Union[str, List[str]],
OpenBBCustomParameter(
description="Symbol to get data for. Multiple items allowed for provider(s): fmp, yfinance."
description="Symbol to get data for. Multiple items allowed for provider(s): fmp, intrinio, yfinance."
),
],
period: Annotated[
@@ -2436,7 +2436,7 @@ class ROUTER_equity_fundamental(Container):
Parameters
----------
symbol : Union[str, List[str]]
Symbol to get data for. Multiple items allowed for provider(s): fmp, yfinance.
Symbol to get data for. Multiple items allowed for provider(s): fmp, intrinio, yfinance.
period : Optional[Literal['annual', 'quarter']]
Time period of the data to return.
limit : Optional[int]
@@ -2451,7 +2451,7 @@ class ROUTER_equity_fundamental(Container):
Returns
-------
OBBject
results : Union[List[KeyMetrics], KeyMetrics]
results : List[KeyMetrics]
Serializable results.
provider : Optional[Literal['fmp', 'intrinio', 'yfinance']]
Provider name.
@@ -2494,8 +2494,8 @@ class ROUTER_equity_fundamental(Container):
Shareholders equity per share (provider: fmp)
interest_debt_per_share : Optional[float]
Interest debt per share (provider: fmp)
enterprise_value : Optional[Union[float, int]]
Enterprise value (provider: fmp, yfinance)
enterprise_value : Optional[int]
Enterprise value (provider: fmp, intrinio, yfinance)
price_to_sales_ratio : Optional[float]
Price-to-sales ratio (provider: fmp)
pocf_ratio : Optional[float]
@@ -2515,7 +2515,8 @@ class ROUTER_equity_fundamental(Container):
ev_to_free_cash_flow : Optional[float]
Enterprise value-to-free cash flow ratio (provider: fmp)
earnings_yield : Optional[float]
Earnings yield (provider: fmp)
Earnings yield (provider: fmp);
Earnings yield, as a normalized percent. (provider: intrinio)
free_cash_flow_yield : Optional[float]
Free cash flow yield (provider: fmp)
debt_to_equity : Optional[float]
@@ -2586,15 +2587,73 @@ class ROUTER_equity_fundamental(Container):
Return on equity (provider: fmp)
capex_per_share : Optional[float]
Capital expenditures per share (provider: fmp)
price_to_book : Optional[float]
Price to book ratio. (provider: intrinio, yfinance)
price_to_tangible_book : Optional[float]
Price to tangible book ratio. (provider: intrinio)
price_to_revenue : Optional[float]
Price to revenue ratio. (provider: intrinio)
quick_ratio : Optional[float]
Quick ratio. (provider: intrinio, yfinance)
gross_margin : Optional[float]
Gross margin, as a normalized percent. (provider: intrinio, yfinance)
ebit_margin : Optional[float]
EBIT margin, as a normalized percent. (provider: intrinio)
profit_margin : Optional[float]
Profit margin, as a normalized percent. (provider: intrinio, yfinance)
eps : Optional[float]
Basic earnings per share. (provider: intrinio)
eps_growth : Optional[float]
EPS growth, as a normalized percent. (provider: intrinio)
revenue_growth : Optional[float]
Revenue growth, as a normalized percent. (provider: intrinio, yfinance)
ebitda_growth : Optional[float]
EBITDA growth, as a normalized percent. (provider: intrinio)
ebit_growth : Optional[float]
EBIT growth, as a normalized percent. (provider: intrinio)
net_income_growth : Optional[float]
Net income growth, as a normalized percent. (provider: intrinio)
free_cash_flow_to_firm_growth : Optional[float]
Free cash flow to firm growth, as a normalized percent. (provider: intrinio)
invested_capital_growth : Optional[float]
Invested capital growth, as a normalized percent. (provider: intrinio)
return_on_assets : Optional[float]
Return on assets, as a normalized percent. (provider: intrinio, yfinance)
return_on_equity : Optional[float]
Return on equity, as a normalized percent. (provider: intrinio, yfinance)
return_on_invested_capital : Optional[float]
Return on invested capital, as a normalized percent. (provider: intrinio)
ebitda : Optional[int]
Earnings before interest, taxes, depreciation, and amortization. (provider: intrinio)
ebit : Optional[int]
Earnings before interest and taxes. (provider: intrinio)
long_term_debt : Optional[int]
Long-term debt. (provider: intrinio)
total_debt : Optional[int]
Total debt. (provider: intrinio)
total_capital : Optional[int]
The sum of long-term debt and total shareholder equity. (provider: intrinio)
free_cash_flow_to_firm : Optional[int]
Free cash flow to firm. (provider: intrinio)
altman_z_score : Optional[float]
Altman Z-score. (provider: intrinio)
beta : Optional[float]
Beta relative to the broad market calculated on a rolling three-year basis. (provider: intrinio);
Beta relative to the broad market (rolling three-year). (provider: intrinio);
Beta relative to the broad market (5-year monthly). (provider: yfinance)
volume : Optional[float]
Volume (provider: intrinio)
fifty_two_week_high : Optional[float]
last_price : Optional[float]
Last price of the stock. (provider: intrinio)
year_high : Optional[float]
52 week high (provider: intrinio)
fifty_two_week_low : Optional[float]
year_low : Optional[float]
52 week low (provider: intrinio)
volume_avg : Optional[int]
Average daily volume. (provider: intrinio)
short_interest : Optional[int]
Number of shares reported as sold short. (provider: intrinio)
shares_outstanding : Optional[int]
Weighted average shares outstanding (TTM). (provider: intrinio)
days_to_cover : Optional[float]
Days to cover short interest, based on average daily volume. (provider: intrinio)
forward_pe : Optional[float]
Forward price-to-earnings ratio. (provider: yfinance)
peg_ratio : Optional[float]
@@ -2611,30 +2670,16 @@ class ROUTER_equity_fundamental(Container):
Earnings growth (Year Over Year), as a normalized percent. (provider: yfinance)
earnings_growth_quarterly : Optional[float]
Quarterly earnings growth (Year Over Year), as a normalized percent. (provider: yfinance)
revenue_growth : Optional[float]
Revenue growth (Year Over Year), as a normalized percent. (provider: yfinance)
enterprise_to_revenue : Optional[float]
Enterprise value to revenue ratio. (provider: yfinance)
quick_ratio : Optional[float]
Quick ratio. (provider: yfinance)
gross_margin : Optional[float]
Gross margin, as a normalized percent. (provider: yfinance)
operating_margin : Optional[float]
Operating margin, as a normalized percent. (provider: yfinance)
ebitda_margin : Optional[float]
EBITDA margin, as a normalized percent. (provider: yfinance)
profit_margin : Optional[float]
Profit margin, as a normalized percent. (provider: yfinance)
return_on_assets : Optional[float]
Return on assets, as a normalized percent. (provider: yfinance)
return_on_equity : Optional[float]
Return on equity, as a normalized percent. (provider: yfinance)
dividend_yield_5y_avg : Optional[float]
5-year average dividend yield, as a normalized percent. (provider: yfinance)
book_value : Optional[float]
Book value per share. (provider: yfinance)
price_to_book : Optional[float]
Price-to-book ratio. (provider: yfinance)
overall_risk : Optional[float]
Overall risk score. (provider: yfinance)
audit_risk : Optional[float]
@@ -2673,7 +2718,11 @@ class ROUTER_equity_fundamental(Container):
"limit": limit,
},
extra_params=kwargs,
extra_info={"symbol": {"multiple_items_allowed": ["fmp", "yfinance"]}},
extra_info={
"symbol": {
"multiple_items_allowed": ["fmp", "intrinio", "yfinance"]
}
},
)
)

View File

@@ -3,6 +3,7 @@
# pylint: disable=unused-argument
import asyncio
import json
from datetime import datetime
from io import BytesIO
from typing import Any, Dict, List, Literal, Optional
@@ -213,70 +214,84 @@ class AVEquityHistoricalFetcher(
if result:
data = read_csv(BytesIO(result)) # type: ignore
if len(data) > 0:
data.rename(
columns={
"timestamp": "date",
"dividend_amount": "dividend",
"adjusted close": "adj_close",
"dividend amount": "dividend",
"adjusted_close": "adj_close",
"split_coefficient": "split_factor",
},
inplace=True,
)
data["date"] = data["date"].apply(to_datetime)
data.set_index("date", inplace=True)
# The returned data when 'adjusted=true' from the API does not return a usable OHLCV data set.
# We need to calculate the adjusted prices manually.
if query.adjustment != "unadjusted":
temp = data.copy()
temp["dividend_factor"] = (
temp["close"] - temp["dividend"]
) / temp["close"]
temp["volume_factor"] = temp["split_factor"]
temp["split_factor"] = 1 / temp["split_factor"]
adj_cols = ["open", "high", "low", "close", "volume"]
divs = query.adjustment == "splits_and_dividends"
for col in adj_cols:
divs = False if col == "volume" else divs
if col in temp.columns:
temp = calculate_adjusted_prices(temp, col, divs)
temp["adj_dividend"] = (
temp["adj_close"] * (1 - temp["dividend_factor"])
if query.adjustment == "splits_only"
else temp["close"] * (1 - temp["dividend_factor"])
if all(
col in data.columns
for col in [
"timestamp",
"dividend_amount",
"adjusted close",
"split_coefficient",
]
):
data.rename(
columns={
"timestamp": "date",
"dividend_amount": "dividend",
"adjusted close": "adj_close",
"split_coefficient": "split_factor",
},
inplace=True,
)
data["open"] = round(temp["adj_open"], 4)
data["high"] = round(temp["adj_high"], 4)
data["low"] = round(temp["adj_low"], 4)
data["close"] = round(temp["adj_close"], 4)
data["volume"] = round(temp["adj_volume"]).astype(int)
data["dividend"] = round(temp["adj_dividend"], 4)
data.drop(columns=["adj_close"], inplace=True)
# Resample the daily data for the interval requested.
freq = ""
agg_dict = {
"open": "first",
"high": "max",
"low": "min",
"close": "last",
"volume": "sum",
"dividend": "sum",
"split_factor": "prod",
}
if query.adjustment == "unadjusted":
agg_dict.pop("dividend")
agg_dict.pop("split_factor")
if query.interval == "1M":
freq = "M"
if query.interval == "1W":
freq = "W-FRI"
if freq in ["M", "W-FRI"]:
data = data.resample(freq).agg({**agg_dict})
if len(query.symbol.split(",")) > 1:
data.loc[:, "symbol"] = symbol
data["date"] = data["date"].apply(to_datetime)
data.set_index("date", inplace=True)
# The returned data when 'adjusted=true' from the API does not return a usable OHLCV data set.
# We need to calculate the adjusted prices manually.
if query.adjustment != "unadjusted":
temp = data.copy()
temp["dividend_factor"] = (
temp["close"] - temp["dividend"]
) / temp["close"]
temp["volume_factor"] = temp["split_factor"]
temp["split_factor"] = 1 / temp["split_factor"]
adj_cols = ["open", "high", "low", "close", "volume"]
divs = query.adjustment == "splits_and_dividends"
for col in adj_cols:
divs = False if col == "volume" else divs
if col in temp.columns:
temp = calculate_adjusted_prices(temp, col, divs)
temp["adj_dividend"] = (
temp["adj_close"] * (1 - temp["dividend_factor"])
if query.adjustment == "splits_only"
else temp["close"] * (1 - temp["dividend_factor"])
)
data["open"] = round(temp["adj_open"], 4)
data["high"] = round(temp["adj_high"], 4)
data["low"] = round(temp["adj_low"], 4)
data["close"] = round(temp["adj_close"], 4)
data["volume"] = round(temp["adj_volume"]).astype(int)
data["dividend"] = round(temp["adj_dividend"], 4)
data.drop(columns=["adj_close"], inplace=True)
# Resample the daily data for the interval requested.
freq = ""
agg_dict = {
"open": "first",
"high": "max",
"low": "min",
"close": "last",
"volume": "sum",
"dividend": "sum",
"split_factor": "prod",
}
if query.adjustment == "unadjusted":
agg_dict.pop("dividend")
agg_dict.pop("split_factor")
if query.interval == "1M":
freq = "M"
if query.interval == "1W":
freq = "W-FRI"
if freq in ["M", "W-FRI"]:
data = data.resample(freq).agg({**agg_dict})
if len(query.symbol.split(",")) > 1:
data.loc[:, "symbol"] = symbol
results.extend(data.reset_index().to_dict("records"))
results.extend(data.reset_index().to_dict("records"))
else:
try:
data = json.loads(result.decode())
if "Information" in data:
warn(f"{data['Information']}")
except json.JSONDecodeError:
warn(f"Failed to parse data for {symbol}.")
return results

View File

@@ -1,12 +1,18 @@
"""Intrinio Key Metrics Model."""
from typing import Any, Dict, List, Optional
# pylint: disable=unused-argument
import asyncio
from typing import Any, Dict, List, Optional
from warnings import warn
from openbb_core.provider.abstract.data import ForceInt
from openbb_core.provider.abstract.fetcher import Fetcher
from openbb_core.provider.standard_models.key_metrics import (
KeyMetricsData,
KeyMetricsQueryParams,
)
from openbb_core.provider.utils.errors import EmptyDataError
from openbb_core.provider.utils.helpers import (
ClientResponse,
amake_requests,
@@ -15,15 +21,13 @@ from pydantic import Field
class IntrinioKeyMetricsQueryParams(KeyMetricsQueryParams):
"""Intrinio Key Metrics Query.
Source: https://data.intrinio.com/data-tag/beta
https://data.intrinio.com/data-tag/volume
https://data.intrinio.com/data-tag/52_week_high
https://data.intrinio.com/data-tag/52_week_low
https://data.intrinio.com/data-tag/dividendyield
https://data.intrinio.com/data-tag/pricetoearnings
"""
Intrinio Key Metrics Query.
Source: https://data.intrinio.com/data-tags/
"""
__json_schema_extra__ = {"symbol": ["multiple_items_allowed"]}
class IntrinioKeyMetricsData(KeyMetricsData):
@@ -32,26 +36,200 @@ class IntrinioKeyMetricsData(KeyMetricsData):
__alias_dict__ = {
"market_cap": "marketcap",
"pe_ratio": "pricetoearnings",
"price_to_book": "pricetobook",
"price_to_tangible_book": "pricetotangiblebook",
"price_to_revenue": "pricetorevenue",
"long_term_debt": "ltdebtandcapleases",
"total_debt": "debt",
"total_capital": "totalcapital",
"enterprise_value": "enterprisevalue",
"eps_growth": "epsgrowth",
"ebit_growth": "ebitgrowth",
"ebitda_growth": "ebitdagrowth",
"revenue_growth": "revenuegrowth",
"net_income_growth": "netincomegrowth",
"free_cash_flow_to_firm_growth": "fcffgrowth",
"invested_capital_growth": "investedcapitalgrowth",
"quick_ratio": "quickratio",
"gross_margin": "grossmargin",
"ebit_margin": "ebitmargin",
"profit_margin": "profitmargin",
"return_on_assets": "roa",
"return_on_equity": "roe",
"return_on_invested_capital": "roic",
"free_cash_flow_to_firm": "freecashflow",
"altman_z_score": "altmanzscore",
"earnings_yield": "earningsyield",
"dividend_yield": "dividendyield",
"year_high": "52_week_high",
"year_low": "52_week_low",
"volume_avg": "average_daily_volume",
"shares_outstanding": "adjweightedavebasicsharesos",
"eps": "basiceps",
}
beta: float = Field(
description="Beta relative to the broad market calculated on a rolling three-year basis."
price_to_book: Optional[float] = Field(
default=None,
description="Price to book ratio.",
)
volume: float = Field(description="Volume")
fifty_two_week_high: float = Field(description="52 week high", alias="52_week_high")
fifty_two_week_low: float = Field(description="52 week low", alias="52_week_low")
dividend_yield: float = Field(
price_to_tangible_book: Optional[float] = Field(
default=None,
description="Price to tangible book ratio.",
)
price_to_revenue: Optional[float] = Field(
default=None,
description="Price to revenue ratio.",
)
quick_ratio: Optional[float] = Field(
default=None,
description="Quick ratio.",
)
gross_margin: Optional[float] = Field(
default=None,
description="Gross margin, as a normalized percent.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
ebit_margin: Optional[float] = Field(
default=None,
description="EBIT margin, as a normalized percent.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
profit_margin: Optional[float] = Field(
default=None,
description="Profit margin, as a normalized percent.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
eps: Optional[float] = Field(
default=None,
description="Basic earnings per share.",
)
eps_growth: Optional[float] = Field(
default=None,
description="EPS growth, as a normalized percent.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
revenue_growth: Optional[float] = Field(
default=None,
description="Revenue growth, as a normalized percent.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
ebitda_growth: Optional[float] = Field(
default=None,
description="EBITDA growth, as a normalized percent.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
ebit_growth: Optional[float] = Field(
default=None,
description="EBIT growth, as a normalized percent.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
net_income_growth: Optional[float] = Field(
default=None,
description="Net income growth, as a normalized percent.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
free_cash_flow_to_firm_growth: Optional[float] = Field(
default=None,
description="Free cash flow to firm growth, as a normalized percent.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
invested_capital_growth: Optional[float] = Field(
default=None,
description="Invested capital growth, as a normalized percent.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
return_on_assets: Optional[float] = Field(
default=None,
description="Return on assets, as a normalized percent.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
return_on_equity: Optional[float] = Field(
default=None,
description="Return on equity, as a normalized percent.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
return_on_invested_capital: Optional[float] = Field(
default=None,
description="Return on invested capital, as a normalized percent.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
ebitda: Optional[ForceInt] = Field(
default=None,
description="Earnings before interest, taxes, depreciation, and amortization.",
)
ebit: Optional[ForceInt] = Field(
default=None,
description="Earnings before interest and taxes.",
)
long_term_debt: Optional[ForceInt] = Field(
default=None,
description="Long-term debt.",
)
total_debt: Optional[ForceInt] = Field(
default=None,
description="Total debt.",
)
total_capital: Optional[ForceInt] = Field(
default=None,
description="The sum of long-term debt and total shareholder equity.",
)
enterprise_value: Optional[ForceInt] = Field(
default=None,
description="Enterprise value.",
)
free_cash_flow_to_firm: Optional[ForceInt] = Field(
default=None,
description="Free cash flow to firm.",
)
altman_z_score: Optional[float] = Field(
default=None,
description="Altman Z-score.",
)
beta: Optional[float] = Field(
default=None,
description="Beta relative to the broad market (rolling three-year).",
)
dividend_yield: Optional[float] = Field(
default=None,
description="Dividend yield, as a normalized percent.",
json_schema_extra={"unit_measurement": "percent", "frontend_multiply": 100},
alias="dividendyield",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
earnings_yield: Optional[float] = Field(
default=None,
description="Earnings yield, as a normalized percent.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
last_price: Optional[float] = Field(
default=None,
description="Last price of the stock.",
)
year_high: Optional[float] = Field(
default=None,
description="52 week high",
)
year_low: float = Field(default=None, description="52 week low")
volume_avg: Optional[ForceInt] = Field(
default=None,
description="Average daily volume.",
)
short_interest: Optional[ForceInt] = Field(
default=None,
description="Number of shares reported as sold short.",
)
shares_outstanding: Optional[ForceInt] = Field(
default=None,
description="Weighted average shares outstanding (TTM).",
)
days_to_cover: Optional[float] = Field(
default=None,
description="Days to cover short interest, based on average daily volume.",
)
class IntrinioKeyMetricsFetcher(
Fetcher[
IntrinioKeyMetricsQueryParams,
IntrinioKeyMetricsData,
List[IntrinioKeyMetricsData],
]
):
"""Transform the query, extract and transform the data from the Intrinio endpoints."""
@@ -59,44 +237,128 @@ class IntrinioKeyMetricsFetcher(
@staticmethod
def transform_query(params: Dict[str, Any]) -> IntrinioKeyMetricsQueryParams:
"""Transform the query params."""
if params.get("period") is not None and params.get("period") != "annual":
warn(
"The period parameter is not available for this Intrinio endpoint, it will be ignored."
)
return IntrinioKeyMetricsQueryParams(**params)
@staticmethod
async def aextract_data(
query: IntrinioKeyMetricsQueryParams, # pylint: disable=unused-argument
query: IntrinioKeyMetricsQueryParams,
credentials: Optional[Dict[str, str]],
**kwargs: Any,
) -> Dict:
) -> List[Dict]:
"""Return the raw data from the Intrinio endpoint."""
api_key = credentials.get("intrinio_api_key") if credentials else ""
tags = [
"beta",
"volume",
"average_daily_volume",
"basiceps",
"basicdilutedeps",
"marketcap",
"last_price",
"52_week_high",
"52_week_low",
"dividendyield",
"pricetoearnings",
"pricetorevenue",
"pricetobook",
"pricetotangiblebook",
"grossmargin",
"ebitmargin",
"profitmargin",
"roa",
"roe",
"roic",
"earningsyield",
"days_to_cover",
"short_interest",
"revenuegrowth",
"ebitgrowth",
"ebitdagrowth",
"epsgrowth",
"netincomegrowth",
"investedcapitalgrowth",
"debt",
"totalcapital",
"enterprisevalue",
"freecashflow",
"fcffgrowth",
"ltdebtandcapleases",
"altmanzscore",
"quickratio",
"ebit",
"ebitda",
"adjweightedavebasicsharesos",
]
results = []
urls = []
symbols = query.symbol.split(",")
urls = [
f"https://api-v2.intrinio.com/companies/{query.symbol}/data_point/{tag}?api_key={api_key}"
for tag in tags
]
async def get_one(symbol: str):
"""Get data for one symbol."""
async def callback(response: ClientResponse, _: Any) -> Dict:
"""Return the response."""
return {response.url.parts[-1]: await response.json()}
_urls = [
f"https://api-v2.intrinio.com/companies/{symbol}/data_point/{tag}?api_key={api_key}"
for tag in tags
]
urls.extend(_urls)
data: Dict = {"symbol": query.symbol}
for result in await amake_requests(urls, callback, **kwargs):
data.update(result)
async def callback(response: ClientResponse, _: Any) -> Dict:
"""Return the response."""
return {response.url.parts[-1]: await response.json()}
return data
data = {}
for result in await amake_requests(urls, callback, **kwargs):
data.update(result)
if data:
data["symbol"] = symbol
results.append(data)
tasks = [get_one(symbol) for symbol in symbols]
await asyncio.gather(*tasks)
if not results:
raise EmptyDataError()
return results
@staticmethod
def transform_data(
query: IntrinioKeyMetricsQueryParams, data: List[Dict], **kwargs: Any
) -> IntrinioKeyMetricsData:
"""Return the transformed data."""
return IntrinioKeyMetricsData.model_validate(data)
query: IntrinioKeyMetricsQueryParams,
data: List[Dict],
**kwargs: Any,
) -> List[IntrinioKeyMetricsData]:
"""Validate and transform the data."""
# Sort the results by the order of the symbols in the query.
symbols = query.symbol.split(",")
data = sorted(
data,
key=(
lambda item: (
symbols.index(item["symbol"])
if item["symbol"] in symbols
else len(symbols)
)
),
)
results: List[IntrinioKeyMetricsData] = []
for item in data:
if item.get("marketcap") is None or isinstance(item.get("marketcap"), dict):
warn(f"Symbol Error: No data found for {item.get('symbol')}")
continue
for key, value in item.copy().items():
# A bad response in a field will return a dict here, so we remove it.
if isinstance(value, dict):
_ = item.pop(key)
results.append(IntrinioKeyMetricsData.model_validate(item))
return results

View File

@@ -2,9 +2,12 @@
# pylint: disable=unused-argument
import asyncio
import warnings
from datetime import datetime
from datetime import (
date as dateType,
datetime,
)
from typing import Any, Dict, List, Literal, Optional, Union
from warnings import warn
import pytz
from openbb_core.provider.abstract.fetcher import Fetcher
@@ -24,8 +27,6 @@ from openbb_tmx.utils.helpers import (
from pandas import DataFrame, to_datetime
from pydantic import Field, field_validator
_warn = warnings.warn
class TmxEquityHistoricalQueryParams(EquityHistoricalQueryParams):
"""
@@ -112,6 +113,8 @@ class TmxEquityHistoricalData(EquityHistoricalData):
@classmethod
def date_validate(cls, v): # pylint: disable=W0221
"""Validate the datetime format."""
if isinstance(v, (datetime, dateType)):
return v if v.hour != 0 and v.minute != 0 and v.second != 0 else v.date() # type: ignore
try:
dt = datetime.strptime(v, "%Y-%m-%d %H:%M:%S%z")
return dt.astimezone(pytz.timezone("America/New_York"))
@@ -133,7 +136,7 @@ class TmxEquityHistoricalFetcher(
and adjustment != "splits_only"
and params.get("interval") not in ["day", "1d"]
):
_warn("Adjustment parameter is only available for daily data.")
warn("Adjustment parameter is only available for daily data.")
return TmxEquityHistoricalQueryParams(**params)
@staticmethod
@@ -179,7 +182,7 @@ class TmxEquityHistoricalFetcher(
results.extend(data)
if data == []:
_warn(f"No data found for {symbol}.")
warn(f"No data found for {symbol}.")
return results

View File

@@ -1,6 +1,6 @@
[tool.poetry]
name = "openbb-tmx"
version = "1.0.0"
version = "1.0.0b"
description = "Unofficial TMX data provider extension for the OpenBB Platform - Public Canadian markets data for Python and Fast API."
authors = ["OpenBB <hello@openbb.co>"]
readme = "README.md"

View File

@@ -20,6 +20,7 @@ tradier_provider = Provider(
], # account_type is either "sandbox" or "live"
fetcher_dict={
"EquityHistorical": TradierEquityHistoricalFetcher,
"EtfHistorical": TradierEquityHistoricalFetcher,
"EquityQuote": TradierEquityQuoteFetcher,
"EquitySearch": TradierEquitySearchFetcher,
"OptionsChains": TradierOptionsChainsFetcher,

View File

@@ -1,6 +1,6 @@
[tool.poetry]
name = "openbb-tradier"
version = "1.0.0"
version = "1.0.0b"
description = "Tradier Provider Extension for the OpenBB Platform"
authors = ["OpenBB <hello@openbb.co>"]
readme = "README.md"

View File

@@ -45,8 +45,8 @@ openbb-government-us = { version = "^1.1.2", optional = true }
openbb-nasdaq = { version = "^1.1.3", optional = true }
openbb-seeking-alpha = { version = "^1.1.2", optional = true }
openbb-stockgrid = { version = "^1.1.2", optional = true }
openbb-tmx = { version = "^1.0.0", optional = true }
openbb-tradier = { version = "^1.0.0", optional = true }
openbb-tmx = { version = "^1.0.0b", optional = true }
openbb-tradier = { version = "^1.0.0b", optional = true }
openbb-wsj = { version = "^1.1.2", optional = true }
openbb-charting = { version = "^2.0.0", optional = true }