Skip to content
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/compare-etf-countries: Add multi-ticker support to etf_countries #5670

Merged
merged 5 commits into from
Nov 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ def upper_symbol(cls, v: Union[str, List[str], Set[str]]):
class EtfCountriesData(Data):
"""ETF Countries Data."""

country: str = Field(description="The country of the exposure.")
weight: float = Field(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this PR. The only thing I am not sure about is eliminating any standardization of the "weight". Before this change we had at least one place where we explain what the number means (the field description) and now we don't. If different providers would return percentage and normalized weight the user would need to infer the meaning of that with no documentation.
Can we somehow leave the standardization of the weight? Is that possible based on your understanding?

description="Exposure of the ETF to the country in normalized percentage points."
country: str = Field(
description="The country of the exposure. Corresponding values are normalized percentage points."
)
51 changes: 39 additions & 12 deletions openbb_platform/providers/fmp/openbb_fmp/models/etf_countries.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from typing import Any, Dict, List, Optional

import pandas as pd
from openbb_fmp.utils.helpers import create_url, get_data_many
from openbb_provider.abstract.fetcher import Fetcher
from openbb_provider.standard_models.etf_countries import (
Expand All @@ -17,8 +18,6 @@ class FMPEtfCountriesQueryParams(EtfCountriesQueryParams):
class FMPEtfCountriesData(EtfCountriesData):
"""FMP ETF Country Weighting Data."""

__alias_dict__ = {"weight": "weightPercentage"}


class FMPEtfCountriesFetcher(
Fetcher[
Expand All @@ -40,24 +39,52 @@ def extract_data(
**kwargs: Any,
) -> List[Dict]:
"""Return the raw data from the FMP endpoint."""

api_key = credentials.get("fmp_api_key") if credentials else ""
symbols = (
query.symbol.split(",") if "," in query.symbol else [query.symbol.upper()]
)
results = {}

url = create_url(
version=3,
endpoint=f"etf-country-weightings/{query.symbol.upper()}",
api_key=api_key,
for symbol in symbols:
data = {}
url = create_url(
version=3,
endpoint=f"etf-country-weightings/{symbol}",
api_key=api_key,
)
result = get_data_many(url, **kwargs)
df = pd.DataFrame(result).set_index("country")
if len(df) > 0:
for i in df.index:
data.update(
{
i: float(df.loc[i]["weightPercentage"].replace("%", ""))
* 0.01
}
)
results.update({symbol: data})

output = (
pd.DataFrame(results)
.transpose()
.reset_index()
.fillna(value=0)
.replace(0, None)
.rename(columns={"index": "symbol"})
).transpose()
output.columns = output.loc["symbol"].to_list()
output = output.drop("symbol", axis=0).sort_values(
by=output.columns[0], ascending=False
)

return get_data_many(url, **kwargs)
return (
output.reset_index().rename(columns={"index": "country"}).to_dict("records")
)

@staticmethod
def transform_data(
query: FMPEtfCountriesQueryParams, data: List[Dict], **kwargs: Any
) -> List[FMPEtfCountriesData]:
"""Return the transformed data."""
for d in data:
if d["weightPercentage"] is not None and d["weightPercentage"].endswith(
"%"
):
d["weightPercentage"] = float(d["weightPercentage"][:-1]) / 100
return [FMPEtfCountriesData.model_validate(d) for d in data]
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,18 @@ interactions:
Accept:
- '*/*'
Accept-Encoding:
- gzip, deflate
- gzip, deflate, br
Connection:
- keep-alive
method: GET
uri: https://financialmodelingprep.com/api/v3/etf-country-weightings/MISL?apikey=MOCK_API_KEY
uri: https://financialmodelingprep.com/api/v3/etf-country-weightings/VTI?apikey=MOCK_API_KEY
response:
body:
string: "[\n {\n \"country\": \"United States\",\n \"weightPercentage\":
\"98.35%\"\n },\n {\n \"country\": \"Other\",\n \"weightPercentage\":
\"1.65%\"\n }\n]"
string: !!binary |
H4sIAAAAAAAAA6XSTQuCQBAG4Hu/YlnoJqKG9nGrTkZUEJ2iw6SDCrnCOItZ9N9TusYYdNjTvPPw
srvnkVLP7iilk8oaplYvlD6ZgjFVRwbGWjufeYNFlvMBKUHDkGEfnEfuzB/rLvByvkp7zpEkwXOn
kQTEhDcwqUT4bhhKxLEp+IE0xHjuRGTWYCAFWQgCSVjamgluxQDiTSVkhVTa/3qsoS3BqLjur0R8
366MKG2QamxFQVrfYf89hltIxtbesbxWlrIfiNHlDcqTD93xAgAA
headers:
Access-Control-Allow-Credentials:
- 'true'
Expand All @@ -29,16 +31,178 @@ interactions:
- '3600'
Connection:
- keep-alive
Content-Length:
- '141'
Content-Encoding:
- gzip
Content-Type:
- application/json; charset=utf-8
Date:
- Wed, 01 Nov 2023 13:27:52 GMT
- Sun, 05 Nov 2023 05:15:21 GMT
ETag:
- W/"8d-2VmJo306+5EGW4NWPOj2rTFnMzA"
- W/"2f1-z5/4g/ulC3y6Aw5o83OT94EFKKU"
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
- request:
body: null
headers:
Accept:
- '*/*'
Accept-Encoding:
- gzip, deflate, br
Connection:
- keep-alive
method: GET
uri: https://financialmodelingprep.com/api/v3/etf-country-weightings/QQQ?apikey=MOCK_API_KEY
response:
body:
string: !!binary |
H4sIAAAAAAAAA4XPwQrCMAyA4fueIhS8jTI3nc6beFdBPImHsIa2MCp0KTLEd3djV8kOOSV8/Hlk
AJ9xAFT7SoHjoA6g7sEzGbgxMvUqn/dv8tbxlWJLgdHSdNjUulmpcf/N/0Mx2YSDRBR6KxIn5wPK
QC0CZ2JHscNgxFcKvd+JHRjQLIRsSkm4TB0yUK0l4Jh6jtj5hYqympHs+QNActKX3gEAAA==
headers:
Access-Control-Allow-Credentials:
- 'true'
Access-Control-Allow-Headers:
- X-Requested-With, content-type, auth-token, Authorization, stripe-signature,
APPS
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:
- Sun, 05 Nov 2023 05:15:21 GMT
ETag:
- W/"1de-u/Rt0fxDvduVLpiU+/1mhnLcZeE"
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
- request:
body: null
headers:
Accept:
- '*/*'
Accept-Encoding:
- gzip, deflate, br
Connection:
- keep-alive
method: GET
uri: https://financialmodelingprep.com/api/v3/etf-country-weightings/VOO?apikey=MOCK_API_KEY
response:
body:
string: !!binary |
H4sIAAAAAAAAA4XQPQvCQAwG4L2/Ihy4leOqpX6Mbi4qFCdxOHqhPagnpClFxf/uHV0lDpne5CHJ
NQN4xwJQzWMMTE+1A3UJntFBzZZxUPmcT+jbjs9IDQa2LabG7XqhYvrJfzIHwt4GJwGFrjYSceIO
SQKMrqT5evL8Qvq3htHlSmL2SPfRWZkoROKI6ZS0iPjRyJTiTwey2MuCWc5CdvsCtZKIl98BAAA=
headers:
Access-Control-Allow-Credentials:
- 'true'
Access-Control-Allow-Headers:
- X-Requested-With, content-type, auth-token, Authorization, stripe-signature,
APPS
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:
- Sun, 05 Nov 2023 05:15:22 GMT
ETag:
- W/"1df-OEmfqekeHOVvwG/zgq/j7mJKmsQ"
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
- request:
body: null
headers:
Accept:
- '*/*'
Accept-Encoding:
- gzip, deflate, br
Connection:
- keep-alive
method: GET
uri: https://financialmodelingprep.com/api/v3/etf-country-weightings/IWM?apikey=MOCK_API_KEY
response:
body:
string: !!binary |
H4sIAAAAAAAAA6XTTUvDQBAG4Ht/xbLQWwlJa9R6jNAS8aNQPImHaTIkgWQik100iv/dBPEm7woe
9rTLw7vz8bQw5mM6xtii9+J0tFfG5sotSWlX3zev3FS1O7AWLI4qnp/EUbpZ2un+c/Ur8SiN49Ic
HTkeELRNo80aSdc0diQmH+ZIkJoynSEpY+18SYhIovMEEQ+uZsUZtrAud71Q0WMhuUTCsZGKXnrl
AAJLsVOSIiQgIB+UuP1PhEzpvQkI8QWeDCHczjhap0i49W/cnXqvVSAHVDKqqaPAaMawGIfpK13g
KzEcrL1nlYHHgAFX7YY1LMCx2DcnpdYR3hEk3PO8Yn/Y9Z+eLJ6/ANaC2CnFBAAA
headers:
Access-Control-Allow-Credentials:
- 'true'
Access-Control-Allow-Headers:
- X-Requested-With, content-type, auth-token, Authorization, stripe-signature,
APPS
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:
- Sun, 05 Nov 2023 05:15:22 GMT
ETag:
- W/"4c5-b5x+LUJL2Tk17h5pypIQmPJe3ag"
Server:
- nginx/1.18.0 (Ubuntu)
Transfer-Encoding:
- chunked
Vary:
- Accept-Encoding
X-Frame-Options:
- SAMEORIGIN
X-Powered-By:
Expand Down
2 changes: 1 addition & 1 deletion openbb_platform/providers/fmp/tests/test_fmp_fetchers.py
Original file line number Diff line number Diff line change
Expand Up @@ -542,7 +542,7 @@ def test_fmp_price_performance_fetcher(credentials=test_credentials):

@pytest.mark.record_http
def test_fmp_etf_countries_fetcher(credentials=test_credentials):
params = {"symbol": "MISL"}
params = {"symbol": "VTI,QQQ,VOO,IWM"}

fetcher = FMPEtfCountriesFetcher()
result = fetcher.test(params, credentials)
Expand Down
Loading