[Feature]try to add fmp senate trading data api (#6957)

* [Feature] Add Government Trades

* [BugFix] Supplement the missing files

* [Enhancement] Fix branch goverment_trades

* [Enhancement] Fix branch government_trades

* Capture test cassettes.

* [Enhancement] Fix branch government_trades

* some style things and prevent page from exceeding 30

* black

* linter again

* else None instead of Other

---------

Co-authored-by: Danglewood <85772166+deeleeramone@users.noreply.github.com>
This commit is contained in:
joshuaBri
2024-12-11 00:06:04 +08:00
committed by GitHub
parent 7d7456a938
commit d704de554e
12 changed files with 871 additions and 1 deletions

View File

@@ -0,0 +1,47 @@
"""Government Trades Standard Model."""
from datetime import date as dateType
from typing import Literal, Optional
from openbb_core.provider.abstract.data import Data
from openbb_core.provider.abstract.query_params import QueryParams
from openbb_core.provider.utils.descriptions import (
DATA_DESCRIPTIONS,
QUERY_DESCRIPTIONS,
)
from pydantic import Field, NonNegativeInt, field_validator
class GovernmentTradesQueryParams(QueryParams):
"""Government Trades Query."""
symbol: Optional[str] = Field(
default=None, description=QUERY_DESCRIPTIONS.get("symbol", "")
)
chamber: Literal["house", "senate", "all"] = Field(
default="all", description="Government Chamber."
)
limit: Optional[NonNegativeInt] = Field(
default=100, description=QUERY_DESCRIPTIONS.get("limit", "")
)
@field_validator("symbol", mode="before", check_fields=False)
@classmethod
def to_upper(cls, v: str):
"""Convert field to uppercase."""
return v.upper() if v else None
class GovernmentTradesData(Data):
"""Government Trades data."""
symbol: Optional[str] = Field(
default=None, description=DATA_DESCRIPTIONS.get("symbol", "")
)
date: dateType = Field(description=DATA_DESCRIPTIONS.get("date", ""))
transaction_date: Optional[dateType] = Field(
default=None, description="Date of Transaction."
)
representative: Optional[str] = Field(
default=None, description="Name of Representative."
)

View File

@@ -2156,3 +2156,36 @@ def test_equity_discovery_latest_financial_reports(params, headers):
result = requests.get(url, headers=headers, timeout=10)
assert isinstance(result, requests.Response)
assert result.status_code == 200
@parametrize(
"params",
[
(
{
"chamber": "all",
"symbol": "AAPL",
"provider": "fmp",
"limit": None,
}
),
(
{
"symbol": None,
"chamber": "all",
"limit": 300,
"provider": "fmp",
}
),
],
)
@pytest.mark.integration
def test_equity_ownership_government_trades(params, headers):
"""Test the equity ownership government trades endpoint."""
params = {p: v for p, v in params.items() if v}
query_str = get_querystring(params, [])
url = f"http://0.0.0.0:8000/api/v1/equity/ownership/government_trades?{query_str}"
result = requests.get(url, headers=headers, timeout=10)
assert isinstance(result, requests.Response)
assert result.status_code == 200

View File

@@ -2017,3 +2017,35 @@ def test_equity_discovery_latest_financial_reports(params, obb):
assert result
assert isinstance(result, OBBject)
assert len(result.results) > 0
@parametrize(
"params",
[
(
{
"chamber": "all",
"symbol": "AAPL",
"provider": "fmp",
"limit": None,
}
),
(
{
"symbol": None,
"chamber": "all",
"limit": 300,
"provider": "fmp",
}
),
],
)
@pytest.mark.integration
def test_equity_ownership_government_trades(params, obb):
"""Test the equity ownership government trades endpoint."""
params = {p: v for p, v in params.items() if v}
result = obb.equity.ownership.government_trades(**params)
assert result
assert isinstance(result, OBBject)
assert len(result.results) > 0

View File

@@ -112,3 +112,22 @@ async def form_13f(
their investment strategy from competitors and the public.
"""
return await OBBject.from_query(Query(**locals()))
@router.command(
model="GovernmentTrades",
examples=[
APIEx(parameters={"symbol": "AAPL", "chamber": "all", "provider": "fmp"}),
APIEx(parameters={"limit": 500, "chamber": "all", "provider": "fmp"}),
],
)
async def government_trades(
cc: CommandContext,
provider_choices: ProviderChoices,
standard_params: StandardParams,
extra_params: ExtraParams,
) -> OBBject:
"""Obtain government transaction data, including data from the Senate
and the House of Representatives.
"""
return await OBBject.from_query(Query(**locals()))

View File

@@ -3,7 +3,7 @@
"info": {
"title": "OpenBB Platform (Python)",
"description": "Investment research for everyone, anywhere.",
"core": "1.3.6",
"core": "1.3.7",
"extensions": {
"openbb_core_extension": [
"commodity@1.2.6",
@@ -10271,6 +10271,7 @@
"dominica",
"dominican_republic",
"east_germany",
"eastern_caribbean_currency_union",
"ecuador",
"egypt",
"el_salvador",
@@ -10526,6 +10527,7 @@
"dominica",
"dominican_republic",
"east_germany",
"eastern_caribbean_currency_union",
"ecuador",
"egypt",
"el_salvador",
@@ -28322,6 +28324,175 @@
},
"model": "Form13FHR"
},
"/equity/ownership/government_trades": {
"deprecated": {
"flag": null,
"message": null
},
"description": "Obtain government transaction data, including data from the Senate\nand the House of Representatives.",
"examples": "\nExamples\n--------\n\n```python\nfrom openbb import obb\nobb.equity.ownership.government_trades(symbol='AAPL', chamber='all', provider='fmp')\nobb.equity.ownership.government_trades(limit=500, chamber='all', provider='fmp')\n```\n\n",
"parameters": {
"standard": [
{
"name": "symbol",
"type": "Union[str, List[str]]",
"description": "Symbol to get data for. Multiple items allowed for provider(s): fmp.",
"default": null,
"optional": true,
"choices": null
},
{
"name": "chamber",
"type": "Literal['house', 'senate', 'all']",
"description": "Government Chamber.",
"default": "all",
"optional": true,
"choices": null
},
{
"name": "limit",
"type": "Annotated[int, Ge(ge=0)]",
"description": "The number of data entries to return.",
"default": 100,
"optional": true,
"choices": null
}
],
"fmp": []
},
"returns": {
"OBBject": [
{
"name": "results",
"type": "List[GovernmentTrades]",
"description": "Serializable results."
},
{
"name": "provider",
"type": "Optional[Literal['fmp']]",
"description": "Provider name."
},
{
"name": "warnings",
"type": "Optional[List[Warning_]]",
"description": "List of warnings."
},
{
"name": "chart",
"type": "Optional[Chart]",
"description": "Chart object."
},
{
"name": "extra",
"type": "Dict[str, Any]",
"description": "Extra info."
}
]
},
"data": {
"standard": [
{
"name": "symbol",
"type": "str",
"description": "Symbol representing the entity requested in the data.",
"default": null,
"optional": true,
"choices": null
},
{
"name": "date",
"type": "Union[date, str]",
"description": "The date of the data.",
"default": "",
"optional": false,
"choices": null
},
{
"name": "transaction_date",
"type": "date",
"description": "Date of Transaction.",
"default": null,
"optional": true,
"choices": null
},
{
"name": "representative",
"type": "str",
"description": "Name of Representative.",
"default": null,
"optional": true,
"choices": null
}
],
"fmp": [
{
"name": "chamber",
"type": "Literal['house', 'senate']",
"description": "Government Chamber - House or Senate.",
"default": "",
"optional": false,
"choices": null
},
{
"name": "owner",
"type": "str",
"description": "Ownership status (e.g., Spouse, Joint).",
"default": null,
"optional": true,
"choices": null
},
{
"name": "asset_type",
"type": "str",
"description": "Type of asset involved in the transaction.",
"default": null,
"optional": true,
"choices": null
},
{
"name": "asset_description",
"type": "str",
"description": "Description of the asset.",
"default": null,
"optional": true,
"choices": null
},
{
"name": "transaction_type",
"type": "str",
"description": "Type of transaction (e.g., Sale, Purchase).",
"default": null,
"optional": true,
"choices": null
},
{
"name": "amount",
"type": "str",
"description": "Transaction amount range.",
"default": null,
"optional": true,
"choices": null
},
{
"name": "comment",
"type": "str",
"description": "Additional comments on the transaction.",
"default": null,
"optional": true,
"choices": null
},
{
"name": "url",
"type": "str",
"description": "Link to the transaction document.",
"default": null,
"optional": true,
"choices": null
}
]
},
"model": "GovernmentTrades"
},
"/equity/price/quote": {
"deprecated": {
"flag": null,

View File

@@ -1429,6 +1429,7 @@ class ROUTER_economy(Container):
"dominica",
"dominican_republic",
"east_germany",
"eastern_caribbean_currency_union",
"ecuador",
"egypt",
"el_salvador",
@@ -1682,6 +1683,7 @@ class ROUTER_economy(Container):
"dominica",
"dominican_republic",
"east_germany",
"eastern_caribbean_currency_union",
"ecuador",
"egypt",
"el_salvador",

View File

@@ -3,6 +3,7 @@
import datetime
from typing import List, Literal, Optional, Union
from annotated_types import Ge
from openbb_core.app.model.field import OpenBBField
from openbb_core.app.model.obbject import OBBject
from openbb_core.app.static.container import Container
@@ -14,6 +15,7 @@ from typing_extensions import Annotated
class ROUTER_equity_ownership(Container):
"""/equity/ownership
form_13f
government_trades
insider_trading
institutional
major_holders
@@ -148,6 +150,117 @@ class ROUTER_equity_ownership(Container):
)
)
@exception_handler
@validate
def government_trades(
self,
symbol: Annotated[
Union[str, None, List[Optional[str]]],
OpenBBField(
description="Symbol to get data for. Multiple comma separated items allowed for provider(s): fmp."
),
] = None,
chamber: Annotated[
Literal["house", "senate", "all"],
OpenBBField(description="Government Chamber."),
] = "all",
limit: Annotated[
Optional[Annotated[int, Ge(ge=0)]],
OpenBBField(description="The number of data entries to return."),
] = 100,
provider: Annotated[
Optional[Literal["fmp"]],
OpenBBField(
description="The provider to use, by default None. If None, the priority list configured in the settings is used. Default priority: fmp."
),
] = None,
**kwargs
) -> OBBject:
"""Obtain government transaction data, including data from the Senate
and the House of Representatives.
Parameters
----------
symbol : Union[str, None, List[Optional[str]]]
Symbol to get data for. Multiple comma separated items allowed for provider(s): fmp.
chamber : Literal['house', 'senate', 'all']
Government Chamber.
limit : Optional[Annotated[int, Ge(ge=0)]]
The number of data entries to return.
provider : Optional[Literal['fmp']]
The provider to use, by default None. If None, the priority list configured in the settings is used. Default priority: fmp.
Returns
-------
OBBject
results : List[GovernmentTrades]
Serializable results.
provider : Optional[Literal['fmp']]
Provider name.
warnings : Optional[List[Warning_]]
List of warnings.
chart : Optional[Chart]
Chart object.
extra : Dict[str, Any]
Extra info.
GovernmentTrades
----------------
symbol : Optional[str]
Symbol representing the entity requested in the data.
date : date
The date of the data.
transaction_date : Optional[date]
Date of Transaction.
representative : Optional[str]
Name of Representative.
chamber : Optional[Literal['house', 'senate']]
Government Chamber - House or Senate. (provider: fmp)
owner : Optional[str]
Ownership status (e.g., Spouse, Joint). (provider: fmp)
asset_type : Optional[str]
Type of asset involved in the transaction. (provider: fmp)
asset_description : Optional[str]
Description of the asset. (provider: fmp)
transaction_type : Optional[str]
Type of transaction (e.g., Sale, Purchase). (provider: fmp)
amount : Optional[str]
Transaction amount range. (provider: fmp)
comment : Optional[str]
Additional comments on the transaction. (provider: fmp)
url : Optional[str]
Link to the transaction document. (provider: fmp)
Examples
--------
>>> from openbb import obb
>>> obb.equity.ownership.government_trades(symbol='AAPL', chamber='all', provider='fmp')
>>> obb.equity.ownership.government_trades(limit=500, chamber='all', provider='fmp')
""" # noqa: E501
return self._run(
"/equity/ownership/government_trades",
**filter_inputs(
provider_choices={
"provider": self._get_provider(
provider,
"equity.ownership.government_trades",
("fmp",),
)
},
standard_params={
"symbol": symbol,
"chamber": chamber,
"limit": limit,
},
extra_params=kwargs,
info={
"symbol": {"fmp": {"multiple_items_allowed": True, "choices": None}}
},
)
)
@exception_handler
@validate
def insider_trading(

View File

@@ -41,6 +41,7 @@ from openbb_fmp.models.executive_compensation import FMPExecutiveCompensationFet
from openbb_fmp.models.financial_ratios import FMPFinancialRatiosFetcher
from openbb_fmp.models.forward_ebitda_estimates import FMPForwardEbitdaEstimatesFetcher
from openbb_fmp.models.forward_eps_estimates import FMPForwardEpsEstimatesFetcher
from openbb_fmp.models.government_trades import FMPGovernmentTradesFetcher
from openbb_fmp.models.historical_dividends import FMPHistoricalDividendsFetcher
from openbb_fmp.models.historical_employees import FMPHistoricalEmployeesFetcher
from openbb_fmp.models.historical_eps import FMPHistoricalEpsFetcher
@@ -137,6 +138,7 @@ stock market information (news, currencies, and stock prices).""",
"WorldNews": FMPWorldNewsFetcher,
"EtfHistorical": FMPEquityHistoricalFetcher,
"YieldCurve": FMPYieldCurveFetcher,
"GovernmentTrades": FMPGovernmentTradesFetcher,
},
repr_name="Financial Modeling Prep (FMP)",
deprecated_credentials={"API_KEY_FINANCIALMODELINGPREP": "fmp_api_key"},

View File

@@ -0,0 +1,226 @@
"""FMP Government Trades Model."""
# pylint: disable=unused-argument,too-many-locals
from typing import Any, Literal, Optional
from openbb_core.provider.abstract.fetcher import Fetcher
from openbb_core.provider.standard_models.government_trades import (
GovernmentTradesData,
GovernmentTradesQueryParams,
)
from pydantic import Field, model_validator
class FMPGovernmentTradesQueryParams(GovernmentTradesQueryParams):
"""Government Trades Query Parameters.
Source: https://site.financialmodelingprep.com/developer/docs#senate-trading
"""
__json_schema_extra__ = {"symbol": {"multiple_items_allowed": True}}
class FMPGovernmentTradesData(GovernmentTradesData):
"""Government Trades Data Model."""
__alias_dict__ = {
"symbol": "ticker",
"transaction_date": "transactionDate",
"representative": "office",
"url": "link",
"transaction_type": "type",
}
chamber: Literal["house", "senate"] = Field(
description="Government Chamber - House or Senate."
)
owner: Optional[str] = Field(
default=None, description="Ownership status (e.g., Spouse, Joint)."
)
asset_type: Optional[str] = Field(
default=None, description="Type of asset involved in the transaction."
)
asset_description: Optional[str] = Field(
default=None, description="Description of the asset."
)
transaction_type: Optional[str] = Field(
default=None, description="Type of transaction (e.g., Sale, Purchase)."
)
amount: Optional[str] = Field(default=None, description="Transaction amount range.")
comment: Optional[str] = Field(
default=None, description="Additional comments on the transaction."
)
url: Optional[str] = Field(
default=None, description="Link to the transaction document."
)
@model_validator(mode="before")
@classmethod
def _fill_missing(cls, values):
"""Fill missing information that can be identified."""
description = values.get("assetDescription", "").lower()
if not values.get("owner"):
values["owner"] = "Self"
if (values.get("ticker") or values.get("symbol")) and not values.get(
"assetType"
):
values["asset_type"] = "ETF" if "etf" in description else "Stock"
elif (
not values.get("ticker")
and not values.get("symbol")
and not values.get("assetType")
):
values["asset_type"] = (
"Treasury"
if "treasury" in description or "bill" in description
else (
"Bond"
if "%" in description
or "due" in description
or "pct" in description
else (
"Fund"
if "fund" in description
else ("ETF" if "etf" in description else None)
)
)
)
return values
class FMPGovernmentTradesFetcher(
Fetcher[
FMPGovernmentTradesQueryParams,
list[FMPGovernmentTradesData],
]
):
"""FMP Government Trades Fetcher."""
@staticmethod
def transform_query(params: dict[str, Any]) -> FMPGovernmentTradesQueryParams:
"""Transform the query params."""
return FMPGovernmentTradesQueryParams(**params)
@staticmethod
async def aextract_data(
query: FMPGovernmentTradesQueryParams,
credentials: Optional[dict[str, str]] = None,
**kwargs: Any,
) -> list[dict]:
"""Return the raw data from the Government Trades endpoint."""
# pylint: disable=import-outside-toplevel
import asyncio # noqa
import math
from openbb_core.app.model.abstract.error import OpenBBError
from openbb_core.provider.utils.errors import EmptyDataError
from openbb_core.provider.utils.helpers import amake_request
from openbb_fmp.utils.helpers import create_url, response_callback
from warnings import warn
symbols: list = []
if query.symbol:
symbols = query.symbol.split(",")
results: list[dict] = []
chamber_url_dict = {
"house": ["senate-disclosure"],
"senate": ["senate-trading"],
"all": ["senate-disclosure", "senate-trading"],
}
api_key = credentials.get("fmp_api_key") if credentials else ""
keys_to_remove = {
"district",
"capitalGainsOver200USD",
"disclosureYear",
"firstName",
"lastName",
}
keys_to_rename = {"dateRecieved": "date", "disclosureDate": "date"}
async def get_one(url):
"""Get data for one URL."""
result = await amake_request(
url, response_callback=response_callback, **kwargs
)
processed_list: list = []
for entry in result:
new_entry = {
keys_to_rename.get(k, k): v
for k, v in entry.items()
if k not in keys_to_remove
}
new_entry["chamber"] = "senate" if "senate-trading" in url else "house"
processed_list.append(new_entry)
if not processed_list or len(processed_list) == 0:
warn(f"No data found for {url.replace(api_key, 'API_KEY')}")
if processed_list:
results.extend(processed_list)
urls_list: list = []
try:
if symbols:
for symbol in symbols:
query.symbol = symbol
url = [
create_url(
4,
f"{i}",
api_key=api_key,
query=query,
exclude=["chamber", "limit"],
)
for i in chamber_url_dict[query.chamber]
]
urls_list.extend(url)
await asyncio.gather(*[get_one(url) for url in urls_list])
else:
pages = math.ceil(query.limit / 100)
if pages > 30:
warn(
f"Limit value - {query.limit} exceeds the available data for {query.chamber},"
" returning all available data."
)
pages = 31
for page in range(pages):
query.page = page
url = [
create_url(
4,
f"{i}-rss-feed",
api_key=api_key,
query=query,
exclude=["chamber", "limit"],
)
for i in chamber_url_dict[query.chamber]
]
urls_list.extend(url)
await asyncio.gather(*[get_one(url) for url in urls_list])
if not results:
raise EmptyDataError("No data returned for the given symbol.")
return results
except OpenBBError as e:
raise e from e
@staticmethod
def transform_data(
query: FMPGovernmentTradesQueryParams, data: list[dict], **kwargs: Any
) -> list[FMPGovernmentTradesData]:
"""Return the transformed data."""
return sorted(
[
FMPGovernmentTradesData(
**{k: v for k, v in d.items() if v and v != "--"}
)
for d in data
],
key=lambda x: x.date,
reverse=True,
)[: query.limit or len(data)]

View File

@@ -0,0 +1,105 @@
interactions:
- request:
body: null
headers:
Accept:
- application/json
Accept-Encoding:
- gzip, deflate
Connection:
- keep-alive
method: GET
uri: https://financialmodelingprep.com/api/v4/senate-trading-rss-feed?apikey=MOCK_API_KEY&page=0
response:
body:
string: !!binary |
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
headers:
Access-Control-Allow-Credentials:
- 'true'
Access-Control-Allow-Headers:
- X-Requested-With, content-type, auth-token, Authorization, stripe-signature,
APPS, publicauthkey, privateauthkey
Access-Control-Allow-Methods:
- GET, POST, OPTIONS
Access-Control-Allow-Origin:
- '*'
Access-Control-Max-Age:
- '3600'
Connection:
- keep-alive
Content-Encoding:
- gzip
Content-Type:
- application/json; charset=utf-8
Date:
- Fri, 06 Dec 2024 00:39:23 GMT
Etag:
- W/"b488-L685VcG067aMlCDn+EpysmcRcnQ"
Server:
- nginx/1.18.0 (Ubuntu)
Transfer-Encoding:
- chunked
Vary:
- Accept-Encoding
X-Frame-Options:
- SAMEORIGIN
X-Powered-By:
- Express
status:
code: 200
message: OK
version: 1

View File

@@ -0,0 +1,105 @@
interactions:
- request:
body: null
headers:
Accept:
- application/json
Accept-Encoding:
- gzip, deflate
Connection:
- keep-alive
method: GET
uri: https://financialmodelingprep.com/api/v4/senate-trading-rss-feed?apikey=MOCK_API_KEY&page=0
response:
body:
string: !!binary |
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
headers:
Access-Control-Allow-Credentials:
- 'true'
Access-Control-Allow-Headers:
- X-Requested-With, content-type, auth-token, Authorization, stripe-signature,
APPS, publicauthkey, privateauthkey
Access-Control-Allow-Methods:
- GET, POST, OPTIONS
Access-Control-Allow-Origin:
- '*'
Access-Control-Max-Age:
- '3600'
Connection:
- keep-alive
Content-Encoding:
- gzip
Content-Type:
- application/json; charset=utf-8
Date:
- Fri, 06 Dec 2024 00:37:01 GMT
Etag:
- W/"b485-xkA9fqZ3cTgtbLcCqSB0Ue9nHu4"
Server:
- nginx/1.18.0 (Ubuntu)
Transfer-Encoding:
- chunked
Vary:
- Accept-Encoding
X-Frame-Options:
- SAMEORIGIN
X-Powered-By:
- Express
status:
code: 200
message: OK
version: 1

View File

@@ -71,6 +71,7 @@ from openbb_fmp.models.share_statistics import FMPShareStatisticsFetcher
from openbb_fmp.models.treasury_rates import FMPTreasuryRatesFetcher
from openbb_fmp.models.world_news import FMPWorldNewsFetcher
from openbb_fmp.models.yield_curve import FMPYieldCurveFetcher
from openbb_fmp.models.government_trades import FMPGovernmentTradesFetcher
test_credentials = UserService().default_user_settings.credentials.model_dump(
mode="json"
@@ -762,3 +763,17 @@ def test_fmp_historical_market_cap_fetcher(credentials=test_credentials):
fetcher = FmpHistoricalMarketCapFetcher()
result = fetcher.test(params, credentials)
assert result is None
@pytest.mark.record_http
def test_fmp_government_trades_fetcher(credentials=test_credentials):
"""Test FMP government trades fetcher.
params limit only functions when there is no parameter symbol.
"""
params = {
"chamber": "senate",
"limit": 1,
}
fetcher = FMPGovernmentTradesFetcher()
result = fetcher.test(params, credentials)
assert result is None