-
Notifications
You must be signed in to change notification settings - Fork 3.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Feature] Add the 13f data directly from the FMP #6956
base: develop
Are you sure you want to change the base?
Changes from 14 commits
7a545ec
1769dd3
4ca868e
5975494
82e7c32
3175e28
0f21e07
3d8d366
1484a18
c40ec7e
6a6a620
56171f1
3169067
3a4d203
ade080d
bac8206
899777d
534a6c4
14f213b
ba5c270
e5b6b3a
18b3632
2b47779
9b4899f
0ed8c83
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
"""Form 13f Model.""" | ||
|
||
import asyncio | ||
from datetime import date as dateType, datetime | ||
from typing import Any, Dict, List, Optional | ||
from warnings import warn | ||
|
||
from openbb_core.app.model.abstract.error import OpenBBError | ||
from openbb_fmp.models.equity_profile import FMPEquityProfileFetcher | ||
from pydantic import Field | ||
|
||
from openbb_core.provider.abstract.fetcher import Fetcher | ||
from openbb_core.provider.standard_models.form_13FHR import ( | ||
Form13FHRData, | ||
Form13FHRQueryParams, | ||
) | ||
from openbb_core.provider.utils.descriptions import DATA_DESCRIPTIONS | ||
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 | ||
|
||
|
||
class FMPForm13FHRQueryParams(Form13FHRQueryParams): | ||
"""Form 13f Query Parameters. | ||
|
||
Source: https://financialmodelingprep.com/api/v3/form-thirteen/0001388838?date=2021-09-30 | ||
""" | ||
|
||
|
||
class FMPForm13FHRData(Form13FHRData): | ||
"""Form13 FHR Data Model.""" | ||
|
||
__alias_dict__ = { | ||
"period_ending": "date", | ||
"issuer": "nameOfIssuer", | ||
"principal_amount": "shares", | ||
"asset_class": "titleOfClass", | ||
"filling_date": "fillingDate", | ||
"accepted_date": "acceptedDate", | ||
"symbol": "tickercusip", | ||
"final_link": "finalLink", | ||
} | ||
symbol: Optional[str] = Field( | ||
default=None, description=DATA_DESCRIPTIONS.get("symbol", "") | ||
) | ||
filling_date: Optional[dateType] = Field( | ||
default=None, description="Date when the filing was submitted to the SEC." | ||
) | ||
accepted_date: Optional[dateType] = Field( | ||
default=None, description="Date when the filing was accepted by the SEC." | ||
) | ||
link: Optional[str] = Field( | ||
default=None, description="URL link to the SEC filing on the SEC website." | ||
) | ||
final_link: Optional[str] = Field( | ||
default=None, | ||
description="URL link to the XML information table of the SEC filing.", | ||
) | ||
|
||
|
||
class FMPForm13FHRFetcher( | ||
Fetcher[ | ||
FMPForm13FHRQueryParams, | ||
List[FMPForm13FHRData], | ||
] | ||
): | ||
"""Fetches and transforms data from the Form 13f endpoints.""" | ||
|
||
@staticmethod | ||
def transform_query(params: Dict[str, Any]) -> FMPForm13FHRQueryParams: | ||
"""Transform the query params.""" | ||
return FMPForm13FHRQueryParams(**params) | ||
|
||
@staticmethod | ||
async def aextract_data( | ||
query: FMPForm13FHRQueryParams, | ||
credentials: Optional[Dict[str, str]] = None, | ||
**kwargs: Any, | ||
) -> List[Dict]: | ||
"""Return the raw data from the Form 13f endpoint.""" | ||
api_key = credentials.get("fmp_api_key") if credentials else "" | ||
if query.symbol.isnumeric(): | ||
cik = query.symbol | ||
else: | ||
try: | ||
profile = await FMPEquityProfileFetcher.fetch_data({"symbol": query.symbol}, {"fmp_api_key": api_key}) | ||
cik = getattr(profile[0], "cik", "") | ||
if not cik: | ||
raise OpenBBError(f"Invalid symbol -> {query.symbol}") | ||
except OpenBBError as e: | ||
raise OpenBBError(f"Invalid symbol -> {query.symbol} -> {e}") | ||
date_url = create_url( | ||
3, | ||
f"form-thirteen-date/{cik}", | ||
api_key, | ||
query, | ||
exclude=["symbol", "limit"], | ||
) | ||
dates = await amake_request(date_url, response_callback=response_callback, **kwargs) | ||
if not dates: | ||
raise EmptyDataError("No data returned for the given symbol.") | ||
if not dates or len(dates) == 0: | ||
warn(f"Symbol Error: No data found for symbol {cik}") | ||
results: List[Dict] = [] | ||
|
||
async def get_one(date): | ||
"""Get data for the given date.""" | ||
date = datetime.strptime(date, "%Y-%m-%d").date() | ||
query.date = date | ||
url = create_url( | ||
3, | ||
f"form-thirteen/{cik}", | ||
api_key, | ||
query, | ||
exclude=["symbol", "limit"], | ||
) | ||
result = await amake_request(url, response_callback=response_callback, **kwargs) | ||
if not result or len(result) == 0: | ||
warn(f"Symbol Error: No data found for symbol {symbol}") | ||
joshuaBri marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if result: | ||
results.extend(result) | ||
|
||
await asyncio.gather(*[get_one(date) for date in dates]) | ||
joshuaBri marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
if not results: | ||
raise EmptyDataError("No data returned for the given symbol.") | ||
if query.limit: | ||
return results[:query.limit] | ||
return results | ||
|
||
@staticmethod | ||
def transform_data( | ||
query: FMPForm13FHRQueryParams, data: List[Dict], **kwargs: Any | ||
) -> List[FMPForm13FHRData]: | ||
"""Return the transformed data.""" | ||
if query.symbol.isnumeric(): | ||
[d.pop("cik", None) for d in data] | ||
return [FMPForm13FHRData(**d) for d in data] | ||
joshuaBri marked this conversation as resolved.
Show resolved
Hide resolved
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,7 @@ | |
|
||
import pytest | ||
from openbb_core.app.service.user_service import UserService | ||
from openbb_fmp.models.form_13FHR import FMPForm13FHRFetcher | ||
from openbb_fmp.models.analyst_estimates import FMPAnalystEstimatesFetcher | ||
from openbb_fmp.models.available_indices import FMPAvailableIndicesFetcher | ||
from openbb_fmp.models.balance_sheet import FMPBalanceSheetFetcher | ||
|
@@ -765,6 +766,19 @@ def test_fmp_historical_market_cap_fetcher(credentials=test_credentials): | |
assert result is None | ||
|
||
|
||
|
||
@pytest.mark.record_http | ||
def test_fmp_form_13f_fetcher(credentials=test_credentials): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In, |
||
"""Test FMP form 13f fetcher.""" | ||
params = { | ||
"symbol": "NVDA", | ||
"limit": 1, | ||
} | ||
fetcher = FMPForm13FHRFetcher() | ||
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. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't need to warn here because we are raising in the statement above.