Upload 111 files
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- openbb_platform/providers/README.md +29 -0
- openbb_platform/providers/__init__.py +1 -0
- openbb_platform/providers/wsj/README.md +13 -0
- openbb_platform/providers/wsj/__init__.py +1 -0
- openbb_platform/providers/wsj/openbb_wsj/__init__.py +25 -0
- openbb_platform/providers/wsj/openbb_wsj/models/active.py +104 -0
- openbb_platform/providers/wsj/openbb_wsj/models/gainers.py +110 -0
- openbb_platform/providers/wsj/openbb_wsj/models/losers.py +109 -0
- openbb_platform/providers/wsj/poetry.lock +0 -0
- openbb_platform/providers/wsj/pyproject.toml +19 -0
- openbb_platform/providers/wsj/tests/record/http/test_wsj_fetchers/test_wsj_active_fetcher_urllib3_v1.yaml +139 -0
- openbb_platform/providers/wsj/tests/record/http/test_wsj_fetchers/test_wsj_active_fetcher_urllib3_v2.yaml +125 -0
- openbb_platform/providers/wsj/tests/record/http/test_wsj_fetchers/test_wsj_gainers_fetcher_urllib3_v1.yaml +139 -0
- openbb_platform/providers/wsj/tests/record/http/test_wsj_fetchers/test_wsj_gainers_fetcher_urllib3_v2.yaml +125 -0
- openbb_platform/providers/wsj/tests/record/http/test_wsj_fetchers/test_wsj_losers_fetcher_urllib3_v1.yaml +141 -0
- openbb_platform/providers/wsj/tests/record/http/test_wsj_fetchers/test_wsj_losers_fetcher_urllib3_v2.yaml +125 -0
- openbb_platform/providers/wsj/tests/test_wsj_fetchers.py +52 -0
- openbb_platform/providers/yfinance/README.md +13 -0
- openbb_platform/providers/yfinance/__init__.py +1 -0
- openbb_platform/providers/yfinance/openbb_yfinance/__init__.py +79 -0
- openbb_platform/providers/yfinance/openbb_yfinance/models/__init__.py +1 -0
- openbb_platform/providers/yfinance/openbb_yfinance/models/active.py +90 -0
- openbb_platform/providers/yfinance/openbb_yfinance/models/aggressive_small_caps.py +89 -0
- openbb_platform/providers/yfinance/openbb_yfinance/models/available_indices.py +68 -0
- openbb_platform/providers/yfinance/openbb_yfinance/models/balance_sheet.py +124 -0
- openbb_platform/providers/yfinance/openbb_yfinance/models/cash_flow.py +123 -0
- openbb_platform/providers/yfinance/openbb_yfinance/models/company_news.py +119 -0
- openbb_platform/providers/yfinance/openbb_yfinance/models/crypto_historical.py +137 -0
- openbb_platform/providers/yfinance/openbb_yfinance/models/currency_historical.py +135 -0
- openbb_platform/providers/yfinance/openbb_yfinance/models/equity_historical.py +193 -0
- openbb_platform/providers/yfinance/openbb_yfinance/models/equity_profile.py +211 -0
- openbb_platform/providers/yfinance/openbb_yfinance/models/equity_quote.py +146 -0
- openbb_platform/providers/yfinance/openbb_yfinance/models/equity_screener.py +250 -0
- openbb_platform/providers/yfinance/openbb_yfinance/models/etf_info.py +332 -0
- openbb_platform/providers/yfinance/openbb_yfinance/models/futures_curve.py +69 -0
- openbb_platform/providers/yfinance/openbb_yfinance/models/futures_historical.py +151 -0
- openbb_platform/providers/yfinance/openbb_yfinance/models/gainers.py +90 -0
- openbb_platform/providers/yfinance/openbb_yfinance/models/growth_tech_equities.py +96 -0
- openbb_platform/providers/yfinance/openbb_yfinance/models/historical_dividends.py +78 -0
- openbb_platform/providers/yfinance/openbb_yfinance/models/income_statement.py +125 -0
- openbb_platform/providers/yfinance/openbb_yfinance/models/index_historical.py +154 -0
- openbb_platform/providers/yfinance/openbb_yfinance/models/key_executives.py +95 -0
- openbb_platform/providers/yfinance/openbb_yfinance/models/key_metrics.py +339 -0
- openbb_platform/providers/yfinance/openbb_yfinance/models/losers.py +90 -0
- openbb_platform/providers/yfinance/openbb_yfinance/models/options_chains.py +191 -0
- openbb_platform/providers/yfinance/openbb_yfinance/models/price_target_consensus.py +156 -0
- openbb_platform/providers/yfinance/openbb_yfinance/models/share_statistics.py +196 -0
- openbb_platform/providers/yfinance/openbb_yfinance/models/undervalued_growth_equities.py +100 -0
- openbb_platform/providers/yfinance/openbb_yfinance/models/undervalued_large_caps.py +96 -0
- openbb_platform/providers/yfinance/openbb_yfinance/py.typed +0 -0
openbb_platform/providers/README.md
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Providers
|
| 2 |
+
|
| 3 |
+
In this folder you can find the providers that were created or are supported by OpenBB.
|
| 4 |
+
|
| 5 |
+
## Recommended structure
|
| 6 |
+
|
| 7 |
+
Every provider is located within a directory, with the following structure:
|
| 8 |
+
|
| 9 |
+
```{.bash}
|
| 10 |
+
openbb_platform
|
| 11 |
+
└───providers
|
| 12 |
+
└───<provider_name>
|
| 13 |
+
| README.md
|
| 14 |
+
│ pyproject.toml
|
| 15 |
+
│ poetry.lock
|
| 16 |
+
|───tests
|
| 17 |
+
└───openbb_<provider_name>
|
| 18 |
+
│ __init__.py
|
| 19 |
+
|───models
|
| 20 |
+
| |───<some model>.py
|
| 21 |
+
| └───...
|
| 22 |
+
└───utils
|
| 23 |
+
|───<some helper>.py
|
| 24 |
+
└───...
|
| 25 |
+
```
|
| 26 |
+
|
| 27 |
+
The models define the data structures that are used to query the provider endpoints and store the response data.
|
| 28 |
+
|
| 29 |
+
See [CONTRIBUTING file](../CONTRIBUTING.md) for more details
|
openbb_platform/providers/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
"""OpenBB Platform Providers."""
|
openbb_platform/providers/wsj/README.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# OpenBB Wall St Journal Provider
|
| 2 |
+
|
| 3 |
+
This extension integrates the [WSJ](https://wsj.com/) data provider into the OpenBB Platform.
|
| 4 |
+
|
| 5 |
+
## Installation
|
| 6 |
+
|
| 7 |
+
To install the extension:
|
| 8 |
+
|
| 9 |
+
```bash
|
| 10 |
+
pip install openbb-wsj
|
| 11 |
+
```
|
| 12 |
+
|
| 13 |
+
Documentation available [here](https://docs.openbb.co/platform/developer_guide/contributing).
|
openbb_platform/providers/wsj/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
"""WSJ provider."""
|
openbb_platform/providers/wsj/openbb_wsj/__init__.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""WSJ provider module."""
|
| 2 |
+
|
| 3 |
+
from openbb_core.provider.abstract.provider import Provider
|
| 4 |
+
from openbb_wsj.models.active import WSJActiveFetcher
|
| 5 |
+
from openbb_wsj.models.gainers import WSJGainersFetcher
|
| 6 |
+
from openbb_wsj.models.losers import WSJLosersFetcher
|
| 7 |
+
|
| 8 |
+
wsj_provider = Provider(
|
| 9 |
+
name="wsj",
|
| 10 |
+
website="https://www.wsj.com",
|
| 11 |
+
description="""WSJ (Wall Street Journal) is a business-focused, English-language
|
| 12 |
+
international daily newspaper based in New York City. The Journal is published six
|
| 13 |
+
days a week by Dow Jones & Company, a division of News Corp, along with its Asian
|
| 14 |
+
and European editions. The newspaper is published in the broadsheet format and
|
| 15 |
+
online. The Journal has been printed continuously since its inception on
|
| 16 |
+
July 8, 1889, by Charles Dow, Edward Jones, and Charles Bergstresser.
|
| 17 |
+
The WSJ is the largest newspaper in the United States, by circulation.
|
| 18 |
+
""",
|
| 19 |
+
fetcher_dict={
|
| 20 |
+
"ETFGainers": WSJGainersFetcher,
|
| 21 |
+
"ETFLosers": WSJLosersFetcher,
|
| 22 |
+
"ETFActive": WSJActiveFetcher,
|
| 23 |
+
},
|
| 24 |
+
repr_name="Wall Street Journal (WSJ)",
|
| 25 |
+
)
|
openbb_platform/providers/wsj/openbb_wsj/models/active.py
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""WSJ Asset Performance Active Model."""
|
| 2 |
+
|
| 3 |
+
# pylint: disable=unused-argument
|
| 4 |
+
|
| 5 |
+
from datetime import datetime
|
| 6 |
+
from typing import Any, Dict, List, Optional
|
| 7 |
+
|
| 8 |
+
from openbb_core.provider.abstract.fetcher import Fetcher
|
| 9 |
+
from openbb_core.provider.standard_models.etf_performance import (
|
| 10 |
+
ETFPerformanceData,
|
| 11 |
+
ETFPerformanceQueryParams,
|
| 12 |
+
)
|
| 13 |
+
from pydantic import Field, field_validator
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
class WSJActiveQueryParams(ETFPerformanceQueryParams):
|
| 17 |
+
"""WSJ Asset Performance Active Query.
|
| 18 |
+
|
| 19 |
+
Source: https://www.wsj.com/market-data/mutualfunds-etfs/etfmovers
|
| 20 |
+
"""
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
class WSJActiveData(ETFPerformanceData):
|
| 24 |
+
"""WSJ Asset Performance Active Data."""
|
| 25 |
+
|
| 26 |
+
__alias_dict__ = {
|
| 27 |
+
"symbol": "ticker",
|
| 28 |
+
"last_price": "lastPrice",
|
| 29 |
+
"percent_change": "percentChange",
|
| 30 |
+
"net_change": "priceChange",
|
| 31 |
+
"date": "timestamp",
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
country: str = Field(
|
| 35 |
+
description="Country of the entity.",
|
| 36 |
+
)
|
| 37 |
+
mantissa: int = Field(
|
| 38 |
+
description="Mantissa.",
|
| 39 |
+
)
|
| 40 |
+
type: str = Field(
|
| 41 |
+
description="Type of the entity.",
|
| 42 |
+
)
|
| 43 |
+
formatted_price: str = Field(
|
| 44 |
+
description="Formatted price.",
|
| 45 |
+
)
|
| 46 |
+
formatted_volume: str = Field(
|
| 47 |
+
description="Formatted volume.",
|
| 48 |
+
)
|
| 49 |
+
formatted_price_change: str = Field(
|
| 50 |
+
description="Formatted price change.",
|
| 51 |
+
)
|
| 52 |
+
formatted_percent_change: str = Field(
|
| 53 |
+
description="Formatted percent change.",
|
| 54 |
+
)
|
| 55 |
+
url: str = Field(
|
| 56 |
+
description="The source url.",
|
| 57 |
+
)
|
| 58 |
+
|
| 59 |
+
@field_validator("date", mode="before", check_fields=False)
|
| 60 |
+
def date_validate(cls, v): # pylint: disable=E0213
|
| 61 |
+
"""Return the datetime object from the date string."""
|
| 62 |
+
return datetime.strptime(v[:10], "%Y-%m-%d").date()
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
class WSJActiveFetcher(Fetcher[WSJActiveQueryParams, List[WSJActiveData]]):
|
| 66 |
+
"""Transform the query, extract and transform the data from the WSJ endpoints."""
|
| 67 |
+
|
| 68 |
+
@staticmethod
|
| 69 |
+
def transform_query(params: Dict[str, Any]) -> WSJActiveQueryParams:
|
| 70 |
+
"""Transform query params."""
|
| 71 |
+
return WSJActiveQueryParams(**params)
|
| 72 |
+
|
| 73 |
+
@staticmethod
|
| 74 |
+
def extract_data(
|
| 75 |
+
query: WSJActiveQueryParams,
|
| 76 |
+
credentials: Optional[Dict[str, str]],
|
| 77 |
+
**kwargs: Any,
|
| 78 |
+
) -> List[Dict]:
|
| 79 |
+
"""Get data from WSJ."""
|
| 80 |
+
# pylint: disable=import-outside-toplevel
|
| 81 |
+
from openbb_core.provider.utils.helpers import make_request
|
| 82 |
+
|
| 83 |
+
url = (
|
| 84 |
+
"https://www.wsj.com/market-data/mutualfunds-etfs/etfmovers?id=%7B%22application"
|
| 85 |
+
"%22%3A%22WSJ%22%2C%22etfMover%22%3A%22most_active%22%2C%22count%22%3A25%7D&type="
|
| 86 |
+
"mdc_etfmovers"
|
| 87 |
+
)
|
| 88 |
+
data = make_request(url).json()
|
| 89 |
+
|
| 90 |
+
return data["data"]["instruments"]
|
| 91 |
+
|
| 92 |
+
@staticmethod
|
| 93 |
+
def transform_data(
|
| 94 |
+
query: ETFPerformanceQueryParams,
|
| 95 |
+
data: List[Dict],
|
| 96 |
+
**kwargs: Any,
|
| 97 |
+
) -> List[WSJActiveData]:
|
| 98 |
+
"""Transform data."""
|
| 99 |
+
data = data[: query.limit]
|
| 100 |
+
data = sorted(
|
| 101 |
+
data,
|
| 102 |
+
key=lambda x: x["volume"] if query.sort == "asc" else -x["volume"],
|
| 103 |
+
)
|
| 104 |
+
return [WSJActiveData.model_validate(d) for d in data]
|
openbb_platform/providers/wsj/openbb_wsj/models/gainers.py
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""WSJ Asset Performance Gainers Model."""
|
| 2 |
+
|
| 3 |
+
# pylint: disable=unused-argument
|
| 4 |
+
|
| 5 |
+
from datetime import datetime
|
| 6 |
+
from typing import Any, Dict, List, Optional
|
| 7 |
+
|
| 8 |
+
from openbb_core.provider.abstract.fetcher import Fetcher
|
| 9 |
+
from openbb_core.provider.standard_models.etf_performance import (
|
| 10 |
+
ETFPerformanceData,
|
| 11 |
+
ETFPerformanceQueryParams,
|
| 12 |
+
)
|
| 13 |
+
from pydantic import Field, field_validator
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
class WSJGainersQueryParams(ETFPerformanceQueryParams):
|
| 17 |
+
"""WSJ Asset Performance Gainers Query.
|
| 18 |
+
|
| 19 |
+
Source: https://www.wsj.com/market-data/mutualfunds-etfs/etfmovers
|
| 20 |
+
"""
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
class WSJGainersData(ETFPerformanceData):
|
| 24 |
+
"""WSJ Asset Performance Gainers Data."""
|
| 25 |
+
|
| 26 |
+
__alias_dict__ = {
|
| 27 |
+
"symbol": "ticker",
|
| 28 |
+
"last_price": "lastPrice",
|
| 29 |
+
"percent_change": "percentChange",
|
| 30 |
+
"net_change": "priceChange",
|
| 31 |
+
"date": "timestamp",
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
bluegrass_channel: Optional[str] = Field(
|
| 35 |
+
description="Bluegrass channel.", default=None
|
| 36 |
+
)
|
| 37 |
+
country: str = Field(
|
| 38 |
+
description="Country of the entity.",
|
| 39 |
+
)
|
| 40 |
+
mantissa: int = Field(
|
| 41 |
+
description="Mantissa.",
|
| 42 |
+
)
|
| 43 |
+
type: str = Field(
|
| 44 |
+
description="Type of the entity.",
|
| 45 |
+
)
|
| 46 |
+
formatted_price: str = Field(
|
| 47 |
+
description="Formatted price.",
|
| 48 |
+
)
|
| 49 |
+
formatted_volume: str = Field(
|
| 50 |
+
description="Formatted volume.",
|
| 51 |
+
)
|
| 52 |
+
formatted_price_change: str = Field(
|
| 53 |
+
description="Formatted price change.",
|
| 54 |
+
)
|
| 55 |
+
formatted_percent_change: str = Field(
|
| 56 |
+
description="Formatted percent change.",
|
| 57 |
+
)
|
| 58 |
+
url: str = Field(
|
| 59 |
+
description="The source url.",
|
| 60 |
+
)
|
| 61 |
+
|
| 62 |
+
@field_validator("date", mode="before", check_fields=False)
|
| 63 |
+
def date_validate(cls, v): # pylint: disable=E0213
|
| 64 |
+
"""Return the datetime object from the date string."""
|
| 65 |
+
return datetime.strptime(v[:10], "%Y-%m-%d").date()
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
class WSJGainersFetcher(Fetcher[WSJGainersQueryParams, List[WSJGainersData]]):
|
| 69 |
+
"""Transform the query, extract and transform the data from the WSJ endpoints."""
|
| 70 |
+
|
| 71 |
+
# pylint: disable=unused-argument
|
| 72 |
+
@staticmethod
|
| 73 |
+
def transform_query(params: Dict[str, Any]) -> WSJGainersQueryParams:
|
| 74 |
+
"""Transform query params."""
|
| 75 |
+
return WSJGainersQueryParams(**params)
|
| 76 |
+
|
| 77 |
+
@staticmethod
|
| 78 |
+
def extract_data(
|
| 79 |
+
query: WSJGainersQueryParams,
|
| 80 |
+
credentials: Optional[Dict[str, str]],
|
| 81 |
+
**kwargs: Any,
|
| 82 |
+
) -> List[Dict]:
|
| 83 |
+
"""Get data from WSJ."""
|
| 84 |
+
# pylint: disable=import-outside-toplevel
|
| 85 |
+
from openbb_core.provider.utils.helpers import make_request
|
| 86 |
+
|
| 87 |
+
url = (
|
| 88 |
+
"https://www.wsj.com/market-data/mutualfunds-etfs/etfmovers?id=%7B%22application"
|
| 89 |
+
"%22%3A%22WSJ%22%2C%22etfMover%22%3A%22leaders%22%2C%22count%22%3A25%7D&type="
|
| 90 |
+
"mdc_etfmovers"
|
| 91 |
+
)
|
| 92 |
+
data = make_request(url).json()
|
| 93 |
+
|
| 94 |
+
return data["data"]["instruments"]
|
| 95 |
+
|
| 96 |
+
@staticmethod
|
| 97 |
+
def transform_data(
|
| 98 |
+
query: ETFPerformanceQueryParams,
|
| 99 |
+
data: List[Dict],
|
| 100 |
+
**kwargs: Any,
|
| 101 |
+
) -> List[WSJGainersData]:
|
| 102 |
+
"""Transform data."""
|
| 103 |
+
data = data[: query.limit]
|
| 104 |
+
data = sorted(
|
| 105 |
+
data,
|
| 106 |
+
key=lambda x: (
|
| 107 |
+
x["percentChange"] if query.sort == "asc" else -x["percentChange"]
|
| 108 |
+
),
|
| 109 |
+
)
|
| 110 |
+
return [WSJGainersData.model_validate(d) for d in data]
|
openbb_platform/providers/wsj/openbb_wsj/models/losers.py
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""WSJ Asset Performance Losers Model."""
|
| 2 |
+
|
| 3 |
+
# pylint: disable=unused-argument
|
| 4 |
+
|
| 5 |
+
from datetime import datetime
|
| 6 |
+
from typing import Any, Dict, List, Optional
|
| 7 |
+
|
| 8 |
+
from openbb_core.provider.abstract.fetcher import Fetcher
|
| 9 |
+
from openbb_core.provider.standard_models.etf_performance import (
|
| 10 |
+
ETFPerformanceData,
|
| 11 |
+
ETFPerformanceQueryParams,
|
| 12 |
+
)
|
| 13 |
+
from pydantic import Field, field_validator
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
class WSJLosersQueryParams(ETFPerformanceQueryParams):
|
| 17 |
+
"""WSJ Asset Performance Losers Query.
|
| 18 |
+
|
| 19 |
+
Source: https://www.wsj.com/market-data/mutualfunds-etfs/etfmovers
|
| 20 |
+
"""
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
class WSJLosersData(ETFPerformanceData):
|
| 24 |
+
"""WSJ Asset Performance Losers Data."""
|
| 25 |
+
|
| 26 |
+
__alias_dict__ = {
|
| 27 |
+
"symbol": "ticker",
|
| 28 |
+
"last_price": "lastPrice",
|
| 29 |
+
"percent_change": "percentChange",
|
| 30 |
+
"net_change": "priceChange",
|
| 31 |
+
"date": "timestamp",
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
bluegrass_channel: Optional[str] = Field(
|
| 35 |
+
description="Bluegrass channel.", default=None
|
| 36 |
+
)
|
| 37 |
+
country: str = Field(
|
| 38 |
+
description="Country of the entity.",
|
| 39 |
+
)
|
| 40 |
+
mantissa: int = Field(
|
| 41 |
+
description="Mantissa.",
|
| 42 |
+
)
|
| 43 |
+
type: str = Field(
|
| 44 |
+
description="Type of the entity.",
|
| 45 |
+
)
|
| 46 |
+
formatted_price: str = Field(
|
| 47 |
+
description="Formatted price.",
|
| 48 |
+
)
|
| 49 |
+
formatted_volume: str = Field(
|
| 50 |
+
description="Formatted volume.",
|
| 51 |
+
)
|
| 52 |
+
formatted_price_change: str = Field(
|
| 53 |
+
description="Formatted price change.",
|
| 54 |
+
)
|
| 55 |
+
formatted_percent_change: str = Field(
|
| 56 |
+
description="Formatted percent change.",
|
| 57 |
+
)
|
| 58 |
+
url: str = Field(
|
| 59 |
+
description="The source url.",
|
| 60 |
+
)
|
| 61 |
+
|
| 62 |
+
@field_validator("date", mode="before", check_fields=False)
|
| 63 |
+
def date_validate(cls, v): # pylint: disable=E0213
|
| 64 |
+
"""Return the datetime object from the date string."""
|
| 65 |
+
return datetime.strptime(v[:10], "%Y-%m-%d").date()
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
class WSJLosersFetcher(Fetcher[WSJLosersQueryParams, List[WSJLosersData]]):
|
| 69 |
+
"""Transform the query, extract and transform the data from the WSJ endpoints."""
|
| 70 |
+
|
| 71 |
+
@staticmethod
|
| 72 |
+
def transform_query(params: Dict[str, Any]) -> WSJLosersQueryParams:
|
| 73 |
+
"""Transform query params."""
|
| 74 |
+
return WSJLosersQueryParams(**params)
|
| 75 |
+
|
| 76 |
+
@staticmethod
|
| 77 |
+
def extract_data(
|
| 78 |
+
query: WSJLosersQueryParams,
|
| 79 |
+
credentials: Optional[Dict[str, str]],
|
| 80 |
+
**kwargs: Any,
|
| 81 |
+
) -> List[Dict]:
|
| 82 |
+
"""Get data from WSJ."""
|
| 83 |
+
# pylint: disable=import-outside-toplevel
|
| 84 |
+
from openbb_core.provider.utils.helpers import make_request
|
| 85 |
+
|
| 86 |
+
url = (
|
| 87 |
+
"https://www.wsj.com/market-data/mutualfunds-etfs/etfmovers?id=%7B%22application"
|
| 88 |
+
"%22%3A%22WSJ%22%2C%22etfMover%22%3A%22laggards%22%2C%22count%22%3A25%7D&type="
|
| 89 |
+
"mdc_etfmovers"
|
| 90 |
+
)
|
| 91 |
+
data = make_request(url).json()
|
| 92 |
+
|
| 93 |
+
return data["data"]["instruments"]
|
| 94 |
+
|
| 95 |
+
@staticmethod
|
| 96 |
+
def transform_data(
|
| 97 |
+
query: ETFPerformanceQueryParams,
|
| 98 |
+
data: List[Dict],
|
| 99 |
+
**kwargs: Any,
|
| 100 |
+
) -> List[WSJLosersData]:
|
| 101 |
+
"""Transform data."""
|
| 102 |
+
data = data[: query.limit]
|
| 103 |
+
data = sorted(
|
| 104 |
+
data,
|
| 105 |
+
key=lambda x: (
|
| 106 |
+
x["percentChange"] if query.sort == "desc" else -x["percentChange"]
|
| 107 |
+
),
|
| 108 |
+
)
|
| 109 |
+
return [WSJLosersData.model_validate(d) for d in data]
|
openbb_platform/providers/wsj/poetry.lock
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
openbb_platform/providers/wsj/pyproject.toml
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[tool.poetry]
|
| 2 |
+
name = "openbb-wsj"
|
| 3 |
+
version = "1.4.1"
|
| 4 |
+
description = "wsj extension for OpenBB"
|
| 5 |
+
authors = ["OpenBB Team <hello@openbb.co>"]
|
| 6 |
+
license = "AGPL-3.0-only"
|
| 7 |
+
readme = "README.md"
|
| 8 |
+
packages = [{ include = "openbb_wsj" }]
|
| 9 |
+
|
| 10 |
+
[tool.poetry.dependencies]
|
| 11 |
+
python = ">=3.9.21,<3.13"
|
| 12 |
+
openbb-core = "^1.4.6"
|
| 13 |
+
|
| 14 |
+
[build-system]
|
| 15 |
+
requires = ["poetry-core"]
|
| 16 |
+
build-backend = "poetry.core.masonry.api"
|
| 17 |
+
|
| 18 |
+
[tool.poetry.plugins."openbb_provider_extension"]
|
| 19 |
+
wsj = "openbb_wsj:wsj_provider"
|
openbb_platform/providers/wsj/tests/record/http/test_wsj_fetchers/test_wsj_active_fetcher_urllib3_v1.yaml
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
interactions:
|
| 2 |
+
- request:
|
| 3 |
+
body: null
|
| 4 |
+
headers:
|
| 5 |
+
Accept:
|
| 6 |
+
- '*/*'
|
| 7 |
+
Accept-Encoding:
|
| 8 |
+
- gzip, deflate
|
| 9 |
+
Connection:
|
| 10 |
+
- keep-alive
|
| 11 |
+
method: GET
|
| 12 |
+
uri: https://www.wsj.com/market-data/mutualfunds-etfs/etfmovers?id=%7B%22application%22%3A%22WSJ%22%2C%22etfMover%22%3A%22most_active%22%2C%22count%22%3A25%7D&type=mdc_etfmovers
|
| 13 |
+
response:
|
| 14 |
+
body:
|
| 15 |
+
string: !!binary |
|
| 16 |
+
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
|
| 81 |
+
AA==
|
| 82 |
+
headers:
|
| 83 |
+
Cache-Control:
|
| 84 |
+
- public, max-age=120, no-cache=Set-Cookie
|
| 85 |
+
Connection:
|
| 86 |
+
- keep-alive
|
| 87 |
+
Content-Encoding:
|
| 88 |
+
- gzip
|
| 89 |
+
Content-Security-Policy:
|
| 90 |
+
- 'default-src ''self'' cdn.privacy-mgmt.com;script-src ''self'' *.wsj.net *.wsj.com
|
| 91 |
+
''unsafe-inline'' ''unsafe-eval'';script-src-elem * ''unsafe-inline'';manifest-src
|
| 92 |
+
''self'' *.wsj.com;media-src * data: blob: https:;worker-src * ''unsafe-inline''
|
| 93 |
+
''unsafe-eval'' blob: data:;frame-src * ''unsafe-inline'';connect-src * ''unsafe-inline''
|
| 94 |
+
''unsafe-eval'';form-action * ''unsafe-inline'';frame-ancestors *;script-src-attr
|
| 95 |
+
''unsafe-inline'';object-src ''self'' ''unsafe-inline'';img-src * data: blob:
|
| 96 |
+
https:;font-src ''self'' * ''unsafe-inline'';upgrade-insecure-requests;base-uri
|
| 97 |
+
''self'';style-src ''self'' https: ''unsafe-inline'''
|
| 98 |
+
Content-Type:
|
| 99 |
+
- application/json; charset=utf-8
|
| 100 |
+
Cross-Origin-Resource-Policy:
|
| 101 |
+
- same-site
|
| 102 |
+
Date:
|
| 103 |
+
- Mon, 01 Jul 2024 21:23:38 GMT
|
| 104 |
+
ETag:
|
| 105 |
+
- W/"6208-aSWJ479odA/1qJ7uf5aiu1h8BA8"
|
| 106 |
+
GC-Versions:
|
| 107 |
+
- 2.2.363|0.4.1243|4.1.2
|
| 108 |
+
Origin-Agent-Cluster:
|
| 109 |
+
- ?1
|
| 110 |
+
Referrer-Policy:
|
| 111 |
+
- strict-origin-when-cross-origin,unsafe-url
|
| 112 |
+
Strict-Transport-Security:
|
| 113 |
+
- max-age=63072000; includeSubDomains; preload
|
| 114 |
+
Transfer-Encoding:
|
| 115 |
+
- chunked
|
| 116 |
+
Via:
|
| 117 |
+
- 1.1 0b68d76a38a9449f14204cc8596730e4.cloudfront.net (CloudFront)
|
| 118 |
+
X-Amz-Cf-Id:
|
| 119 |
+
- e_JfNEAUxhkA94toGkPacLzzbwcEA-QvyNbUXFc9FjDjhYCFpyR9aw==
|
| 120 |
+
X-Amz-Cf-Pop:
|
| 121 |
+
- YVR52-P1
|
| 122 |
+
X-Cache:
|
| 123 |
+
- Miss from cloudfront
|
| 124 |
+
X-Content-Type-Options:
|
| 125 |
+
- nosniff
|
| 126 |
+
X-DNS-Prefetch-Control:
|
| 127 |
+
- 'off'
|
| 128 |
+
X-Download-Options:
|
| 129 |
+
- noopen
|
| 130 |
+
X-Frame-Options:
|
| 131 |
+
- SAMEORIGIN
|
| 132 |
+
X-Permitted-Cross-Domain-Policies:
|
| 133 |
+
- none
|
| 134 |
+
X-XSS-Protection:
|
| 135 |
+
- 1; mode=block
|
| 136 |
+
status:
|
| 137 |
+
code: 200
|
| 138 |
+
message: OK
|
| 139 |
+
version: 1
|
openbb_platform/providers/wsj/tests/record/http/test_wsj_fetchers/test_wsj_active_fetcher_urllib3_v2.yaml
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
interactions:
|
| 2 |
+
- request:
|
| 3 |
+
body: null
|
| 4 |
+
headers:
|
| 5 |
+
Accept:
|
| 6 |
+
- '*/*'
|
| 7 |
+
Accept-Encoding:
|
| 8 |
+
- gzip, deflate
|
| 9 |
+
Connection:
|
| 10 |
+
- keep-alive
|
| 11 |
+
method: GET
|
| 12 |
+
uri: https://www.wsj.com/market-data/mutualfunds-etfs/etfmovers?id=%7B%22application%22%3A%22WSJ%22%2C%22etfMover%22%3A%22most_active%22%2C%22count%22%3A25%7D&type=mdc_etfmovers
|
| 13 |
+
response:
|
| 14 |
+
body:
|
| 15 |
+
string: '{"id":"{\"application\":\"WSJ\",\"etfMover\":\"most_active\",\"count\":25}","type":"mdc_etfmovers","data":{"instruments":[{"bluegrassChannel":"/zigman2/quotes/202448809/composite","country":"US","lastPrice":8.18,"mantissa":4,"percentChange":-0.6,"priceChange":-0.0495,"name":"ProShares
|
| 16 |
+
UltraPro Short QQQ","ticker":"SQQQ","timestamp":"2024-06-26T20:00:00.088+00:00","type":"ExchangeTradedFund","volume":109715685,"formattedPrice":"8.18","formattedPriceChange":"-0.05","formattedPercentChange":"-0.60","formattedVolume":"109.7M","url":"https://www.wsj.com/market-data/quotes/etf/US/SQQQ"},{"bluegrassChannel":"/zigman2/quotes/237549522/composite","country":"US","lastPrice":9.06,"mantissa":4,"percentChange":9.55,"priceChange":0.79,"name":"Direxion
|
| 17 |
+
Daily TSLA Bull 2X Shares","ticker":"TSLL","timestamp":"2024-06-26T20:00:00.071+00:00","type":"ExchangeTradedFund","volume":54560126,"formattedPrice":"9.06","formattedPriceChange":"0.79","formattedPercentChange":"9.55","formattedVolume":"54.6M","url":"https://www.wsj.com/market-data/quotes/etf/US/TSLL"},{"bluegrassChannel":"/zigman2/quotes/247790416/composite","country":"US","lastPrice":2.11,"mantissa":4,"percentChange":0,"priceChange":0,"name":"GraniteShares
|
| 18 |
+
2x Short NVDA Daily ETF","ticker":"NVD","timestamp":"2024-06-26T20:00:00.236+00:00","type":"ExchangeTradedFund","volume":41814748,"formattedPrice":"2.11","formattedPriceChange":"0.00","formattedPercentChange":"0.00","formattedVolume":"41.8M","url":"https://www.wsj.com/market-data/quotes/etf/US/NVD"},{"bluegrassChannel":"/zigman2/quotes/205533314/composite","country":"US","lastPrice":54.78,"mantissa":4,"percentChange":-0.94,"priceChange":-0.52,"name":"Direxion
|
| 19 |
+
Daily Semiconductor Bull 3X Shares","ticker":"SOXL","timestamp":"2024-06-26T20:00:00.244+00:00","type":"ExchangeTradedFund","volume":41093441,"formattedPrice":"54.78","formattedPriceChange":"-0.52","formattedPercentChange":"-0.94","formattedVolume":"41.1M","url":"https://www.wsj.com/market-data/quotes/etf/US/SOXL"},{"bluegrassChannel":"/zigman2/quotes/209901640/composite","country":"US","lastPrice":545.51,"mantissa":4,"percentChange":0.12,"priceChange":0.68,"name":"SPDR
|
| 20 |
+
S&P 500 ETF Trust","ticker":"SPY","timestamp":"2024-06-26T20:00:00.134+00:00","type":"ExchangeTradedFund","volume":38550637,"formattedPrice":"545.51","formattedPriceChange":"0.68","formattedPercentChange":"0.12","formattedVolume":"38.6M","url":"https://www.wsj.com/market-data/quotes/etf/US/SPY"},{"bluegrassChannel":"/zigman2/quotes/206026314/composite","country":"US","lastPrice":93.15,"mantissa":4,"percentChange":-1.43,"priceChange":-1.35,"name":"iShares
|
| 21 |
+
20+ Year Treasury Bond ETF","ticker":"TLT","timestamp":"2024-06-26T20:00:00.242+00:00","type":"ExchangeTradedFund","volume":38489135,"formattedPrice":"93.15","formattedPriceChange":"-1.35","formattedPercentChange":"-1.43","formattedVolume":"38.5M","url":"https://www.wsj.com/market-data/quotes/etf/US/TLT"},{"bluegrassChannel":"/zigman2/quotes/209660484/composite","country":"US","lastPrice":41.02,"mantissa":4,"percentChange":-0.63,"priceChange":-0.26,"name":"Financial
|
| 22 |
+
Select Sector SPDR ETF","ticker":"XLF","timestamp":"2024-06-26T20:00:00.064+00:00","type":"ExchangeTradedFund","volume":37978480,"formattedPrice":"41.02","formattedPriceChange":"-0.26","formattedPercentChange":"-0.63","formattedVolume":"38.0M","url":"https://www.wsj.com/market-data/quotes/etf/US/XLF"},{"bluegrassChannel":"/zigman2/quotes/200945942/composite","country":"US","lastPrice":74.46,"mantissa":4,"percentChange":0.63,"priceChange":0.4628,"name":"ProShares
|
| 23 |
+
UltraPro QQQ","ticker":"TQQQ","timestamp":"2024-06-26T20:00:00.229+00:00","type":"ExchangeTradedFund","volume":36274772,"formattedPrice":"74.46","formattedPriceChange":"0.46","formattedPercentChange":"0.63","formattedVolume":"36.3M","url":"https://www.wsj.com/market-data/quotes/etf/US/TQQQ"},{"bluegrassChannel":"/zigman2/quotes/209486038/composite","country":"US","lastPrice":22.94,"mantissa":4,"percentChange":1.37,"priceChange":0.31,"name":"Direxion
|
| 24 |
+
Daily Semiconductor Bear 3X Shares","ticker":"SOXS","timestamp":"2024-06-26T20:00:00.088+00:00","type":"ExchangeTradedFund","volume":36032945,"formattedPrice":"22.94","formattedPriceChange":"0.31","formattedPercentChange":"1.37","formattedVolume":"36.0M","url":"https://www.wsj.com/market-data/quotes/etf/US/SOXS"},{"bluegrassChannel":"/zigman2/quotes/203240683/composite","country":"US","lastPrice":7.77,"mantissa":4,"percentChange":-0.38,"priceChange":-0.03,"name":"Direxion
|
| 25 |
+
Daily S&P 500 Bear 3X Shares","ticker":"SPXS","timestamp":"2024-06-26T20:00:00.083+00:00","type":"ExchangeTradedFund","volume":31602201,"formattedPrice":"7.77","formattedPriceChange":"-0.03","formattedPercentChange":"-0.38","formattedVolume":"31.6M","url":"https://www.wsj.com/market-data/quotes/etf/US/SPXS"},{"bluegrassChannel":"/zigman2/quotes/249081679/composite","country":"US","lastPrice":1.91,"mantissa":4,"percentChange":-0.52,"priceChange":-0.01,"name":"T-Rex
|
| 26 |
+
2X Inverse NVIDIA Daily Target ETF","ticker":"NVDQ","timestamp":"2024-06-26T20:00:00.2+00:00","type":"ExchangeTradedFund","volume":29285090,"formattedPrice":"1.91","formattedPriceChange":"-0.01","formattedPercentChange":"-0.52","formattedVolume":"29.3M","url":"https://www.wsj.com/market-data/quotes/etf/US/NVDQ"},{"bluegrassChannel":"/zigman2/quotes/204471305/composite","country":"US","lastPrice":77.24,"mantissa":4,"percentChange":-0.22,"priceChange":-0.17,"name":"iShares
|
| 27 |
+
iBoxx $ High Yield Corporate Bond ETF","ticker":"HYG","timestamp":"2024-06-26T20:00:00.08+00:00","type":"ExchangeTradedFund","volume":29282017,"formattedPrice":"77.24","formattedPriceChange":"-0.17","formattedPercentChange":"-0.22","formattedVolume":"29.3M","url":"https://www.wsj.com/market-data/quotes/etf/US/HYG"},{"bluegrassChannel":"/zigman2/quotes/206919681/composite","country":"US","lastPrice":107.6,"mantissa":4,"percentChange":-0.55,"priceChange":-0.59,"name":"iShares
|
| 28 |
+
iBoxx $ Investment Grade Corporate Bond ETF","ticker":"LQD","timestamp":"2024-06-26T20:00:00.073+00:00","type":"ExchangeTradedFund","volume":26381309,"formattedPrice":"107.60","formattedPriceChange":"-0.59","formattedPercentChange":"-0.55","formattedVolume":"26.4M","url":"https://www.wsj.com/market-data/quotes/etf/US/LQD"},{"bluegrassChannel":"/zigman2/quotes/241156308/composite","country":"US","lastPrice":73.03,"mantissa":4,"percentChange":0.22,"priceChange":0.16,"name":"GraniteShares
|
| 29 |
+
2x Long NVDA Daily ETF","ticker":"NVDL","timestamp":"2024-06-26T20:00:00.072+00:00","type":"ExchangeTradedFund","volume":24933557,"formattedPrice":"73.03","formattedPriceChange":"0.16","formattedPercentChange":"0.22","formattedVolume":"24.9M","url":"https://www.wsj.com/market-data/quotes/etf/US/NVDL"},{"bluegrassChannel":"/zigman2/quotes/208575548/composite","country":"US","lastPrice":480.37,"mantissa":4,"percentChange":0.21,"priceChange":0.99,"name":"Invesco
|
| 30 |
+
QQQ Trust Series I","ticker":"QQQ","timestamp":"2024-06-26T20:00:00.092+00:00","type":"ExchangeTradedFund","volume":22737105,"formattedPrice":"480.37","formattedPriceChange":"0.99","formattedPercentChange":"0.21","formattedVolume":"22.7M","url":"https://www.wsj.com/market-data/quotes/etf/US/QQQ"},{"bluegrassChannel":"/zigman2/quotes/208893627/composite","country":"US","lastPrice":27.36,"mantissa":4,"percentChange":-0.87,"priceChange":-0.24,"name":"iShares
|
| 31 |
+
MSCI Brazil ETF","ticker":"EWZ","timestamp":"2024-06-26T20:00:00.074+00:00","type":"ExchangeTradedFund","volume":21855698,"formattedPrice":"27.36","formattedPriceChange":"-0.24","formattedPercentChange":"-0.87","formattedVolume":"21.9M","url":"https://www.wsj.com/market-data/quotes/etf/US/EWZ"},{"bluegrassChannel":"/zigman2/quotes/209961116/composite","country":"US","lastPrice":200.03,"mantissa":4,"percentChange":-0.26,"priceChange":-0.53,"name":"iShares
|
| 32 |
+
Russell 2000 ETF","ticker":"IWM","timestamp":"2024-06-26T20:00:00.066+00:00","type":"ExchangeTradedFund","volume":19313461,"formattedPrice":"200.03","formattedPriceChange":"-0.53","formattedPercentChange":"-0.26","formattedVolume":"19.3M","url":"https://www.wsj.com/market-data/quotes/etf/US/IWM"},{"bluegrassChannel":"/zigman2/quotes/201454250/composite","country":"US","lastPrice":42.48,"mantissa":4,"percentChange":-0.21,"priceChange":-0.09,"name":"iShares
|
| 33 |
+
MSCI Emerging Markets ETF","ticker":"EEM","timestamp":"2024-06-26T20:00:00.136+00:00","type":"ExchangeTradedFund","volume":18961923,"formattedPrice":"42.48","formattedPriceChange":"-0.09","formattedPercentChange":"-0.21","formattedVolume":"19.0M","url":"https://www.wsj.com/market-data/quotes/etf/US/EEM"},{"bluegrassChannel":"/zigman2/quotes/210267549/composite","country":"US","lastPrice":21.01,"mantissa":4,"percentChange":-0.05,"priceChange":-0.01,"name":"Invesco
|
| 34 |
+
Senior Loan ETF","ticker":"BKLN","timestamp":"2024-06-26T20:10:00.002+00:00","type":"ExchangeTradedFund","volume":16108264,"formattedPrice":"21.01","formattedPriceChange":"-0.01","formattedPercentChange":"-0.05","formattedVolume":"16.1M","url":"https://www.wsj.com/market-data/quotes/etf/US/BKLN"},{"bluegrassChannel":"/zigman2/quotes/208670743/composite","country":"US","lastPrice":26.52,"mantissa":4,"percentChange":0.04,"priceChange":0.01,"name":"iShares
|
| 35 |
+
China Large-Cap ETF","ticker":"FXI","timestamp":"2024-06-26T20:00:00.101+00:00","type":"ExchangeTradedFund","volume":15806507,"formattedPrice":"26.52","formattedPriceChange":"0.01","formattedPercentChange":"0.04","formattedVolume":"15.8M","url":"https://www.wsj.com/market-data/quotes/etf/US/FXI"},{"bluegrassChannel":"/zigman2/quotes/205744453/composite","country":"US","lastPrice":26.31,"mantissa":4,"percentChange":-0.34,"priceChange":-0.09,"name":"iShares
|
| 36 |
+
Silver Trust","ticker":"SLV","timestamp":"2024-06-26T20:00:00.068+00:00","type":"ExchangeTradedFund","volume":13855742,"formattedPrice":"26.31","formattedPriceChange":"-0.09","formattedPercentChange":"-0.34","formattedVolume":"13.9M","url":"https://www.wsj.com/market-data/quotes/etf/US/SLV"},{"bluegrassChannel":"/zigman2/quotes/205592609/composite","country":"US","lastPrice":7.41,"mantissa":4,"percentChange":2.63,"priceChange":0.19,"name":"Direxion
|
| 37 |
+
Daily S&P Biotech Bear 3X Shares","ticker":"LABD","timestamp":"2024-06-26T20:00:00.116+00:00","type":"ExchangeTradedFund","volume":13794173,"formattedPrice":"7.41","formattedPriceChange":"0.19","formattedPercentChange":"2.63","formattedVolume":"13.8M","url":"https://www.wsj.com/market-data/quotes/etf/US/LABD"},{"bluegrassChannel":"/zigman2/quotes/204696134/composite","country":"US","lastPrice":19.29,"mantissa":4,"percentChange":0.68,"priceChange":0.13,"name":"Direxion
|
| 38 |
+
Daily Small Cap Bear 3x Shares","ticker":"TZA","timestamp":"2024-06-26T20:00:00.033+00:00","type":"ExchangeTradedFund","volume":13762870,"formattedPrice":"19.29","formattedPriceChange":"0.13","formattedPercentChange":"0.68","formattedVolume":"13.8M","url":"https://www.wsj.com/market-data/quotes/etf/US/TZA"},{"bluegrassChannel":"/zigman2/quotes/206399889/composite","country":"US","lastPrice":33.82,"mantissa":4,"percentChange":-0.06,"priceChange":-0.02,"name":"VanEck
|
| 39 |
+
Gold Miners ETF","ticker":"GDX","timestamp":"2024-06-26T20:00:00.132+00:00","type":"ExchangeTradedFund","volume":13325500,"formattedPrice":"33.82","formattedPriceChange":"-0.02","formattedPercentChange":"-0.06","formattedVolume":"13.3M","url":"https://www.wsj.com/market-data/quotes/etf/US/GDX"},{"bluegrassChannel":"/zigman2/quotes/205521875/composite","country":"US","lastPrice":5.84,"mantissa":4,"percentChange":0.34,"priceChange":0.02,"name":"Direxion
|
| 40 |
+
Daily Technology Bear 3x Shares","ticker":"TECS","timestamp":"2024-06-26T20:00:00.052+00:00","type":"ExchangeTradedFund","volume":13118034,"formattedPrice":"5.84","formattedPriceChange":"0.02","formattedPercentChange":"0.34","formattedVolume":"13.1M","url":"https://www.wsj.com/market-data/quotes/etf/US/TECS"}],"latestTimestamp":"2024-06-26T20:10:00.002+00:00","timestamp":"4:10
|
| 41 |
+
PM EDT 6/26/24"},"hash":"{\"id\":\"{\\\"application\\\":\\\"WSJ\\\",\\\"etfMover\\\":\\\"most_active\\\",\\\"count\\\":25}\",\"type\":\"mdc_etfmovers\",\"data\":{\"instruments\":[{\"bluegrassChannel\":\"/zigman2/quotes/202448809/composite\",\"country\":\"US\",\"lastPrice\":8.18,\"mantissa\":4,\"percentChange\":-0.6,\"priceChange\":-0.0495,\"name\":\"ProShares
|
| 42 |
+
UltraPro Short QQQ\",\"ticker\":\"SQQQ\",\"timestamp\":\"2024-06-26T20:00:00.088+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":109715685,\"formattedPrice\":\"8.18\",\"formattedPriceChange\":\"-0.05\",\"formattedPercentChange\":\"-0.60\",\"formattedVolume\":\"109.7M\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/SQQQ\"},{\"bluegrassChannel\":\"/zigman2/quotes/237549522/composite\",\"country\":\"US\",\"lastPrice\":9.06,\"mantissa\":4,\"percentChange\":9.55,\"priceChange\":0.79,\"name\":\"Direxion
|
| 43 |
+
Daily TSLA Bull 2X Shares\",\"ticker\":\"TSLL\",\"timestamp\":\"2024-06-26T20:00:00.071+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":54560126,\"formattedPrice\":\"9.06\",\"formattedPriceChange\":\"0.79\",\"formattedPercentChange\":\"9.55\",\"formattedVolume\":\"54.6M\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/TSLL\"},{\"bluegrassChannel\":\"/zigman2/quotes/247790416/composite\",\"country\":\"US\",\"lastPrice\":2.11,\"mantissa\":4,\"percentChange\":0,\"priceChange\":0,\"name\":\"GraniteShares
|
| 44 |
+
2x Short NVDA Daily ETF\",\"ticker\":\"NVD\",\"timestamp\":\"2024-06-26T20:00:00.236+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":41814748,\"formattedPrice\":\"2.11\",\"formattedPriceChange\":\"0.00\",\"formattedPercentChange\":\"0.00\",\"formattedVolume\":\"41.8M\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/NVD\"},{\"bluegrassChannel\":\"/zigman2/quotes/205533314/composite\",\"country\":\"US\",\"lastPrice\":54.78,\"mantissa\":4,\"percentChange\":-0.94,\"priceChange\":-0.52,\"name\":\"Direxion
|
| 45 |
+
Daily Semiconductor Bull 3X Shares\",\"ticker\":\"SOXL\",\"timestamp\":\"2024-06-26T20:00:00.244+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":41093441,\"formattedPrice\":\"54.78\",\"formattedPriceChange\":\"-0.52\",\"formattedPercentChange\":\"-0.94\",\"formattedVolume\":\"41.1M\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/SOXL\"},{\"bluegrassChannel\":\"/zigman2/quotes/209901640/composite\",\"country\":\"US\",\"lastPrice\":545.51,\"mantissa\":4,\"percentChange\":0.12,\"priceChange\":0.68,\"name\":\"SPDR
|
| 46 |
+
S&P 500 ETF Trust\",\"ticker\":\"SPY\",\"timestamp\":\"2024-06-26T20:00:00.134+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":38550637,\"formattedPrice\":\"545.51\",\"formattedPriceChange\":\"0.68\",\"formattedPercentChange\":\"0.12\",\"formattedVolume\":\"38.6M\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/SPY\"},{\"bluegrassChannel\":\"/zigman2/quotes/206026314/composite\",\"country\":\"US\",\"lastPrice\":93.15,\"mantissa\":4,\"percentChange\":-1.43,\"priceChange\":-1.35,\"name\":\"iShares
|
| 47 |
+
20+ Year Treasury Bond ETF\",\"ticker\":\"TLT\",\"timestamp\":\"2024-06-26T20:00:00.242+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":38489135,\"formattedPrice\":\"93.15\",\"formattedPriceChange\":\"-1.35\",\"formattedPercentChange\":\"-1.43\",\"formattedVolume\":\"38.5M\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/TLT\"},{\"bluegrassChannel\":\"/zigman2/quotes/209660484/composite\",\"country\":\"US\",\"lastPrice\":41.02,\"mantissa\":4,\"percentChange\":-0.63,\"priceChange\":-0.26,\"name\":\"Financial
|
| 48 |
+
Select Sector SPDR ETF\",\"ticker\":\"XLF\",\"timestamp\":\"2024-06-26T20:00:00.064+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":37978480,\"formattedPrice\":\"41.02\",\"formattedPriceChange\":\"-0.26\",\"formattedPercentChange\":\"-0.63\",\"formattedVolume\":\"38.0M\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/XLF\"},{\"bluegrassChannel\":\"/zigman2/quotes/200945942/composite\",\"country\":\"US\",\"lastPrice\":74.46,\"mantissa\":4,\"percentChange\":0.63,\"priceChange\":0.4628,\"name\":\"ProShares
|
| 49 |
+
UltraPro QQQ\",\"ticker\":\"TQQQ\",\"timestamp\":\"2024-06-26T20:00:00.229+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":36274772,\"formattedPrice\":\"74.46\",\"formattedPriceChange\":\"0.46\",\"formattedPercentChange\":\"0.63\",\"formattedVolume\":\"36.3M\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/TQQQ\"},{\"bluegrassChannel\":\"/zigman2/quotes/209486038/composite\",\"country\":\"US\",\"lastPrice\":22.94,\"mantissa\":4,\"percentChange\":1.37,\"priceChange\":0.31,\"name\":\"Direxion
|
| 50 |
+
Daily Semiconductor Bear 3X Shares\",\"ticker\":\"SOXS\",\"timestamp\":\"2024-06-26T20:00:00.088+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":36032945,\"formattedPrice\":\"22.94\",\"formattedPriceChange\":\"0.31\",\"formattedPercentChange\":\"1.37\",\"formattedVolume\":\"36.0M\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/SOXS\"},{\"bluegrassChannel\":\"/zigman2/quotes/203240683/composite\",\"country\":\"US\",\"lastPrice\":7.77,\"mantissa\":4,\"percentChange\":-0.38,\"priceChange\":-0.03,\"name\":\"Direxion
|
| 51 |
+
Daily S&P 500 Bear 3X Shares\",\"ticker\":\"SPXS\",\"timestamp\":\"2024-06-26T20:00:00.083+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":31602201,\"formattedPrice\":\"7.77\",\"formattedPriceChange\":\"-0.03\",\"formattedPercentChange\":\"-0.38\",\"formattedVolume\":\"31.6M\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/SPXS\"},{\"bluegrassChannel\":\"/zigman2/quotes/249081679/composite\",\"country\":\"US\",\"lastPrice\":1.91,\"mantissa\":4,\"percentChange\":-0.52,\"priceChange\":-0.01,\"name\":\"T-Rex
|
| 52 |
+
2X Inverse NVIDIA Daily Target ETF\",\"ticker\":\"NVDQ\",\"timestamp\":\"2024-06-26T20:00:00.2+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":29285090,\"formattedPrice\":\"1.91\",\"formattedPriceChange\":\"-0.01\",\"formattedPercentChange\":\"-0.52\",\"formattedVolume\":\"29.3M\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/NVDQ\"},{\"bluegrassChannel\":\"/zigman2/quotes/204471305/composite\",\"country\":\"US\",\"lastPrice\":77.24,\"mantissa\":4,\"percentChange\":-0.22,\"priceChange\":-0.17,\"name\":\"iShares
|
| 53 |
+
iBoxx $ High Yield Corporate Bond ETF\",\"ticker\":\"HYG\",\"timestamp\":\"2024-06-26T20:00:00.08+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":29282017,\"formattedPrice\":\"77.24\",\"formattedPriceChange\":\"-0.17\",\"formattedPercentChange\":\"-0.22\",\"formattedVolume\":\"29.3M\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/HYG\"},{\"bluegrassChannel\":\"/zigman2/quotes/206919681/composite\",\"country\":\"US\",\"lastPrice\":107.6,\"mantissa\":4,\"percentChange\":-0.55,\"priceChange\":-0.59,\"name\":\"iShares
|
| 54 |
+
iBoxx $ Investment Grade Corporate Bond ETF\",\"ticker\":\"LQD\",\"timestamp\":\"2024-06-26T20:00:00.073+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":26381309,\"formattedPrice\":\"107.60\",\"formattedPriceChange\":\"-0.59\",\"formattedPercentChange\":\"-0.55\",\"formattedVolume\":\"26.4M\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/LQD\"},{\"bluegrassChannel\":\"/zigman2/quotes/241156308/composite\",\"country\":\"US\",\"lastPrice\":73.03,\"mantissa\":4,\"percentChange\":0.22,\"priceChange\":0.16,\"name\":\"GraniteShares
|
| 55 |
+
2x Long NVDA Daily ETF\",\"ticker\":\"NVDL\",\"timestamp\":\"2024-06-26T20:00:00.072+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":24933557,\"formattedPrice\":\"73.03\",\"formattedPriceChange\":\"0.16\",\"formattedPercentChange\":\"0.22\",\"formattedVolume\":\"24.9M\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/NVDL\"},{\"bluegrassChannel\":\"/zigman2/quotes/208575548/composite\",\"country\":\"US\",\"lastPrice\":480.37,\"mantissa\":4,\"percentChange\":0.21,\"priceChange\":0.99,\"name\":\"Invesco
|
| 56 |
+
QQQ Trust Series I\",\"ticker\":\"QQQ\",\"timestamp\":\"2024-06-26T20:00:00.092+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":22737105,\"formattedPrice\":\"480.37\",\"formattedPriceChange\":\"0.99\",\"formattedPercentChange\":\"0.21\",\"formattedVolume\":\"22.7M\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/QQQ\"},{\"bluegrassChannel\":\"/zigman2/quotes/208893627/composite\",\"country\":\"US\",\"lastPrice\":27.36,\"mantissa\":4,\"percentChange\":-0.87,\"priceChange\":-0.24,\"name\":\"iShares
|
| 57 |
+
MSCI Brazil ETF\",\"ticker\":\"EWZ\",\"timestamp\":\"2024-06-26T20:00:00.074+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":21855698,\"formattedPrice\":\"27.36\",\"formattedPriceChange\":\"-0.24\",\"formattedPercentChange\":\"-0.87\",\"formattedVolume\":\"21.9M\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/EWZ\"},{\"bluegrassChannel\":\"/zigman2/quotes/209961116/composite\",\"country\":\"US\",\"lastPrice\":200.03,\"mantissa\":4,\"percentChange\":-0.26,\"priceChange\":-0.53,\"name\":\"iShares
|
| 58 |
+
Russell 2000 ETF\",\"ticker\":\"IWM\",\"timestamp\":\"2024-06-26T20:00:00.066+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":19313461,\"formattedPrice\":\"200.03\",\"formattedPriceChange\":\"-0.53\",\"formattedPercentChange\":\"-0.26\",\"formattedVolume\":\"19.3M\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/IWM\"},{\"bluegrassChannel\":\"/zigman2/quotes/201454250/composite\",\"country\":\"US\",\"lastPrice\":42.48,\"mantissa\":4,\"percentChange\":-0.21,\"priceChange\":-0.09,\"name\":\"iShares
|
| 59 |
+
MSCI Emerging Markets ETF\",\"ticker\":\"EEM\",\"timestamp\":\"2024-06-26T20:00:00.136+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":18961923,\"formattedPrice\":\"42.48\",\"formattedPriceChange\":\"-0.09\",\"formattedPercentChange\":\"-0.21\",\"formattedVolume\":\"19.0M\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/EEM\"},{\"bluegrassChannel\":\"/zigman2/quotes/210267549/composite\",\"country\":\"US\",\"lastPrice\":21.01,\"mantissa\":4,\"percentChange\":-0.05,\"priceChange\":-0.01,\"name\":\"Invesco
|
| 60 |
+
Senior Loan ETF\",\"ticker\":\"BKLN\",\"timestamp\":\"2024-06-26T20:10:00.002+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":16108264,\"formattedPrice\":\"21.01\",\"formattedPriceChange\":\"-0.01\",\"formattedPercentChange\":\"-0.05\",\"formattedVolume\":\"16.1M\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/BKLN\"},{\"bluegrassChannel\":\"/zigman2/quotes/208670743/composite\",\"country\":\"US\",\"lastPrice\":26.52,\"mantissa\":4,\"percentChange\":0.04,\"priceChange\":0.01,\"name\":\"iShares
|
| 61 |
+
China Large-Cap ETF\",\"ticker\":\"FXI\",\"timestamp\":\"2024-06-26T20:00:00.101+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":15806507,\"formattedPrice\":\"26.52\",\"formattedPriceChange\":\"0.01\",\"formattedPercentChange\":\"0.04\",\"formattedVolume\":\"15.8M\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/FXI\"},{\"bluegrassChannel\":\"/zigman2/quotes/205744453/composite\",\"country\":\"US\",\"lastPrice\":26.31,\"mantissa\":4,\"percentChange\":-0.34,\"priceChange\":-0.09,\"name\":\"iShares
|
| 62 |
+
Silver Trust\",\"ticker\":\"SLV\",\"timestamp\":\"2024-06-26T20:00:00.068+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":13855742,\"formattedPrice\":\"26.31\",\"formattedPriceChange\":\"-0.09\",\"formattedPercentChange\":\"-0.34\",\"formattedVolume\":\"13.9M\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/SLV\"},{\"bluegrassChannel\":\"/zigman2/quotes/205592609/composite\",\"country\":\"US\",\"lastPrice\":7.41,\"mantissa\":4,\"percentChange\":2.63,\"priceChange\":0.19,\"name\":\"Direxion
|
| 63 |
+
Daily S&P Biotech Bear 3X Shares\",\"ticker\":\"LABD\",\"timestamp\":\"2024-06-26T20:00:00.116+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":13794173,\"formattedPrice\":\"7.41\",\"formattedPriceChange\":\"0.19\",\"formattedPercentChange\":\"2.63\",\"formattedVolume\":\"13.8M\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/LABD\"},{\"bluegrassChannel\":\"/zigman2/quotes/204696134/composite\",\"country\":\"US\",\"lastPrice\":19.29,\"mantissa\":4,\"percentChange\":0.68,\"priceChange\":0.13,\"name\":\"Direxion
|
| 64 |
+
Daily Small Cap Bear 3x Shares\",\"ticker\":\"TZA\",\"timestamp\":\"2024-06-26T20:00:00.033+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":13762870,\"formattedPrice\":\"19.29\",\"formattedPriceChange\":\"0.13\",\"formattedPercentChange\":\"0.68\",\"formattedVolume\":\"13.8M\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/TZA\"},{\"bluegrassChannel\":\"/zigman2/quotes/206399889/composite\",\"country\":\"US\",\"lastPrice\":33.82,\"mantissa\":4,\"percentChange\":-0.06,\"priceChange\":-0.02,\"name\":\"VanEck
|
| 65 |
+
Gold Miners ETF\",\"ticker\":\"GDX\",\"timestamp\":\"2024-06-26T20:00:00.132+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":13325500,\"formattedPrice\":\"33.82\",\"formattedPriceChange\":\"-0.02\",\"formattedPercentChange\":\"-0.06\",\"formattedVolume\":\"13.3M\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/GDX\"},{\"bluegrassChannel\":\"/zigman2/quotes/205521875/composite\",\"country\":\"US\",\"lastPrice\":5.84,\"mantissa\":4,\"percentChange\":0.34,\"priceChange\":0.02,\"name\":\"Direxion
|
| 66 |
+
Daily Technology Bear 3x Shares\",\"ticker\":\"TECS\",\"timestamp\":\"2024-06-26T20:00:00.052+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":13118034,\"formattedPrice\":\"5.84\",\"formattedPriceChange\":\"0.02\",\"formattedPercentChange\":\"0.34\",\"formattedVolume\":\"13.1M\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/TECS\"}],\"latestTimestamp\":\"2024-06-26T20:10:00.002+00:00\",\"timestamp\":\"4:10
|
| 67 |
+
PM EDT 6/26/24\"}}"}'
|
| 68 |
+
headers:
|
| 69 |
+
Cache-Control:
|
| 70 |
+
- public, max-age=120, no-cache=Set-Cookie
|
| 71 |
+
Connection:
|
| 72 |
+
- keep-alive
|
| 73 |
+
Content-Encoding:
|
| 74 |
+
- gzip
|
| 75 |
+
Content-Security-Policy:
|
| 76 |
+
- 'default-src ''self'' cdn.privacy-mgmt.com;script-src ''self'' *.wsj.net *.wsj.com
|
| 77 |
+
''unsafe-inline'' ''unsafe-eval'';script-src-elem * ''unsafe-inline'';manifest-src
|
| 78 |
+
''self'' *.wsj.com;media-src * data: blob: https:;worker-src * ''unsafe-inline''
|
| 79 |
+
''unsafe-eval'' blob: data:;frame-src * ''unsafe-inline'';connect-src * ''unsafe-inline''
|
| 80 |
+
''unsafe-eval'';form-action * ''unsafe-inline'';frame-ancestors *;script-src-attr
|
| 81 |
+
''unsafe-inline'';object-src ''self'' ''unsafe-inline'';img-src * data: blob:
|
| 82 |
+
https:;font-src ''self'' * ''unsafe-inline'';upgrade-insecure-requests;base-uri
|
| 83 |
+
''self'';style-src ''self'' https: ''unsafe-inline'''
|
| 84 |
+
Content-Type:
|
| 85 |
+
- application/json; charset=utf-8
|
| 86 |
+
Cross-Origin-Resource-Policy:
|
| 87 |
+
- same-site
|
| 88 |
+
Date:
|
| 89 |
+
- Thu, 27 Jun 2024 10:16:50 GMT
|
| 90 |
+
ETag:
|
| 91 |
+
- W/"6238-5zVnvwoPXGUnn4Djm9yp/P0xFEA"
|
| 92 |
+
GC-Versions:
|
| 93 |
+
- 2.2.354|0.4.1241|4.1.2
|
| 94 |
+
Origin-Agent-Cluster:
|
| 95 |
+
- ?1
|
| 96 |
+
Referrer-Policy:
|
| 97 |
+
- strict-origin-when-cross-origin,unsafe-url
|
| 98 |
+
Strict-Transport-Security:
|
| 99 |
+
- max-age=63072000; includeSubDomains; preload
|
| 100 |
+
Transfer-Encoding:
|
| 101 |
+
- chunked
|
| 102 |
+
Via:
|
| 103 |
+
- 1.1 6cbc993371a5407a8b834ea22f7fcbd2.cloudfront.net (CloudFront)
|
| 104 |
+
X-Amz-Cf-Id:
|
| 105 |
+
- nIZJk6MpzjoYRnhvlEQBlw0XC-vWMQMmd5RcHYhSMUq2StLS5wbEIQ==
|
| 106 |
+
X-Amz-Cf-Pop:
|
| 107 |
+
- AMS58-P1
|
| 108 |
+
X-Cache:
|
| 109 |
+
- Miss from cloudfront
|
| 110 |
+
X-Content-Type-Options:
|
| 111 |
+
- nosniff
|
| 112 |
+
X-DNS-Prefetch-Control:
|
| 113 |
+
- 'off'
|
| 114 |
+
X-Download-Options:
|
| 115 |
+
- noopen
|
| 116 |
+
X-Frame-Options:
|
| 117 |
+
- SAMEORIGIN
|
| 118 |
+
X-Permitted-Cross-Domain-Policies:
|
| 119 |
+
- none
|
| 120 |
+
X-XSS-Protection:
|
| 121 |
+
- 1; mode=block
|
| 122 |
+
status:
|
| 123 |
+
code: 200
|
| 124 |
+
message: OK
|
| 125 |
+
version: 1
|
openbb_platform/providers/wsj/tests/record/http/test_wsj_fetchers/test_wsj_gainers_fetcher_urllib3_v1.yaml
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
interactions:
|
| 2 |
+
- request:
|
| 3 |
+
body: null
|
| 4 |
+
headers:
|
| 5 |
+
Accept:
|
| 6 |
+
- '*/*'
|
| 7 |
+
Accept-Encoding:
|
| 8 |
+
- gzip, deflate
|
| 9 |
+
Connection:
|
| 10 |
+
- keep-alive
|
| 11 |
+
method: GET
|
| 12 |
+
uri: https://www.wsj.com/market-data/mutualfunds-etfs/etfmovers?id=%7B%22application%22%3A%22WSJ%22%2C%22etfMover%22%3A%22leaders%22%2C%22count%22%3A25%7D&type=mdc_etfmovers
|
| 13 |
+
response:
|
| 14 |
+
body:
|
| 15 |
+
string: !!binary |
|
| 16 |
+
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
|
| 81 |
+
yZFKYQAA
|
| 82 |
+
headers:
|
| 83 |
+
Cache-Control:
|
| 84 |
+
- public, max-age=120, no-cache=Set-Cookie
|
| 85 |
+
Connection:
|
| 86 |
+
- keep-alive
|
| 87 |
+
Content-Encoding:
|
| 88 |
+
- gzip
|
| 89 |
+
Content-Security-Policy:
|
| 90 |
+
- 'default-src ''self'' cdn.privacy-mgmt.com;script-src ''self'' *.wsj.net *.wsj.com
|
| 91 |
+
''unsafe-inline'' ''unsafe-eval'';script-src-elem * ''unsafe-inline'';manifest-src
|
| 92 |
+
''self'' *.wsj.com;media-src * data: blob: https:;worker-src * ''unsafe-inline''
|
| 93 |
+
''unsafe-eval'' blob: data:;frame-src * ''unsafe-inline'';connect-src * ''unsafe-inline''
|
| 94 |
+
''unsafe-eval'';form-action * ''unsafe-inline'';frame-ancestors *;script-src-attr
|
| 95 |
+
''unsafe-inline'';object-src ''self'' ''unsafe-inline'';img-src * data: blob:
|
| 96 |
+
https:;font-src ''self'' * ''unsafe-inline'';upgrade-insecure-requests;base-uri
|
| 97 |
+
''self'';style-src ''self'' https: ''unsafe-inline'''
|
| 98 |
+
Content-Type:
|
| 99 |
+
- application/json; charset=utf-8
|
| 100 |
+
Cross-Origin-Resource-Policy:
|
| 101 |
+
- same-site
|
| 102 |
+
Date:
|
| 103 |
+
- Mon, 01 Jul 2024 21:23:37 GMT
|
| 104 |
+
ETag:
|
| 105 |
+
- W/"614a-ctBWmkU5EgB+Snc3sGQvbL6pGxQ"
|
| 106 |
+
GC-Versions:
|
| 107 |
+
- 2.2.363|0.4.1243|4.1.2
|
| 108 |
+
Origin-Agent-Cluster:
|
| 109 |
+
- ?1
|
| 110 |
+
Referrer-Policy:
|
| 111 |
+
- strict-origin-when-cross-origin,unsafe-url
|
| 112 |
+
Strict-Transport-Security:
|
| 113 |
+
- max-age=63072000; includeSubDomains; preload
|
| 114 |
+
Transfer-Encoding:
|
| 115 |
+
- chunked
|
| 116 |
+
Via:
|
| 117 |
+
- 1.1 df6cbd0e5f9defbb1b16ba675eeaeaa8.cloudfront.net (CloudFront)
|
| 118 |
+
X-Amz-Cf-Id:
|
| 119 |
+
- bJ33j80poXyz6iVUnCmA7FqJNptifp8vBSktAf8akv3H6lmsq36ncA==
|
| 120 |
+
X-Amz-Cf-Pop:
|
| 121 |
+
- YVR52-P1
|
| 122 |
+
X-Cache:
|
| 123 |
+
- Miss from cloudfront
|
| 124 |
+
X-Content-Type-Options:
|
| 125 |
+
- nosniff
|
| 126 |
+
X-DNS-Prefetch-Control:
|
| 127 |
+
- 'off'
|
| 128 |
+
X-Download-Options:
|
| 129 |
+
- noopen
|
| 130 |
+
X-Frame-Options:
|
| 131 |
+
- SAMEORIGIN
|
| 132 |
+
X-Permitted-Cross-Domain-Policies:
|
| 133 |
+
- none
|
| 134 |
+
X-XSS-Protection:
|
| 135 |
+
- 1; mode=block
|
| 136 |
+
status:
|
| 137 |
+
code: 200
|
| 138 |
+
message: OK
|
| 139 |
+
version: 1
|
openbb_platform/providers/wsj/tests/record/http/test_wsj_fetchers/test_wsj_gainers_fetcher_urllib3_v2.yaml
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
interactions:
|
| 2 |
+
- request:
|
| 3 |
+
body: null
|
| 4 |
+
headers:
|
| 5 |
+
Accept:
|
| 6 |
+
- '*/*'
|
| 7 |
+
Accept-Encoding:
|
| 8 |
+
- gzip, deflate
|
| 9 |
+
Connection:
|
| 10 |
+
- keep-alive
|
| 11 |
+
method: GET
|
| 12 |
+
uri: https://www.wsj.com/market-data/mutualfunds-etfs/etfmovers?id=%7B%22application%22%3A%22WSJ%22%2C%22etfMover%22%3A%22leaders%22%2C%22count%22%3A25%7D&type=mdc_etfmovers
|
| 13 |
+
response:
|
| 14 |
+
body:
|
| 15 |
+
string: '{"id":"{\"application\":\"WSJ\",\"etfMover\":\"leaders\",\"count\":25}","type":"mdc_etfmovers","data":{"instruments":[{"bluegrassChannel":"/zigman2/quotes/247790433/composite","country":"US","lastPrice":13.74,"mantissa":4,"percentChange":9.66,"priceChange":1.21,"name":"GraniteShares
|
| 16 |
+
2x Long TSLA Daily ETF","ticker":"TSLR","timestamp":"2024-06-26T20:00:00.05+00:00","type":"ExchangeTradedFund","volume":968702,"formattedPrice":"13.74","formattedPriceChange":"1.21","formattedPercentChange":"9.66","formattedVolume":"968.7K","url":"https://www.wsj.com/market-data/quotes/etf/US/TSLR"},{"bluegrassChannel":"/zigman2/quotes/237549522/composite","country":"US","lastPrice":9.06,"mantissa":4,"percentChange":9.55,"priceChange":0.79,"name":"Direxion
|
| 17 |
+
Daily TSLA Bull 2X Shares","ticker":"TSLL","timestamp":"2024-06-26T20:00:00.071+00:00","type":"ExchangeTradedFund","volume":54560126,"formattedPrice":"9.06","formattedPriceChange":"0.79","formattedPercentChange":"9.55","formattedVolume":"54.6M","url":"https://www.wsj.com/market-data/quotes/etf/US/TSLL"},{"bluegrassChannel":"/zigman2/quotes/249145372/composite","country":"US","lastPrice":12.45,"mantissa":4,"percentChange":9.4,"priceChange":1.07,"name":"T-Rex
|
| 18 |
+
2X Long Tesla Daily Target ETF","ticker":"TSLT","timestamp":"2024-06-26T20:00:00.073+00:00","type":"ExchangeTradedFund","volume":8589108,"formattedPrice":"12.45","formattedPriceChange":"1.07","formattedPercentChange":"9.40","formattedVolume":"8.6M","url":"https://www.wsj.com/market-data/quotes/etf/US/TSLT"},{"bluegrassChannel":"/zigman2/quotes/253529414/composite","country":"US","lastPrice":29.61,"mantissa":4,"percentChange":7.71,"priceChange":2.12,"name":"GraniteShares
|
| 19 |
+
2x Long AMZN Daily ETF","ticker":"AMZZ","timestamp":"2024-06-26T19:59:56.953+00:00","type":"ExchangeTradedFund","volume":263692,"formattedPrice":"29.61","formattedPriceChange":"2.12","formattedPercentChange":"7.71","formattedVolume":"263.7K","url":"https://www.wsj.com/market-data/quotes/etf/US/AMZZ"},{"bluegrassChannel":"/zigman2/quotes/238651765/composite","country":"US","lastPrice":37.47,"mantissa":4,"percentChange":7.67,"priceChange":2.67,"name":"Direxion
|
| 20 |
+
Daily AMZN Bull 2X Shares","ticker":"AMZU","timestamp":"2024-06-26T20:00:00.312+00:00","type":"ExchangeTradedFund","volume":1715784,"formattedPrice":"37.47","formattedPriceChange":"2.67","formattedPercentChange":"7.67","formattedVolume":"1.7M","url":"https://www.wsj.com/market-data/quotes/etf/US/AMZU"},{"bluegrassChannel":"/zigman2/quotes/209954846/composite","country":"US","lastPrice":44.8,"mantissa":4,"percentChange":6.36,"priceChange":2.68,"name":"ProShares
|
| 21 |
+
UltraShort Bloomberg Natural Gas","ticker":"KOLD","timestamp":"2024-06-26T20:00:00.099+00:00","type":"ExchangeTradedFund","volume":1741155,"formattedPrice":"44.80","formattedPriceChange":"2.68","formattedPercentChange":"6.36","formattedVolume":"1.7M","url":"https://www.wsj.com/market-data/quotes/etf/US/KOLD"},{"bluegrassChannel":"/zigman2/quotes/238277527/composite","country":"US","lastPrice":3.26,"mantissa":4,"percentChange":6.19,"priceChange":0.19,"name":"AdvisorShares
|
| 22 |
+
MSOS 2x Daily ETF","ticker":"MSOX","timestamp":"2024-06-26T20:00:00.08+00:00","type":"ExchangeTradedFund","volume":2141033,"formattedPrice":"3.26","formattedPriceChange":"0.19","formattedPercentChange":"6.19","formattedVolume":"2.1M","url":"https://www.wsj.com/market-data/quotes/etf/US/MSOX"},{"bluegrassChannel":"/zigman2/quotes/237549578/composite","country":"US","lastPrice":7.76,"mantissa":4,"percentChange":6.01,"priceChange":0.44,"name":"GraniteShares
|
| 23 |
+
1.25x Long Tesla Daily ETF","ticker":"TSL","timestamp":"2024-06-26T20:00:00.281+00:00","type":"ExchangeTradedFund","volume":1003507,"formattedPrice":"7.76","formattedPriceChange":"0.44","formattedPercentChange":"6.01","formattedVolume":"1.0M","url":"https://www.wsj.com/market-data/quotes/etf/US/TSL"},{"bluegrassChannel":"/zigman2/quotes/203857810/composite","country":"US","lastPrice":446.05,"mantissa":4,"percentChange":4.96,"priceChange":21.07,"name":"MicroSectors
|
| 24 |
+
FANG+ Index 3X Leveraged ETN","ticker":"FNGU","timestamp":"2024-06-26T20:00:00.072+00:00","type":"ExchangeTradedFund","volume":754444,"formattedPrice":"446.05","formattedPriceChange":"21.07","formattedPercentChange":"4.96","formattedVolume":"754.4K","url":"https://www.wsj.com/market-data/quotes/etf/US/FNGU"},{"bluegrassChannel":"/zigman2/quotes/204339773/composite","country":"US","lastPrice":34.35,"mantissa":4,"percentChange":4.31,"priceChange":1.42,"name":"Direxion
|
| 25 |
+
Daily 20+ Year Treasury Bear 3X Shares","ticker":"TMV","timestamp":"2024-06-26T20:00:00.012+00:00","type":"ExchangeTradedFund","volume":688939,"formattedPrice":"34.35","formattedPriceChange":"1.42","formattedPercentChange":"4.31","formattedVolume":"688.9K","url":"https://www.wsj.com/market-data/quotes/etf/US/TMV"},{"bluegrassChannel":"/zigman2/quotes/237549511/composite","country":"US","lastPrice":25.99,"mantissa":4,"percentChange":3.84,"priceChange":0.96,"name":"GraniteShares
|
| 26 |
+
2x Long AAPL Daily ETF","ticker":"AAPB","timestamp":"2024-06-26T20:00:00.045+00:00","type":"ExchangeTradedFund","volume":324531,"formattedPrice":"25.99","formattedPriceChange":"0.96","formattedPercentChange":"3.84","formattedVolume":"324.5K","url":"https://www.wsj.com/market-data/quotes/etf/US/AAPB"},{"bluegrassChannel":"/zigman2/quotes/237549554/composite","country":"US","lastPrice":33.37,"mantissa":4,"percentChange":3.83,"priceChange":1.23,"name":"Direxion
|
| 27 |
+
Daily AAPL Bull 2X Shares","ticker":"AAPU","timestamp":"2024-06-26T20:00:00.214+00:00","type":"ExchangeTradedFund","volume":1473096,"formattedPrice":"33.37","formattedPriceChange":"1.23","formattedPercentChange":"3.83","formattedVolume":"1.5M","url":"https://www.wsj.com/market-data/quotes/etf/US/AAPU"},{"bluegrassChannel":"/zigman2/quotes/226726502/composite","country":"US","lastPrice":1.690645,"mantissa":4,"percentChange":3.8,"priceChange":0.062,"name":"Amplify
|
| 28 |
+
U.S. Alternative Harvest ETF","ticker":"MJUS","timestamp":"2024-06-26T20:00:00.081+00:00","type":"ExchangeTradedFund","volume":147495,"formattedPrice":"1.69","formattedPriceChange":"0.06","formattedPercentChange":"3.80","formattedVolume":"147.5K","url":"https://www.wsj.com/market-data/quotes/etf/US/MJUS"},{"bluegrassChannel":"/zigman2/quotes/253849481/composite","country":"US","lastPrice":44.71,"mantissa":4,"percentChange":3.52,"priceChange":1.52,"name":"ProShares
|
| 29 |
+
UltraShort Bitcoin ETF","ticker":"SBIT","timestamp":"2024-06-26T20:00:00.156+00:00","type":"ExchangeTradedFund","volume":106709,"formattedPrice":"44.71","formattedPriceChange":"1.52","formattedPercentChange":"3.52","formattedVolume":"106.7K","url":"https://www.wsj.com/market-data/quotes/etf/US/SBIT"},{"bluegrassChannel":"/zigman2/quotes/210103444/composite","country":"US","lastPrice":11.8,"mantissa":4,"percentChange":3.51,"priceChange":0.4,"name":"Breakwave
|
| 30 |
+
Dry Bulk Shipping ETF","ticker":"BDRY","timestamp":"2024-06-26T20:10:00.003+00:00","type":"ExchangeTradedFund","volume":61909,"formattedPrice":"11.80","formattedPriceChange":"0.40","formattedPercentChange":"3.51","formattedVolume":"61.9K","url":"https://www.wsj.com/market-data/quotes/etf/US/BDRY"},{"bluegrassChannel":"/zigman2/quotes/220307682/composite","country":"US","lastPrice":7.82,"mantissa":4,"percentChange":3.17,"priceChange":0.24,"name":"AdvisorShares
|
| 31 |
+
Pure US Cannabis ETF","ticker":"MSOS","timestamp":"2024-06-26T20:00:00.151+00:00","type":"ExchangeTradedFund","volume":8472660,"formattedPrice":"7.82","formattedPriceChange":"0.24","formattedPercentChange":"3.17","formattedVolume":"8.5M","url":"https://www.wsj.com/market-data/quotes/etf/US/MSOS"},{"bluegrassChannel":"/zigman2/quotes/208067690/composite","country":"US","lastPrice":39.909905,"mantissa":4,"percentChange":3.05,"priceChange":1.18,"name":"Global
|
| 32 |
+
X Lithium & Battery Tech ETF","ticker":"LIT","timestamp":"2024-06-26T20:00:00.093+00:00","type":"ExchangeTradedFund","volume":346696,"formattedPrice":"39.91","formattedPriceChange":"1.18","formattedPercentChange":"3.05","formattedVolume":"346.7K","url":"https://www.wsj.com/market-data/quotes/etf/US/LIT"},{"bluegrassChannel":"/zigman2/quotes/206361625/composite","country":"US","lastPrice":93,"mantissa":4,"percentChange":2.91,"priceChange":2.63,"name":"abrdn
|
| 33 |
+
Physical Platinum Shares ETF","ticker":"PPLT","timestamp":"2024-06-26T20:10:00.003+00:00","type":"ExchangeTradedFund","volume":468357,"formattedPrice":"93.00","formattedPriceChange":"2.63","formattedPercentChange":"2.91","formattedVolume":"468.4K","url":"https://www.wsj.com/market-data/quotes/etf/US/PPLT"},{"bluegrassChannel":"/zigman2/quotes/205602686/composite","country":"US","lastPrice":43.85,"mantissa":4,"percentChange":2.91,"priceChange":1.24,"name":"VanEck
|
| 34 |
+
Rare Earth & Strategic Metals ETF","ticker":"REMX","timestamp":"2024-06-26T20:10:00.004+00:00","type":"ExchangeTradedFund","volume":62127,"formattedPrice":"43.85","formattedPriceChange":"1.24","formattedPercentChange":"2.91","formattedVolume":"62.1K","url":"https://www.wsj.com/market-data/quotes/etf/US/REMX"},{"bluegrassChannel":"/zigman2/quotes/200202000/composite","country":"US","lastPrice":33.5,"mantissa":4,"percentChange":2.87,"priceChange":0.9355,"name":"ProShares
|
| 35 |
+
UltraShort 20+ Year Treasury","ticker":"TBT","timestamp":"2024-06-26T20:00:00.07+00:00","type":"ExchangeTradedFund","volume":279593,"formattedPrice":"33.50","formattedPriceChange":"0.94","formattedPercentChange":"2.87","formattedVolume":"279.6K","url":"https://www.wsj.com/market-data/quotes/etf/US/TBT"},{"bluegrassChannel":"/zigman2/quotes/202654666/composite","country":"US","lastPrice":9.8255,"mantissa":4,"percentChange":2.83,"priceChange":0.2705,"name":"GraniteShares
|
| 36 |
+
Platinum Shares","ticker":"PLTM","timestamp":"2024-06-26T20:10:00.002+00:00","type":"ExchangeTradedFund","volume":93652,"formattedPrice":"9.83","formattedPriceChange":"0.27","formattedPercentChange":"2.83","formattedVolume":"93.7K","url":"https://www.wsj.com/market-data/quotes/etf/US/PLTM"},{"bluegrassChannel":"/zigman2/quotes/205592609/composite","country":"US","lastPrice":7.41,"mantissa":4,"percentChange":2.63,"priceChange":0.19,"name":"Direxion
|
| 37 |
+
Daily S&P Biotech Bear 3X Shares","ticker":"LABD","timestamp":"2024-06-26T20:00:00.116+00:00","type":"ExchangeTradedFund","volume":13794173,"formattedPrice":"7.41","formattedPriceChange":"0.19","formattedPercentChange":"2.63","formattedVolume":"13.8M","url":"https://www.wsj.com/market-data/quotes/etf/US/LABD"},{"bluegrassChannel":"/zigman2/quotes/201314823/composite","country":"US","lastPrice":23.2,"mantissa":4,"percentChange":2.16,"priceChange":0.49,"name":"Direxion
|
| 38 |
+
Daily Energy Bear 2x Shares","ticker":"ERY","timestamp":"2024-06-26T20:00:00.012+00:00","type":"ExchangeTradedFund","volume":290868,"formattedPrice":"23.20","formattedPriceChange":"0.49","formattedPercentChange":"2.16","formattedVolume":"290.9K","url":"https://www.wsj.com/market-data/quotes/etf/US/ERY"},{"bluegrassChannel":"/zigman2/quotes/205355270/composite","country":"US","lastPrice":9.62,"mantissa":4,"percentChange":2.12,"priceChange":0.2,"name":"Direxion
|
| 39 |
+
Daily S&P Oil & Gas Exp. & Prod. Bear 2X Shares","ticker":"DRIP","timestamp":"2024-06-26T19:59:56.224+00:00","type":"ExchangeTradedFund","volume":867825,"formattedPrice":"9.62","formattedPriceChange":"0.20","formattedPercentChange":"2.12","formattedVolume":"867.8K","url":"https://www.wsj.com/market-data/quotes/etf/US/DRIP"},{"bluegrassChannel":"/zigman2/quotes/204291195/composite","country":"US","lastPrice":28.94,"mantissa":4,"percentChange":2.11,"priceChange":0.5994,"name":"ALPS
|
| 40 |
+
Clean Energy ETF","ticker":"ACES","timestamp":"2024-06-26T20:00:00.283+00:00","type":"ExchangeTradedFund","volume":115181,"formattedPrice":"28.94","formattedPriceChange":"0.60","formattedPercentChange":"2.11","formattedVolume":"115.2K","url":"https://www.wsj.com/market-data/quotes/etf/US/ACES"}],"latestTimestamp":"2024-06-26T20:10:00.004+00:00","timestamp":"4:10
|
| 41 |
+
PM EDT 6/26/24"},"hash":"{\"id\":\"{\\\"application\\\":\\\"WSJ\\\",\\\"etfMover\\\":\\\"leaders\\\",\\\"count\\\":25}\",\"type\":\"mdc_etfmovers\",\"data\":{\"instruments\":[{\"bluegrassChannel\":\"/zigman2/quotes/247790433/composite\",\"country\":\"US\",\"lastPrice\":13.74,\"mantissa\":4,\"percentChange\":9.66,\"priceChange\":1.21,\"name\":\"GraniteShares
|
| 42 |
+
2x Long TSLA Daily ETF\",\"ticker\":\"TSLR\",\"timestamp\":\"2024-06-26T20:00:00.05+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":968702,\"formattedPrice\":\"13.74\",\"formattedPriceChange\":\"1.21\",\"formattedPercentChange\":\"9.66\",\"formattedVolume\":\"968.7K\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/TSLR\"},{\"bluegrassChannel\":\"/zigman2/quotes/237549522/composite\",\"country\":\"US\",\"lastPrice\":9.06,\"mantissa\":4,\"percentChange\":9.55,\"priceChange\":0.79,\"name\":\"Direxion
|
| 43 |
+
Daily TSLA Bull 2X Shares\",\"ticker\":\"TSLL\",\"timestamp\":\"2024-06-26T20:00:00.071+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":54560126,\"formattedPrice\":\"9.06\",\"formattedPriceChange\":\"0.79\",\"formattedPercentChange\":\"9.55\",\"formattedVolume\":\"54.6M\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/TSLL\"},{\"bluegrassChannel\":\"/zigman2/quotes/249145372/composite\",\"country\":\"US\",\"lastPrice\":12.45,\"mantissa\":4,\"percentChange\":9.4,\"priceChange\":1.07,\"name\":\"T-Rex
|
| 44 |
+
2X Long Tesla Daily Target ETF\",\"ticker\":\"TSLT\",\"timestamp\":\"2024-06-26T20:00:00.073+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":8589108,\"formattedPrice\":\"12.45\",\"formattedPriceChange\":\"1.07\",\"formattedPercentChange\":\"9.40\",\"formattedVolume\":\"8.6M\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/TSLT\"},{\"bluegrassChannel\":\"/zigman2/quotes/253529414/composite\",\"country\":\"US\",\"lastPrice\":29.61,\"mantissa\":4,\"percentChange\":7.71,\"priceChange\":2.12,\"name\":\"GraniteShares
|
| 45 |
+
2x Long AMZN Daily ETF\",\"ticker\":\"AMZZ\",\"timestamp\":\"2024-06-26T19:59:56.953+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":263692,\"formattedPrice\":\"29.61\",\"formattedPriceChange\":\"2.12\",\"formattedPercentChange\":\"7.71\",\"formattedVolume\":\"263.7K\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/AMZZ\"},{\"bluegrassChannel\":\"/zigman2/quotes/238651765/composite\",\"country\":\"US\",\"lastPrice\":37.47,\"mantissa\":4,\"percentChange\":7.67,\"priceChange\":2.67,\"name\":\"Direxion
|
| 46 |
+
Daily AMZN Bull 2X Shares\",\"ticker\":\"AMZU\",\"timestamp\":\"2024-06-26T20:00:00.312+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":1715784,\"formattedPrice\":\"37.47\",\"formattedPriceChange\":\"2.67\",\"formattedPercentChange\":\"7.67\",\"formattedVolume\":\"1.7M\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/AMZU\"},{\"bluegrassChannel\":\"/zigman2/quotes/209954846/composite\",\"country\":\"US\",\"lastPrice\":44.8,\"mantissa\":4,\"percentChange\":6.36,\"priceChange\":2.68,\"name\":\"ProShares
|
| 47 |
+
UltraShort Bloomberg Natural Gas\",\"ticker\":\"KOLD\",\"timestamp\":\"2024-06-26T20:00:00.099+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":1741155,\"formattedPrice\":\"44.80\",\"formattedPriceChange\":\"2.68\",\"formattedPercentChange\":\"6.36\",\"formattedVolume\":\"1.7M\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/KOLD\"},{\"bluegrassChannel\":\"/zigman2/quotes/238277527/composite\",\"country\":\"US\",\"lastPrice\":3.26,\"mantissa\":4,\"percentChange\":6.19,\"priceChange\":0.19,\"name\":\"AdvisorShares
|
| 48 |
+
MSOS 2x Daily ETF\",\"ticker\":\"MSOX\",\"timestamp\":\"2024-06-26T20:00:00.08+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":2141033,\"formattedPrice\":\"3.26\",\"formattedPriceChange\":\"0.19\",\"formattedPercentChange\":\"6.19\",\"formattedVolume\":\"2.1M\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/MSOX\"},{\"bluegrassChannel\":\"/zigman2/quotes/237549578/composite\",\"country\":\"US\",\"lastPrice\":7.76,\"mantissa\":4,\"percentChange\":6.01,\"priceChange\":0.44,\"name\":\"GraniteShares
|
| 49 |
+
1.25x Long Tesla Daily ETF\",\"ticker\":\"TSL\",\"timestamp\":\"2024-06-26T20:00:00.281+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":1003507,\"formattedPrice\":\"7.76\",\"formattedPriceChange\":\"0.44\",\"formattedPercentChange\":\"6.01\",\"formattedVolume\":\"1.0M\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/TSL\"},{\"bluegrassChannel\":\"/zigman2/quotes/203857810/composite\",\"country\":\"US\",\"lastPrice\":446.05,\"mantissa\":4,\"percentChange\":4.96,\"priceChange\":21.07,\"name\":\"MicroSectors
|
| 50 |
+
FANG+ Index 3X Leveraged ETN\",\"ticker\":\"FNGU\",\"timestamp\":\"2024-06-26T20:00:00.072+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":754444,\"formattedPrice\":\"446.05\",\"formattedPriceChange\":\"21.07\",\"formattedPercentChange\":\"4.96\",\"formattedVolume\":\"754.4K\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/FNGU\"},{\"bluegrassChannel\":\"/zigman2/quotes/204339773/composite\",\"country\":\"US\",\"lastPrice\":34.35,\"mantissa\":4,\"percentChange\":4.31,\"priceChange\":1.42,\"name\":\"Direxion
|
| 51 |
+
Daily 20+ Year Treasury Bear 3X Shares\",\"ticker\":\"TMV\",\"timestamp\":\"2024-06-26T20:00:00.012+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":688939,\"formattedPrice\":\"34.35\",\"formattedPriceChange\":\"1.42\",\"formattedPercentChange\":\"4.31\",\"formattedVolume\":\"688.9K\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/TMV\"},{\"bluegrassChannel\":\"/zigman2/quotes/237549511/composite\",\"country\":\"US\",\"lastPrice\":25.99,\"mantissa\":4,\"percentChange\":3.84,\"priceChange\":0.96,\"name\":\"GraniteShares
|
| 52 |
+
2x Long AAPL Daily ETF\",\"ticker\":\"AAPB\",\"timestamp\":\"2024-06-26T20:00:00.045+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":324531,\"formattedPrice\":\"25.99\",\"formattedPriceChange\":\"0.96\",\"formattedPercentChange\":\"3.84\",\"formattedVolume\":\"324.5K\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/AAPB\"},{\"bluegrassChannel\":\"/zigman2/quotes/237549554/composite\",\"country\":\"US\",\"lastPrice\":33.37,\"mantissa\":4,\"percentChange\":3.83,\"priceChange\":1.23,\"name\":\"Direxion
|
| 53 |
+
Daily AAPL Bull 2X Shares\",\"ticker\":\"AAPU\",\"timestamp\":\"2024-06-26T20:00:00.214+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":1473096,\"formattedPrice\":\"33.37\",\"formattedPriceChange\":\"1.23\",\"formattedPercentChange\":\"3.83\",\"formattedVolume\":\"1.5M\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/AAPU\"},{\"bluegrassChannel\":\"/zigman2/quotes/226726502/composite\",\"country\":\"US\",\"lastPrice\":1.690645,\"mantissa\":4,\"percentChange\":3.8,\"priceChange\":0.062,\"name\":\"Amplify
|
| 54 |
+
U.S. Alternative Harvest ETF\",\"ticker\":\"MJUS\",\"timestamp\":\"2024-06-26T20:00:00.081+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":147495,\"formattedPrice\":\"1.69\",\"formattedPriceChange\":\"0.06\",\"formattedPercentChange\":\"3.80\",\"formattedVolume\":\"147.5K\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/MJUS\"},{\"bluegrassChannel\":\"/zigman2/quotes/253849481/composite\",\"country\":\"US\",\"lastPrice\":44.71,\"mantissa\":4,\"percentChange\":3.52,\"priceChange\":1.52,\"name\":\"ProShares
|
| 55 |
+
UltraShort Bitcoin ETF\",\"ticker\":\"SBIT\",\"timestamp\":\"2024-06-26T20:00:00.156+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":106709,\"formattedPrice\":\"44.71\",\"formattedPriceChange\":\"1.52\",\"formattedPercentChange\":\"3.52\",\"formattedVolume\":\"106.7K\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/SBIT\"},{\"bluegrassChannel\":\"/zigman2/quotes/210103444/composite\",\"country\":\"US\",\"lastPrice\":11.8,\"mantissa\":4,\"percentChange\":3.51,\"priceChange\":0.4,\"name\":\"Breakwave
|
| 56 |
+
Dry Bulk Shipping ETF\",\"ticker\":\"BDRY\",\"timestamp\":\"2024-06-26T20:10:00.003+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":61909,\"formattedPrice\":\"11.80\",\"formattedPriceChange\":\"0.40\",\"formattedPercentChange\":\"3.51\",\"formattedVolume\":\"61.9K\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/BDRY\"},{\"bluegrassChannel\":\"/zigman2/quotes/220307682/composite\",\"country\":\"US\",\"lastPrice\":7.82,\"mantissa\":4,\"percentChange\":3.17,\"priceChange\":0.24,\"name\":\"AdvisorShares
|
| 57 |
+
Pure US Cannabis ETF\",\"ticker\":\"MSOS\",\"timestamp\":\"2024-06-26T20:00:00.151+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":8472660,\"formattedPrice\":\"7.82\",\"formattedPriceChange\":\"0.24\",\"formattedPercentChange\":\"3.17\",\"formattedVolume\":\"8.5M\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/MSOS\"},{\"bluegrassChannel\":\"/zigman2/quotes/208067690/composite\",\"country\":\"US\",\"lastPrice\":39.909905,\"mantissa\":4,\"percentChange\":3.05,\"priceChange\":1.18,\"name\":\"Global
|
| 58 |
+
X Lithium & Battery Tech ETF\",\"ticker\":\"LIT\",\"timestamp\":\"2024-06-26T20:00:00.093+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":346696,\"formattedPrice\":\"39.91\",\"formattedPriceChange\":\"1.18\",\"formattedPercentChange\":\"3.05\",\"formattedVolume\":\"346.7K\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/LIT\"},{\"bluegrassChannel\":\"/zigman2/quotes/206361625/composite\",\"country\":\"US\",\"lastPrice\":93,\"mantissa\":4,\"percentChange\":2.91,\"priceChange\":2.63,\"name\":\"abrdn
|
| 59 |
+
Physical Platinum Shares ETF\",\"ticker\":\"PPLT\",\"timestamp\":\"2024-06-26T20:10:00.003+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":468357,\"formattedPrice\":\"93.00\",\"formattedPriceChange\":\"2.63\",\"formattedPercentChange\":\"2.91\",\"formattedVolume\":\"468.4K\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/PPLT\"},{\"bluegrassChannel\":\"/zigman2/quotes/205602686/composite\",\"country\":\"US\",\"lastPrice\":43.85,\"mantissa\":4,\"percentChange\":2.91,\"priceChange\":1.24,\"name\":\"VanEck
|
| 60 |
+
Rare Earth & Strategic Metals ETF\",\"ticker\":\"REMX\",\"timestamp\":\"2024-06-26T20:10:00.004+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":62127,\"formattedPrice\":\"43.85\",\"formattedPriceChange\":\"1.24\",\"formattedPercentChange\":\"2.91\",\"formattedVolume\":\"62.1K\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/REMX\"},{\"bluegrassChannel\":\"/zigman2/quotes/200202000/composite\",\"country\":\"US\",\"lastPrice\":33.5,\"mantissa\":4,\"percentChange\":2.87,\"priceChange\":0.9355,\"name\":\"ProShares
|
| 61 |
+
UltraShort 20+ Year Treasury\",\"ticker\":\"TBT\",\"timestamp\":\"2024-06-26T20:00:00.07+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":279593,\"formattedPrice\":\"33.50\",\"formattedPriceChange\":\"0.94\",\"formattedPercentChange\":\"2.87\",\"formattedVolume\":\"279.6K\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/TBT\"},{\"bluegrassChannel\":\"/zigman2/quotes/202654666/composite\",\"country\":\"US\",\"lastPrice\":9.8255,\"mantissa\":4,\"percentChange\":2.83,\"priceChange\":0.2705,\"name\":\"GraniteShares
|
| 62 |
+
Platinum Shares\",\"ticker\":\"PLTM\",\"timestamp\":\"2024-06-26T20:10:00.002+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":93652,\"formattedPrice\":\"9.83\",\"formattedPriceChange\":\"0.27\",\"formattedPercentChange\":\"2.83\",\"formattedVolume\":\"93.7K\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/PLTM\"},{\"bluegrassChannel\":\"/zigman2/quotes/205592609/composite\",\"country\":\"US\",\"lastPrice\":7.41,\"mantissa\":4,\"percentChange\":2.63,\"priceChange\":0.19,\"name\":\"Direxion
|
| 63 |
+
Daily S&P Biotech Bear 3X Shares\",\"ticker\":\"LABD\",\"timestamp\":\"2024-06-26T20:00:00.116+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":13794173,\"formattedPrice\":\"7.41\",\"formattedPriceChange\":\"0.19\",\"formattedPercentChange\":\"2.63\",\"formattedVolume\":\"13.8M\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/LABD\"},{\"bluegrassChannel\":\"/zigman2/quotes/201314823/composite\",\"country\":\"US\",\"lastPrice\":23.2,\"mantissa\":4,\"percentChange\":2.16,\"priceChange\":0.49,\"name\":\"Direxion
|
| 64 |
+
Daily Energy Bear 2x Shares\",\"ticker\":\"ERY\",\"timestamp\":\"2024-06-26T20:00:00.012+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":290868,\"formattedPrice\":\"23.20\",\"formattedPriceChange\":\"0.49\",\"formattedPercentChange\":\"2.16\",\"formattedVolume\":\"290.9K\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/ERY\"},{\"bluegrassChannel\":\"/zigman2/quotes/205355270/composite\",\"country\":\"US\",\"lastPrice\":9.62,\"mantissa\":4,\"percentChange\":2.12,\"priceChange\":0.2,\"name\":\"Direxion
|
| 65 |
+
Daily S&P Oil & Gas Exp. & Prod. Bear 2X Shares\",\"ticker\":\"DRIP\",\"timestamp\":\"2024-06-26T19:59:56.224+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":867825,\"formattedPrice\":\"9.62\",\"formattedPriceChange\":\"0.20\",\"formattedPercentChange\":\"2.12\",\"formattedVolume\":\"867.8K\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/DRIP\"},{\"bluegrassChannel\":\"/zigman2/quotes/204291195/composite\",\"country\":\"US\",\"lastPrice\":28.94,\"mantissa\":4,\"percentChange\":2.11,\"priceChange\":0.5994,\"name\":\"ALPS
|
| 66 |
+
Clean Energy ETF\",\"ticker\":\"ACES\",\"timestamp\":\"2024-06-26T20:00:00.283+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":115181,\"formattedPrice\":\"28.94\",\"formattedPriceChange\":\"0.60\",\"formattedPercentChange\":\"2.11\",\"formattedVolume\":\"115.2K\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/ACES\"}],\"latestTimestamp\":\"2024-06-26T20:10:00.004+00:00\",\"timestamp\":\"4:10
|
| 67 |
+
PM EDT 6/26/24\"}}"}'
|
| 68 |
+
headers:
|
| 69 |
+
Cache-Control:
|
| 70 |
+
- public, max-age=120, no-cache=Set-Cookie
|
| 71 |
+
Connection:
|
| 72 |
+
- keep-alive
|
| 73 |
+
Content-Encoding:
|
| 74 |
+
- gzip
|
| 75 |
+
Content-Security-Policy:
|
| 76 |
+
- 'default-src ''self'' cdn.privacy-mgmt.com;script-src ''self'' *.wsj.net *.wsj.com
|
| 77 |
+
''unsafe-inline'' ''unsafe-eval'';script-src-elem * ''unsafe-inline'';manifest-src
|
| 78 |
+
''self'' *.wsj.com;media-src * data: blob: https:;worker-src * ''unsafe-inline''
|
| 79 |
+
''unsafe-eval'' blob: data:;frame-src * ''unsafe-inline'';connect-src * ''unsafe-inline''
|
| 80 |
+
''unsafe-eval'';form-action * ''unsafe-inline'';frame-ancestors *;script-src-attr
|
| 81 |
+
''unsafe-inline'';object-src ''self'' ''unsafe-inline'';img-src * data: blob:
|
| 82 |
+
https:;font-src ''self'' * ''unsafe-inline'';upgrade-insecure-requests;base-uri
|
| 83 |
+
''self'';style-src ''self'' https: ''unsafe-inline'''
|
| 84 |
+
Content-Type:
|
| 85 |
+
- application/json; charset=utf-8
|
| 86 |
+
Cross-Origin-Resource-Policy:
|
| 87 |
+
- same-site
|
| 88 |
+
Date:
|
| 89 |
+
- Thu, 27 Jun 2024 10:16:49 GMT
|
| 90 |
+
ETag:
|
| 91 |
+
- W/"6250-dC9fQ3IzOiKHMLU+CGjUNlMKgjI"
|
| 92 |
+
GC-Versions:
|
| 93 |
+
- 2.2.354|0.4.1241|4.1.2
|
| 94 |
+
Origin-Agent-Cluster:
|
| 95 |
+
- ?1
|
| 96 |
+
Referrer-Policy:
|
| 97 |
+
- strict-origin-when-cross-origin,unsafe-url
|
| 98 |
+
Strict-Transport-Security:
|
| 99 |
+
- max-age=63072000; includeSubDomains; preload
|
| 100 |
+
Transfer-Encoding:
|
| 101 |
+
- chunked
|
| 102 |
+
Via:
|
| 103 |
+
- 1.1 432282689bafd802e8ec9636c256a3b0.cloudfront.net (CloudFront)
|
| 104 |
+
X-Amz-Cf-Id:
|
| 105 |
+
- 16ynGXYSKPY1hVz7Q3HhmlOXx_UPJbpoNvoxUOdayjFosHPv9Z_MXQ==
|
| 106 |
+
X-Amz-Cf-Pop:
|
| 107 |
+
- AMS58-P1
|
| 108 |
+
X-Cache:
|
| 109 |
+
- Miss from cloudfront
|
| 110 |
+
X-Content-Type-Options:
|
| 111 |
+
- nosniff
|
| 112 |
+
X-DNS-Prefetch-Control:
|
| 113 |
+
- 'off'
|
| 114 |
+
X-Download-Options:
|
| 115 |
+
- noopen
|
| 116 |
+
X-Frame-Options:
|
| 117 |
+
- SAMEORIGIN
|
| 118 |
+
X-Permitted-Cross-Domain-Policies:
|
| 119 |
+
- none
|
| 120 |
+
X-XSS-Protection:
|
| 121 |
+
- 1; mode=block
|
| 122 |
+
status:
|
| 123 |
+
code: 200
|
| 124 |
+
message: OK
|
| 125 |
+
version: 1
|
openbb_platform/providers/wsj/tests/record/http/test_wsj_fetchers/test_wsj_losers_fetcher_urllib3_v1.yaml
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
interactions:
|
| 2 |
+
- request:
|
| 3 |
+
body: null
|
| 4 |
+
headers:
|
| 5 |
+
Accept:
|
| 6 |
+
- '*/*'
|
| 7 |
+
Accept-Encoding:
|
| 8 |
+
- gzip, deflate
|
| 9 |
+
Connection:
|
| 10 |
+
- keep-alive
|
| 11 |
+
method: GET
|
| 12 |
+
uri: https://www.wsj.com/market-data/mutualfunds-etfs/etfmovers?id=%7B%22application%22%3A%22WSJ%22%2C%22etfMover%22%3A%22laggards%22%2C%22count%22%3A25%7D&type=mdc_etfmovers
|
| 13 |
+
response:
|
| 14 |
+
body:
|
| 15 |
+
string: !!binary |
|
| 16 |
+
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
|
| 83 |
+
QGtCI6yxas8b5Eby218GL/8DLfvCUmxjAAA=
|
| 84 |
+
headers:
|
| 85 |
+
Cache-Control:
|
| 86 |
+
- public, max-age=120, no-cache=Set-Cookie
|
| 87 |
+
Connection:
|
| 88 |
+
- keep-alive
|
| 89 |
+
Content-Encoding:
|
| 90 |
+
- gzip
|
| 91 |
+
Content-Security-Policy:
|
| 92 |
+
- 'default-src ''self'' cdn.privacy-mgmt.com;script-src ''self'' *.wsj.net *.wsj.com
|
| 93 |
+
''unsafe-inline'' ''unsafe-eval'';script-src-elem * ''unsafe-inline'';manifest-src
|
| 94 |
+
''self'' *.wsj.com;media-src * data: blob: https:;worker-src * ''unsafe-inline''
|
| 95 |
+
''unsafe-eval'' blob: data:;frame-src * ''unsafe-inline'';connect-src * ''unsafe-inline''
|
| 96 |
+
''unsafe-eval'';form-action * ''unsafe-inline'';frame-ancestors *;script-src-attr
|
| 97 |
+
''unsafe-inline'';object-src ''self'' ''unsafe-inline'';img-src * data: blob:
|
| 98 |
+
https:;font-src ''self'' * ''unsafe-inline'';upgrade-insecure-requests;base-uri
|
| 99 |
+
''self'';style-src ''self'' https: ''unsafe-inline'''
|
| 100 |
+
Content-Type:
|
| 101 |
+
- application/json; charset=utf-8
|
| 102 |
+
Cross-Origin-Resource-Policy:
|
| 103 |
+
- same-site
|
| 104 |
+
Date:
|
| 105 |
+
- Mon, 01 Jul 2024 21:23:38 GMT
|
| 106 |
+
ETag:
|
| 107 |
+
- W/"636c-eJv3RdEgiAlOHTRWZrTTZEH0VVQ"
|
| 108 |
+
GC-Versions:
|
| 109 |
+
- 2.2.363|0.4.1243|4.1.2
|
| 110 |
+
Origin-Agent-Cluster:
|
| 111 |
+
- ?1
|
| 112 |
+
Referrer-Policy:
|
| 113 |
+
- strict-origin-when-cross-origin,unsafe-url
|
| 114 |
+
Strict-Transport-Security:
|
| 115 |
+
- max-age=63072000; includeSubDomains; preload
|
| 116 |
+
Transfer-Encoding:
|
| 117 |
+
- chunked
|
| 118 |
+
Via:
|
| 119 |
+
- 1.1 b037e6b8c0bc85fd4b1c564301f71226.cloudfront.net (CloudFront)
|
| 120 |
+
X-Amz-Cf-Id:
|
| 121 |
+
- q8miUuoeymXaVVgX3S-VhQelrflb8do-TZ-PzZGuYMXlhZQvwL3cfw==
|
| 122 |
+
X-Amz-Cf-Pop:
|
| 123 |
+
- YVR52-P1
|
| 124 |
+
X-Cache:
|
| 125 |
+
- Miss from cloudfront
|
| 126 |
+
X-Content-Type-Options:
|
| 127 |
+
- nosniff
|
| 128 |
+
X-DNS-Prefetch-Control:
|
| 129 |
+
- 'off'
|
| 130 |
+
X-Download-Options:
|
| 131 |
+
- noopen
|
| 132 |
+
X-Frame-Options:
|
| 133 |
+
- SAMEORIGIN
|
| 134 |
+
X-Permitted-Cross-Domain-Policies:
|
| 135 |
+
- none
|
| 136 |
+
X-XSS-Protection:
|
| 137 |
+
- 1; mode=block
|
| 138 |
+
status:
|
| 139 |
+
code: 200
|
| 140 |
+
message: OK
|
| 141 |
+
version: 1
|
openbb_platform/providers/wsj/tests/record/http/test_wsj_fetchers/test_wsj_losers_fetcher_urllib3_v2.yaml
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
interactions:
|
| 2 |
+
- request:
|
| 3 |
+
body: null
|
| 4 |
+
headers:
|
| 5 |
+
Accept:
|
| 6 |
+
- '*/*'
|
| 7 |
+
Accept-Encoding:
|
| 8 |
+
- gzip, deflate
|
| 9 |
+
Connection:
|
| 10 |
+
- keep-alive
|
| 11 |
+
method: GET
|
| 12 |
+
uri: https://www.wsj.com/market-data/mutualfunds-etfs/etfmovers?id=%7B%22application%22%3A%22WSJ%22%2C%22etfMover%22%3A%22laggards%22%2C%22count%22%3A25%7D&type=mdc_etfmovers
|
| 13 |
+
response:
|
| 14 |
+
body:
|
| 15 |
+
string: '{"id":"{\"application\":\"WSJ\",\"etfMover\":\"laggards\",\"count\":25}","type":"mdc_etfmovers","data":{"instruments":[{"bluegrassChannel":"/zigman2/quotes/249145351/composite","country":"US","lastPrice":24.85,"mantissa":4,"percentChange":-9.54,"priceChange":-2.62,"name":"T-Rex
|
| 16 |
+
2X Inverse Tesla Daily Target ETF","ticker":"TSLZ","timestamp":"2024-06-26T20:00:00.215+00:00","type":"ExchangeTradedFund","volume":1905472,"formattedPrice":"24.85","formattedPriceChange":"-2.62","formattedPercentChange":"-9.54","formattedVolume":"1.9M","url":"https://www.wsj.com/market-data/quotes/etf/US/TSLZ"},{"bluegrassChannel":"/zigman2/quotes/247790480/composite","country":"US","lastPrice":15.77,"mantissa":4,"percentChange":-9.47,"priceChange":-1.65,"name":"GraniteShares
|
| 17 |
+
2x Short TSLA Daily ETF","ticker":"TSDD","timestamp":"2024-06-26T19:59:57.99+00:00","type":"ExchangeTradedFund","volume":400480,"formattedPrice":"15.77","formattedPriceChange":"-1.65","formattedPercentChange":"-9.47","formattedVolume":"400.5K","url":"https://www.wsj.com/market-data/quotes/etf/US/TSDD"},{"bluegrassChannel":"/zigman2/quotes/249343380/composite","country":"US","lastPrice":15.82,"mantissa":4,"percentChange":-9.44,"priceChange":-1.65,"name":"YieldMax
|
| 18 |
+
MRNA Option Income Strategy ETF","ticker":"MRNY","timestamp":"2024-06-26T20:00:00.083+00:00","type":"ExchangeTradedFund","volume":127586,"formattedPrice":"15.82","formattedPriceChange":"-1.65","formattedPercentChange":"-9.44","formattedVolume":"127.6K","url":"https://www.wsj.com/market-data/quotes/etf/US/MRNY"},{"bluegrassChannel":"/zigman2/quotes/237549538/composite","country":"US","lastPrice":42.46,"mantissa":4,"percentChange":-6.78,"priceChange":-3.09,"name":"GraniteShares
|
| 19 |
+
2x Long COIN Daily ETF","ticker":"CONL","timestamp":"2024-06-26T20:00:00.008+00:00","type":"ExchangeTradedFund","volume":1870246,"formattedPrice":"42.46","formattedPriceChange":"-3.09","formattedPercentChange":"-6.78","formattedVolume":"1.9M","url":"https://www.wsj.com/market-data/quotes/etf/US/CONL"},{"bluegrassChannel":"/zigman2/quotes/210336898/composite","country":"US","lastPrice":17.25,"mantissa":4,"percentChange":-6.45,"priceChange":-1.19,"name":"ProShares
|
| 20 |
+
Ultra Bloomberg Natural Gas","ticker":"BOIL","timestamp":"2024-06-26T20:00:00.266+00:00","type":"ExchangeTradedFund","volume":7026297,"formattedPrice":"17.25","formattedPriceChange":"-1.19","formattedPercentChange":"-6.45","formattedVolume":"7.0M","url":"https://www.wsj.com/market-data/quotes/etf/US/BOIL"},{"bluegrassChannel":"/zigman2/quotes/254694745/composite","country":"US","lastPrice":18.1944,"mantissa":4,"percentChange":-4.99,"priceChange":-0.9556,"name":"YieldMax
|
| 21 |
+
Short TSLA Option Income Strategy ETF","ticker":"CRSH","timestamp":"2024-06-26T20:00:00.091+00:00","type":"ExchangeTradedFund","volume":81941,"formattedPrice":"18.19","formattedPriceChange":"-0.96","formattedPercentChange":"-4.99","formattedVolume":"81.9K","url":"https://www.wsj.com/market-data/quotes/etf/US/CRSH"},{"bluegrassChannel":"/zigman2/quotes/236905666/composite","country":"US","lastPrice":32.21,"mantissa":4,"percentChange":-4.79,"priceChange":-1.62,"name":"Tradr
|
| 22 |
+
TSLA Bear Daily ETF","ticker":"TSLQ","timestamp":"2024-06-26T20:00:00.414+00:00","type":"ExchangeTradedFund","volume":2690615,"formattedPrice":"32.21","formattedPriceChange":"-1.62","formattedPercentChange":"-4.79","formattedVolume":"2.7M","url":"https://www.wsj.com/market-data/quotes/etf/US/TSLQ"},{"bluegrassChannel":"/zigman2/quotes/237549519/composite","country":"US","lastPrice":21.18,"mantissa":4,"percentChange":-4.77,"priceChange":-1.06,"name":"Direxion
|
| 23 |
+
Daily TSLA Bear 1X Shares","ticker":"TSLS","timestamp":"2024-06-26T20:00:00.053+00:00","type":"ExchangeTradedFund","volume":1741663,"formattedPrice":"21.18","formattedPriceChange":"-1.06","formattedPercentChange":"-4.77","formattedVolume":"1.7M","url":"https://www.wsj.com/market-data/quotes/etf/US/TSLS"},{"bluegrassChannel":"/zigman2/quotes/209848629/composite","country":"US","lastPrice":24.71,"mantissa":4,"percentChange":-4.67,"priceChange":-1.21,"name":"MicroSectors
|
| 24 |
+
FANG+ Index -3X Inverse Leveraged ETN","ticker":"FNGD","timestamp":"2024-06-26T20:00:00.06+00:00","type":"ExchangeTradedFund","volume":1214469,"formattedPrice":"24.71","formattedPriceChange":"-1.21","formattedPercentChange":"-4.67","formattedVolume":"1.2M","url":"https://www.wsj.com/market-data/quotes/etf/US/FNGD"},{"bluegrassChannel":"/zigman2/quotes/209018185/composite","country":"US","lastPrice":50.8,"mantissa":4,"percentChange":-4.26,"priceChange":-2.26,"name":"Direxion
|
| 25 |
+
Daily 20+ Year Treasury Bull 3X Shares","ticker":"TMF","timestamp":"2024-06-26T20:00:00.105+00:00","type":"ExchangeTradedFund","volume":5399325,"formattedPrice":"50.80","formattedPriceChange":"-2.26","formattedPercentChange":"-4.26","formattedVolume":"5.4M","url":"https://www.wsj.com/market-data/quotes/etf/US/TMF"},{"bluegrassChannel":"/zigman2/quotes/206220955/composite","country":"US","lastPrice":17.1,"mantissa":4,"percentChange":-4.2,"priceChange":-0.75,"name":"Direxion
|
| 26 |
+
Daily MSCI Mexico Bull 3X Shares","ticker":"MEXX","timestamp":"2024-06-26T20:10:00.002+00:00","type":"ExchangeTradedFund","volume":82612,"formattedPrice":"17.10","formattedPriceChange":"-0.75","formattedPercentChange":"-4.20","formattedVolume":"82.6K","url":"https://www.wsj.com/market-data/quotes/etf/US/MEXX"},{"bluegrassChannel":"/zigman2/quotes/238651729/composite","country":"US","lastPrice":13.46,"mantissa":4,"percentChange":-3.98,"priceChange":-0.5573,"name":"Direxion
|
| 27 |
+
Daily AMZN Bear 1X Shares","ticker":"AMZD","timestamp":"2024-06-26T19:59:52.565+00:00","type":"ExchangeTradedFund","volume":76239,"formattedPrice":"13.46","formattedPriceChange":"-0.56","formattedPercentChange":"-3.98","formattedVolume":"76.2K","url":"https://www.wsj.com/market-data/quotes/etf/US/AMZD"},{"bluegrassChannel":"/zigman2/quotes/205527422/composite","country":"US","lastPrice":18.31,"mantissa":4,"percentChange":-3.58,"priceChange":-0.68,"name":"United
|
| 28 |
+
States Natural Gas Fund L.P.","ticker":"UNG","timestamp":"2024-06-26T20:00:00.075+00:00","type":"ExchangeTradedFund","volume":4092304,"formattedPrice":"18.31","formattedPriceChange":"-0.68","formattedPercentChange":"-3.58","formattedVolume":"4.1M","url":"https://www.wsj.com/market-data/quotes/etf/US/UNG"},{"bluegrassChannel":"/zigman2/quotes/253849496/composite","country":"US","lastPrice":27.66,"mantissa":4,"percentChange":-3.52,"priceChange":-1.01,"name":"ProShares
|
| 29 |
+
Ultra Bitcoin ETF","ticker":"BITU","timestamp":"2024-06-26T20:00:00.082+00:00","type":"ExchangeTradedFund","volume":642213,"formattedPrice":"27.66","formattedPriceChange":"-1.01","formattedPercentChange":"-3.52","formattedVolume":"642.2K","url":"https://www.wsj.com/market-data/quotes/etf/US/BITU"},{"bluegrassChannel":"/zigman2/quotes/246315011/composite","country":"US","lastPrice":32.9,"mantissa":4,"percentChange":-3.46,"priceChange":-1.18,"name":"2x
|
| 30 |
+
Bitcoin Strategy ETF","ticker":"BITX","timestamp":"2024-06-26T20:00:00.194+00:00","type":"ExchangeTradedFund","volume":3516762,"formattedPrice":"32.90","formattedPriceChange":"-1.18","formattedPercentChange":"-3.46","formattedVolume":"3.5M","url":"https://www.wsj.com/market-data/quotes/etf/US/BITX"},{"bluegrassChannel":"/zigman2/quotes/253529498/composite","country":"US","lastPrice":15.52,"mantissa":4,"percentChange":-3.42,"priceChange":-0.55,"name":"GraniteShares
|
| 31 |
+
2x Long AMD Daily ETF","ticker":"AMDL","timestamp":"2024-06-26T19:59:54.565+00:00","type":"ExchangeTradedFund","volume":827467,"formattedPrice":"15.52","formattedPriceChange":"-0.55","formattedPercentChange":"-3.42","formattedVolume":"827.5K","url":"https://www.wsj.com/market-data/quotes/etf/US/AMDL"},{"bluegrassChannel":"/zigman2/quotes/252759250/composite","country":"US","lastPrice":28.83,"mantissa":4,"percentChange":-3.32,"priceChange":-0.99,"name":"Valkyrie
|
| 32 |
+
Bitcoin Futures Leveraged Strategy ETF","ticker":"BTFX","timestamp":"2024-06-26T20:00:01.082+00:00","type":"ExchangeTradedFund","volume":68929,"formattedPrice":"28.83","formattedPriceChange":"-0.99","formattedPercentChange":"-3.32","formattedVolume":"68.9K","url":"https://www.wsj.com/market-data/quotes/etf/US/BTFX"},{"bluegrassChannel":"/zigman2/quotes/201714904/composite","country":"US","lastPrice":111.84,"mantissa":4,"percentChange":-3.11,"priceChange":-3.59,"name":"Direxion
|
| 33 |
+
Daily S&P Biotech Bull 3X Shares","ticker":"LABU","timestamp":"2024-06-26T20:00:00.099+00:00","type":"ExchangeTradedFund","volume":1228704,"formattedPrice":"111.84","formattedPriceChange":"-3.59","formattedPercentChange":"-3.11","formattedVolume":"1.2M","url":"https://www.wsj.com/market-data/quotes/etf/US/LABU"},{"bluegrassChannel":"/zigman2/quotes/207070561/composite","country":"US","lastPrice":19.48,"mantissa":4,"percentChange":-2.82,"priceChange":-0.5647,"name":"ProShares
|
| 34 |
+
Ultra 20+ Year Treasury","ticker":"UBT","timestamp":"2024-06-26T20:10:00.004+00:00","type":"ExchangeTradedFund","volume":54857,"formattedPrice":"19.48","formattedPriceChange":"-0.56","formattedPercentChange":"-2.82","formattedVolume":"54.9K","url":"https://www.wsj.com/market-data/quotes/etf/US/UBT"},{"bluegrassChannel":"/zigman2/quotes/204010694/composite","country":"US","lastPrice":13.11,"mantissa":4,"percentChange":-2.82,"priceChange":-0.38,"name":"First
|
| 35 |
+
Trust SkyBridge Crypto Industry & Digital Economy ETF","ticker":"CRPT","timestamp":"2024-06-26T20:10:00.002+00:00","type":"ExchangeTradedFund","volume":58628,"formattedPrice":"13.11","formattedPriceChange":"-0.38","formattedPercentChange":"-2.82","formattedVolume":"58.6K","url":"https://www.wsj.com/market-data/quotes/etf/US/CRPT"},{"bluegrassChannel":"/zigman2/quotes/247441496/composite","country":"US","lastPrice":19.72,"mantissa":4,"percentChange":-2.62,"priceChange":-0.53,"name":"YieldMax
|
| 36 |
+
COIN Option Income Strategy ETF","ticker":"CONY","timestamp":"2024-06-26T20:00:00.075+00:00","type":"ExchangeTradedFund","volume":969522,"formattedPrice":"19.72","formattedPriceChange":"-0.53","formattedPercentChange":"-2.62","formattedVolume":"969.5K","url":"https://www.wsj.com/market-data/quotes/etf/US/CONY"},{"bluegrassChannel":"/zigman2/quotes/234313102/composite","country":"US","lastPrice":5.54,"mantissa":4,"percentChange":-2.29,"priceChange":-0.13,"name":"2x
|
| 37 |
+
Long VIX Futures ETF","ticker":"UVIX","timestamp":"2024-06-26T20:00:00.091+00:00","type":"ExchangeTradedFund","volume":4818570,"formattedPrice":"5.54","formattedPriceChange":"-0.13","formattedPercentChange":"-2.29","formattedVolume":"4.8M","url":"https://www.wsj.com/market-data/quotes/etf/US/UVIX"},{"bluegrassChannel":"/zigman2/quotes/221335101/composite","country":"US","lastPrice":11.05,"mantissa":4,"percentChange":-2.21,"priceChange":-0.25,"name":"iShares
|
| 38 |
+
25+ Year Treasury STRIPS Bond ETF","ticker":"GOVZ","timestamp":"2024-06-26T20:10:02.157+00:00","type":"ExchangeTradedFund","volume":123225,"formattedPrice":"11.05","formattedPriceChange":"-0.25","formattedPercentChange":"-2.21","formattedVolume":"123.2K","url":"https://www.wsj.com/market-data/quotes/etf/US/GOVZ"},{"bluegrassChannel":"/zigman2/quotes/206454610/composite","country":"US","lastPrice":23.75,"mantissa":4,"percentChange":-2.02,"priceChange":-0.49,"name":"ARK
|
| 39 |
+
Genomic Revolution ETF","ticker":"ARKG","timestamp":"2024-06-26T20:00:00.119+00:00","type":"ExchangeTradedFund","volume":2498682,"formattedPrice":"23.75","formattedPriceChange":"-0.49","formattedPercentChange":"-2.02","formattedVolume":"2.5M","url":"https://www.wsj.com/market-data/quotes/etf/US/ARKG"},{"bluegrassChannel":"/zigman2/quotes/202473148/composite","country":"US","lastPrice":64.21,"mantissa":4,"percentChange":-2,"priceChange":-1.31,"name":"Direxion
|
| 40 |
+
Daily Energy Bull 2X Shares","ticker":"ERX","timestamp":"2024-06-26T20:10:00.003+00:00","type":"ExchangeTradedFund","volume":366870,"formattedPrice":"64.21","formattedPriceChange":"-1.31","formattedPercentChange":"-2.00","formattedVolume":"366.9K","url":"https://www.wsj.com/market-data/quotes/etf/US/ERX"}],"latestTimestamp":"2024-06-26T20:10:02.157+00:00","timestamp":"4:10
|
| 41 |
+
PM EDT 6/26/24"},"hash":"{\"id\":\"{\\\"application\\\":\\\"WSJ\\\",\\\"etfMover\\\":\\\"laggards\\\",\\\"count\\\":25}\",\"type\":\"mdc_etfmovers\",\"data\":{\"instruments\":[{\"bluegrassChannel\":\"/zigman2/quotes/249145351/composite\",\"country\":\"US\",\"lastPrice\":24.85,\"mantissa\":4,\"percentChange\":-9.54,\"priceChange\":-2.62,\"name\":\"T-Rex
|
| 42 |
+
2X Inverse Tesla Daily Target ETF\",\"ticker\":\"TSLZ\",\"timestamp\":\"2024-06-26T20:00:00.215+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":1905472,\"formattedPrice\":\"24.85\",\"formattedPriceChange\":\"-2.62\",\"formattedPercentChange\":\"-9.54\",\"formattedVolume\":\"1.9M\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/TSLZ\"},{\"bluegrassChannel\":\"/zigman2/quotes/247790480/composite\",\"country\":\"US\",\"lastPrice\":15.77,\"mantissa\":4,\"percentChange\":-9.47,\"priceChange\":-1.65,\"name\":\"GraniteShares
|
| 43 |
+
2x Short TSLA Daily ETF\",\"ticker\":\"TSDD\",\"timestamp\":\"2024-06-26T19:59:57.99+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":400480,\"formattedPrice\":\"15.77\",\"formattedPriceChange\":\"-1.65\",\"formattedPercentChange\":\"-9.47\",\"formattedVolume\":\"400.5K\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/TSDD\"},{\"bluegrassChannel\":\"/zigman2/quotes/249343380/composite\",\"country\":\"US\",\"lastPrice\":15.82,\"mantissa\":4,\"percentChange\":-9.44,\"priceChange\":-1.65,\"name\":\"YieldMax
|
| 44 |
+
MRNA Option Income Strategy ETF\",\"ticker\":\"MRNY\",\"timestamp\":\"2024-06-26T20:00:00.083+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":127586,\"formattedPrice\":\"15.82\",\"formattedPriceChange\":\"-1.65\",\"formattedPercentChange\":\"-9.44\",\"formattedVolume\":\"127.6K\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/MRNY\"},{\"bluegrassChannel\":\"/zigman2/quotes/237549538/composite\",\"country\":\"US\",\"lastPrice\":42.46,\"mantissa\":4,\"percentChange\":-6.78,\"priceChange\":-3.09,\"name\":\"GraniteShares
|
| 45 |
+
2x Long COIN Daily ETF\",\"ticker\":\"CONL\",\"timestamp\":\"2024-06-26T20:00:00.008+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":1870246,\"formattedPrice\":\"42.46\",\"formattedPriceChange\":\"-3.09\",\"formattedPercentChange\":\"-6.78\",\"formattedVolume\":\"1.9M\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/CONL\"},{\"bluegrassChannel\":\"/zigman2/quotes/210336898/composite\",\"country\":\"US\",\"lastPrice\":17.25,\"mantissa\":4,\"percentChange\":-6.45,\"priceChange\":-1.19,\"name\":\"ProShares
|
| 46 |
+
Ultra Bloomberg Natural Gas\",\"ticker\":\"BOIL\",\"timestamp\":\"2024-06-26T20:00:00.266+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":7026297,\"formattedPrice\":\"17.25\",\"formattedPriceChange\":\"-1.19\",\"formattedPercentChange\":\"-6.45\",\"formattedVolume\":\"7.0M\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/BOIL\"},{\"bluegrassChannel\":\"/zigman2/quotes/254694745/composite\",\"country\":\"US\",\"lastPrice\":18.1944,\"mantissa\":4,\"percentChange\":-4.99,\"priceChange\":-0.9556,\"name\":\"YieldMax
|
| 47 |
+
Short TSLA Option Income Strategy ETF\",\"ticker\":\"CRSH\",\"timestamp\":\"2024-06-26T20:00:00.091+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":81941,\"formattedPrice\":\"18.19\",\"formattedPriceChange\":\"-0.96\",\"formattedPercentChange\":\"-4.99\",\"formattedVolume\":\"81.9K\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/CRSH\"},{\"bluegrassChannel\":\"/zigman2/quotes/236905666/composite\",\"country\":\"US\",\"lastPrice\":32.21,\"mantissa\":4,\"percentChange\":-4.79,\"priceChange\":-1.62,\"name\":\"Tradr
|
| 48 |
+
TSLA Bear Daily ETF\",\"ticker\":\"TSLQ\",\"timestamp\":\"2024-06-26T20:00:00.414+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":2690615,\"formattedPrice\":\"32.21\",\"formattedPriceChange\":\"-1.62\",\"formattedPercentChange\":\"-4.79\",\"formattedVolume\":\"2.7M\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/TSLQ\"},{\"bluegrassChannel\":\"/zigman2/quotes/237549519/composite\",\"country\":\"US\",\"lastPrice\":21.18,\"mantissa\":4,\"percentChange\":-4.77,\"priceChange\":-1.06,\"name\":\"Direxion
|
| 49 |
+
Daily TSLA Bear 1X Shares\",\"ticker\":\"TSLS\",\"timestamp\":\"2024-06-26T20:00:00.053+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":1741663,\"formattedPrice\":\"21.18\",\"formattedPriceChange\":\"-1.06\",\"formattedPercentChange\":\"-4.77\",\"formattedVolume\":\"1.7M\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/TSLS\"},{\"bluegrassChannel\":\"/zigman2/quotes/209848629/composite\",\"country\":\"US\",\"lastPrice\":24.71,\"mantissa\":4,\"percentChange\":-4.67,\"priceChange\":-1.21,\"name\":\"MicroSectors
|
| 50 |
+
FANG+ Index -3X Inverse Leveraged ETN\",\"ticker\":\"FNGD\",\"timestamp\":\"2024-06-26T20:00:00.06+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":1214469,\"formattedPrice\":\"24.71\",\"formattedPriceChange\":\"-1.21\",\"formattedPercentChange\":\"-4.67\",\"formattedVolume\":\"1.2M\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/FNGD\"},{\"bluegrassChannel\":\"/zigman2/quotes/209018185/composite\",\"country\":\"US\",\"lastPrice\":50.8,\"mantissa\":4,\"percentChange\":-4.26,\"priceChange\":-2.26,\"name\":\"Direxion
|
| 51 |
+
Daily 20+ Year Treasury Bull 3X Shares\",\"ticker\":\"TMF\",\"timestamp\":\"2024-06-26T20:00:00.105+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":5399325,\"formattedPrice\":\"50.80\",\"formattedPriceChange\":\"-2.26\",\"formattedPercentChange\":\"-4.26\",\"formattedVolume\":\"5.4M\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/TMF\"},{\"bluegrassChannel\":\"/zigman2/quotes/206220955/composite\",\"country\":\"US\",\"lastPrice\":17.1,\"mantissa\":4,\"percentChange\":-4.2,\"priceChange\":-0.75,\"name\":\"Direxion
|
| 52 |
+
Daily MSCI Mexico Bull 3X Shares\",\"ticker\":\"MEXX\",\"timestamp\":\"2024-06-26T20:10:00.002+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":82612,\"formattedPrice\":\"17.10\",\"formattedPriceChange\":\"-0.75\",\"formattedPercentChange\":\"-4.20\",\"formattedVolume\":\"82.6K\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/MEXX\"},{\"bluegrassChannel\":\"/zigman2/quotes/238651729/composite\",\"country\":\"US\",\"lastPrice\":13.46,\"mantissa\":4,\"percentChange\":-3.98,\"priceChange\":-0.5573,\"name\":\"Direxion
|
| 53 |
+
Daily AMZN Bear 1X Shares\",\"ticker\":\"AMZD\",\"timestamp\":\"2024-06-26T19:59:52.565+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":76239,\"formattedPrice\":\"13.46\",\"formattedPriceChange\":\"-0.56\",\"formattedPercentChange\":\"-3.98\",\"formattedVolume\":\"76.2K\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/AMZD\"},{\"bluegrassChannel\":\"/zigman2/quotes/205527422/composite\",\"country\":\"US\",\"lastPrice\":18.31,\"mantissa\":4,\"percentChange\":-3.58,\"priceChange\":-0.68,\"name\":\"United
|
| 54 |
+
States Natural Gas Fund L.P.\",\"ticker\":\"UNG\",\"timestamp\":\"2024-06-26T20:00:00.075+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":4092304,\"formattedPrice\":\"18.31\",\"formattedPriceChange\":\"-0.68\",\"formattedPercentChange\":\"-3.58\",\"formattedVolume\":\"4.1M\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/UNG\"},{\"bluegrassChannel\":\"/zigman2/quotes/253849496/composite\",\"country\":\"US\",\"lastPrice\":27.66,\"mantissa\":4,\"percentChange\":-3.52,\"priceChange\":-1.01,\"name\":\"ProShares
|
| 55 |
+
Ultra Bitcoin ETF\",\"ticker\":\"BITU\",\"timestamp\":\"2024-06-26T20:00:00.082+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":642213,\"formattedPrice\":\"27.66\",\"formattedPriceChange\":\"-1.01\",\"formattedPercentChange\":\"-3.52\",\"formattedVolume\":\"642.2K\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/BITU\"},{\"bluegrassChannel\":\"/zigman2/quotes/246315011/composite\",\"country\":\"US\",\"lastPrice\":32.9,\"mantissa\":4,\"percentChange\":-3.46,\"priceChange\":-1.18,\"name\":\"2x
|
| 56 |
+
Bitcoin Strategy ETF\",\"ticker\":\"BITX\",\"timestamp\":\"2024-06-26T20:00:00.194+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":3516762,\"formattedPrice\":\"32.90\",\"formattedPriceChange\":\"-1.18\",\"formattedPercentChange\":\"-3.46\",\"formattedVolume\":\"3.5M\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/BITX\"},{\"bluegrassChannel\":\"/zigman2/quotes/253529498/composite\",\"country\":\"US\",\"lastPrice\":15.52,\"mantissa\":4,\"percentChange\":-3.42,\"priceChange\":-0.55,\"name\":\"GraniteShares
|
| 57 |
+
2x Long AMD Daily ETF\",\"ticker\":\"AMDL\",\"timestamp\":\"2024-06-26T19:59:54.565+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":827467,\"formattedPrice\":\"15.52\",\"formattedPriceChange\":\"-0.55\",\"formattedPercentChange\":\"-3.42\",\"formattedVolume\":\"827.5K\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/AMDL\"},{\"bluegrassChannel\":\"/zigman2/quotes/252759250/composite\",\"country\":\"US\",\"lastPrice\":28.83,\"mantissa\":4,\"percentChange\":-3.32,\"priceChange\":-0.99,\"name\":\"Valkyrie
|
| 58 |
+
Bitcoin Futures Leveraged Strategy ETF\",\"ticker\":\"BTFX\",\"timestamp\":\"2024-06-26T20:00:01.082+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":68929,\"formattedPrice\":\"28.83\",\"formattedPriceChange\":\"-0.99\",\"formattedPercentChange\":\"-3.32\",\"formattedVolume\":\"68.9K\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/BTFX\"},{\"bluegrassChannel\":\"/zigman2/quotes/201714904/composite\",\"country\":\"US\",\"lastPrice\":111.84,\"mantissa\":4,\"percentChange\":-3.11,\"priceChange\":-3.59,\"name\":\"Direxion
|
| 59 |
+
Daily S&P Biotech Bull 3X Shares\",\"ticker\":\"LABU\",\"timestamp\":\"2024-06-26T20:00:00.099+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":1228704,\"formattedPrice\":\"111.84\",\"formattedPriceChange\":\"-3.59\",\"formattedPercentChange\":\"-3.11\",\"formattedVolume\":\"1.2M\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/LABU\"},{\"bluegrassChannel\":\"/zigman2/quotes/207070561/composite\",\"country\":\"US\",\"lastPrice\":19.48,\"mantissa\":4,\"percentChange\":-2.82,\"priceChange\":-0.5647,\"name\":\"ProShares
|
| 60 |
+
Ultra 20+ Year Treasury\",\"ticker\":\"UBT\",\"timestamp\":\"2024-06-26T20:10:00.004+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":54857,\"formattedPrice\":\"19.48\",\"formattedPriceChange\":\"-0.56\",\"formattedPercentChange\":\"-2.82\",\"formattedVolume\":\"54.9K\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/UBT\"},{\"bluegrassChannel\":\"/zigman2/quotes/204010694/composite\",\"country\":\"US\",\"lastPrice\":13.11,\"mantissa\":4,\"percentChange\":-2.82,\"priceChange\":-0.38,\"name\":\"First
|
| 61 |
+
Trust SkyBridge Crypto Industry & Digital Economy ETF\",\"ticker\":\"CRPT\",\"timestamp\":\"2024-06-26T20:10:00.002+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":58628,\"formattedPrice\":\"13.11\",\"formattedPriceChange\":\"-0.38\",\"formattedPercentChange\":\"-2.82\",\"formattedVolume\":\"58.6K\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/CRPT\"},{\"bluegrassChannel\":\"/zigman2/quotes/247441496/composite\",\"country\":\"US\",\"lastPrice\":19.72,\"mantissa\":4,\"percentChange\":-2.62,\"priceChange\":-0.53,\"name\":\"YieldMax
|
| 62 |
+
COIN Option Income Strategy ETF\",\"ticker\":\"CONY\",\"timestamp\":\"2024-06-26T20:00:00.075+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":969522,\"formattedPrice\":\"19.72\",\"formattedPriceChange\":\"-0.53\",\"formattedPercentChange\":\"-2.62\",\"formattedVolume\":\"969.5K\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/CONY\"},{\"bluegrassChannel\":\"/zigman2/quotes/234313102/composite\",\"country\":\"US\",\"lastPrice\":5.54,\"mantissa\":4,\"percentChange\":-2.29,\"priceChange\":-0.13,\"name\":\"2x
|
| 63 |
+
Long VIX Futures ETF\",\"ticker\":\"UVIX\",\"timestamp\":\"2024-06-26T20:00:00.091+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":4818570,\"formattedPrice\":\"5.54\",\"formattedPriceChange\":\"-0.13\",\"formattedPercentChange\":\"-2.29\",\"formattedVolume\":\"4.8M\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/UVIX\"},{\"bluegrassChannel\":\"/zigman2/quotes/221335101/composite\",\"country\":\"US\",\"lastPrice\":11.05,\"mantissa\":4,\"percentChange\":-2.21,\"priceChange\":-0.25,\"name\":\"iShares
|
| 64 |
+
25+ Year Treasury STRIPS Bond ETF\",\"ticker\":\"GOVZ\",\"timestamp\":\"2024-06-26T20:10:02.157+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":123225,\"formattedPrice\":\"11.05\",\"formattedPriceChange\":\"-0.25\",\"formattedPercentChange\":\"-2.21\",\"formattedVolume\":\"123.2K\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/GOVZ\"},{\"bluegrassChannel\":\"/zigman2/quotes/206454610/composite\",\"country\":\"US\",\"lastPrice\":23.75,\"mantissa\":4,\"percentChange\":-2.02,\"priceChange\":-0.49,\"name\":\"ARK
|
| 65 |
+
Genomic Revolution ETF\",\"ticker\":\"ARKG\",\"timestamp\":\"2024-06-26T20:00:00.119+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":2498682,\"formattedPrice\":\"23.75\",\"formattedPriceChange\":\"-0.49\",\"formattedPercentChange\":\"-2.02\",\"formattedVolume\":\"2.5M\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/ARKG\"},{\"bluegrassChannel\":\"/zigman2/quotes/202473148/composite\",\"country\":\"US\",\"lastPrice\":64.21,\"mantissa\":4,\"percentChange\":-2,\"priceChange\":-1.31,\"name\":\"Direxion
|
| 66 |
+
Daily Energy Bull 2X Shares\",\"ticker\":\"ERX\",\"timestamp\":\"2024-06-26T20:10:00.003+00:00\",\"type\":\"ExchangeTradedFund\",\"volume\":366870,\"formattedPrice\":\"64.21\",\"formattedPriceChange\":\"-1.31\",\"formattedPercentChange\":\"-2.00\",\"formattedVolume\":\"366.9K\",\"url\":\"https://www.wsj.com/market-data/quotes/etf/US/ERX\"}],\"latestTimestamp\":\"2024-06-26T20:10:02.157+00:00\",\"timestamp\":\"4:10
|
| 67 |
+
PM EDT 6/26/24\"}}"}'
|
| 68 |
+
headers:
|
| 69 |
+
Cache-Control:
|
| 70 |
+
- public, max-age=120, no-cache=Set-Cookie
|
| 71 |
+
Connection:
|
| 72 |
+
- keep-alive
|
| 73 |
+
Content-Encoding:
|
| 74 |
+
- gzip
|
| 75 |
+
Content-Security-Policy:
|
| 76 |
+
- 'default-src ''self'' cdn.privacy-mgmt.com;script-src ''self'' *.wsj.net *.wsj.com
|
| 77 |
+
''unsafe-inline'' ''unsafe-eval'';script-src-elem * ''unsafe-inline'';manifest-src
|
| 78 |
+
''self'' *.wsj.com;media-src * data: blob: https:;worker-src * ''unsafe-inline''
|
| 79 |
+
''unsafe-eval'' blob: data:;frame-src * ''unsafe-inline'';connect-src * ''unsafe-inline''
|
| 80 |
+
''unsafe-eval'';form-action * ''unsafe-inline'';frame-ancestors *;script-src-attr
|
| 81 |
+
''unsafe-inline'';object-src ''self'' ''unsafe-inline'';img-src * data: blob:
|
| 82 |
+
https:;font-src ''self'' * ''unsafe-inline'';upgrade-insecure-requests;base-uri
|
| 83 |
+
''self'';style-src ''self'' https: ''unsafe-inline'''
|
| 84 |
+
Content-Type:
|
| 85 |
+
- application/json; charset=utf-8
|
| 86 |
+
Cross-Origin-Resource-Policy:
|
| 87 |
+
- same-site
|
| 88 |
+
Date:
|
| 89 |
+
- Thu, 27 Jun 2024 10:16:49 GMT
|
| 90 |
+
ETag:
|
| 91 |
+
- W/"6340-oPfvAxlKBZm5NTuyEjgGYZSA4EY"
|
| 92 |
+
GC-Versions:
|
| 93 |
+
- 2.2.354|0.4.1241|4.1.2
|
| 94 |
+
Origin-Agent-Cluster:
|
| 95 |
+
- ?1
|
| 96 |
+
Referrer-Policy:
|
| 97 |
+
- strict-origin-when-cross-origin,unsafe-url
|
| 98 |
+
Strict-Transport-Security:
|
| 99 |
+
- max-age=63072000; includeSubDomains; preload
|
| 100 |
+
Transfer-Encoding:
|
| 101 |
+
- chunked
|
| 102 |
+
Via:
|
| 103 |
+
- 1.1 5ca3eb318b3d637b6c83037daa75f174.cloudfront.net (CloudFront)
|
| 104 |
+
X-Amz-Cf-Id:
|
| 105 |
+
- X1RnB2jyjQni3zkLM2tLUoRnuo547J2fDtxCh52bxJM5JCW49vKbGg==
|
| 106 |
+
X-Amz-Cf-Pop:
|
| 107 |
+
- AMS58-P1
|
| 108 |
+
X-Cache:
|
| 109 |
+
- Miss from cloudfront
|
| 110 |
+
X-Content-Type-Options:
|
| 111 |
+
- nosniff
|
| 112 |
+
X-DNS-Prefetch-Control:
|
| 113 |
+
- 'off'
|
| 114 |
+
X-Download-Options:
|
| 115 |
+
- noopen
|
| 116 |
+
X-Frame-Options:
|
| 117 |
+
- SAMEORIGIN
|
| 118 |
+
X-Permitted-Cross-Domain-Policies:
|
| 119 |
+
- none
|
| 120 |
+
X-XSS-Protection:
|
| 121 |
+
- 1; mode=block
|
| 122 |
+
status:
|
| 123 |
+
code: 200
|
| 124 |
+
message: OK
|
| 125 |
+
version: 1
|
openbb_platform/providers/wsj/tests/test_wsj_fetchers.py
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Tests for the WSJ fetchers."""
|
| 2 |
+
|
| 3 |
+
import pytest
|
| 4 |
+
from openbb_core.app.service.user_service import UserService
|
| 5 |
+
from openbb_wsj.models.active import WSJActiveFetcher
|
| 6 |
+
from openbb_wsj.models.gainers import WSJGainersFetcher
|
| 7 |
+
from openbb_wsj.models.losers import WSJLosersFetcher
|
| 8 |
+
|
| 9 |
+
test_credentials = UserService().default_user_settings.credentials.model_dump(
|
| 10 |
+
mode="json"
|
| 11 |
+
)
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
@pytest.fixture(scope="module")
|
| 15 |
+
def vcr_config():
|
| 16 |
+
"""VCR configuration."""
|
| 17 |
+
return {
|
| 18 |
+
"filter_headers": [("User-Agent", None)],
|
| 19 |
+
"filter_query_parameters": [
|
| 20 |
+
("token", "MOCK_TOKEN"),
|
| 21 |
+
],
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
@pytest.mark.record_http
|
| 26 |
+
def test_wsj_gainers_fetcher(credentials=test_credentials):
|
| 27 |
+
"""Test the WSJ Gainers fetcher."""
|
| 28 |
+
params = {}
|
| 29 |
+
|
| 30 |
+
fetcher = WSJGainersFetcher()
|
| 31 |
+
result = fetcher.test(params, credentials)
|
| 32 |
+
assert result is None
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
@pytest.mark.record_http
|
| 36 |
+
def test_wsj_losers_fetcher(credentials=test_credentials):
|
| 37 |
+
"""Test the WSJ Losers fetcher."""
|
| 38 |
+
params = {}
|
| 39 |
+
|
| 40 |
+
fetcher = WSJLosersFetcher()
|
| 41 |
+
result = fetcher.test(params, credentials)
|
| 42 |
+
assert result is None
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
@pytest.mark.record_http
|
| 46 |
+
def test_wsj_active_fetcher(credentials=test_credentials):
|
| 47 |
+
"""Test the WSJ Active fetcher."""
|
| 48 |
+
params = {}
|
| 49 |
+
|
| 50 |
+
fetcher = WSJActiveFetcher()
|
| 51 |
+
result = fetcher.test(params, credentials)
|
| 52 |
+
assert result is None
|
openbb_platform/providers/yfinance/README.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# OpenBB Yahoo!Finance Provider
|
| 2 |
+
|
| 3 |
+
This extension integrates the [Yahoo!Finance](https://finance.yahoo.com/) data provider into the OpenBB Platform.
|
| 4 |
+
|
| 5 |
+
## Installation
|
| 6 |
+
|
| 7 |
+
To install the extension:
|
| 8 |
+
|
| 9 |
+
```bash
|
| 10 |
+
pip install openbb-yfinance
|
| 11 |
+
```
|
| 12 |
+
|
| 13 |
+
Documentation available [here](https://docs.openbb.co/platform/developer_guide/contributing).
|
openbb_platform/providers/yfinance/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
"""YFinance provider."""
|
openbb_platform/providers/yfinance/openbb_yfinance/__init__.py
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Yahoo Finance provider module."""
|
| 2 |
+
|
| 3 |
+
from openbb_core.provider.abstract.provider import Provider
|
| 4 |
+
from openbb_yfinance.models.active import YFActiveFetcher
|
| 5 |
+
from openbb_yfinance.models.aggressive_small_caps import YFAggressiveSmallCapsFetcher
|
| 6 |
+
from openbb_yfinance.models.available_indices import YFinanceAvailableIndicesFetcher
|
| 7 |
+
from openbb_yfinance.models.balance_sheet import YFinanceBalanceSheetFetcher
|
| 8 |
+
from openbb_yfinance.models.cash_flow import YFinanceCashFlowStatementFetcher
|
| 9 |
+
from openbb_yfinance.models.company_news import YFinanceCompanyNewsFetcher
|
| 10 |
+
from openbb_yfinance.models.crypto_historical import YFinanceCryptoHistoricalFetcher
|
| 11 |
+
from openbb_yfinance.models.currency_historical import YFinanceCurrencyHistoricalFetcher
|
| 12 |
+
from openbb_yfinance.models.equity_historical import YFinanceEquityHistoricalFetcher
|
| 13 |
+
from openbb_yfinance.models.equity_profile import YFinanceEquityProfileFetcher
|
| 14 |
+
from openbb_yfinance.models.equity_quote import YFinanceEquityQuoteFetcher
|
| 15 |
+
from openbb_yfinance.models.equity_screener import YFinanceEquityScreenerFetcher
|
| 16 |
+
from openbb_yfinance.models.etf_info import YFinanceEtfInfoFetcher
|
| 17 |
+
from openbb_yfinance.models.futures_curve import YFinanceFuturesCurveFetcher
|
| 18 |
+
from openbb_yfinance.models.futures_historical import YFinanceFuturesHistoricalFetcher
|
| 19 |
+
from openbb_yfinance.models.gainers import YFGainersFetcher
|
| 20 |
+
from openbb_yfinance.models.growth_tech_equities import YFGrowthTechEquitiesFetcher
|
| 21 |
+
from openbb_yfinance.models.historical_dividends import (
|
| 22 |
+
YFinanceHistoricalDividendsFetcher,
|
| 23 |
+
)
|
| 24 |
+
from openbb_yfinance.models.income_statement import YFinanceIncomeStatementFetcher
|
| 25 |
+
from openbb_yfinance.models.index_historical import (
|
| 26 |
+
YFinanceIndexHistoricalFetcher,
|
| 27 |
+
)
|
| 28 |
+
from openbb_yfinance.models.key_executives import YFinanceKeyExecutivesFetcher
|
| 29 |
+
from openbb_yfinance.models.key_metrics import YFinanceKeyMetricsFetcher
|
| 30 |
+
from openbb_yfinance.models.losers import YFLosersFetcher
|
| 31 |
+
from openbb_yfinance.models.options_chains import YFinanceOptionsChainsFetcher
|
| 32 |
+
from openbb_yfinance.models.price_target_consensus import (
|
| 33 |
+
YFinancePriceTargetConsensusFetcher,
|
| 34 |
+
)
|
| 35 |
+
from openbb_yfinance.models.share_statistics import YFinanceShareStatisticsFetcher
|
| 36 |
+
from openbb_yfinance.models.undervalued_growth_equities import (
|
| 37 |
+
YFUndervaluedGrowthEquitiesFetcher,
|
| 38 |
+
)
|
| 39 |
+
from openbb_yfinance.models.undervalued_large_caps import YFUndervaluedLargeCapsFetcher
|
| 40 |
+
|
| 41 |
+
yfinance_provider = Provider(
|
| 42 |
+
name="yfinance",
|
| 43 |
+
website="https://finance.yahoo.com",
|
| 44 |
+
description="""Yahoo! Finance is a web-based platform that offers financial news,
|
| 45 |
+
data, and tools for investors and individuals interested in tracking and analyzing
|
| 46 |
+
financial markets and assets.""",
|
| 47 |
+
fetcher_dict={
|
| 48 |
+
"AvailableIndices": YFinanceAvailableIndicesFetcher,
|
| 49 |
+
"BalanceSheet": YFinanceBalanceSheetFetcher,
|
| 50 |
+
"CashFlowStatement": YFinanceCashFlowStatementFetcher,
|
| 51 |
+
"CompanyNews": YFinanceCompanyNewsFetcher,
|
| 52 |
+
"CryptoHistorical": YFinanceCryptoHistoricalFetcher,
|
| 53 |
+
"CurrencyHistorical": YFinanceCurrencyHistoricalFetcher,
|
| 54 |
+
"EquityActive": YFActiveFetcher,
|
| 55 |
+
"EquityAggressiveSmallCaps": YFAggressiveSmallCapsFetcher,
|
| 56 |
+
"EquityGainers": YFGainersFetcher,
|
| 57 |
+
"EquityHistorical": YFinanceEquityHistoricalFetcher,
|
| 58 |
+
"EquityInfo": YFinanceEquityProfileFetcher,
|
| 59 |
+
"EquityLosers": YFLosersFetcher,
|
| 60 |
+
"EquityQuote": YFinanceEquityQuoteFetcher,
|
| 61 |
+
"EquityScreener": YFinanceEquityScreenerFetcher,
|
| 62 |
+
"EquityUndervaluedGrowth": YFUndervaluedGrowthEquitiesFetcher,
|
| 63 |
+
"EquityUndervaluedLargeCaps": YFUndervaluedLargeCapsFetcher,
|
| 64 |
+
"EtfHistorical": YFinanceEquityHistoricalFetcher,
|
| 65 |
+
"EtfInfo": YFinanceEtfInfoFetcher,
|
| 66 |
+
"FuturesCurve": YFinanceFuturesCurveFetcher,
|
| 67 |
+
"FuturesHistorical": YFinanceFuturesHistoricalFetcher,
|
| 68 |
+
"GrowthTechEquities": YFGrowthTechEquitiesFetcher,
|
| 69 |
+
"HistoricalDividends": YFinanceHistoricalDividendsFetcher,
|
| 70 |
+
"IncomeStatement": YFinanceIncomeStatementFetcher,
|
| 71 |
+
"IndexHistorical": YFinanceIndexHistoricalFetcher,
|
| 72 |
+
"KeyExecutives": YFinanceKeyExecutivesFetcher,
|
| 73 |
+
"KeyMetrics": YFinanceKeyMetricsFetcher,
|
| 74 |
+
"OptionsChains": YFinanceOptionsChainsFetcher,
|
| 75 |
+
"PriceTargetConsensus": YFinancePriceTargetConsensusFetcher,
|
| 76 |
+
"ShareStatistics": YFinanceShareStatisticsFetcher,
|
| 77 |
+
},
|
| 78 |
+
repr_name="Yahoo Finance",
|
| 79 |
+
)
|
openbb_platform/providers/yfinance/openbb_yfinance/models/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
"""Yahoo Finance models directory."""
|
openbb_platform/providers/yfinance/openbb_yfinance/models/active.py
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Yahoo Finance Asset Performance Active Model."""
|
| 2 |
+
|
| 3 |
+
# pylint: disable=unused-argument
|
| 4 |
+
|
| 5 |
+
from typing import Any, Optional
|
| 6 |
+
|
| 7 |
+
from openbb_core.provider.abstract.fetcher import Fetcher
|
| 8 |
+
from openbb_core.provider.standard_models.equity_performance import (
|
| 9 |
+
EquityPerformanceQueryParams,
|
| 10 |
+
)
|
| 11 |
+
from openbb_yfinance.utils.references import YFPredefinedScreenerData
|
| 12 |
+
from pydantic import Field
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
class YFActiveQueryParams(EquityPerformanceQueryParams):
|
| 16 |
+
"""Yahoo Finance Most Active Query.
|
| 17 |
+
|
| 18 |
+
Source: https://finance.yahoo.com/screener/predefined/most_actives
|
| 19 |
+
"""
|
| 20 |
+
|
| 21 |
+
limit: Optional[int] = Field(
|
| 22 |
+
default=200,
|
| 23 |
+
description="Limit the number of results.",
|
| 24 |
+
)
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
class YFActiveData(YFPredefinedScreenerData):
|
| 28 |
+
"""Yahoo Finance Most Active Data."""
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
class YFActiveFetcher(Fetcher[YFActiveQueryParams, list[YFActiveData]]):
|
| 32 |
+
"""Transform the query, extract and transform the data from the Yahoo Finance endpoints."""
|
| 33 |
+
|
| 34 |
+
@staticmethod
|
| 35 |
+
def transform_query(params: dict[str, Any]) -> YFActiveQueryParams:
|
| 36 |
+
"""Transform query params."""
|
| 37 |
+
return YFActiveQueryParams(**params)
|
| 38 |
+
|
| 39 |
+
@staticmethod
|
| 40 |
+
async def aextract_data(
|
| 41 |
+
query: YFActiveQueryParams,
|
| 42 |
+
credentials: Optional[dict[str, str]],
|
| 43 |
+
**kwargs: Any,
|
| 44 |
+
) -> list[dict]:
|
| 45 |
+
"""Get data from YF."""
|
| 46 |
+
# pylint: disable=import-outside-toplevel
|
| 47 |
+
from openbb_yfinance.utils.helpers import get_custom_screener
|
| 48 |
+
|
| 49 |
+
body = {
|
| 50 |
+
"offset": 0,
|
| 51 |
+
"size": 250,
|
| 52 |
+
"sortField": "eodvolume",
|
| 53 |
+
"sortType": "desc",
|
| 54 |
+
"quoteType": "equity",
|
| 55 |
+
"query": {
|
| 56 |
+
"operator": "and",
|
| 57 |
+
"operands": [
|
| 58 |
+
{"operator": "gt", "operands": ["intradaymarketcap", 2000000000]},
|
| 59 |
+
{
|
| 60 |
+
"operator": "or",
|
| 61 |
+
"operands": [
|
| 62 |
+
{"operator": "eq", "operands": ["exchange", "NMS"]},
|
| 63 |
+
{"operator": "eq", "operands": ["exchange", "NYQ"]},
|
| 64 |
+
],
|
| 65 |
+
},
|
| 66 |
+
{"operator": "gt", "operands": ["davolume", 1000000]},
|
| 67 |
+
{"operator": "gt", "operands": ["intradayprice", 5]},
|
| 68 |
+
],
|
| 69 |
+
},
|
| 70 |
+
"userId": "",
|
| 71 |
+
"userIdType": "guid",
|
| 72 |
+
}
|
| 73 |
+
|
| 74 |
+
return await get_custom_screener(body=body, limit=query.limit)
|
| 75 |
+
|
| 76 |
+
@staticmethod
|
| 77 |
+
def transform_data(
|
| 78 |
+
query: EquityPerformanceQueryParams,
|
| 79 |
+
data: list[dict],
|
| 80 |
+
**kwargs: Any,
|
| 81 |
+
) -> list[YFActiveData]:
|
| 82 |
+
"""Transform data."""
|
| 83 |
+
return [
|
| 84 |
+
YFActiveData.model_validate(d)
|
| 85 |
+
for d in sorted(
|
| 86 |
+
data,
|
| 87 |
+
key=lambda x: x["regularMarketVolume"],
|
| 88 |
+
reverse=query.sort == "desc",
|
| 89 |
+
)
|
| 90 |
+
]
|
openbb_platform/providers/yfinance/openbb_yfinance/models/aggressive_small_caps.py
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Yahoo Finance Aggressive Small Caps Model."""
|
| 2 |
+
|
| 3 |
+
# pylint: disable=unused-argument
|
| 4 |
+
|
| 5 |
+
from typing import Any, Optional
|
| 6 |
+
|
| 7 |
+
from openbb_core.provider.abstract.fetcher import Fetcher
|
| 8 |
+
from openbb_core.provider.standard_models.equity_performance import (
|
| 9 |
+
EquityPerformanceQueryParams,
|
| 10 |
+
)
|
| 11 |
+
from openbb_yfinance.utils.references import YFPredefinedScreenerData
|
| 12 |
+
from pydantic import Field
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
class YFAggressiveSmallCapsQueryParams(EquityPerformanceQueryParams):
|
| 16 |
+
"""Yahoo Finance Aggressive Small Caps Query.
|
| 17 |
+
|
| 18 |
+
Source: https://finance.yahoo.com/screener/predefined/aggressive_small_caps
|
| 19 |
+
"""
|
| 20 |
+
|
| 21 |
+
limit: Optional[int] = Field(
|
| 22 |
+
default=None,
|
| 23 |
+
description="Limit the number of results. Default is all.",
|
| 24 |
+
)
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
class YFAggressiveSmallCapsData(YFPredefinedScreenerData):
|
| 28 |
+
"""Yahoo Finance Aggressive Small Caps Data."""
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
class YFAggressiveSmallCapsFetcher(
|
| 32 |
+
Fetcher[YFAggressiveSmallCapsQueryParams, list[YFAggressiveSmallCapsData]]
|
| 33 |
+
):
|
| 34 |
+
"""Transform the query, extract and transform the data from the Yahoo Finance endpoints."""
|
| 35 |
+
|
| 36 |
+
@staticmethod
|
| 37 |
+
def transform_query(params: dict[str, Any]) -> YFAggressiveSmallCapsQueryParams:
|
| 38 |
+
"""Transform query params."""
|
| 39 |
+
return YFAggressiveSmallCapsQueryParams(**params)
|
| 40 |
+
|
| 41 |
+
@staticmethod
|
| 42 |
+
async def aextract_data(
|
| 43 |
+
query: YFAggressiveSmallCapsQueryParams,
|
| 44 |
+
credentials: Optional[dict[str, str]],
|
| 45 |
+
**kwargs: Any,
|
| 46 |
+
) -> list[dict]:
|
| 47 |
+
"""Get data from YF."""
|
| 48 |
+
# pylint: disable=import-outside-toplevel
|
| 49 |
+
from openbb_yfinance.utils.helpers import get_custom_screener
|
| 50 |
+
|
| 51 |
+
# The predefined screener doesn't match what yFinance has for the settings. We'll have to create our own.
|
| 52 |
+
body = {
|
| 53 |
+
"offset": 0,
|
| 54 |
+
"size": 250,
|
| 55 |
+
"sortField": "totalrevenues1yrgrowth.lasttwelvemonths",
|
| 56 |
+
"sortType": "desc",
|
| 57 |
+
"quoteType": "equity",
|
| 58 |
+
"query": {
|
| 59 |
+
"operator": "and",
|
| 60 |
+
"operands": [
|
| 61 |
+
{"operator": "lt", "operands": ["intradaymarketcap", 2000000000]},
|
| 62 |
+
{
|
| 63 |
+
"operator": "or",
|
| 64 |
+
"operands": [
|
| 65 |
+
{"operator": "eq", "operands": ["exchange", "NMS"]},
|
| 66 |
+
{"operator": "eq", "operands": ["exchange", "NYQ"]},
|
| 67 |
+
],
|
| 68 |
+
},
|
| 69 |
+
{"operator": "gt", "operands": ["epsgrowth.lasttwelvemonths", 25]},
|
| 70 |
+
{"operator": "gt", "operands": ["intradayprice", 5]},
|
| 71 |
+
],
|
| 72 |
+
},
|
| 73 |
+
"userId": "",
|
| 74 |
+
"userIdType": "guid",
|
| 75 |
+
}
|
| 76 |
+
return await get_custom_screener(body=body, limit=query.limit)
|
| 77 |
+
|
| 78 |
+
@staticmethod
|
| 79 |
+
def transform_data(
|
| 80 |
+
query: EquityPerformanceQueryParams,
|
| 81 |
+
data: list[dict],
|
| 82 |
+
**kwargs: Any,
|
| 83 |
+
) -> list[YFAggressiveSmallCapsData]:
|
| 84 |
+
"""Transform data."""
|
| 85 |
+
return sorted(
|
| 86 |
+
[YFAggressiveSmallCapsData.model_validate(d) for d in data],
|
| 87 |
+
key=lambda x: x.percent_change,
|
| 88 |
+
reverse=query.sort == "desc",
|
| 89 |
+
)
|
openbb_platform/providers/yfinance/openbb_yfinance/models/available_indices.py
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Yahoo Finance Available Indices Model."""
|
| 2 |
+
|
| 3 |
+
# pylint: disable=unused-argument
|
| 4 |
+
|
| 5 |
+
from typing import Any, Dict, List, Optional
|
| 6 |
+
|
| 7 |
+
from openbb_core.provider.abstract.fetcher import Fetcher
|
| 8 |
+
from openbb_core.provider.standard_models.available_indices import (
|
| 9 |
+
AvailableIndicesData,
|
| 10 |
+
AvailableIndicesQueryParams,
|
| 11 |
+
)
|
| 12 |
+
from openbb_yfinance.utils.references import INDICES
|
| 13 |
+
from pydantic import Field
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
class YFinanceAvailableIndicesQueryParams(AvailableIndicesQueryParams):
|
| 17 |
+
"""Yahoo Finance Available Indices Query.
|
| 18 |
+
|
| 19 |
+
Source: https://finance.yahoo.com/
|
| 20 |
+
"""
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
class YFinanceAvailableIndicesData(AvailableIndicesData):
|
| 24 |
+
"""Yahoo Finance Available Indices Data."""
|
| 25 |
+
|
| 26 |
+
__alias_dict__ = {
|
| 27 |
+
"symbol": "ticker",
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
code: str = Field(
|
| 31 |
+
description="ID code for keying the index in the OpenBB Terminal."
|
| 32 |
+
)
|
| 33 |
+
symbol: str = Field(description="Symbol for the index.")
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
class YFinanceAvailableIndicesFetcher(
|
| 37 |
+
Fetcher[
|
| 38 |
+
YFinanceAvailableIndicesQueryParams,
|
| 39 |
+
List[YFinanceAvailableIndicesData],
|
| 40 |
+
]
|
| 41 |
+
):
|
| 42 |
+
"""Transform the query, extract and transform the data from the Yahoo Finance endpoints."""
|
| 43 |
+
|
| 44 |
+
@staticmethod
|
| 45 |
+
def transform_query(params: Dict[str, Any]) -> YFinanceAvailableIndicesQueryParams:
|
| 46 |
+
"""Transform the query params."""
|
| 47 |
+
return YFinanceAvailableIndicesQueryParams(**params)
|
| 48 |
+
|
| 49 |
+
@staticmethod
|
| 50 |
+
def extract_data(
|
| 51 |
+
query: YFinanceAvailableIndicesQueryParams, # pylint disable=unused-argument
|
| 52 |
+
credentials: Optional[Dict[str, str]],
|
| 53 |
+
**kwargs: Any,
|
| 54 |
+
) -> List[Dict]:
|
| 55 |
+
"""Extract the data."""
|
| 56 |
+
from pandas import DataFrame # pylint: disable=import-outside-toplevel
|
| 57 |
+
|
| 58 |
+
indices = DataFrame(INDICES).transpose().reset_index()
|
| 59 |
+
indices.columns = ["code", "name", "ticker"]
|
| 60 |
+
|
| 61 |
+
return indices.to_dict("records")
|
| 62 |
+
|
| 63 |
+
@staticmethod
|
| 64 |
+
def transform_data(
|
| 65 |
+
query: YFinanceAvailableIndicesQueryParams, data: List[Dict], **kwargs: Any
|
| 66 |
+
) -> List[YFinanceAvailableIndicesData]:
|
| 67 |
+
"""Return the transformed data."""
|
| 68 |
+
return [YFinanceAvailableIndicesData.model_validate(d) for d in data]
|
openbb_platform/providers/yfinance/openbb_yfinance/models/balance_sheet.py
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Yahoo Finance Balance Sheet Model."""
|
| 2 |
+
|
| 3 |
+
# pylint: disable=unused-argument
|
| 4 |
+
|
| 5 |
+
from datetime import datetime
|
| 6 |
+
from typing import Any, Literal, Optional
|
| 7 |
+
|
| 8 |
+
from openbb_core.provider.abstract.fetcher import Fetcher
|
| 9 |
+
from openbb_core.provider.standard_models.balance_sheet import (
|
| 10 |
+
BalanceSheetData,
|
| 11 |
+
BalanceSheetQueryParams,
|
| 12 |
+
)
|
| 13 |
+
from openbb_core.provider.utils.descriptions import QUERY_DESCRIPTIONS
|
| 14 |
+
from pydantic import Field, field_validator
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
class YFinanceBalanceSheetQueryParams(BalanceSheetQueryParams):
|
| 18 |
+
"""Yahoo Finance Balance Sheet Query.
|
| 19 |
+
|
| 20 |
+
Source: https://finance.yahoo.com/
|
| 21 |
+
"""
|
| 22 |
+
|
| 23 |
+
__json_schema_extra__ = {
|
| 24 |
+
"period": {
|
| 25 |
+
"choices": ["annual", "quarter"],
|
| 26 |
+
}
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
period: Literal["annual", "quarter"] = Field(
|
| 30 |
+
default="annual",
|
| 31 |
+
description=QUERY_DESCRIPTIONS.get("period", ""),
|
| 32 |
+
)
|
| 33 |
+
limit: Optional[int] = Field(
|
| 34 |
+
default=5,
|
| 35 |
+
description=QUERY_DESCRIPTIONS.get("limit", ""),
|
| 36 |
+
le=5,
|
| 37 |
+
)
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
class YFinanceBalanceSheetData(BalanceSheetData):
|
| 41 |
+
"""Yahoo Finance Balance Sheet Data."""
|
| 42 |
+
|
| 43 |
+
__alias_dict__ = {
|
| 44 |
+
"short_term_investments": "other_short_term_investments",
|
| 45 |
+
"net_receivables": "receivables",
|
| 46 |
+
"inventories": "inventory",
|
| 47 |
+
"total_current_assets": "current_assets",
|
| 48 |
+
"plant_property_equipment_gross": "gross_ppe",
|
| 49 |
+
"plant_property_equipment_net": "net_ppe",
|
| 50 |
+
"total_common_equity": "stockholders_equity",
|
| 51 |
+
"total_equity_non_controlling_interests": "total_equity_gross_minority_interest",
|
| 52 |
+
}
|
| 53 |
+
|
| 54 |
+
@field_validator("period_ending", mode="before", check_fields=False)
|
| 55 |
+
@classmethod
|
| 56 |
+
def date_validate(cls, v): # pylint: disable=E0213
|
| 57 |
+
"""Return datetime object from string."""
|
| 58 |
+
if isinstance(v, str):
|
| 59 |
+
return datetime.strptime(v, "%Y-%m-%d %H:%M:%S").date()
|
| 60 |
+
return v
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
class YFinanceBalanceSheetFetcher(
|
| 64 |
+
Fetcher[
|
| 65 |
+
YFinanceBalanceSheetQueryParams,
|
| 66 |
+
list[YFinanceBalanceSheetData],
|
| 67 |
+
]
|
| 68 |
+
):
|
| 69 |
+
"""Yahoo Finance Balance Sheet Fetcher."""
|
| 70 |
+
|
| 71 |
+
@staticmethod
|
| 72 |
+
def transform_query(params: dict[str, Any]) -> YFinanceBalanceSheetQueryParams:
|
| 73 |
+
"""Transform the query parameters."""
|
| 74 |
+
return YFinanceBalanceSheetQueryParams(**params)
|
| 75 |
+
|
| 76 |
+
@staticmethod
|
| 77 |
+
def extract_data(
|
| 78 |
+
query: YFinanceBalanceSheetQueryParams,
|
| 79 |
+
credentials: Optional[dict[str, str]],
|
| 80 |
+
**kwargs: Any,
|
| 81 |
+
) -> list[dict]:
|
| 82 |
+
"""Extract the data from the Yahoo Finance endpoints."""
|
| 83 |
+
# pylint: disable=import-outside-toplevel
|
| 84 |
+
import json # noqa
|
| 85 |
+
from curl_adapter import CurlCffiAdapter
|
| 86 |
+
from numpy import nan
|
| 87 |
+
from openbb_core.provider.utils.errors import EmptyDataError
|
| 88 |
+
from openbb_core.provider.utils.helpers import (
|
| 89 |
+
get_requests_session,
|
| 90 |
+
to_snake_case,
|
| 91 |
+
)
|
| 92 |
+
from yfinance import Ticker
|
| 93 |
+
|
| 94 |
+
period = "yearly" if query.period == "annual" else "quarterly" # type: ignore
|
| 95 |
+
session = get_requests_session()
|
| 96 |
+
session.mount("https://", CurlCffiAdapter())
|
| 97 |
+
session.mount("http://", CurlCffiAdapter())
|
| 98 |
+
data = Ticker(
|
| 99 |
+
query.symbol,
|
| 100 |
+
session=session,
|
| 101 |
+
).get_balance_sheet(as_dict=False, pretty=False, freq=period)
|
| 102 |
+
|
| 103 |
+
if data is None:
|
| 104 |
+
raise EmptyDataError()
|
| 105 |
+
|
| 106 |
+
if query.limit:
|
| 107 |
+
data = data.iloc[:, : query.limit]
|
| 108 |
+
|
| 109 |
+
data.index = [to_snake_case(i) for i in data.index]
|
| 110 |
+
data = data.reset_index().sort_index(ascending=False).set_index("index")
|
| 111 |
+
data = data.replace({nan: None}).to_dict()
|
| 112 |
+
data = [{"period_ending": str(key), **value} for key, value in data.items()]
|
| 113 |
+
data = json.loads(json.dumps(data))
|
| 114 |
+
|
| 115 |
+
return data
|
| 116 |
+
|
| 117 |
+
@staticmethod
|
| 118 |
+
def transform_data(
|
| 119 |
+
query: YFinanceBalanceSheetQueryParams,
|
| 120 |
+
data: list[dict],
|
| 121 |
+
**kwargs: Any,
|
| 122 |
+
) -> list[YFinanceBalanceSheetData]:
|
| 123 |
+
"""Transform the data."""
|
| 124 |
+
return [YFinanceBalanceSheetData.model_validate(d) for d in data]
|
openbb_platform/providers/yfinance/openbb_yfinance/models/cash_flow.py
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Yahoo Finance Cash Flow Statement Model."""
|
| 2 |
+
|
| 3 |
+
# pylint: disable=unused-argument
|
| 4 |
+
|
| 5 |
+
from datetime import datetime
|
| 6 |
+
from typing import Any, Literal, Optional
|
| 7 |
+
|
| 8 |
+
from openbb_core.provider.abstract.fetcher import Fetcher
|
| 9 |
+
from openbb_core.provider.standard_models.cash_flow import (
|
| 10 |
+
CashFlowStatementData,
|
| 11 |
+
CashFlowStatementQueryParams,
|
| 12 |
+
)
|
| 13 |
+
from openbb_core.provider.utils.descriptions import QUERY_DESCRIPTIONS
|
| 14 |
+
from pydantic import Field, field_validator
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
class YFinanceCashFlowStatementQueryParams(CashFlowStatementQueryParams):
|
| 18 |
+
"""Yahoo Finance Cash Flow Statement Query.
|
| 19 |
+
|
| 20 |
+
Source: https://finance.yahoo.com/
|
| 21 |
+
"""
|
| 22 |
+
|
| 23 |
+
__json_schema_extra__ = {
|
| 24 |
+
"period": {
|
| 25 |
+
"choices": ["annual", "quarter"],
|
| 26 |
+
}
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
period: Literal["annual", "quarter"] = Field(
|
| 30 |
+
default="annual",
|
| 31 |
+
description=QUERY_DESCRIPTIONS.get("period", ""),
|
| 32 |
+
)
|
| 33 |
+
limit: Optional[int] = Field(
|
| 34 |
+
default=5,
|
| 35 |
+
description=QUERY_DESCRIPTIONS.get("limit", ""),
|
| 36 |
+
le=5,
|
| 37 |
+
)
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
class YFinanceCashFlowStatementData(CashFlowStatementData):
|
| 41 |
+
"""Yahoo Finance Cash Flow Statement Data."""
|
| 42 |
+
|
| 43 |
+
__alias_dict__ = {
|
| 44 |
+
"investments_in_property_plant_and_equipment": "purchase_of_ppe",
|
| 45 |
+
"issuance_of_common_equity": "common_stock_issuance",
|
| 46 |
+
"repurchase_of_common_equity": "common_stock_payments",
|
| 47 |
+
"cash_dividends_paid": "payment_of_dividends",
|
| 48 |
+
"net_change_in_cash_and_equivalents": "changes_in_cash",
|
| 49 |
+
}
|
| 50 |
+
|
| 51 |
+
@field_validator("period_ending", mode="before", check_fields=False)
|
| 52 |
+
@classmethod
|
| 53 |
+
def date_validate(cls, v):
|
| 54 |
+
"""Return datetime object from string."""
|
| 55 |
+
if isinstance(v, str):
|
| 56 |
+
return datetime.strptime(v, "%Y-%m-%d %H:%M:%S").date()
|
| 57 |
+
return v
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
class YFinanceCashFlowStatementFetcher(
|
| 61 |
+
Fetcher[
|
| 62 |
+
YFinanceCashFlowStatementQueryParams,
|
| 63 |
+
list[YFinanceCashFlowStatementData],
|
| 64 |
+
]
|
| 65 |
+
):
|
| 66 |
+
"""Yahoo Finance Cash Flow Statement Fetcher."""
|
| 67 |
+
|
| 68 |
+
@staticmethod
|
| 69 |
+
def transform_query(params: dict[str, Any]) -> YFinanceCashFlowStatementQueryParams:
|
| 70 |
+
"""Transform the query parameters."""
|
| 71 |
+
return YFinanceCashFlowStatementQueryParams(**params)
|
| 72 |
+
|
| 73 |
+
@staticmethod
|
| 74 |
+
def extract_data(
|
| 75 |
+
query: YFinanceCashFlowStatementQueryParams,
|
| 76 |
+
credentials: Optional[dict[str, str]],
|
| 77 |
+
**kwargs: Any,
|
| 78 |
+
) -> list[YFinanceCashFlowStatementData]:
|
| 79 |
+
"""Extract the data from the Yahoo Finance endpoints."""
|
| 80 |
+
# pylint: disable=import-outside-toplevel
|
| 81 |
+
import json # noqa
|
| 82 |
+
from curl_adapter import CurlCffiAdapter
|
| 83 |
+
from numpy import nan
|
| 84 |
+
from openbb_core.provider.utils.errors import EmptyDataError
|
| 85 |
+
from openbb_core.provider.utils.helpers import (
|
| 86 |
+
get_requests_session,
|
| 87 |
+
to_snake_case,
|
| 88 |
+
)
|
| 89 |
+
from yfinance import Ticker
|
| 90 |
+
|
| 91 |
+
period = "yearly" if query.period == "annual" else "quarterly" # type: ignore
|
| 92 |
+
session = get_requests_session()
|
| 93 |
+
session.mount("https://", CurlCffiAdapter())
|
| 94 |
+
session.mount("http://", CurlCffiAdapter())
|
| 95 |
+
|
| 96 |
+
data = Ticker(
|
| 97 |
+
query.symbol,
|
| 98 |
+
session=session,
|
| 99 |
+
).get_cash_flow(as_dict=False, pretty=False, freq=period)
|
| 100 |
+
|
| 101 |
+
if data is None:
|
| 102 |
+
raise EmptyDataError()
|
| 103 |
+
|
| 104 |
+
if query.limit:
|
| 105 |
+
data = data.iloc[:, : query.limit]
|
| 106 |
+
|
| 107 |
+
data.index = [to_snake_case(i) for i in data.index]
|
| 108 |
+
data = data.reset_index().sort_index(ascending=False).set_index("index")
|
| 109 |
+
data = data.replace({nan: None}).to_dict()
|
| 110 |
+
data = [{"period_ending": str(key), **value} for key, value in data.items()]
|
| 111 |
+
|
| 112 |
+
data = json.loads(json.dumps(data))
|
| 113 |
+
|
| 114 |
+
return data
|
| 115 |
+
|
| 116 |
+
@staticmethod
|
| 117 |
+
def transform_data(
|
| 118 |
+
query: YFinanceCashFlowStatementQueryParams,
|
| 119 |
+
data: list[dict],
|
| 120 |
+
**kwargs: Any,
|
| 121 |
+
) -> list[YFinanceCashFlowStatementData]:
|
| 122 |
+
"""Transform the data."""
|
| 123 |
+
return [YFinanceCashFlowStatementData.model_validate(d) for d in data]
|
openbb_platform/providers/yfinance/openbb_yfinance/models/company_news.py
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Yahoo Finance Company News Model."""
|
| 2 |
+
|
| 3 |
+
# pylint: disable=unused-argument
|
| 4 |
+
|
| 5 |
+
from typing import Any, Optional
|
| 6 |
+
|
| 7 |
+
from openbb_core.provider.abstract.fetcher import Fetcher
|
| 8 |
+
from openbb_core.provider.standard_models.company_news import (
|
| 9 |
+
CompanyNewsData,
|
| 10 |
+
CompanyNewsQueryParams,
|
| 11 |
+
)
|
| 12 |
+
from pydantic import Field, field_validator
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
class YFinanceCompanyNewsQueryParams(CompanyNewsQueryParams):
|
| 16 |
+
"""YFinance Company News Query.
|
| 17 |
+
|
| 18 |
+
Source: https://finance.yahoo.com/news/
|
| 19 |
+
"""
|
| 20 |
+
|
| 21 |
+
__json_schema_extra__ = {"symbol": {"multiple_items_allowed": True}}
|
| 22 |
+
|
| 23 |
+
@field_validator("symbol", mode="before", check_fields=False)
|
| 24 |
+
@classmethod
|
| 25 |
+
def _symbol_mandatory(cls, v):
|
| 26 |
+
"""Symbol mandatory validator."""
|
| 27 |
+
if not v:
|
| 28 |
+
raise ValueError("Required field missing -> symbol")
|
| 29 |
+
return v
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
class YFinanceCompanyNewsData(CompanyNewsData):
|
| 33 |
+
"""YFinance Company News Data."""
|
| 34 |
+
|
| 35 |
+
source: Optional[str] = Field(
|
| 36 |
+
default=None, description="Source of the news article"
|
| 37 |
+
)
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
class YFinanceCompanyNewsFetcher(
|
| 41 |
+
Fetcher[
|
| 42 |
+
YFinanceCompanyNewsQueryParams,
|
| 43 |
+
list[YFinanceCompanyNewsData],
|
| 44 |
+
]
|
| 45 |
+
):
|
| 46 |
+
"""Transform the query, extract and transform the data from the Yahoo Finance endpoints."""
|
| 47 |
+
|
| 48 |
+
@staticmethod
|
| 49 |
+
def transform_query(params: dict[str, Any]) -> YFinanceCompanyNewsQueryParams:
|
| 50 |
+
"""Transform query params."""
|
| 51 |
+
return YFinanceCompanyNewsQueryParams(**params)
|
| 52 |
+
|
| 53 |
+
@staticmethod
|
| 54 |
+
async def aextract_data(
|
| 55 |
+
query: YFinanceCompanyNewsQueryParams,
|
| 56 |
+
credentials: Optional[dict[str, str]],
|
| 57 |
+
**kwargs: Any,
|
| 58 |
+
) -> list[dict]:
|
| 59 |
+
"""Extract data."""
|
| 60 |
+
# pylint: disable=import-outside-toplevel
|
| 61 |
+
import asyncio # noqa
|
| 62 |
+
from curl_adapter import CurlCffiAdapter
|
| 63 |
+
from openbb_core.provider.utils.errors import EmptyDataError
|
| 64 |
+
from openbb_core.provider.utils.helpers import get_requests_session
|
| 65 |
+
from yfinance import Ticker
|
| 66 |
+
|
| 67 |
+
results: list = []
|
| 68 |
+
symbols = query.symbol.split(",") # type: ignore
|
| 69 |
+
session = get_requests_session()
|
| 70 |
+
session.mount("https://", CurlCffiAdapter())
|
| 71 |
+
session.mount("http://", CurlCffiAdapter())
|
| 72 |
+
|
| 73 |
+
async def get_one(symbol):
|
| 74 |
+
data = Ticker(symbol, session=session).get_news(
|
| 75 |
+
count=query.limit,
|
| 76 |
+
tab="all",
|
| 77 |
+
)
|
| 78 |
+
for d in data:
|
| 79 |
+
new_content: dict = {}
|
| 80 |
+
content = d.get("content")
|
| 81 |
+
if not content:
|
| 82 |
+
continue
|
| 83 |
+
if thumbnail := content.get("thumbnail"):
|
| 84 |
+
images = thumbnail.get("resolutions")
|
| 85 |
+
if images:
|
| 86 |
+
new_content["images"] = [
|
| 87 |
+
{k: str(v) for k, v in img.items()} for img in images
|
| 88 |
+
]
|
| 89 |
+
new_content["url"] = content.get("canonicalUrl", {}).get("url")
|
| 90 |
+
new_content["source"] = content.get("provider", {}).get("displayName")
|
| 91 |
+
new_content["title"] = content.get("title")
|
| 92 |
+
new_content["date"] = content.get("pubDate")
|
| 93 |
+
description = content.get("description")
|
| 94 |
+
summary = content.get("summary")
|
| 95 |
+
|
| 96 |
+
if description:
|
| 97 |
+
new_content["text"] = description
|
| 98 |
+
elif summary:
|
| 99 |
+
new_content["text"] = summary
|
| 100 |
+
|
| 101 |
+
results.append(new_content)
|
| 102 |
+
|
| 103 |
+
tasks = [get_one(symbol) for symbol in symbols]
|
| 104 |
+
|
| 105 |
+
await asyncio.gather(*tasks)
|
| 106 |
+
|
| 107 |
+
if not results:
|
| 108 |
+
raise EmptyDataError("No data was returned for the given symbol(s)")
|
| 109 |
+
|
| 110 |
+
return results
|
| 111 |
+
|
| 112 |
+
@staticmethod
|
| 113 |
+
def transform_data(
|
| 114 |
+
query: YFinanceCompanyNewsQueryParams,
|
| 115 |
+
data: list[dict],
|
| 116 |
+
**kwargs: Any,
|
| 117 |
+
) -> list[YFinanceCompanyNewsData]:
|
| 118 |
+
"""Transform data."""
|
| 119 |
+
return [YFinanceCompanyNewsData.model_validate(d) for d in data]
|
openbb_platform/providers/yfinance/openbb_yfinance/models/crypto_historical.py
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Yahoo Finance Crypto Historical Price Model."""
|
| 2 |
+
|
| 3 |
+
# pylint: disable=unused-argument
|
| 4 |
+
|
| 5 |
+
from datetime import datetime
|
| 6 |
+
from typing import Any, Dict, List, Literal, Optional
|
| 7 |
+
|
| 8 |
+
from openbb_core.provider.abstract.fetcher import Fetcher
|
| 9 |
+
from openbb_core.provider.standard_models.crypto_historical import (
|
| 10 |
+
CryptoHistoricalData,
|
| 11 |
+
CryptoHistoricalQueryParams,
|
| 12 |
+
)
|
| 13 |
+
from openbb_core.provider.utils.descriptions import QUERY_DESCRIPTIONS
|
| 14 |
+
from openbb_core.provider.utils.errors import EmptyDataError
|
| 15 |
+
from openbb_yfinance.utils.references import INTERVALS_DICT
|
| 16 |
+
from pydantic import Field
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
class YFinanceCryptoHistoricalQueryParams(CryptoHistoricalQueryParams):
|
| 20 |
+
"""Yahoo Finance Crypto Historical Price Query.
|
| 21 |
+
|
| 22 |
+
Source: https://finance.yahoo.com/crypto/
|
| 23 |
+
"""
|
| 24 |
+
|
| 25 |
+
__json_schema_extra__ = {
|
| 26 |
+
"symbol": {"multiple_items_allowed": True},
|
| 27 |
+
"interval": {
|
| 28 |
+
"choices": [
|
| 29 |
+
"1m",
|
| 30 |
+
"2m",
|
| 31 |
+
"5m",
|
| 32 |
+
"15m",
|
| 33 |
+
"30m",
|
| 34 |
+
"60m",
|
| 35 |
+
"90m",
|
| 36 |
+
"1h",
|
| 37 |
+
"1d",
|
| 38 |
+
"5d",
|
| 39 |
+
"1W",
|
| 40 |
+
"1M",
|
| 41 |
+
"1Q",
|
| 42 |
+
]
|
| 43 |
+
},
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
interval: Literal[
|
| 47 |
+
"1m",
|
| 48 |
+
"2m",
|
| 49 |
+
"5m",
|
| 50 |
+
"15m",
|
| 51 |
+
"30m",
|
| 52 |
+
"60m",
|
| 53 |
+
"90m",
|
| 54 |
+
"1h",
|
| 55 |
+
"1d",
|
| 56 |
+
"5d",
|
| 57 |
+
"1W",
|
| 58 |
+
"1M",
|
| 59 |
+
"1Q",
|
| 60 |
+
] = Field(
|
| 61 |
+
default="1d",
|
| 62 |
+
description=QUERY_DESCRIPTIONS.get("interval", ""),
|
| 63 |
+
)
|
| 64 |
+
|
| 65 |
+
|
| 66 |
+
class YFinanceCryptoHistoricalData(CryptoHistoricalData):
|
| 67 |
+
"""Yahoo Finance Crypto Historical Price Data."""
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
class YFinanceCryptoHistoricalFetcher(
|
| 71 |
+
Fetcher[
|
| 72 |
+
YFinanceCryptoHistoricalQueryParams,
|
| 73 |
+
List[YFinanceCryptoHistoricalData],
|
| 74 |
+
]
|
| 75 |
+
):
|
| 76 |
+
"""Transform the query, extract and transform the data from the Yahoo Finance endpoints."""
|
| 77 |
+
|
| 78 |
+
@staticmethod
|
| 79 |
+
def transform_query(params: Dict[str, Any]) -> YFinanceCryptoHistoricalQueryParams:
|
| 80 |
+
"""Transform the query."""
|
| 81 |
+
# pylint: disable=import-outside-toplevel
|
| 82 |
+
from dateutil.relativedelta import relativedelta
|
| 83 |
+
|
| 84 |
+
transformed_params = params
|
| 85 |
+
now = datetime.now().date()
|
| 86 |
+
|
| 87 |
+
if params.get("start_date") is None:
|
| 88 |
+
transformed_params["start_date"] = now - relativedelta(years=1)
|
| 89 |
+
|
| 90 |
+
if params.get("end_date") is None:
|
| 91 |
+
transformed_params["end_date"] = now
|
| 92 |
+
|
| 93 |
+
return YFinanceCryptoHistoricalQueryParams(**transformed_params)
|
| 94 |
+
|
| 95 |
+
@staticmethod
|
| 96 |
+
def extract_data(
|
| 97 |
+
query: YFinanceCryptoHistoricalQueryParams,
|
| 98 |
+
credentials: Optional[Dict[str, str]],
|
| 99 |
+
**kwargs: Any,
|
| 100 |
+
) -> List[Dict]:
|
| 101 |
+
"""Return the raw data from the Yahoo Finance endpoint."""
|
| 102 |
+
# pylint: disable=import-outside-toplevel
|
| 103 |
+
from openbb_yfinance.utils.helpers import yf_download
|
| 104 |
+
|
| 105 |
+
tickers = query.symbol.split(",")
|
| 106 |
+
new_tickers = []
|
| 107 |
+
for ticker in tickers:
|
| 108 |
+
new_ticker = (
|
| 109 |
+
ticker[:-3] + "-" + ticker[-3:] if "-" not in ticker else ticker
|
| 110 |
+
)
|
| 111 |
+
new_tickers.append(new_ticker)
|
| 112 |
+
|
| 113 |
+
symbols = ",".join(new_tickers)
|
| 114 |
+
|
| 115 |
+
data = yf_download(
|
| 116 |
+
symbols,
|
| 117 |
+
start_date=query.start_date,
|
| 118 |
+
end_date=query.end_date,
|
| 119 |
+
interval=INTERVALS_DICT.get(query.interval, "1d"), # type: ignore
|
| 120 |
+
auto_adjust=False,
|
| 121 |
+
actions=False,
|
| 122 |
+
prepost=True,
|
| 123 |
+
)
|
| 124 |
+
|
| 125 |
+
if data.empty:
|
| 126 |
+
raise EmptyDataError()
|
| 127 |
+
|
| 128 |
+
return data.to_dict("records")
|
| 129 |
+
|
| 130 |
+
@staticmethod
|
| 131 |
+
def transform_data(
|
| 132 |
+
query: YFinanceCryptoHistoricalQueryParams,
|
| 133 |
+
data: List[Dict],
|
| 134 |
+
**kwargs: Any,
|
| 135 |
+
) -> List[YFinanceCryptoHistoricalData]:
|
| 136 |
+
"""Transform the data to the standard format."""
|
| 137 |
+
return [YFinanceCryptoHistoricalData.model_validate(d) for d in data]
|
openbb_platform/providers/yfinance/openbb_yfinance/models/currency_historical.py
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Yahoo Finance Currency Price Model."""
|
| 2 |
+
|
| 3 |
+
# pylint: disable=unused-argument
|
| 4 |
+
|
| 5 |
+
from datetime import datetime
|
| 6 |
+
from typing import Any, Dict, List, Literal, Optional
|
| 7 |
+
|
| 8 |
+
from openbb_core.provider.abstract.fetcher import Fetcher
|
| 9 |
+
from openbb_core.provider.standard_models.currency_historical import (
|
| 10 |
+
CurrencyHistoricalData,
|
| 11 |
+
CurrencyHistoricalQueryParams,
|
| 12 |
+
)
|
| 13 |
+
from openbb_core.provider.utils.descriptions import QUERY_DESCRIPTIONS
|
| 14 |
+
from openbb_core.provider.utils.errors import EmptyDataError
|
| 15 |
+
from openbb_yfinance.utils.references import INTERVALS_DICT
|
| 16 |
+
from pydantic import Field
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
class YFinanceCurrencyHistoricalQueryParams(CurrencyHistoricalQueryParams):
|
| 20 |
+
"""Yahoo Finance Currency Price Query.
|
| 21 |
+
|
| 22 |
+
Source: https://finance.yahoo.com/currencies/
|
| 23 |
+
"""
|
| 24 |
+
|
| 25 |
+
__json_schema_extra__ = {
|
| 26 |
+
"symbol": {"multiple_items_allowed": True},
|
| 27 |
+
"interval": {
|
| 28 |
+
"choices": [
|
| 29 |
+
"1m",
|
| 30 |
+
"2m",
|
| 31 |
+
"5m",
|
| 32 |
+
"15m",
|
| 33 |
+
"30m",
|
| 34 |
+
"60m",
|
| 35 |
+
"90m",
|
| 36 |
+
"1h",
|
| 37 |
+
"1d",
|
| 38 |
+
"5d",
|
| 39 |
+
"1W",
|
| 40 |
+
"1M",
|
| 41 |
+
"1Q",
|
| 42 |
+
]
|
| 43 |
+
},
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
interval: Literal[
|
| 47 |
+
"1m",
|
| 48 |
+
"2m",
|
| 49 |
+
"5m",
|
| 50 |
+
"15m",
|
| 51 |
+
"30m",
|
| 52 |
+
"60m",
|
| 53 |
+
"90m",
|
| 54 |
+
"1h",
|
| 55 |
+
"1d",
|
| 56 |
+
"5d",
|
| 57 |
+
"1W",
|
| 58 |
+
"1M",
|
| 59 |
+
"1Q",
|
| 60 |
+
] = Field(
|
| 61 |
+
default="1d",
|
| 62 |
+
description=QUERY_DESCRIPTIONS.get("interval", ""),
|
| 63 |
+
)
|
| 64 |
+
|
| 65 |
+
|
| 66 |
+
class YFinanceCurrencyHistoricalData(CurrencyHistoricalData):
|
| 67 |
+
"""Yahoo Finance Currency Price Data."""
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
class YFinanceCurrencyHistoricalFetcher(
|
| 71 |
+
Fetcher[
|
| 72 |
+
YFinanceCurrencyHistoricalQueryParams,
|
| 73 |
+
List[YFinanceCurrencyHistoricalData],
|
| 74 |
+
]
|
| 75 |
+
):
|
| 76 |
+
"""Transform the query, extract and transform the data from the Yahoo Finance endpoints."""
|
| 77 |
+
|
| 78 |
+
@staticmethod
|
| 79 |
+
def transform_query(
|
| 80 |
+
params: Dict[str, Any]
|
| 81 |
+
) -> YFinanceCurrencyHistoricalQueryParams:
|
| 82 |
+
"""Transform the query."""
|
| 83 |
+
# pylint: disable=import-outside-toplevel
|
| 84 |
+
from dateutil.relativedelta import relativedelta
|
| 85 |
+
|
| 86 |
+
transformed_params = params
|
| 87 |
+
symbols = params["symbol"].split(",")
|
| 88 |
+
new_symbols = [
|
| 89 |
+
f"{s.upper()}=X" if "=X" not in s.upper() else s.upper() for s in symbols
|
| 90 |
+
]
|
| 91 |
+
transformed_params["symbol"] = ",".join(new_symbols)
|
| 92 |
+
|
| 93 |
+
now = datetime.now().date()
|
| 94 |
+
|
| 95 |
+
if params.get("start_date") is None:
|
| 96 |
+
transformed_params["start_date"] = now - relativedelta(years=1)
|
| 97 |
+
|
| 98 |
+
if params.get("end_date") is None:
|
| 99 |
+
transformed_params["end_date"] = now
|
| 100 |
+
|
| 101 |
+
return YFinanceCurrencyHistoricalQueryParams(**transformed_params)
|
| 102 |
+
|
| 103 |
+
@staticmethod
|
| 104 |
+
def extract_data(
|
| 105 |
+
query: YFinanceCurrencyHistoricalQueryParams,
|
| 106 |
+
credentials: Optional[Dict[str, str]],
|
| 107 |
+
**kwargs: Any,
|
| 108 |
+
) -> List[Dict]:
|
| 109 |
+
"""Return the raw data from the Yahoo Finance endpoint."""
|
| 110 |
+
# pylint: disable=import-outside-toplevel
|
| 111 |
+
from openbb_yfinance.utils.helpers import yf_download
|
| 112 |
+
|
| 113 |
+
data = yf_download(
|
| 114 |
+
query.symbol,
|
| 115 |
+
start_date=query.start_date,
|
| 116 |
+
end_date=query.end_date,
|
| 117 |
+
interval=INTERVALS_DICT.get(query.interval, "1d"), # type: ignore
|
| 118 |
+
auto_adjust=False,
|
| 119 |
+
actions=False,
|
| 120 |
+
prepost=True,
|
| 121 |
+
)
|
| 122 |
+
|
| 123 |
+
if data.empty:
|
| 124 |
+
raise EmptyDataError()
|
| 125 |
+
|
| 126 |
+
return data.to_dict("records")
|
| 127 |
+
|
| 128 |
+
@staticmethod
|
| 129 |
+
def transform_data(
|
| 130 |
+
query: YFinanceCurrencyHistoricalQueryParams,
|
| 131 |
+
data: List[Dict],
|
| 132 |
+
**kwargs: Any,
|
| 133 |
+
) -> List[YFinanceCurrencyHistoricalData]:
|
| 134 |
+
"""Transform the data to the standard format."""
|
| 135 |
+
return [YFinanceCurrencyHistoricalData.model_validate(d) for d in data]
|
openbb_platform/providers/yfinance/openbb_yfinance/models/equity_historical.py
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Yahoo Finance Equity Historical Price Model."""
|
| 2 |
+
|
| 3 |
+
# pylint: disable=unused-argument
|
| 4 |
+
|
| 5 |
+
from datetime import datetime
|
| 6 |
+
from typing import TYPE_CHECKING, Any, Dict, List, Literal, Optional
|
| 7 |
+
from warnings import warn
|
| 8 |
+
|
| 9 |
+
from openbb_core.provider.abstract.fetcher import Fetcher
|
| 10 |
+
from openbb_core.provider.standard_models.equity_historical import (
|
| 11 |
+
EquityHistoricalData,
|
| 12 |
+
EquityHistoricalQueryParams,
|
| 13 |
+
)
|
| 14 |
+
from openbb_core.provider.utils.descriptions import QUERY_DESCRIPTIONS
|
| 15 |
+
from openbb_core.provider.utils.errors import EmptyDataError
|
| 16 |
+
from openbb_yfinance.utils.references import INTERVALS_DICT, PERIODS
|
| 17 |
+
from pydantic import Field, PrivateAttr
|
| 18 |
+
|
| 19 |
+
if TYPE_CHECKING:
|
| 20 |
+
from pandas import DataFrame
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
class YFinanceEquityHistoricalQueryParams(EquityHistoricalQueryParams):
|
| 24 |
+
"""Yahoo Finance Equity Historical Price Query.
|
| 25 |
+
|
| 26 |
+
Source: https://finance.yahoo.com/
|
| 27 |
+
"""
|
| 28 |
+
|
| 29 |
+
__json_schema_extra__ = {
|
| 30 |
+
"symbol": {"multiple_items_allowed": True},
|
| 31 |
+
"interval": {
|
| 32 |
+
"choices": [
|
| 33 |
+
"1m",
|
| 34 |
+
"2m",
|
| 35 |
+
"5m",
|
| 36 |
+
"15m",
|
| 37 |
+
"30m",
|
| 38 |
+
"60m",
|
| 39 |
+
"90m",
|
| 40 |
+
"1h",
|
| 41 |
+
"1d",
|
| 42 |
+
"5d",
|
| 43 |
+
"1W",
|
| 44 |
+
"1M",
|
| 45 |
+
"1Q",
|
| 46 |
+
]
|
| 47 |
+
},
|
| 48 |
+
}
|
| 49 |
+
|
| 50 |
+
interval: Literal[
|
| 51 |
+
"1m",
|
| 52 |
+
"2m",
|
| 53 |
+
"5m",
|
| 54 |
+
"15m",
|
| 55 |
+
"30m",
|
| 56 |
+
"60m",
|
| 57 |
+
"90m",
|
| 58 |
+
"1h",
|
| 59 |
+
"1d",
|
| 60 |
+
"5d",
|
| 61 |
+
"1W",
|
| 62 |
+
"1M",
|
| 63 |
+
"1Q",
|
| 64 |
+
] = Field(
|
| 65 |
+
default="1d",
|
| 66 |
+
description=QUERY_DESCRIPTIONS.get("interval", ""),
|
| 67 |
+
)
|
| 68 |
+
extended_hours: bool = Field(
|
| 69 |
+
default=False,
|
| 70 |
+
description="Include Pre and Post market data.",
|
| 71 |
+
)
|
| 72 |
+
include_actions: bool = Field(
|
| 73 |
+
default=True,
|
| 74 |
+
description="Include dividends and stock splits in results.",
|
| 75 |
+
)
|
| 76 |
+
adjustment: Literal["splits_only", "splits_and_dividends"] = Field(
|
| 77 |
+
default="splits_only",
|
| 78 |
+
description="The adjustment factor to apply. Default is splits only.",
|
| 79 |
+
)
|
| 80 |
+
|
| 81 |
+
_ignore_tz: bool = PrivateAttr(default=True)
|
| 82 |
+
_progress: bool = PrivateAttr(default=False)
|
| 83 |
+
_keepna: bool = PrivateAttr(default=False)
|
| 84 |
+
_period: PERIODS = PrivateAttr(default="max")
|
| 85 |
+
_rounding: bool = PrivateAttr(default=False)
|
| 86 |
+
_repair: bool = PrivateAttr(default=False)
|
| 87 |
+
_group_by: Literal["ticker", "column"] = PrivateAttr(default="ticker")
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
class YFinanceEquityHistoricalData(EquityHistoricalData):
|
| 91 |
+
"""Yahoo Finance Equity Historical Price Data."""
|
| 92 |
+
|
| 93 |
+
__alias_dict__ = {
|
| 94 |
+
"split_ratio": "stock_splits",
|
| 95 |
+
"dividend": "dividends",
|
| 96 |
+
}
|
| 97 |
+
|
| 98 |
+
split_ratio: Optional[float] = Field(
|
| 99 |
+
default=None,
|
| 100 |
+
description="Ratio of the equity split, if a split occurred.",
|
| 101 |
+
)
|
| 102 |
+
dividend: Optional[float] = Field(
|
| 103 |
+
default=None,
|
| 104 |
+
description="Dividend amount (split-adjusted), if a dividend was paid.",
|
| 105 |
+
)
|
| 106 |
+
|
| 107 |
+
|
| 108 |
+
class YFinanceEquityHistoricalFetcher(
|
| 109 |
+
Fetcher[
|
| 110 |
+
YFinanceEquityHistoricalQueryParams,
|
| 111 |
+
List[YFinanceEquityHistoricalData],
|
| 112 |
+
]
|
| 113 |
+
):
|
| 114 |
+
"""Transform the query, extract and transform the data from the Yahoo Finance endpoints."""
|
| 115 |
+
|
| 116 |
+
@staticmethod
|
| 117 |
+
def transform_query(params: Dict[str, Any]) -> YFinanceEquityHistoricalQueryParams:
|
| 118 |
+
"""Transform the query."""
|
| 119 |
+
# pylint: disable=import-outside-toplevel
|
| 120 |
+
from dateutil.relativedelta import relativedelta
|
| 121 |
+
|
| 122 |
+
transformed_params = params
|
| 123 |
+
now = datetime.now().date()
|
| 124 |
+
|
| 125 |
+
if params.get("start_date") is None:
|
| 126 |
+
transformed_params["start_date"] = now - relativedelta(years=1)
|
| 127 |
+
|
| 128 |
+
if params.get("end_date") is None:
|
| 129 |
+
transformed_params["end_date"] = now
|
| 130 |
+
|
| 131 |
+
return YFinanceEquityHistoricalQueryParams(**params)
|
| 132 |
+
|
| 133 |
+
@staticmethod
|
| 134 |
+
def extract_data(
|
| 135 |
+
query: YFinanceEquityHistoricalQueryParams,
|
| 136 |
+
credentials: Optional[Dict[str, str]],
|
| 137 |
+
**kwargs: Any,
|
| 138 |
+
) -> "DataFrame":
|
| 139 |
+
"""Return the raw data from the Yahoo Finance endpoint."""
|
| 140 |
+
# pylint: disable=import-outside-toplevel
|
| 141 |
+
from openbb_yfinance.utils.helpers import yf_download
|
| 142 |
+
|
| 143 |
+
adjusted = query.adjustment == "splits_and_dividends"
|
| 144 |
+
kwargs = {"auto_adjust": True, "back_adjust": True} if adjusted is True else {}
|
| 145 |
+
# pylint: disable=protected-access
|
| 146 |
+
data = yf_download(
|
| 147 |
+
symbol=query.symbol,
|
| 148 |
+
start_date=query.start_date,
|
| 149 |
+
end_date=query.end_date,
|
| 150 |
+
interval=INTERVALS_DICT[query.interval], # type: ignore
|
| 151 |
+
period=query._period,
|
| 152 |
+
prepost=query.extended_hours,
|
| 153 |
+
actions=query.include_actions,
|
| 154 |
+
progress=query._progress,
|
| 155 |
+
ignore_tz=query._ignore_tz,
|
| 156 |
+
keepna=query._keepna,
|
| 157 |
+
repair=query._repair,
|
| 158 |
+
rounding=query._rounding,
|
| 159 |
+
group_by=query._group_by,
|
| 160 |
+
adjusted=adjusted,
|
| 161 |
+
**kwargs,
|
| 162 |
+
)
|
| 163 |
+
|
| 164 |
+
if data.empty:
|
| 165 |
+
raise EmptyDataError()
|
| 166 |
+
|
| 167 |
+
return data
|
| 168 |
+
|
| 169 |
+
@staticmethod
|
| 170 |
+
def transform_data(
|
| 171 |
+
query: YFinanceEquityHistoricalQueryParams,
|
| 172 |
+
data: "DataFrame",
|
| 173 |
+
**kwargs: Any,
|
| 174 |
+
) -> List[YFinanceEquityHistoricalData]:
|
| 175 |
+
"""Transform the data to the standard format."""
|
| 176 |
+
if "capital_gains" in data.columns:
|
| 177 |
+
data = (
|
| 178 |
+
data.drop(columns=["capital_gains"])
|
| 179 |
+
if query.include_actions is False
|
| 180 |
+
else data
|
| 181 |
+
)
|
| 182 |
+
query_symbols = query.symbol.upper().split(",")
|
| 183 |
+
|
| 184 |
+
if len(query_symbols) > 1:
|
| 185 |
+
symbols = data.symbol.unique().tolist()
|
| 186 |
+
for symbol in query_symbols:
|
| 187 |
+
if symbol not in symbols:
|
| 188 |
+
warn(f"Data for '{symbol}' was not found.")
|
| 189 |
+
|
| 190 |
+
return [
|
| 191 |
+
YFinanceEquityHistoricalData.model_validate(d)
|
| 192 |
+
for d in data.to_dict("records")
|
| 193 |
+
]
|
openbb_platform/providers/yfinance/openbb_yfinance/models/equity_profile.py
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""YFinance Equity Profile Model."""
|
| 2 |
+
|
| 3 |
+
# pylint: disable=unused-argument
|
| 4 |
+
|
| 5 |
+
from typing import Any, Dict, List, Optional
|
| 6 |
+
|
| 7 |
+
from openbb_core.provider.abstract.fetcher import Fetcher
|
| 8 |
+
from openbb_core.provider.standard_models.equity_info import (
|
| 9 |
+
EquityInfoData,
|
| 10 |
+
EquityInfoQueryParams,
|
| 11 |
+
)
|
| 12 |
+
from pydantic import Field, field_validator
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
class YFinanceEquityProfileQueryParams(EquityInfoQueryParams):
|
| 16 |
+
"""YFinance Equity Profile Query."""
|
| 17 |
+
|
| 18 |
+
__json_schema_extra__ = {"symbol": {"multiple_items_allowed": True}}
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
class YFinanceEquityProfileData(EquityInfoData):
|
| 22 |
+
"""YFinance Equity Profile Data."""
|
| 23 |
+
|
| 24 |
+
__alias_dict__ = {
|
| 25 |
+
"name": "longName",
|
| 26 |
+
"issue_type": "quoteType",
|
| 27 |
+
"stock_exchange": "exchange",
|
| 28 |
+
"first_stock_price_date": "firstTradeDateEpochUtc",
|
| 29 |
+
"exchange_timezone": "timeZoneFullName",
|
| 30 |
+
"industry_category": "industry",
|
| 31 |
+
"hq_country": "country",
|
| 32 |
+
"hq_address1": "address1",
|
| 33 |
+
"hq_address_city": "city",
|
| 34 |
+
"hq_address_postal_code": "zip",
|
| 35 |
+
"hq_state": "state",
|
| 36 |
+
"business_phone_no": "phone",
|
| 37 |
+
"company_url": "website",
|
| 38 |
+
"long_description": "longBusinessSummary",
|
| 39 |
+
"employees": "fullTimeEmployees",
|
| 40 |
+
"market_cap": "marketCap",
|
| 41 |
+
"shares_outstanding": "sharesOutstanding",
|
| 42 |
+
"shares_float": "floatShares",
|
| 43 |
+
"shares_implied_outstanding": "impliedSharesOutstanding",
|
| 44 |
+
"shares_short": "sharesShort",
|
| 45 |
+
"dividend_yield": "yield",
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
+
exchange_timezone: Optional[str] = Field(
|
| 49 |
+
description="The timezone of the exchange.",
|
| 50 |
+
default=None,
|
| 51 |
+
)
|
| 52 |
+
issue_type: Optional[str] = Field(
|
| 53 |
+
description="The issuance type of the asset.",
|
| 54 |
+
default=None,
|
| 55 |
+
)
|
| 56 |
+
currency: Optional[str] = Field(
|
| 57 |
+
description="The currency in which the asset is traded.", default=None
|
| 58 |
+
)
|
| 59 |
+
market_cap: Optional[int] = Field(
|
| 60 |
+
description="The market capitalization of the asset.",
|
| 61 |
+
default=None,
|
| 62 |
+
)
|
| 63 |
+
shares_outstanding: Optional[int] = Field(
|
| 64 |
+
description="The number of listed shares outstanding.",
|
| 65 |
+
default=None,
|
| 66 |
+
)
|
| 67 |
+
shares_float: Optional[int] = Field(
|
| 68 |
+
description="The number of shares in the public float.",
|
| 69 |
+
default=None,
|
| 70 |
+
)
|
| 71 |
+
shares_implied_outstanding: Optional[int] = Field(
|
| 72 |
+
description=(
|
| 73 |
+
"Implied shares outstanding of common equity"
|
| 74 |
+
"assuming the conversion of all convertible subsidiary equity into common."
|
| 75 |
+
),
|
| 76 |
+
default=None,
|
| 77 |
+
)
|
| 78 |
+
shares_short: Optional[int] = Field(
|
| 79 |
+
description="The reported number of shares short.",
|
| 80 |
+
default=None,
|
| 81 |
+
)
|
| 82 |
+
dividend_yield: Optional[float] = Field(
|
| 83 |
+
description="The dividend yield of the asset, as a normalized percent.",
|
| 84 |
+
default=None,
|
| 85 |
+
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
|
| 86 |
+
)
|
| 87 |
+
beta: Optional[float] = Field(
|
| 88 |
+
description="The beta of the asset relative to the broad market.",
|
| 89 |
+
default=None,
|
| 90 |
+
)
|
| 91 |
+
|
| 92 |
+
@field_validator("first_stock_price_date", mode="before", check_fields=False)
|
| 93 |
+
@classmethod
|
| 94 |
+
def validate_first_trade_date(cls, v):
|
| 95 |
+
"""Validate first stock price date."""
|
| 96 |
+
# pylint: disable=import-outside-toplevel
|
| 97 |
+
from datetime import timezone # noqa
|
| 98 |
+
from openbb_core.provider.utils.helpers import safe_fromtimestamp # noqa
|
| 99 |
+
|
| 100 |
+
return safe_fromtimestamp(v, tz=timezone.utc).date() if v else None
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
class YFinanceEquityProfileFetcher(
|
| 104 |
+
Fetcher[YFinanceEquityProfileQueryParams, List[YFinanceEquityProfileData]]
|
| 105 |
+
):
|
| 106 |
+
"""YFinance Equity Profile fetcher."""
|
| 107 |
+
|
| 108 |
+
@staticmethod
|
| 109 |
+
def transform_query(params: Dict[str, Any]) -> YFinanceEquityProfileQueryParams:
|
| 110 |
+
"""Transform the query."""
|
| 111 |
+
return YFinanceEquityProfileQueryParams(**params)
|
| 112 |
+
|
| 113 |
+
@staticmethod
|
| 114 |
+
async def aextract_data(
|
| 115 |
+
query: YFinanceEquityProfileQueryParams,
|
| 116 |
+
credentials: Optional[Dict[str, str]],
|
| 117 |
+
**kwargs: Any,
|
| 118 |
+
) -> List[Dict]:
|
| 119 |
+
"""Extract the raw data from YFinance."""
|
| 120 |
+
# pylint: disable=import-outside-toplevel
|
| 121 |
+
import asyncio # noqa
|
| 122 |
+
from curl_adapter import CurlCffiAdapter
|
| 123 |
+
from openbb_core.app.model.abstract.error import OpenBBError
|
| 124 |
+
from openbb_core.provider.utils.errors import EmptyDataError
|
| 125 |
+
from openbb_core.provider.utils.helpers import get_requests_session
|
| 126 |
+
from warnings import warn
|
| 127 |
+
from yfinance import Ticker
|
| 128 |
+
|
| 129 |
+
symbols = query.symbol.split(",")
|
| 130 |
+
results = []
|
| 131 |
+
fields = [
|
| 132 |
+
"symbol",
|
| 133 |
+
"longName",
|
| 134 |
+
"exchange",
|
| 135 |
+
"timeZoneFullName",
|
| 136 |
+
"quoteType",
|
| 137 |
+
"firstTradeDateEpochUtc",
|
| 138 |
+
"currency",
|
| 139 |
+
"sharesOutstanding",
|
| 140 |
+
"floatShares",
|
| 141 |
+
"impliedSharesOutstanding",
|
| 142 |
+
"sharesShort",
|
| 143 |
+
"sector",
|
| 144 |
+
"industry",
|
| 145 |
+
"address1",
|
| 146 |
+
"city",
|
| 147 |
+
"state",
|
| 148 |
+
"zip",
|
| 149 |
+
"country",
|
| 150 |
+
"phone",
|
| 151 |
+
"website",
|
| 152 |
+
"fullTimeEmployees",
|
| 153 |
+
"longBusinessSummary",
|
| 154 |
+
"marketCap",
|
| 155 |
+
"yield",
|
| 156 |
+
"dividendYield",
|
| 157 |
+
"beta",
|
| 158 |
+
]
|
| 159 |
+
messages: list = []
|
| 160 |
+
session = get_requests_session()
|
| 161 |
+
session.mount("https://", CurlCffiAdapter())
|
| 162 |
+
session.mount("http://", CurlCffiAdapter())
|
| 163 |
+
|
| 164 |
+
async def get_one(symbol):
|
| 165 |
+
"""Get the data for one ticker symbol."""
|
| 166 |
+
result: dict = {}
|
| 167 |
+
ticker: dict = {}
|
| 168 |
+
try:
|
| 169 |
+
ticker = Ticker(
|
| 170 |
+
symbol,
|
| 171 |
+
session=session,
|
| 172 |
+
).get_info()
|
| 173 |
+
except Exception as e:
|
| 174 |
+
messages.append(
|
| 175 |
+
f"Error getting data for {symbol} -> {e.__class__.__name__}: {e}"
|
| 176 |
+
)
|
| 177 |
+
if ticker:
|
| 178 |
+
for field in fields:
|
| 179 |
+
if field in ticker:
|
| 180 |
+
result[
|
| 181 |
+
field.replace("dividendYield", "dividend_yield").replace(
|
| 182 |
+
"issueType", "issue_type"
|
| 183 |
+
)
|
| 184 |
+
] = ticker.get(field, None)
|
| 185 |
+
if result:
|
| 186 |
+
results.append(result)
|
| 187 |
+
|
| 188 |
+
tasks = [get_one(symbol) for symbol in symbols]
|
| 189 |
+
|
| 190 |
+
await asyncio.gather(*tasks)
|
| 191 |
+
|
| 192 |
+
if not results and messages:
|
| 193 |
+
raise OpenBBError("\n".join(messages))
|
| 194 |
+
|
| 195 |
+
if not results and not messages:
|
| 196 |
+
raise EmptyDataError("No data was returned for any symbol")
|
| 197 |
+
|
| 198 |
+
if results and messages:
|
| 199 |
+
for message in messages:
|
| 200 |
+
warn(message)
|
| 201 |
+
|
| 202 |
+
return results
|
| 203 |
+
|
| 204 |
+
@staticmethod
|
| 205 |
+
def transform_data(
|
| 206 |
+
query: YFinanceEquityProfileQueryParams,
|
| 207 |
+
data: List[Dict],
|
| 208 |
+
**kwargs: Any,
|
| 209 |
+
) -> List[YFinanceEquityProfileData]:
|
| 210 |
+
"""Transform the data."""
|
| 211 |
+
return [YFinanceEquityProfileData.model_validate(d) for d in data]
|
openbb_platform/providers/yfinance/openbb_yfinance/models/equity_quote.py
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""YFinance Equity Quote Model."""
|
| 2 |
+
|
| 3 |
+
# pylint: disable=unused-argument
|
| 4 |
+
|
| 5 |
+
from typing import Any, Dict, List, Optional
|
| 6 |
+
from warnings import warn
|
| 7 |
+
|
| 8 |
+
from openbb_core.provider.abstract.fetcher import Fetcher
|
| 9 |
+
from openbb_core.provider.standard_models.equity_quote import (
|
| 10 |
+
EquityQuoteData,
|
| 11 |
+
EquityQuoteQueryParams,
|
| 12 |
+
)
|
| 13 |
+
from pydantic import Field
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
class YFinanceEquityQuoteQueryParams(EquityQuoteQueryParams):
|
| 17 |
+
"""YFinance Equity Quote Query."""
|
| 18 |
+
|
| 19 |
+
__json_schema_extra__ = {"symbol": {"multiple_items_allowed": True}}
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
class YFinanceEquityQuoteData(EquityQuoteData):
|
| 23 |
+
"""YFinance Equity Quote Data."""
|
| 24 |
+
|
| 25 |
+
__alias_dict__ = {
|
| 26 |
+
"name": "longName",
|
| 27 |
+
"asset_type": "quoteType",
|
| 28 |
+
"last_price": "currentPrice",
|
| 29 |
+
"high": "dayHigh",
|
| 30 |
+
"low": "dayLow",
|
| 31 |
+
"prev_close": "previousClose",
|
| 32 |
+
"year_high": "fiftyTwoWeekHigh",
|
| 33 |
+
"year_low": "fiftyTwoWeekLow",
|
| 34 |
+
"ma_50d": "fiftyDayAverage",
|
| 35 |
+
"ma_200d": "twoHundredDayAverage",
|
| 36 |
+
"volume_average": "averageVolume",
|
| 37 |
+
"volume_average_10d": "averageDailyVolume10Day",
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
ma_50d: Optional[float] = Field(
|
| 41 |
+
default=None,
|
| 42 |
+
description="50-day moving average price.",
|
| 43 |
+
)
|
| 44 |
+
ma_200d: Optional[float] = Field(
|
| 45 |
+
default=None,
|
| 46 |
+
description="200-day moving average price.",
|
| 47 |
+
)
|
| 48 |
+
volume_average: Optional[float] = Field(
|
| 49 |
+
default=None,
|
| 50 |
+
description="Average daily trading volume.",
|
| 51 |
+
)
|
| 52 |
+
volume_average_10d: Optional[float] = Field(
|
| 53 |
+
default=None,
|
| 54 |
+
description="Average daily trading volume in the last 10 days.",
|
| 55 |
+
)
|
| 56 |
+
currency: Optional[str] = Field(
|
| 57 |
+
default=None,
|
| 58 |
+
description="Currency of the price.",
|
| 59 |
+
)
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
class YFinanceEquityQuoteFetcher(
|
| 63 |
+
Fetcher[YFinanceEquityQuoteQueryParams, List[YFinanceEquityQuoteData]]
|
| 64 |
+
):
|
| 65 |
+
"""YFinance Equity Quote Fetcher."""
|
| 66 |
+
|
| 67 |
+
@staticmethod
|
| 68 |
+
def transform_query(params: Dict[str, Any]) -> YFinanceEquityQuoteQueryParams:
|
| 69 |
+
"""Transform the query."""
|
| 70 |
+
return YFinanceEquityQuoteQueryParams(**params)
|
| 71 |
+
|
| 72 |
+
@staticmethod
|
| 73 |
+
async def aextract_data(
|
| 74 |
+
query: YFinanceEquityQuoteQueryParams,
|
| 75 |
+
credentials: Optional[Dict[str, str]],
|
| 76 |
+
**kwargs: Any,
|
| 77 |
+
) -> List[Dict]:
|
| 78 |
+
"""Extract the raw data from YFinance."""
|
| 79 |
+
# pylint: disable=import-outside-toplevel
|
| 80 |
+
import asyncio # noqa
|
| 81 |
+
from curl_adapter import CurlCffiAdapter
|
| 82 |
+
from openbb_core.provider.utils.helpers import get_requests_session
|
| 83 |
+
from yfinance import Ticker
|
| 84 |
+
|
| 85 |
+
session = get_requests_session()
|
| 86 |
+
session.mount("https://", CurlCffiAdapter())
|
| 87 |
+
session.mount("http://", CurlCffiAdapter())
|
| 88 |
+
|
| 89 |
+
symbols = query.symbol.split(",")
|
| 90 |
+
results = []
|
| 91 |
+
fields = [
|
| 92 |
+
"symbol",
|
| 93 |
+
"longName",
|
| 94 |
+
"exchange",
|
| 95 |
+
"quoteType",
|
| 96 |
+
"bid",
|
| 97 |
+
"bidSize",
|
| 98 |
+
"ask",
|
| 99 |
+
"askSize",
|
| 100 |
+
"currentPrice",
|
| 101 |
+
"open",
|
| 102 |
+
"dayHigh",
|
| 103 |
+
"dayLow",
|
| 104 |
+
"previousClose",
|
| 105 |
+
"volume",
|
| 106 |
+
"averageVolume",
|
| 107 |
+
"averageDailyVolume10Day",
|
| 108 |
+
"fiftyTwoWeekHigh",
|
| 109 |
+
"fiftyTwoWeekLow",
|
| 110 |
+
"fiftyDayAverage",
|
| 111 |
+
"twoHundredDayAverage",
|
| 112 |
+
"currency",
|
| 113 |
+
]
|
| 114 |
+
|
| 115 |
+
async def get_one(symbol):
|
| 116 |
+
"""Get the data for one ticker symbol."""
|
| 117 |
+
result: dict = {}
|
| 118 |
+
ticker: dict = {}
|
| 119 |
+
try:
|
| 120 |
+
ticker = Ticker(
|
| 121 |
+
symbol,
|
| 122 |
+
session=session,
|
| 123 |
+
).get_info()
|
| 124 |
+
except Exception as e:
|
| 125 |
+
warn(f"Error getting data for {symbol}: {e}")
|
| 126 |
+
if ticker:
|
| 127 |
+
for field in fields:
|
| 128 |
+
if field in ticker:
|
| 129 |
+
result[field] = ticker.get(field, None)
|
| 130 |
+
if result:
|
| 131 |
+
results.append(result)
|
| 132 |
+
|
| 133 |
+
tasks = [get_one(symbol) for symbol in symbols]
|
| 134 |
+
|
| 135 |
+
await asyncio.gather(*tasks)
|
| 136 |
+
|
| 137 |
+
return results
|
| 138 |
+
|
| 139 |
+
@staticmethod
|
| 140 |
+
def transform_data(
|
| 141 |
+
query: YFinanceEquityQuoteQueryParams,
|
| 142 |
+
data: List[Dict],
|
| 143 |
+
**kwargs: Any,
|
| 144 |
+
) -> List[YFinanceEquityQuoteData]:
|
| 145 |
+
"""Transform the data."""
|
| 146 |
+
return [YFinanceEquityQuoteData.model_validate(d) for d in data]
|
openbb_platform/providers/yfinance/openbb_yfinance/models/equity_screener.py
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""YFinance Equity Screener Model."""
|
| 2 |
+
|
| 3 |
+
# pylint: disable=unused-argument, too-many-statements, too-many-branches
|
| 4 |
+
|
| 5 |
+
from typing import Any, Optional
|
| 6 |
+
|
| 7 |
+
from openbb_core.app.model.abstract.error import OpenBBError
|
| 8 |
+
from openbb_core.provider.abstract.fetcher import Fetcher
|
| 9 |
+
from openbb_core.provider.standard_models.equity_screener import (
|
| 10 |
+
EquityScreenerData,
|
| 11 |
+
EquityScreenerQueryParams,
|
| 12 |
+
)
|
| 13 |
+
from openbb_core.provider.utils.errors import EmptyDataError
|
| 14 |
+
from openbb_yfinance.utils.references import (
|
| 15 |
+
COUNTRIES,
|
| 16 |
+
EXCHANGES,
|
| 17 |
+
INDUSTRIES,
|
| 18 |
+
INDUSTRY_MAP,
|
| 19 |
+
PEER_GROUPS,
|
| 20 |
+
SECTOR_MAP,
|
| 21 |
+
SECTORS,
|
| 22 |
+
Exchanges,
|
| 23 |
+
YFPredefinedScreenerData,
|
| 24 |
+
get_industry_sector,
|
| 25 |
+
)
|
| 26 |
+
from pydantic import Field
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
class YFinanceEquityScreenerQueryParams(EquityScreenerQueryParams):
|
| 30 |
+
"""YFinance Equity Screener Query."""
|
| 31 |
+
|
| 32 |
+
__json_schema_extra__ = {
|
| 33 |
+
"country": {
|
| 34 |
+
"multiple_items_allowed": False,
|
| 35 |
+
"choices": COUNTRIES,
|
| 36 |
+
},
|
| 37 |
+
"exchange": {
|
| 38 |
+
"multiple_items_allowed": False,
|
| 39 |
+
"choices": EXCHANGES,
|
| 40 |
+
},
|
| 41 |
+
"sector": {
|
| 42 |
+
"multiple_items_allowed": False,
|
| 43 |
+
"choices": list(SECTOR_MAP),
|
| 44 |
+
},
|
| 45 |
+
"industry": {
|
| 46 |
+
"multiple_items_allowed": False,
|
| 47 |
+
"choices": INDUSTRIES,
|
| 48 |
+
},
|
| 49 |
+
}
|
| 50 |
+
|
| 51 |
+
country: Optional[str] = Field(
|
| 52 |
+
default="us",
|
| 53 |
+
description="Filter by country, as a two-letter country code. Default is, 'us'. Use, 'all', for all countries.",
|
| 54 |
+
)
|
| 55 |
+
exchange: Optional[Exchanges] = Field(
|
| 56 |
+
default=None,
|
| 57 |
+
description="Filter by exchange.",
|
| 58 |
+
)
|
| 59 |
+
sector: Optional[SECTORS] = Field(default=None, description="Filter by sector.")
|
| 60 |
+
industry: Optional[str] = Field(
|
| 61 |
+
default=None,
|
| 62 |
+
description="Filter by industry.",
|
| 63 |
+
)
|
| 64 |
+
mktcap_min: Optional[int] = Field(
|
| 65 |
+
default=500000000,
|
| 66 |
+
description="Filter by market cap greater than this value. Default is 500M.",
|
| 67 |
+
)
|
| 68 |
+
mktcap_max: Optional[int] = Field(
|
| 69 |
+
default=None,
|
| 70 |
+
description="Filter by market cap less than this value.",
|
| 71 |
+
)
|
| 72 |
+
price_min: Optional[float] = Field(
|
| 73 |
+
default=5,
|
| 74 |
+
description="Filter by price greater than this value. Default is, 5",
|
| 75 |
+
)
|
| 76 |
+
price_max: Optional[float] = Field(
|
| 77 |
+
default=None,
|
| 78 |
+
description="Filter by price less than this value.",
|
| 79 |
+
)
|
| 80 |
+
volume_min: Optional[int] = Field(
|
| 81 |
+
default=10000,
|
| 82 |
+
description="Filter by volume greater than this value. Default is, 10K",
|
| 83 |
+
)
|
| 84 |
+
volume_max: Optional[int] = Field(
|
| 85 |
+
default=None,
|
| 86 |
+
description="Filter by volume less than this value.",
|
| 87 |
+
)
|
| 88 |
+
beta_min: Optional[float] = Field(
|
| 89 |
+
default=None,
|
| 90 |
+
description="Filter by a beta greater than this value.",
|
| 91 |
+
)
|
| 92 |
+
beta_max: Optional[float] = Field(
|
| 93 |
+
default=None,
|
| 94 |
+
description="Filter by a beta less than this value.",
|
| 95 |
+
)
|
| 96 |
+
limit: Optional[int] = Field(
|
| 97 |
+
default=200,
|
| 98 |
+
description="Limit the number of results returned. Default is, 200. Set to, 0, for all results.",
|
| 99 |
+
)
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
class YFinanceEquityScreenerData(EquityScreenerData, YFPredefinedScreenerData):
|
| 103 |
+
"""YFinance Equity Screener Data."""
|
| 104 |
+
|
| 105 |
+
|
| 106 |
+
class YFinanceEquityScreenerFetcher(
|
| 107 |
+
Fetcher[YFinanceEquityScreenerQueryParams, list[YFinanceEquityScreenerData]]
|
| 108 |
+
):
|
| 109 |
+
"""YFinance Equity Screener Fetcher."""
|
| 110 |
+
|
| 111 |
+
@staticmethod
|
| 112 |
+
def transform_query(params: dict[str, Any]) -> YFinanceEquityScreenerQueryParams:
|
| 113 |
+
"""Transform query."""
|
| 114 |
+
sector = params.get("sector")
|
| 115 |
+
industry = params.get("industry")
|
| 116 |
+
|
| 117 |
+
if industry and sector:
|
| 118 |
+
sec = get_industry_sector(industry)
|
| 119 |
+
if sec and sec != sector:
|
| 120 |
+
choices = "\n ".join(sorted(INDUSTRY_MAP[sector]))
|
| 121 |
+
raise OpenBBError(
|
| 122 |
+
ValueError(
|
| 123 |
+
f"Industry {industry} does not belong to sector {sector}."
|
| 124 |
+
" Valid choices are:" + "\n\n " + f"{choices}"
|
| 125 |
+
)
|
| 126 |
+
)
|
| 127 |
+
elif industry and not sector:
|
| 128 |
+
choices = "\n".join(INDUSTRIES)
|
| 129 |
+
sector = get_industry_sector(industry)
|
| 130 |
+
if not sector:
|
| 131 |
+
raise OpenBBError(
|
| 132 |
+
ValueError(
|
| 133 |
+
f"Industry {industry} not found. Valid choices are:"
|
| 134 |
+
"\n" + f"{choices}"
|
| 135 |
+
)
|
| 136 |
+
)
|
| 137 |
+
_industry = INDUSTRY_MAP[sector][industry]
|
| 138 |
+
|
| 139 |
+
if _industry not in PEER_GROUPS:
|
| 140 |
+
params["sector"] = get_industry_sector(industry)
|
| 141 |
+
|
| 142 |
+
return YFinanceEquityScreenerQueryParams(**params)
|
| 143 |
+
|
| 144 |
+
@staticmethod
|
| 145 |
+
async def aextract_data(
|
| 146 |
+
query: YFinanceEquityScreenerQueryParams,
|
| 147 |
+
credentials: Optional[dict[str, str]],
|
| 148 |
+
**kwargs: Any,
|
| 149 |
+
) -> list[dict]:
|
| 150 |
+
"""Extract the raw data."""
|
| 151 |
+
# pylint: disable=import-outside-toplevel
|
| 152 |
+
from openbb_yfinance.utils.helpers import get_custom_screener
|
| 153 |
+
|
| 154 |
+
operands: list = []
|
| 155 |
+
|
| 156 |
+
if query.exchange is not None:
|
| 157 |
+
operands.append(
|
| 158 |
+
{"operator": "eq", "operands": ["exchange", query.exchange.upper()]}
|
| 159 |
+
)
|
| 160 |
+
query.country = "all"
|
| 161 |
+
|
| 162 |
+
if query.country and query.country != "all":
|
| 163 |
+
operands.append({"operator": "EQ", "operands": ["region", query.country]})
|
| 164 |
+
|
| 165 |
+
if query.sector is not None:
|
| 166 |
+
sector = SECTOR_MAP[query.sector]
|
| 167 |
+
operands.append({"operator": "EQ", "operands": ["sector", sector]})
|
| 168 |
+
|
| 169 |
+
if query.industry is not None:
|
| 170 |
+
sector = (
|
| 171 |
+
query.sector
|
| 172 |
+
if query.sector is not None
|
| 173 |
+
else get_industry_sector(query.industry)
|
| 174 |
+
)
|
| 175 |
+
industry = INDUSTRY_MAP[sector][query.industry]
|
| 176 |
+
if industry in PEER_GROUPS:
|
| 177 |
+
operands.append(
|
| 178 |
+
{"operator": "EQ", "operands": ["peer_group", industry]}
|
| 179 |
+
)
|
| 180 |
+
else:
|
| 181 |
+
operands.append({"operator": "EQ", "operands": ["industry", industry]})
|
| 182 |
+
|
| 183 |
+
if query.mktcap_min is not None:
|
| 184 |
+
operands.append(
|
| 185 |
+
{"operator": "gt", "operands": ["intradaymarketcap", query.mktcap_min]}
|
| 186 |
+
)
|
| 187 |
+
|
| 188 |
+
if query.mktcap_max is not None:
|
| 189 |
+
operands.append(
|
| 190 |
+
{"operator": "lt", "operands": ["intradaymarketcap", query.mktcap_max]}
|
| 191 |
+
)
|
| 192 |
+
|
| 193 |
+
if query.price_min is not None:
|
| 194 |
+
operands.append(
|
| 195 |
+
{"operator": "gt", "operands": ["intradayprice", query.price_min]}
|
| 196 |
+
)
|
| 197 |
+
|
| 198 |
+
if query.price_max is not None:
|
| 199 |
+
operands.append(
|
| 200 |
+
{"operator": "lt", "operands": ["intradayprice", query.price_max]}
|
| 201 |
+
)
|
| 202 |
+
|
| 203 |
+
if query.volume_min is not None:
|
| 204 |
+
operands.append(
|
| 205 |
+
{"operator": "gt", "operands": ["dayvolume", query.volume_min]}
|
| 206 |
+
)
|
| 207 |
+
|
| 208 |
+
if query.volume_max is not None:
|
| 209 |
+
operands.append(
|
| 210 |
+
{"operator": "lt", "operands": ["dayvolume", query.volume_max]}
|
| 211 |
+
)
|
| 212 |
+
|
| 213 |
+
if query.beta_min is not None:
|
| 214 |
+
operands.append({"operator": "gt", "operands": ["beta", query.beta_min]})
|
| 215 |
+
|
| 216 |
+
if query.beta_max is not None:
|
| 217 |
+
operands.append({"operator": "lt", "operands": ["beta", query.beta_max]})
|
| 218 |
+
|
| 219 |
+
payload = {
|
| 220 |
+
"offset": 0,
|
| 221 |
+
"size": 100,
|
| 222 |
+
"sortField": "percentchange",
|
| 223 |
+
"sortType": "DESC",
|
| 224 |
+
"quoteType": "EQUITY",
|
| 225 |
+
"query": {
|
| 226 |
+
"operands": operands,
|
| 227 |
+
"operator": "AND",
|
| 228 |
+
},
|
| 229 |
+
"userId": "",
|
| 230 |
+
"userIdType": "guid",
|
| 231 |
+
}
|
| 232 |
+
|
| 233 |
+
response = await get_custom_screener(
|
| 234 |
+
body=payload,
|
| 235 |
+
limit=query.limit if query.limit and query.limit not in (0, None) else None,
|
| 236 |
+
)
|
| 237 |
+
|
| 238 |
+
if not response:
|
| 239 |
+
raise EmptyDataError("No results found for the combination of filters.")
|
| 240 |
+
|
| 241 |
+
return response
|
| 242 |
+
|
| 243 |
+
@staticmethod
|
| 244 |
+
def transform_data(
|
| 245 |
+
query: YFinanceEquityScreenerQueryParams,
|
| 246 |
+
data: list[dict],
|
| 247 |
+
**kwargs: Any,
|
| 248 |
+
) -> list[YFinanceEquityScreenerData]:
|
| 249 |
+
"""Transform the data."""
|
| 250 |
+
return [YFinanceEquityScreenerData(**d) for d in data]
|
openbb_platform/providers/yfinance/openbb_yfinance/models/etf_info.py
ADDED
|
@@ -0,0 +1,332 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""YFinance ETF Info Model."""
|
| 2 |
+
|
| 3 |
+
# pylint: disable=unused-argument
|
| 4 |
+
|
| 5 |
+
from typing import Any, Dict, List, Optional
|
| 6 |
+
|
| 7 |
+
from openbb_core.provider.abstract.fetcher import Fetcher
|
| 8 |
+
from openbb_core.provider.standard_models.etf_info import (
|
| 9 |
+
EtfInfoData,
|
| 10 |
+
EtfInfoQueryParams,
|
| 11 |
+
)
|
| 12 |
+
from pydantic import Field, field_validator
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
class YFinanceEtfInfoQueryParams(EtfInfoQueryParams):
|
| 16 |
+
"""YFinance ETF Info Query."""
|
| 17 |
+
|
| 18 |
+
__json_schema_extra__ = {"symbol": {"multiple_items_allowed": True}}
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
class YFinanceEtfInfoData(EtfInfoData):
|
| 22 |
+
"""YFinance ETF Info Data."""
|
| 23 |
+
|
| 24 |
+
__alias_dict__ = {
|
| 25 |
+
"name": "longName",
|
| 26 |
+
"inception_date": "fundInceptionDate",
|
| 27 |
+
"description": "longBusinessSummary",
|
| 28 |
+
"fund_type": "legalType",
|
| 29 |
+
"fund_family": "fundFamily",
|
| 30 |
+
"exchange_timezone": "timeZoneFullName",
|
| 31 |
+
"nav_price": "navPrice",
|
| 32 |
+
"total_assets": "totalAssets",
|
| 33 |
+
"trailing_pe": "trailingPE",
|
| 34 |
+
"dividend_yield": "yield",
|
| 35 |
+
"dividend_rate_ttm": "trailingAnnualDividendRate",
|
| 36 |
+
"dividend_yield_ttm": "trailingAnnualDividendYield",
|
| 37 |
+
"year_high": "fiftyTwoWeekHigh",
|
| 38 |
+
"year_low": "fiftyTwoWeekLow",
|
| 39 |
+
"ma_50d": "fiftyDayAverage",
|
| 40 |
+
"ma_200d": "twoHundredDayAverage",
|
| 41 |
+
"return_ytd": "ytdReturn",
|
| 42 |
+
"return_3y_avg": "threeYearAverageReturn",
|
| 43 |
+
"return_5y_avg": "fiveYearAverageReturn",
|
| 44 |
+
"beta_3y_avg": "beta3Year",
|
| 45 |
+
"volume_avg": "averageVolume",
|
| 46 |
+
"volume_avg_10d": "averageDailyVolume10Day",
|
| 47 |
+
"bid_size": "bidSize",
|
| 48 |
+
"ask_size": "askSize",
|
| 49 |
+
"high": "dayHigh",
|
| 50 |
+
"low": "dayLow",
|
| 51 |
+
"prev_close": "previousClose",
|
| 52 |
+
}
|
| 53 |
+
|
| 54 |
+
fund_type: Optional[str] = Field(
|
| 55 |
+
default=None,
|
| 56 |
+
description="The legal type of fund.",
|
| 57 |
+
)
|
| 58 |
+
fund_family: Optional[str] = Field(
|
| 59 |
+
default=None,
|
| 60 |
+
description="The fund family.",
|
| 61 |
+
)
|
| 62 |
+
category: Optional[str] = Field(
|
| 63 |
+
default=None,
|
| 64 |
+
description="The fund category.",
|
| 65 |
+
)
|
| 66 |
+
exchange: Optional[str] = Field(
|
| 67 |
+
default=None,
|
| 68 |
+
description="The exchange the fund is listed on.",
|
| 69 |
+
)
|
| 70 |
+
exchange_timezone: Optional[str] = Field(
|
| 71 |
+
default=None,
|
| 72 |
+
description="The timezone of the exchange.",
|
| 73 |
+
)
|
| 74 |
+
currency: Optional[str] = Field(
|
| 75 |
+
default=None,
|
| 76 |
+
description="The currency in which the fund is listed.",
|
| 77 |
+
)
|
| 78 |
+
nav_price: Optional[float] = Field(
|
| 79 |
+
default=None,
|
| 80 |
+
description="The net asset value per unit of the fund.",
|
| 81 |
+
)
|
| 82 |
+
total_assets: Optional[int] = Field(
|
| 83 |
+
default=None,
|
| 84 |
+
description="The total value of assets held by the fund.",
|
| 85 |
+
)
|
| 86 |
+
trailing_pe: Optional[float] = Field(
|
| 87 |
+
default=None,
|
| 88 |
+
description="The trailing twelve month P/E ratio of the fund's assets.",
|
| 89 |
+
)
|
| 90 |
+
dividend_yield: Optional[float] = Field(
|
| 91 |
+
default=None,
|
| 92 |
+
description="The dividend yield of the fund, as a normalized percent.",
|
| 93 |
+
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
|
| 94 |
+
)
|
| 95 |
+
dividend_rate_ttm: Optional[float] = Field(
|
| 96 |
+
default=None,
|
| 97 |
+
description="The trailing twelve month annual dividend rate of the fund, in currency units.",
|
| 98 |
+
)
|
| 99 |
+
dividend_yield_ttm: Optional[float] = Field(
|
| 100 |
+
default=None,
|
| 101 |
+
description="The trailing twelve month annual dividend yield of the fund, as a normalized percent.",
|
| 102 |
+
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
|
| 103 |
+
)
|
| 104 |
+
year_high: Optional[float] = Field(
|
| 105 |
+
default=None,
|
| 106 |
+
description="The fifty-two week high price.",
|
| 107 |
+
)
|
| 108 |
+
year_low: Optional[float] = Field(
|
| 109 |
+
default=None,
|
| 110 |
+
description="The fifty-two week low price.",
|
| 111 |
+
)
|
| 112 |
+
ma_50d: Optional[float] = Field(
|
| 113 |
+
default=None,
|
| 114 |
+
description="50-day moving average price.",
|
| 115 |
+
)
|
| 116 |
+
ma_200d: Optional[float] = Field(
|
| 117 |
+
default=None,
|
| 118 |
+
description="200-day moving average price.",
|
| 119 |
+
)
|
| 120 |
+
return_ytd: Optional[float] = Field(
|
| 121 |
+
default=None,
|
| 122 |
+
description="The year-to-date return of the fund, as a normalized percent.",
|
| 123 |
+
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
|
| 124 |
+
)
|
| 125 |
+
return_3y_avg: Optional[float] = Field(
|
| 126 |
+
default=None,
|
| 127 |
+
description="The three year average return of the fund, as a normalized percent.",
|
| 128 |
+
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
|
| 129 |
+
)
|
| 130 |
+
return_5y_avg: Optional[float] = Field(
|
| 131 |
+
default=None,
|
| 132 |
+
description="The five year average return of the fund, as a normalized percent.",
|
| 133 |
+
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
|
| 134 |
+
)
|
| 135 |
+
beta_3y_avg: Optional[float] = Field(
|
| 136 |
+
default=None,
|
| 137 |
+
description="The three year average beta of the fund.",
|
| 138 |
+
)
|
| 139 |
+
volume_avg: Optional[float] = Field(
|
| 140 |
+
default=None,
|
| 141 |
+
description="The average daily trading volume of the fund.",
|
| 142 |
+
)
|
| 143 |
+
volume_avg_10d: Optional[float] = Field(
|
| 144 |
+
default=None,
|
| 145 |
+
description="The average daily trading volume of the fund over the past ten days.",
|
| 146 |
+
)
|
| 147 |
+
bid: Optional[float] = Field(
|
| 148 |
+
default=None,
|
| 149 |
+
description="The current bid price.",
|
| 150 |
+
)
|
| 151 |
+
bid_size: Optional[float] = Field(
|
| 152 |
+
default=None,
|
| 153 |
+
description="The current bid size.",
|
| 154 |
+
)
|
| 155 |
+
ask: Optional[float] = Field(
|
| 156 |
+
default=None,
|
| 157 |
+
description="The current ask price.",
|
| 158 |
+
)
|
| 159 |
+
ask_size: Optional[float] = Field(
|
| 160 |
+
default=None,
|
| 161 |
+
description="The current ask size.",
|
| 162 |
+
)
|
| 163 |
+
open: Optional[float] = Field(
|
| 164 |
+
default=None,
|
| 165 |
+
description="The open price of the most recent trading session.",
|
| 166 |
+
)
|
| 167 |
+
high: Optional[float] = Field(
|
| 168 |
+
default=None,
|
| 169 |
+
description="The highest price of the most recent trading session.",
|
| 170 |
+
)
|
| 171 |
+
low: Optional[float] = Field(
|
| 172 |
+
default=None,
|
| 173 |
+
description="The lowest price of the most recent trading session.",
|
| 174 |
+
)
|
| 175 |
+
volume: Optional[int] = Field(
|
| 176 |
+
default=None,
|
| 177 |
+
description="The trading volume of the most recent trading session.",
|
| 178 |
+
)
|
| 179 |
+
prev_close: Optional[float] = Field(
|
| 180 |
+
default=None,
|
| 181 |
+
description="The previous closing price.",
|
| 182 |
+
)
|
| 183 |
+
|
| 184 |
+
@field_validator("inception_date", mode="before", check_fields=False)
|
| 185 |
+
@classmethod
|
| 186 |
+
def validate_date(cls, v):
|
| 187 |
+
"""Validate first stock price date."""
|
| 188 |
+
from datetime import datetime # pylint: disable=import-outside-toplevel
|
| 189 |
+
|
| 190 |
+
if isinstance(v, datetime):
|
| 191 |
+
return v.date().strftime("%Y-%m-%d")
|
| 192 |
+
return datetime.fromtimestamp(v).date().strftime("%Y-%m-%d") if v else None
|
| 193 |
+
|
| 194 |
+
|
| 195 |
+
class YFinanceEtfInfoFetcher(
|
| 196 |
+
Fetcher[YFinanceEtfInfoQueryParams, List[YFinanceEtfInfoData]]
|
| 197 |
+
):
|
| 198 |
+
"""YFinance ETF Info fetcher."""
|
| 199 |
+
|
| 200 |
+
@staticmethod
|
| 201 |
+
def transform_query(params: Dict[str, Any]) -> YFinanceEtfInfoQueryParams:
|
| 202 |
+
"""Transform the query."""
|
| 203 |
+
return YFinanceEtfInfoQueryParams(**params)
|
| 204 |
+
|
| 205 |
+
@staticmethod
|
| 206 |
+
async def aextract_data(
|
| 207 |
+
query: YFinanceEtfInfoQueryParams,
|
| 208 |
+
credentials: Optional[Dict[str, str]],
|
| 209 |
+
**kwargs: Any,
|
| 210 |
+
) -> List[Dict]:
|
| 211 |
+
"""Extract the raw data from YFinance."""
|
| 212 |
+
# pylint: disable=import-outside-toplevel
|
| 213 |
+
import asyncio # noqa
|
| 214 |
+
from curl_adapter import CurlCffiAdapter
|
| 215 |
+
from openbb_core.app.model.abstract.error import OpenBBError
|
| 216 |
+
from openbb_core.provider.utils.errors import EmptyDataError
|
| 217 |
+
from openbb_core.provider.utils.helpers import (
|
| 218 |
+
get_requests_session,
|
| 219 |
+
safe_fromtimestamp,
|
| 220 |
+
)
|
| 221 |
+
from warnings import warn
|
| 222 |
+
from yfinance import Ticker
|
| 223 |
+
|
| 224 |
+
symbols = query.symbol.split(",")
|
| 225 |
+
results: list = []
|
| 226 |
+
fields = [
|
| 227 |
+
"symbol",
|
| 228 |
+
"quoteType",
|
| 229 |
+
"legalType",
|
| 230 |
+
"longName",
|
| 231 |
+
"fundFamily",
|
| 232 |
+
"category",
|
| 233 |
+
"exchange",
|
| 234 |
+
"timeZoneFullName",
|
| 235 |
+
"fundInceptionDate",
|
| 236 |
+
"currency",
|
| 237 |
+
"navPrice",
|
| 238 |
+
"totalAssets",
|
| 239 |
+
"trailingPE",
|
| 240 |
+
"yield",
|
| 241 |
+
"trailingAnnualDividendRate",
|
| 242 |
+
"trailingAnnualDividendYield",
|
| 243 |
+
"bid",
|
| 244 |
+
"bidSize",
|
| 245 |
+
"ask",
|
| 246 |
+
"askSize",
|
| 247 |
+
"open",
|
| 248 |
+
"dayHigh",
|
| 249 |
+
"dayLow",
|
| 250 |
+
"previousClose",
|
| 251 |
+
"volume",
|
| 252 |
+
"averageVolume",
|
| 253 |
+
"averageDailyVolume10Day",
|
| 254 |
+
"fiftyTwoWeekHigh",
|
| 255 |
+
"fiftyTwoWeekLow",
|
| 256 |
+
"fiftyDayAverage",
|
| 257 |
+
"twoHundredDayAverage",
|
| 258 |
+
"ytdReturn",
|
| 259 |
+
"threeYearAverageReturn",
|
| 260 |
+
"fiveYearAverageReturn",
|
| 261 |
+
"beta3Year",
|
| 262 |
+
"longBusinessSummary",
|
| 263 |
+
"firstTradeDateEpochUtc",
|
| 264 |
+
]
|
| 265 |
+
messages: list = []
|
| 266 |
+
session = get_requests_session()
|
| 267 |
+
session.mount("https://", CurlCffiAdapter())
|
| 268 |
+
session.mount("http://", CurlCffiAdapter())
|
| 269 |
+
|
| 270 |
+
async def get_one(symbol):
|
| 271 |
+
"""Get the data for one ticker symbol."""
|
| 272 |
+
result: dict = {}
|
| 273 |
+
ticker: dict = {}
|
| 274 |
+
try:
|
| 275 |
+
ticker = Ticker(
|
| 276 |
+
symbol,
|
| 277 |
+
session=session,
|
| 278 |
+
).get_info()
|
| 279 |
+
except Exception as e:
|
| 280 |
+
messages.append(
|
| 281 |
+
f"Error getting data for {symbol} -> {e.__class__.__name__}: {e}"
|
| 282 |
+
)
|
| 283 |
+
if ticker:
|
| 284 |
+
quote_type = ticker.pop("quoteType", "")
|
| 285 |
+
if quote_type == "ETF":
|
| 286 |
+
try:
|
| 287 |
+
for field in fields:
|
| 288 |
+
if field in ticker and ticker.get(field) is not None:
|
| 289 |
+
result[field] = ticker.get(field, None)
|
| 290 |
+
if "firstTradeDateEpochUtc" in result:
|
| 291 |
+
_first_trade = result.pop("firstTradeDateEpochUtc")
|
| 292 |
+
if (
|
| 293 |
+
"fundInceptionDate" not in result
|
| 294 |
+
and _first_trade is not None
|
| 295 |
+
):
|
| 296 |
+
result["fundInceptionDate"] = safe_fromtimestamp(
|
| 297 |
+
_first_trade
|
| 298 |
+
)
|
| 299 |
+
except Exception as e:
|
| 300 |
+
messages.append(
|
| 301 |
+
f"Error processing data for {symbol} -> {e.__class__.__name__}: {e}"
|
| 302 |
+
)
|
| 303 |
+
result = {}
|
| 304 |
+
if quote_type != "ETF":
|
| 305 |
+
messages.append(f"{symbol} is not an ETF.")
|
| 306 |
+
if result:
|
| 307 |
+
results.append(result)
|
| 308 |
+
|
| 309 |
+
tasks = [get_one(symbol) for symbol in symbols]
|
| 310 |
+
|
| 311 |
+
await asyncio.gather(*tasks)
|
| 312 |
+
|
| 313 |
+
if not results and not messages:
|
| 314 |
+
raise EmptyDataError("No data was returned for the given symbol(s).")
|
| 315 |
+
|
| 316 |
+
if not results and messages:
|
| 317 |
+
raise OpenBBError("\n".join(messages))
|
| 318 |
+
|
| 319 |
+
if results and messages:
|
| 320 |
+
for message in messages:
|
| 321 |
+
warn(message)
|
| 322 |
+
|
| 323 |
+
return results
|
| 324 |
+
|
| 325 |
+
@staticmethod
|
| 326 |
+
def transform_data(
|
| 327 |
+
query: YFinanceEtfInfoQueryParams,
|
| 328 |
+
data: List[Dict],
|
| 329 |
+
**kwargs: Any,
|
| 330 |
+
) -> List[YFinanceEtfInfoData]:
|
| 331 |
+
"""Transform the data."""
|
| 332 |
+
return [YFinanceEtfInfoData.model_validate(d) for d in data]
|
openbb_platform/providers/yfinance/openbb_yfinance/models/futures_curve.py
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Yahoo Finance Futures Curve Model."""
|
| 2 |
+
|
| 3 |
+
# pylint: disable=unused-argument
|
| 4 |
+
|
| 5 |
+
from typing import Any, Dict, List, Optional
|
| 6 |
+
|
| 7 |
+
from openbb_core.provider.abstract.fetcher import Fetcher
|
| 8 |
+
from openbb_core.provider.standard_models.futures_curve import (
|
| 9 |
+
FuturesCurveData,
|
| 10 |
+
FuturesCurveQueryParams,
|
| 11 |
+
)
|
| 12 |
+
from openbb_core.provider.utils.errors import EmptyDataError
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
class YFinanceFuturesCurveQueryParams(FuturesCurveQueryParams):
|
| 16 |
+
"""Yahoo Finance Futures Curve Query.
|
| 17 |
+
|
| 18 |
+
Source: https://finance.yahoo.com/
|
| 19 |
+
"""
|
| 20 |
+
|
| 21 |
+
__json_schema_extra__ = {
|
| 22 |
+
"date": {"multiple_items_allowed": True},
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
class YFinanceFuturesCurveData(FuturesCurveData):
|
| 27 |
+
"""Yahoo Finance Futures Curve Data."""
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
class YFinanceFuturesCurveFetcher(
|
| 31 |
+
Fetcher[
|
| 32 |
+
YFinanceFuturesCurveQueryParams,
|
| 33 |
+
List[YFinanceFuturesCurveData],
|
| 34 |
+
]
|
| 35 |
+
):
|
| 36 |
+
"""YFiannce Futures Curve Fetcher."""
|
| 37 |
+
|
| 38 |
+
@staticmethod
|
| 39 |
+
def transform_query(params: Dict[str, Any]) -> YFinanceFuturesCurveQueryParams:
|
| 40 |
+
"""Transform the query."""
|
| 41 |
+
return YFinanceFuturesCurveQueryParams(**params)
|
| 42 |
+
|
| 43 |
+
@staticmethod
|
| 44 |
+
async def aextract_data(
|
| 45 |
+
query: YFinanceFuturesCurveQueryParams,
|
| 46 |
+
credentials: Optional[Dict[str, str]],
|
| 47 |
+
**kwargs: Any,
|
| 48 |
+
) -> List[Dict]:
|
| 49 |
+
"""Extract the data from Yahoo."""
|
| 50 |
+
# pylint: disable=import-outside-toplevel
|
| 51 |
+
from openbb_yfinance.utils.helpers import get_futures_curve
|
| 52 |
+
|
| 53 |
+
# TODO: Find a better way to do this.
|
| 54 |
+
data = await get_futures_curve(query.symbol, query.date) # type: ignore
|
| 55 |
+
data = data.to_dict(orient="records")
|
| 56 |
+
|
| 57 |
+
if not data:
|
| 58 |
+
raise EmptyDataError()
|
| 59 |
+
|
| 60 |
+
return data
|
| 61 |
+
|
| 62 |
+
@staticmethod
|
| 63 |
+
def transform_data(
|
| 64 |
+
query: YFinanceFuturesCurveQueryParams,
|
| 65 |
+
data: List[Dict],
|
| 66 |
+
**kwargs: Any,
|
| 67 |
+
) -> List[YFinanceFuturesCurveData]:
|
| 68 |
+
"""Transform the data to the standard format."""
|
| 69 |
+
return [YFinanceFuturesCurveData.model_validate(d) for d in data]
|
openbb_platform/providers/yfinance/openbb_yfinance/models/futures_historical.py
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Yahoo Finance Futures Historical Price Model."""
|
| 2 |
+
|
| 3 |
+
# pylint: disable=unused-argument
|
| 4 |
+
|
| 5 |
+
from datetime import datetime
|
| 6 |
+
from typing import Any, Dict, List, Literal, Optional
|
| 7 |
+
|
| 8 |
+
from openbb_core.provider.abstract.fetcher import Fetcher
|
| 9 |
+
from openbb_core.provider.standard_models.futures_historical import (
|
| 10 |
+
FuturesHistoricalData,
|
| 11 |
+
FuturesHistoricalQueryParams,
|
| 12 |
+
)
|
| 13 |
+
from openbb_core.provider.utils.descriptions import QUERY_DESCRIPTIONS
|
| 14 |
+
from openbb_core.provider.utils.errors import EmptyDataError
|
| 15 |
+
from openbb_yfinance.utils.references import INTERVALS_DICT, MONTHS
|
| 16 |
+
from pydantic import Field, field_validator
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
class YFinanceFuturesHistoricalQueryParams(FuturesHistoricalQueryParams):
|
| 20 |
+
"""Yahoo Finance Futures historical Price Query.
|
| 21 |
+
|
| 22 |
+
Source: https://finance.yahoo.com/crypto/
|
| 23 |
+
"""
|
| 24 |
+
|
| 25 |
+
__json_schema_extra__ = {"symbol": {"multiple_items_allowed": True}}
|
| 26 |
+
|
| 27 |
+
interval: Literal[
|
| 28 |
+
"1m",
|
| 29 |
+
"2m",
|
| 30 |
+
"5m",
|
| 31 |
+
"15m",
|
| 32 |
+
"30m",
|
| 33 |
+
"60m",
|
| 34 |
+
"90m",
|
| 35 |
+
"1h",
|
| 36 |
+
"1d",
|
| 37 |
+
"5d",
|
| 38 |
+
"1W",
|
| 39 |
+
"1M",
|
| 40 |
+
"1Q",
|
| 41 |
+
] = Field(
|
| 42 |
+
default="1d",
|
| 43 |
+
description=QUERY_DESCRIPTIONS.get("interval", ""),
|
| 44 |
+
)
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
class YFinanceFuturesHistoricalData(FuturesHistoricalData):
|
| 48 |
+
"""Yahoo Finance Futures Historical Price Data."""
|
| 49 |
+
|
| 50 |
+
@field_validator("date", mode="before", check_fields=False)
|
| 51 |
+
@classmethod
|
| 52 |
+
def date_validate(cls, v):
|
| 53 |
+
"""Return datetime object from string."""
|
| 54 |
+
# pylint: disable=import-outside-toplevel
|
| 55 |
+
from pandas import Timestamp
|
| 56 |
+
|
| 57 |
+
if isinstance(v, Timestamp):
|
| 58 |
+
return v.to_pydatetime()
|
| 59 |
+
return v
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
class YFinanceFuturesHistoricalFetcher(
|
| 63 |
+
Fetcher[
|
| 64 |
+
YFinanceFuturesHistoricalQueryParams,
|
| 65 |
+
List[YFinanceFuturesHistoricalData],
|
| 66 |
+
]
|
| 67 |
+
):
|
| 68 |
+
"""Transform the query, extract and transform the data from the Yahoo Finance endpoints."""
|
| 69 |
+
|
| 70 |
+
@staticmethod
|
| 71 |
+
def transform_query(params: Dict[str, Any]) -> YFinanceFuturesHistoricalQueryParams:
|
| 72 |
+
"""Transform the query."""
|
| 73 |
+
# pylint: disable=import-outside-toplevel
|
| 74 |
+
from dateutil.relativedelta import relativedelta
|
| 75 |
+
from openbb_yfinance.utils.helpers import get_futures_data
|
| 76 |
+
|
| 77 |
+
transformed_params = params.copy()
|
| 78 |
+
|
| 79 |
+
symbols = params["symbol"].split(",")
|
| 80 |
+
new_symbols = []
|
| 81 |
+
futures_data = get_futures_data()
|
| 82 |
+
for symbol in symbols:
|
| 83 |
+
if params.get("expiration"):
|
| 84 |
+
expiry_date = datetime.strptime(
|
| 85 |
+
transformed_params["expiration"], "%Y-%m"
|
| 86 |
+
)
|
| 87 |
+
if "." not in symbol:
|
| 88 |
+
exchange = futures_data[futures_data["Ticker"] == symbol][
|
| 89 |
+
"Exchange"
|
| 90 |
+
].values[0]
|
| 91 |
+
new_symbol = f"{symbol}{MONTHS[expiry_date.month]}{str(expiry_date.year)[-2:]}.{exchange}"
|
| 92 |
+
else:
|
| 93 |
+
new_symbol = symbol
|
| 94 |
+
new_symbols.append(new_symbol)
|
| 95 |
+
else:
|
| 96 |
+
new_symbols.append(symbol)
|
| 97 |
+
|
| 98 |
+
formatted_symbols = []
|
| 99 |
+
for s in new_symbols:
|
| 100 |
+
if "." not in s.upper() and "=F" not in s.upper():
|
| 101 |
+
formatted_symbols.append(f"{s.upper()}=F")
|
| 102 |
+
else:
|
| 103 |
+
formatted_symbols.append(s.upper())
|
| 104 |
+
|
| 105 |
+
transformed_params["symbol"] = ",".join(formatted_symbols)
|
| 106 |
+
|
| 107 |
+
now = datetime.now()
|
| 108 |
+
|
| 109 |
+
if params.get("start_date") is None:
|
| 110 |
+
transformed_params["start_date"] = (now - relativedelta(years=1)).strftime(
|
| 111 |
+
"%Y-%m-%d"
|
| 112 |
+
)
|
| 113 |
+
|
| 114 |
+
if params.get("end_date") is None:
|
| 115 |
+
transformed_params["end_date"] = now.strftime("%Y-%m-%d")
|
| 116 |
+
|
| 117 |
+
return YFinanceFuturesHistoricalQueryParams(**transformed_params)
|
| 118 |
+
|
| 119 |
+
@staticmethod
|
| 120 |
+
def extract_data(
|
| 121 |
+
query: YFinanceFuturesHistoricalQueryParams,
|
| 122 |
+
credentials: Optional[Dict[str, str]],
|
| 123 |
+
**kwargs: Any,
|
| 124 |
+
) -> List[Dict]:
|
| 125 |
+
"""Return the raw data from the Yahoo Finance endpoint."""
|
| 126 |
+
# pylint: disable=import-outside-toplevel
|
| 127 |
+
from openbb_yfinance.utils.helpers import yf_download
|
| 128 |
+
|
| 129 |
+
data = yf_download(
|
| 130 |
+
query.symbol,
|
| 131 |
+
start_date=query.start_date,
|
| 132 |
+
end_date=query.end_date,
|
| 133 |
+
interval=INTERVALS_DICT[query.interval], # type: ignore
|
| 134 |
+
prepost=True,
|
| 135 |
+
auto_adjust=False,
|
| 136 |
+
actions=False,
|
| 137 |
+
)
|
| 138 |
+
|
| 139 |
+
if data.empty:
|
| 140 |
+
raise EmptyDataError()
|
| 141 |
+
|
| 142 |
+
return data.to_dict("records")
|
| 143 |
+
|
| 144 |
+
@staticmethod
|
| 145 |
+
def transform_data(
|
| 146 |
+
query: YFinanceFuturesHistoricalQueryParams,
|
| 147 |
+
data: List[Dict],
|
| 148 |
+
**kwargs: Any,
|
| 149 |
+
) -> List[YFinanceFuturesHistoricalData]:
|
| 150 |
+
"""Transform the data to the standard format."""
|
| 151 |
+
return [YFinanceFuturesHistoricalData.model_validate(d) for d in data]
|
openbb_platform/providers/yfinance/openbb_yfinance/models/gainers.py
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Yahoo Finance Top Gainers Model."""
|
| 2 |
+
|
| 3 |
+
# pylint: disable=unused-argument
|
| 4 |
+
|
| 5 |
+
from typing import Any, Optional
|
| 6 |
+
|
| 7 |
+
from openbb_core.provider.abstract.fetcher import Fetcher
|
| 8 |
+
from openbb_core.provider.standard_models.equity_performance import (
|
| 9 |
+
EquityPerformanceQueryParams,
|
| 10 |
+
)
|
| 11 |
+
from openbb_yfinance.utils.references import YFPredefinedScreenerData
|
| 12 |
+
from pydantic import Field
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
class YFGainersQueryParams(EquityPerformanceQueryParams):
|
| 16 |
+
"""Yahoo Finance Gainers Query.
|
| 17 |
+
|
| 18 |
+
Source: https://finance.yahoo.com/screener/predefined/day_gainers
|
| 19 |
+
"""
|
| 20 |
+
|
| 21 |
+
limit: Optional[int] = Field(
|
| 22 |
+
default=200,
|
| 23 |
+
description="Limit the number of results.",
|
| 24 |
+
)
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
class YFGainersData(YFPredefinedScreenerData):
|
| 28 |
+
"""Yahoo Finance Gainers Data."""
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
class YFGainersFetcher(Fetcher[YFGainersQueryParams, list[YFGainersData]]):
|
| 32 |
+
"""Yahoo Finance Gainers Fetcher."""
|
| 33 |
+
|
| 34 |
+
@staticmethod
|
| 35 |
+
def transform_query(params: dict[str, Any]) -> YFGainersQueryParams:
|
| 36 |
+
"""Transform query params."""
|
| 37 |
+
return YFGainersQueryParams(**params)
|
| 38 |
+
|
| 39 |
+
@staticmethod
|
| 40 |
+
async def aextract_data(
|
| 41 |
+
query: YFGainersQueryParams,
|
| 42 |
+
credentials: Optional[dict[str, str]],
|
| 43 |
+
**kwargs: Any,
|
| 44 |
+
) -> list[dict]:
|
| 45 |
+
"""Get data from YF."""
|
| 46 |
+
# pylint: disable=import-outside-toplevel
|
| 47 |
+
from openbb_yfinance.utils.helpers import get_custom_screener
|
| 48 |
+
|
| 49 |
+
body = {
|
| 50 |
+
"offset": 0,
|
| 51 |
+
"size": 250,
|
| 52 |
+
"sortField": "percentchange",
|
| 53 |
+
"sortType": "desc",
|
| 54 |
+
"quoteType": "equity",
|
| 55 |
+
"query": {
|
| 56 |
+
"operator": "and",
|
| 57 |
+
"operands": [
|
| 58 |
+
{"operator": "gt", "operands": ["intradaymarketcap", 500000000]},
|
| 59 |
+
{
|
| 60 |
+
"operator": "or",
|
| 61 |
+
"operands": [
|
| 62 |
+
{"operator": "eq", "operands": ["exchange", "NMS"]},
|
| 63 |
+
{"operator": "eq", "operands": ["exchange", "NYQ"]},
|
| 64 |
+
],
|
| 65 |
+
},
|
| 66 |
+
{"operator": "gt", "operands": ["percentchange", 3]},
|
| 67 |
+
{"operator": "gt", "operands": ["intradayprice", 5]},
|
| 68 |
+
],
|
| 69 |
+
},
|
| 70 |
+
"userId": "",
|
| 71 |
+
"userIdType": "guid",
|
| 72 |
+
}
|
| 73 |
+
|
| 74 |
+
return await get_custom_screener(body=body, limit=query.limit)
|
| 75 |
+
|
| 76 |
+
@staticmethod
|
| 77 |
+
def transform_data(
|
| 78 |
+
query: EquityPerformanceQueryParams,
|
| 79 |
+
data: list[dict],
|
| 80 |
+
**kwargs: Any,
|
| 81 |
+
) -> list[YFGainersData]:
|
| 82 |
+
"""Transform data."""
|
| 83 |
+
return [
|
| 84 |
+
YFGainersData.model_validate(d)
|
| 85 |
+
for d in sorted(
|
| 86 |
+
data,
|
| 87 |
+
key=lambda x: x["regularMarketChangePercent"],
|
| 88 |
+
reverse=query.sort == "desc",
|
| 89 |
+
)
|
| 90 |
+
]
|
openbb_platform/providers/yfinance/openbb_yfinance/models/growth_tech_equities.py
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Yahoo Finance Asset Performance Growth Tech Equities Model."""
|
| 2 |
+
|
| 3 |
+
# pylint: disable=unused-argument
|
| 4 |
+
|
| 5 |
+
from typing import Any, Optional
|
| 6 |
+
|
| 7 |
+
from openbb_core.provider.abstract.fetcher import Fetcher
|
| 8 |
+
from openbb_core.provider.standard_models.equity_performance import (
|
| 9 |
+
EquityPerformanceQueryParams,
|
| 10 |
+
)
|
| 11 |
+
from openbb_yfinance.utils.references import YFPredefinedScreenerData
|
| 12 |
+
from pydantic import Field
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
class YFGrowthTechEquitiesQueryParams(EquityPerformanceQueryParams):
|
| 16 |
+
"""Yahoo Finance Growth Tech Stocks Query.
|
| 17 |
+
|
| 18 |
+
Source: https://finance.yahoo.com/screener/predefined/growth_technology_stocks
|
| 19 |
+
"""
|
| 20 |
+
|
| 21 |
+
limit: Optional[int] = Field(
|
| 22 |
+
default=200,
|
| 23 |
+
description="Limit the number of results.",
|
| 24 |
+
)
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
class YFGrowthTechEquitiesData(YFPredefinedScreenerData):
|
| 28 |
+
"""Yahoo Finance Growth Tech Stocks Data."""
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
class YFGrowthTechEquitiesFetcher(
|
| 32 |
+
Fetcher[YFGrowthTechEquitiesQueryParams, list[YFGrowthTechEquitiesData]]
|
| 33 |
+
):
|
| 34 |
+
"""Transform the query, extract and transform the data from the Yahoo Finance endpoints."""
|
| 35 |
+
|
| 36 |
+
@staticmethod
|
| 37 |
+
def transform_query(params: dict[str, Any]) -> YFGrowthTechEquitiesQueryParams:
|
| 38 |
+
"""Transform query params."""
|
| 39 |
+
return YFGrowthTechEquitiesQueryParams(**params)
|
| 40 |
+
|
| 41 |
+
@staticmethod
|
| 42 |
+
async def aextract_data(
|
| 43 |
+
query: YFGrowthTechEquitiesQueryParams,
|
| 44 |
+
credentials: Optional[dict[str, str]],
|
| 45 |
+
**kwargs: Any,
|
| 46 |
+
) -> list[dict]:
|
| 47 |
+
"""Get data from YF."""
|
| 48 |
+
# pylint: disable=import-outside-toplevel
|
| 49 |
+
from openbb_yfinance.utils.helpers import get_custom_screener
|
| 50 |
+
|
| 51 |
+
body = {
|
| 52 |
+
"offset": 0,
|
| 53 |
+
"size": 250,
|
| 54 |
+
"sortField": "eodvolume",
|
| 55 |
+
"sortType": "desc",
|
| 56 |
+
"quoteType": "equity",
|
| 57 |
+
"query": {
|
| 58 |
+
"operator": "and",
|
| 59 |
+
"operands": [
|
| 60 |
+
{"operator": "gt", "operands": ["intradaymarketcap", 500000000]},
|
| 61 |
+
{
|
| 62 |
+
"operator": "or",
|
| 63 |
+
"operands": [
|
| 64 |
+
{"operator": "eq", "operands": ["exchange", "NMS"]},
|
| 65 |
+
{"operator": "eq", "operands": ["exchange", "NYQ"]},
|
| 66 |
+
],
|
| 67 |
+
},
|
| 68 |
+
{
|
| 69 |
+
"operator": "gte",
|
| 70 |
+
"operands": ["quarterlyrevenuegrowth.quarterly", 25],
|
| 71 |
+
},
|
| 72 |
+
{"operator": "gte", "operands": ["epsgrowth.lasttwelvemonths", 25]},
|
| 73 |
+
{"operator": "eq", "operands": ["sector", "Technology"]},
|
| 74 |
+
],
|
| 75 |
+
},
|
| 76 |
+
"userId": "",
|
| 77 |
+
"userIdType": "guid",
|
| 78 |
+
}
|
| 79 |
+
|
| 80 |
+
return await get_custom_screener(body=body, limit=query.limit)
|
| 81 |
+
|
| 82 |
+
@staticmethod
|
| 83 |
+
def transform_data(
|
| 84 |
+
query: EquityPerformanceQueryParams,
|
| 85 |
+
data: list[dict],
|
| 86 |
+
**kwargs: Any,
|
| 87 |
+
) -> list[YFGrowthTechEquitiesData]:
|
| 88 |
+
"""Transform data."""
|
| 89 |
+
return [
|
| 90 |
+
YFGrowthTechEquitiesData.model_validate(d)
|
| 91 |
+
for d in sorted(
|
| 92 |
+
data,
|
| 93 |
+
key=lambda x: x["regularMarketChangePercent"],
|
| 94 |
+
reverse=query.sort == "desc",
|
| 95 |
+
)
|
| 96 |
+
]
|
openbb_platform/providers/yfinance/openbb_yfinance/models/historical_dividends.py
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""YFinance Historical Dividends Model."""
|
| 2 |
+
|
| 3 |
+
# pylint: disable=unused-argument
|
| 4 |
+
from typing import Any, Dict, List, Optional
|
| 5 |
+
|
| 6 |
+
from openbb_core.app.model.abstract.error import OpenBBError
|
| 7 |
+
from openbb_core.provider.abstract.fetcher import Fetcher
|
| 8 |
+
from openbb_core.provider.standard_models.historical_dividends import (
|
| 9 |
+
HistoricalDividendsData,
|
| 10 |
+
HistoricalDividendsQueryParams,
|
| 11 |
+
)
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
class YFinanceHistoricalDividendsQueryParams(HistoricalDividendsQueryParams):
|
| 15 |
+
"""YFinance Historical Dividends Query."""
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
class YFinanceHistoricalDividendsData(HistoricalDividendsData):
|
| 19 |
+
"""YFinance Historical Dividends Data. All data is split-adjusted."""
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
class YFinanceHistoricalDividendsFetcher(
|
| 23 |
+
Fetcher[
|
| 24 |
+
YFinanceHistoricalDividendsQueryParams, List[YFinanceHistoricalDividendsData]
|
| 25 |
+
]
|
| 26 |
+
):
|
| 27 |
+
"""YFinance Historical Dividends Fetcher."""
|
| 28 |
+
|
| 29 |
+
@staticmethod
|
| 30 |
+
def transform_query(
|
| 31 |
+
params: Dict[str, Any],
|
| 32 |
+
) -> YFinanceHistoricalDividendsQueryParams:
|
| 33 |
+
"""Transform the query."""
|
| 34 |
+
return YFinanceHistoricalDividendsQueryParams(**params)
|
| 35 |
+
|
| 36 |
+
@staticmethod
|
| 37 |
+
def extract_data(
|
| 38 |
+
query: YFinanceHistoricalDividendsQueryParams,
|
| 39 |
+
credentials: Optional[Dict[str, str]],
|
| 40 |
+
**kwargs: Any,
|
| 41 |
+
) -> List[Dict]:
|
| 42 |
+
"""Extract the raw data from YFinance."""
|
| 43 |
+
# pylint: disable=import-outside-toplevel
|
| 44 |
+
from curl_adapter import CurlCffiAdapter
|
| 45 |
+
from openbb_core.provider.utils.helpers import get_requests_session
|
| 46 |
+
from yfinance import Ticker
|
| 47 |
+
|
| 48 |
+
session = get_requests_session()
|
| 49 |
+
session.mount("https://", CurlCffiAdapter())
|
| 50 |
+
session.mount("http://", CurlCffiAdapter())
|
| 51 |
+
|
| 52 |
+
try:
|
| 53 |
+
ticker = Ticker(
|
| 54 |
+
query.symbol,
|
| 55 |
+
session=session,
|
| 56 |
+
).get_dividends()
|
| 57 |
+
if isinstance(ticker, List) and not ticker or ticker.empty: # type: ignore
|
| 58 |
+
raise OpenBBError(f"No dividend data found for {query.symbol}")
|
| 59 |
+
except Exception as e:
|
| 60 |
+
raise OpenBBError(f"Error getting data for {query.symbol}: {e}") from e
|
| 61 |
+
ticker.index.name = "ex_dividend_date" # type: ignore[union-attr]
|
| 62 |
+
ticker.name = "amount" # type: ignore
|
| 63 |
+
if query.start_date is not None:
|
| 64 |
+
ticker = ticker[ticker.index.astype(str) >= query.start_date.strftime("%Y-%m-%d")] # type: ignore
|
| 65 |
+
if query.end_date is not None:
|
| 66 |
+
ticker = ticker[ticker.index.astype(str) <= query.end_date.strftime("%Y-%m-%d")] # type: ignore
|
| 67 |
+
dividends = ticker.reset_index().to_dict("records") # type: ignore
|
| 68 |
+
|
| 69 |
+
return dividends
|
| 70 |
+
|
| 71 |
+
@staticmethod
|
| 72 |
+
def transform_data(
|
| 73 |
+
query: YFinanceHistoricalDividendsQueryParams,
|
| 74 |
+
data: List[Dict],
|
| 75 |
+
**kwargs: Any,
|
| 76 |
+
) -> List[YFinanceHistoricalDividendsData]:
|
| 77 |
+
"""Transform the data."""
|
| 78 |
+
return [YFinanceHistoricalDividendsData.model_validate(d) for d in data]
|
openbb_platform/providers/yfinance/openbb_yfinance/models/income_statement.py
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Yahoo Finance Income Statement Model."""
|
| 2 |
+
|
| 3 |
+
# pylint: disable=unused-argument
|
| 4 |
+
|
| 5 |
+
from datetime import datetime
|
| 6 |
+
from typing import Any, Literal, Optional
|
| 7 |
+
|
| 8 |
+
from openbb_core.provider.abstract.fetcher import Fetcher
|
| 9 |
+
from openbb_core.provider.standard_models.income_statement import (
|
| 10 |
+
IncomeStatementData,
|
| 11 |
+
IncomeStatementQueryParams,
|
| 12 |
+
)
|
| 13 |
+
from openbb_core.provider.utils.descriptions import QUERY_DESCRIPTIONS
|
| 14 |
+
from pydantic import Field, field_validator
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
class YFinanceIncomeStatementQueryParams(IncomeStatementQueryParams):
|
| 18 |
+
"""Yahoo Finance Income Statement Query.
|
| 19 |
+
|
| 20 |
+
Source: https://finance.yahoo.com/
|
| 21 |
+
"""
|
| 22 |
+
|
| 23 |
+
__json_schema_extra__ = {
|
| 24 |
+
"period": {
|
| 25 |
+
"choices": ["annual", "quarter"],
|
| 26 |
+
}
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
period: Literal["annual", "quarter"] = Field(
|
| 30 |
+
default="annual",
|
| 31 |
+
description=QUERY_DESCRIPTIONS.get("period", ""),
|
| 32 |
+
)
|
| 33 |
+
limit: Optional[int] = Field(
|
| 34 |
+
default=5,
|
| 35 |
+
description=QUERY_DESCRIPTIONS.get("limit", ""),
|
| 36 |
+
le=5,
|
| 37 |
+
)
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
class YFinanceIncomeStatementData(IncomeStatementData):
|
| 41 |
+
"""Yahoo Finance Income Statement Data."""
|
| 42 |
+
|
| 43 |
+
__alias_dict__ = {
|
| 44 |
+
"selling_general_and_admin_expense": "selling_general_and_administration",
|
| 45 |
+
"research_and_development_expense": "research_and_development",
|
| 46 |
+
"total_pre_tax_income": "pretax_income",
|
| 47 |
+
"net_income_attributable_to_common_shareholders": "net_income_common_stockholders",
|
| 48 |
+
"weighted_average_basic_shares_outstanding": "basic_average_shares",
|
| 49 |
+
"weighted_average_diluted_shares_outstanding": "diluted_average_shares",
|
| 50 |
+
"basic_earnings_per_share": "basic_eps",
|
| 51 |
+
"diluted_earnings_per_share": "diluted_eps",
|
| 52 |
+
}
|
| 53 |
+
|
| 54 |
+
@field_validator("period_ending", mode="before", check_fields=False)
|
| 55 |
+
@classmethod
|
| 56 |
+
def date_validate(cls, v):
|
| 57 |
+
"""Validate the date field."""
|
| 58 |
+
if isinstance(v, str):
|
| 59 |
+
return datetime.strptime(v, "%Y-%m-%d %H:%M:%S").date()
|
| 60 |
+
return v
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
class YFinanceIncomeStatementFetcher(
|
| 64 |
+
Fetcher[
|
| 65 |
+
YFinanceIncomeStatementQueryParams,
|
| 66 |
+
list[YFinanceIncomeStatementData],
|
| 67 |
+
]
|
| 68 |
+
):
|
| 69 |
+
"""Yahoo Finance Income Statement Fetcher."""
|
| 70 |
+
|
| 71 |
+
@staticmethod
|
| 72 |
+
def transform_query(params: dict[str, Any]) -> YFinanceIncomeStatementQueryParams:
|
| 73 |
+
"""Transform the query parameters."""
|
| 74 |
+
return YFinanceIncomeStatementQueryParams(**params)
|
| 75 |
+
|
| 76 |
+
@staticmethod
|
| 77 |
+
def extract_data(
|
| 78 |
+
query: YFinanceIncomeStatementQueryParams,
|
| 79 |
+
credentials: Optional[dict[str, str]],
|
| 80 |
+
**kwargs: Any,
|
| 81 |
+
) -> list[YFinanceIncomeStatementData]:
|
| 82 |
+
"""Extract the data from the Yahoo Finance endpoints."""
|
| 83 |
+
# pylint: disable=import-outside-toplevel
|
| 84 |
+
import json # noqa
|
| 85 |
+
from curl_adapter import CurlCffiAdapter
|
| 86 |
+
from numpy import nan
|
| 87 |
+
from openbb_core.provider.utils.errors import EmptyDataError
|
| 88 |
+
from openbb_core.provider.utils.helpers import (
|
| 89 |
+
get_requests_session,
|
| 90 |
+
to_snake_case,
|
| 91 |
+
)
|
| 92 |
+
from yfinance import Ticker
|
| 93 |
+
|
| 94 |
+
period = "yearly" if query.period == "annual" else "quarterly"
|
| 95 |
+
session = get_requests_session()
|
| 96 |
+
session.mount("https://", CurlCffiAdapter())
|
| 97 |
+
session.mount("http://", CurlCffiAdapter())
|
| 98 |
+
|
| 99 |
+
data = Ticker(
|
| 100 |
+
query.symbol,
|
| 101 |
+
session=session,
|
| 102 |
+
).get_income_stmt(as_dict=False, pretty=False, freq=period)
|
| 103 |
+
|
| 104 |
+
if data is None:
|
| 105 |
+
raise EmptyDataError()
|
| 106 |
+
|
| 107 |
+
if query.limit:
|
| 108 |
+
data = data.iloc[:, : query.limit]
|
| 109 |
+
|
| 110 |
+
data.index = [to_snake_case(i) for i in data.index]
|
| 111 |
+
data = data.reset_index().sort_index(ascending=False).set_index("index")
|
| 112 |
+
data = data.replace({nan: None}).to_dict()
|
| 113 |
+
data = [{"period_ending": str(key), **value} for key, value in data.items()]
|
| 114 |
+
data = json.loads(json.dumps(data))
|
| 115 |
+
|
| 116 |
+
return data
|
| 117 |
+
|
| 118 |
+
@staticmethod
|
| 119 |
+
def transform_data(
|
| 120 |
+
query: YFinanceIncomeStatementQueryParams,
|
| 121 |
+
data: list[dict],
|
| 122 |
+
**kwargs: Any,
|
| 123 |
+
) -> list[YFinanceIncomeStatementData]:
|
| 124 |
+
"""Transform the data."""
|
| 125 |
+
return [YFinanceIncomeStatementData.model_validate(d) for d in data]
|
openbb_platform/providers/yfinance/openbb_yfinance/models/index_historical.py
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Yahoo Finance Index Historical Model."""
|
| 2 |
+
|
| 3 |
+
# pylint: disable=unused-argument
|
| 4 |
+
|
| 5 |
+
from datetime import datetime
|
| 6 |
+
from typing import Any, Dict, List, Literal, Optional
|
| 7 |
+
from warnings import warn
|
| 8 |
+
|
| 9 |
+
from openbb_core.provider.abstract.fetcher import Fetcher
|
| 10 |
+
from openbb_core.provider.standard_models.index_historical import (
|
| 11 |
+
IndexHistoricalData,
|
| 12 |
+
IndexHistoricalQueryParams,
|
| 13 |
+
)
|
| 14 |
+
from openbb_core.provider.utils.descriptions import QUERY_DESCRIPTIONS
|
| 15 |
+
from openbb_core.provider.utils.errors import EmptyDataError
|
| 16 |
+
from openbb_yfinance.utils.references import INDICES, INTERVALS_DICT
|
| 17 |
+
from pydantic import Field
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
class YFinanceIndexHistoricalQueryParams(IndexHistoricalQueryParams):
|
| 21 |
+
"""YFinance Index Historical Query.
|
| 22 |
+
|
| 23 |
+
Source: https://finance.yahoo.com/world-indices
|
| 24 |
+
"""
|
| 25 |
+
|
| 26 |
+
__json_schema_extra__ = {
|
| 27 |
+
"symbol": {"multiple_items_allowed": True},
|
| 28 |
+
"interval": {
|
| 29 |
+
"choices": [
|
| 30 |
+
"1m",
|
| 31 |
+
"2m",
|
| 32 |
+
"5m",
|
| 33 |
+
"15m",
|
| 34 |
+
"30m",
|
| 35 |
+
"60m",
|
| 36 |
+
"90m",
|
| 37 |
+
"1h",
|
| 38 |
+
"1d",
|
| 39 |
+
"5d",
|
| 40 |
+
"1W",
|
| 41 |
+
"1M",
|
| 42 |
+
"1Q",
|
| 43 |
+
]
|
| 44 |
+
},
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
interval: Literal[
|
| 48 |
+
"1m",
|
| 49 |
+
"2m",
|
| 50 |
+
"5m",
|
| 51 |
+
"15m",
|
| 52 |
+
"30m",
|
| 53 |
+
"60m",
|
| 54 |
+
"90m",
|
| 55 |
+
"1h",
|
| 56 |
+
"1d",
|
| 57 |
+
"5d",
|
| 58 |
+
"1W",
|
| 59 |
+
"1M",
|
| 60 |
+
"1Q",
|
| 61 |
+
] = Field(
|
| 62 |
+
default="1d",
|
| 63 |
+
description=QUERY_DESCRIPTIONS.get("interval", ""),
|
| 64 |
+
)
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
class YFinanceIndexHistoricalData(IndexHistoricalData):
|
| 68 |
+
"""YFinance Index Historical Data."""
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
class YFinanceIndexHistoricalFetcher(
|
| 72 |
+
Fetcher[
|
| 73 |
+
YFinanceIndexHistoricalQueryParams,
|
| 74 |
+
List[YFinanceIndexHistoricalData],
|
| 75 |
+
]
|
| 76 |
+
):
|
| 77 |
+
"""Transform the query, extract and transform the data from the Yahoo Finance endpoints."""
|
| 78 |
+
|
| 79 |
+
@staticmethod
|
| 80 |
+
def transform_query(params: Dict[str, Any]) -> YFinanceIndexHistoricalQueryParams:
|
| 81 |
+
"""Transform the query."""
|
| 82 |
+
# pylint: disable=import-outside-toplevel
|
| 83 |
+
from dateutil.relativedelta import relativedelta
|
| 84 |
+
from pandas import DataFrame
|
| 85 |
+
|
| 86 |
+
transformed_params = params
|
| 87 |
+
now = datetime.now().date()
|
| 88 |
+
|
| 89 |
+
if params.get("start_date") is None:
|
| 90 |
+
transformed_params["start_date"] = now - relativedelta(years=1)
|
| 91 |
+
|
| 92 |
+
if params.get("end_date") is None:
|
| 93 |
+
transformed_params["end_date"] = now
|
| 94 |
+
|
| 95 |
+
tickers = params.get("symbol").lower().split(",") # type: ignore
|
| 96 |
+
|
| 97 |
+
new_tickers = []
|
| 98 |
+
for ticker in tickers:
|
| 99 |
+
_ticker = ""
|
| 100 |
+
indices = DataFrame(INDICES).transpose().reset_index()
|
| 101 |
+
indices.columns = ["code", "name", "symbol"]
|
| 102 |
+
|
| 103 |
+
if ticker in indices["code"].values:
|
| 104 |
+
_ticker = indices[indices["code"] == ticker]["symbol"].values[0]
|
| 105 |
+
|
| 106 |
+
if ticker.title() in indices["name"].values:
|
| 107 |
+
_ticker = indices[indices["name"] == ticker.title()]["symbol"].values[0]
|
| 108 |
+
|
| 109 |
+
if "^" + ticker.upper() in indices["symbol"].values:
|
| 110 |
+
_ticker = "^" + ticker.upper()
|
| 111 |
+
|
| 112 |
+
if ticker.upper() in indices["symbol"].values:
|
| 113 |
+
_ticker = ticker.upper()
|
| 114 |
+
|
| 115 |
+
if _ticker != "":
|
| 116 |
+
new_tickers.append(_ticker)
|
| 117 |
+
else:
|
| 118 |
+
warn(f"Symbol Error: {ticker} is not a supported index.")
|
| 119 |
+
|
| 120 |
+
transformed_params["symbol"] = ",".join(new_tickers)
|
| 121 |
+
|
| 122 |
+
return YFinanceIndexHistoricalQueryParams(**params)
|
| 123 |
+
|
| 124 |
+
@staticmethod
|
| 125 |
+
def extract_data(
|
| 126 |
+
query: YFinanceIndexHistoricalQueryParams,
|
| 127 |
+
credentials: Optional[Dict[str, str]],
|
| 128 |
+
**kwargs: Any,
|
| 129 |
+
) -> List[dict]:
|
| 130 |
+
"""Return the raw data from the Yahoo Finance endpoint."""
|
| 131 |
+
# pylint: disable=import-outside-toplevel
|
| 132 |
+
from openbb_yfinance.utils.helpers import yf_download
|
| 133 |
+
|
| 134 |
+
data = yf_download(
|
| 135 |
+
symbol=query.symbol,
|
| 136 |
+
start_date=query.start_date,
|
| 137 |
+
end_date=query.end_date,
|
| 138 |
+
interval=INTERVALS_DICT[query.interval], # type: ignore
|
| 139 |
+
prepost=True,
|
| 140 |
+
)
|
| 141 |
+
|
| 142 |
+
if data.empty:
|
| 143 |
+
raise EmptyDataError()
|
| 144 |
+
|
| 145 |
+
return data.to_dict("records")
|
| 146 |
+
|
| 147 |
+
@staticmethod
|
| 148 |
+
def transform_data(
|
| 149 |
+
query: YFinanceIndexHistoricalQueryParams,
|
| 150 |
+
data: dict,
|
| 151 |
+
**kwargs: Any,
|
| 152 |
+
) -> List[YFinanceIndexHistoricalData]:
|
| 153 |
+
"""Transform the data to the standard format."""
|
| 154 |
+
return [YFinanceIndexHistoricalData.model_validate(d) for d in data]
|
openbb_platform/providers/yfinance/openbb_yfinance/models/key_executives.py
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""YFinance Key Executives Model."""
|
| 2 |
+
|
| 3 |
+
# pylint: disable=unused-argument
|
| 4 |
+
from typing import Any, Dict, List, Optional
|
| 5 |
+
|
| 6 |
+
from openbb_core.provider.abstract.fetcher import Fetcher
|
| 7 |
+
from openbb_core.provider.standard_models.key_executives import (
|
| 8 |
+
KeyExecutivesData,
|
| 9 |
+
KeyExecutivesQueryParams,
|
| 10 |
+
)
|
| 11 |
+
from pydantic import Field
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
class YFinanceKeyExecutivesQueryParams(KeyExecutivesQueryParams):
|
| 15 |
+
"""YFinance Key Executives Query."""
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
class YFinanceKeyExecutivesData(KeyExecutivesData):
|
| 19 |
+
"""YFinance Key Executives Data."""
|
| 20 |
+
|
| 21 |
+
__alias_dict__ = {
|
| 22 |
+
"year_born": "yearBorn",
|
| 23 |
+
"fiscal_year": "fiscalYear",
|
| 24 |
+
"pay": "totalPay",
|
| 25 |
+
"exercised_value": "exercisedValue",
|
| 26 |
+
"unexercised_value": "unexercisedValue",
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
exercised_value: Optional[int] = Field(
|
| 30 |
+
default=None,
|
| 31 |
+
description="Value of shares exercised.",
|
| 32 |
+
)
|
| 33 |
+
unexercised_value: Optional[int] = Field(
|
| 34 |
+
default=None,
|
| 35 |
+
description="Value of shares not exercised.",
|
| 36 |
+
)
|
| 37 |
+
fiscal_year: Optional[int] = Field(
|
| 38 |
+
default=None,
|
| 39 |
+
description="Fiscal year of the pay.",
|
| 40 |
+
)
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
class YFinanceKeyExecutivesFetcher(
|
| 44 |
+
Fetcher[YFinanceKeyExecutivesQueryParams, List[YFinanceKeyExecutivesData]]
|
| 45 |
+
):
|
| 46 |
+
"""YFinance Key Executives Fetcher."""
|
| 47 |
+
|
| 48 |
+
@staticmethod
|
| 49 |
+
def transform_query(params: Dict[str, Any]) -> YFinanceKeyExecutivesQueryParams:
|
| 50 |
+
"""Transform the query."""
|
| 51 |
+
return YFinanceKeyExecutivesQueryParams(**params)
|
| 52 |
+
|
| 53 |
+
@staticmethod
|
| 54 |
+
def extract_data(
|
| 55 |
+
query: YFinanceKeyExecutivesQueryParams,
|
| 56 |
+
credentials: Optional[Dict[str, str]],
|
| 57 |
+
**kwargs: Any,
|
| 58 |
+
) -> List[Dict]:
|
| 59 |
+
"""Extract the raw data from YFinance."""
|
| 60 |
+
# pylint: disable=import-outside-toplevel
|
| 61 |
+
from curl_adapter import CurlCffiAdapter # noqa
|
| 62 |
+
from openbb_core.app.model.abstract.error import OpenBBError
|
| 63 |
+
from openbb_core.provider.utils.helpers import get_requests_session
|
| 64 |
+
from yfinance import Ticker
|
| 65 |
+
|
| 66 |
+
session = get_requests_session()
|
| 67 |
+
session.mount("https://", CurlCffiAdapter())
|
| 68 |
+
session.mount("http://", CurlCffiAdapter())
|
| 69 |
+
|
| 70 |
+
try:
|
| 71 |
+
ticker = Ticker(
|
| 72 |
+
query.symbol,
|
| 73 |
+
session=session,
|
| 74 |
+
).get_info()
|
| 75 |
+
except Exception as e:
|
| 76 |
+
raise OpenBBError(
|
| 77 |
+
f"Error getting data for {query.symbol} -> {e.__class__.__name__}: {e}"
|
| 78 |
+
) from e
|
| 79 |
+
|
| 80 |
+
if ticker.get("companyOfficers") is None:
|
| 81 |
+
raise OpenBBError(f"No executive data found for {query.symbol}")
|
| 82 |
+
|
| 83 |
+
officers_data = ticker.get("companyOfficers", [])
|
| 84 |
+
_ = [d.pop("maxAge", None) for d in officers_data]
|
| 85 |
+
|
| 86 |
+
return officers_data
|
| 87 |
+
|
| 88 |
+
@staticmethod
|
| 89 |
+
def transform_data(
|
| 90 |
+
query: YFinanceKeyExecutivesQueryParams,
|
| 91 |
+
data: List[Dict],
|
| 92 |
+
**kwargs: Any,
|
| 93 |
+
) -> List[YFinanceKeyExecutivesData]:
|
| 94 |
+
"""Transform the data."""
|
| 95 |
+
return [YFinanceKeyExecutivesData.model_validate(d) for d in data]
|
openbb_platform/providers/yfinance/openbb_yfinance/models/key_metrics.py
ADDED
|
@@ -0,0 +1,339 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""YFinance Key Metrics Model."""
|
| 2 |
+
|
| 3 |
+
# pylint: disable=unused-argument
|
| 4 |
+
|
| 5 |
+
from typing import Any, Dict, List, Optional
|
| 6 |
+
|
| 7 |
+
from openbb_core.provider.abstract.fetcher import Fetcher
|
| 8 |
+
from openbb_core.provider.standard_models.key_metrics import (
|
| 9 |
+
KeyMetricsData,
|
| 10 |
+
KeyMetricsQueryParams,
|
| 11 |
+
)
|
| 12 |
+
from pydantic import Field, field_validator
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
class YFinanceKeyMetricsQueryParams(KeyMetricsQueryParams):
|
| 16 |
+
"""YFinance Key Metrics Query."""
|
| 17 |
+
|
| 18 |
+
__json_schema_extra__ = {"symbol": {"multiple_items_allowed": True}}
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
class YFinanceKeyMetricsData(KeyMetricsData):
|
| 22 |
+
"""YFinance Key Metrics Data."""
|
| 23 |
+
|
| 24 |
+
__alias_dict__ = {
|
| 25 |
+
"market_cap": "marketCap",
|
| 26 |
+
"pe_ratio": "trailingPE",
|
| 27 |
+
"forward_pe": "forwardPE",
|
| 28 |
+
"peg_ratio": "pegRatio",
|
| 29 |
+
"peg_ratio_ttm": "trailingPegRatio",
|
| 30 |
+
"eps_ttm": "trailingEps",
|
| 31 |
+
"eps_forward": "forwardEps",
|
| 32 |
+
"enterprise_to_ebitda": "enterpriseToEbitda",
|
| 33 |
+
"earnings_growth": "earningsGrowth",
|
| 34 |
+
"earnings_growth_quarterly": "earningsQuarterlyGrowth",
|
| 35 |
+
"revenue_per_share": "revenuePerShare",
|
| 36 |
+
"revenue_growth": "revenueGrowth",
|
| 37 |
+
"enterprise_to_revenue": "enterpriseToRevenue",
|
| 38 |
+
"cash_per_share": "totalCashPerShare",
|
| 39 |
+
"quick_ratio": "quickRatio",
|
| 40 |
+
"current_ratio": "currentRatio",
|
| 41 |
+
"debt_to_equity": "debtToEquity",
|
| 42 |
+
"gross_margin": "grossMargins",
|
| 43 |
+
"operating_margin": "operatingMargins",
|
| 44 |
+
"ebitda_margin": "ebitdaMargins",
|
| 45 |
+
"profit_margin": "profitMargins",
|
| 46 |
+
"return_on_assets": "returnOnAssets",
|
| 47 |
+
"return_on_equity": "returnOnEquity",
|
| 48 |
+
"dividend_yield": "dividendYield",
|
| 49 |
+
"dividend_yield_5y_avg": "fiveYearAvgDividendYield",
|
| 50 |
+
"payout_ratio": "payoutRatio",
|
| 51 |
+
"book_value": "bookValue",
|
| 52 |
+
"price_to_book": "priceToBook",
|
| 53 |
+
"enterprise_value": "enterpriseValue",
|
| 54 |
+
"overall_risk": "overallRisk",
|
| 55 |
+
"audit_risk": "auditRisk",
|
| 56 |
+
"board_risk": "boardRisk",
|
| 57 |
+
"compensation_risk": "compensationRisk",
|
| 58 |
+
"shareholder_rights_risk": "shareHolderRightsRisk",
|
| 59 |
+
"price_return_1y": "52WeekChange",
|
| 60 |
+
"currency": "financialCurrency",
|
| 61 |
+
}
|
| 62 |
+
|
| 63 |
+
forward_pe: Optional[float] = Field(
|
| 64 |
+
default=None,
|
| 65 |
+
description="Forward price-to-earnings ratio.",
|
| 66 |
+
)
|
| 67 |
+
peg_ratio: Optional[float] = Field(
|
| 68 |
+
default=None,
|
| 69 |
+
description="PEG ratio (5-year expected).",
|
| 70 |
+
)
|
| 71 |
+
peg_ratio_ttm: Optional[float] = Field(
|
| 72 |
+
default=None,
|
| 73 |
+
description="PEG ratio (TTM).",
|
| 74 |
+
)
|
| 75 |
+
eps_ttm: Optional[float] = Field(
|
| 76 |
+
default=None,
|
| 77 |
+
description="Earnings per share (TTM).",
|
| 78 |
+
)
|
| 79 |
+
eps_forward: Optional[float] = Field(
|
| 80 |
+
default=None,
|
| 81 |
+
description="Forward earnings per share.",
|
| 82 |
+
)
|
| 83 |
+
enterprise_to_ebitda: Optional[float] = Field(
|
| 84 |
+
default=None,
|
| 85 |
+
description="Enterprise value to EBITDA ratio.",
|
| 86 |
+
)
|
| 87 |
+
earnings_growth: Optional[float] = Field(
|
| 88 |
+
default=None,
|
| 89 |
+
description="Earnings growth (Year Over Year), as a normalized percent.",
|
| 90 |
+
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
|
| 91 |
+
)
|
| 92 |
+
earnings_growth_quarterly: Optional[float] = Field(
|
| 93 |
+
default=None,
|
| 94 |
+
description="Quarterly earnings growth (Year Over Year), as a normalized percent.",
|
| 95 |
+
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
|
| 96 |
+
)
|
| 97 |
+
revenue_per_share: Optional[float] = Field(
|
| 98 |
+
default=None,
|
| 99 |
+
description="Revenue per share (TTM).",
|
| 100 |
+
)
|
| 101 |
+
revenue_growth: Optional[float] = Field(
|
| 102 |
+
default=None,
|
| 103 |
+
description="Revenue growth (Year Over Year), as a normalized percent.",
|
| 104 |
+
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
|
| 105 |
+
)
|
| 106 |
+
enterprise_to_revenue: Optional[float] = Field(
|
| 107 |
+
default=None,
|
| 108 |
+
description="Enterprise value to revenue ratio.",
|
| 109 |
+
)
|
| 110 |
+
cash_per_share: Optional[float] = Field(
|
| 111 |
+
default=None,
|
| 112 |
+
description="Cash per share.",
|
| 113 |
+
)
|
| 114 |
+
quick_ratio: Optional[float] = Field(
|
| 115 |
+
default=None,
|
| 116 |
+
description="Quick ratio.",
|
| 117 |
+
)
|
| 118 |
+
current_ratio: Optional[float] = Field(
|
| 119 |
+
default=None,
|
| 120 |
+
description="Current ratio.",
|
| 121 |
+
)
|
| 122 |
+
debt_to_equity: Optional[float] = Field(
|
| 123 |
+
default=None,
|
| 124 |
+
description="Debt-to-equity ratio.",
|
| 125 |
+
)
|
| 126 |
+
gross_margin: Optional[float] = Field(
|
| 127 |
+
default=None,
|
| 128 |
+
description="Gross margin, as a normalized percent.",
|
| 129 |
+
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
|
| 130 |
+
)
|
| 131 |
+
operating_margin: Optional[float] = Field(
|
| 132 |
+
default=None,
|
| 133 |
+
description="Operating margin, as a normalized percent.",
|
| 134 |
+
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
|
| 135 |
+
)
|
| 136 |
+
ebitda_margin: Optional[float] = Field(
|
| 137 |
+
default=None,
|
| 138 |
+
description="EBITDA margin, as a normalized percent.",
|
| 139 |
+
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
|
| 140 |
+
)
|
| 141 |
+
profit_margin: Optional[float] = Field(
|
| 142 |
+
default=None,
|
| 143 |
+
description="Profit margin, as a normalized percent.",
|
| 144 |
+
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
|
| 145 |
+
)
|
| 146 |
+
return_on_assets: Optional[float] = Field(
|
| 147 |
+
default=None,
|
| 148 |
+
description="Return on assets, as a normalized percent.",
|
| 149 |
+
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
|
| 150 |
+
)
|
| 151 |
+
return_on_equity: Optional[float] = Field(
|
| 152 |
+
default=None,
|
| 153 |
+
description="Return on equity, as a normalized percent.",
|
| 154 |
+
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
|
| 155 |
+
)
|
| 156 |
+
dividend_yield: Optional[float] = Field(
|
| 157 |
+
default=None,
|
| 158 |
+
description="Dividend yield, as a normalized percent.",
|
| 159 |
+
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
|
| 160 |
+
)
|
| 161 |
+
dividend_yield_5y_avg: Optional[float] = Field(
|
| 162 |
+
default=None,
|
| 163 |
+
description="5-year average dividend yield, as a normalized percent.",
|
| 164 |
+
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
|
| 165 |
+
)
|
| 166 |
+
payout_ratio: Optional[float] = Field(
|
| 167 |
+
default=None,
|
| 168 |
+
description="Payout ratio.",
|
| 169 |
+
)
|
| 170 |
+
book_value: Optional[float] = Field(
|
| 171 |
+
default=None,
|
| 172 |
+
description="Book value per share.",
|
| 173 |
+
)
|
| 174 |
+
price_to_book: Optional[float] = Field(
|
| 175 |
+
default=None,
|
| 176 |
+
description="Price-to-book ratio.",
|
| 177 |
+
)
|
| 178 |
+
enterprise_value: Optional[int] = Field(
|
| 179 |
+
default=None,
|
| 180 |
+
description="Enterprise value.",
|
| 181 |
+
)
|
| 182 |
+
overall_risk: Optional[float] = Field(
|
| 183 |
+
default=None,
|
| 184 |
+
description="Overall risk score.",
|
| 185 |
+
)
|
| 186 |
+
audit_risk: Optional[float] = Field(
|
| 187 |
+
default=None,
|
| 188 |
+
description="Audit risk score.",
|
| 189 |
+
)
|
| 190 |
+
board_risk: Optional[float] = Field(
|
| 191 |
+
default=None,
|
| 192 |
+
description="Board risk score.",
|
| 193 |
+
)
|
| 194 |
+
compensation_risk: Optional[float] = Field(
|
| 195 |
+
default=None,
|
| 196 |
+
description="Compensation risk score.",
|
| 197 |
+
)
|
| 198 |
+
shareholder_rights_risk: Optional[float] = Field(
|
| 199 |
+
default=None,
|
| 200 |
+
description="Shareholder rights risk score.",
|
| 201 |
+
)
|
| 202 |
+
beta: Optional[float] = Field(
|
| 203 |
+
default=None,
|
| 204 |
+
description="Beta relative to the broad market (5-year monthly).",
|
| 205 |
+
)
|
| 206 |
+
price_return_1y: Optional[float] = Field(
|
| 207 |
+
default=None,
|
| 208 |
+
description="One-year price return, as a normalized percent.",
|
| 209 |
+
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
|
| 210 |
+
)
|
| 211 |
+
currency: Optional[str] = Field(
|
| 212 |
+
default=None,
|
| 213 |
+
description="Currency in which the data is presented.",
|
| 214 |
+
)
|
| 215 |
+
|
| 216 |
+
@field_validator("dividend_yield_5y_avg")
|
| 217 |
+
@classmethod
|
| 218 |
+
def normalize_percent(cls, v: float):
|
| 219 |
+
"""Normalize the percent values."""
|
| 220 |
+
return float(v) / 100 if v else None
|
| 221 |
+
|
| 222 |
+
|
| 223 |
+
class YFinanceKeyMetricsFetcher(
|
| 224 |
+
Fetcher[YFinanceKeyMetricsQueryParams, List[YFinanceKeyMetricsData]]
|
| 225 |
+
):
|
| 226 |
+
"""YFinance Key Metrics fetcher."""
|
| 227 |
+
|
| 228 |
+
@staticmethod
|
| 229 |
+
def transform_query(params: Dict[str, Any]) -> YFinanceKeyMetricsQueryParams:
|
| 230 |
+
"""Transform the query."""
|
| 231 |
+
return YFinanceKeyMetricsQueryParams(**params)
|
| 232 |
+
|
| 233 |
+
@staticmethod
|
| 234 |
+
async def aextract_data(
|
| 235 |
+
query: YFinanceKeyMetricsQueryParams,
|
| 236 |
+
credentials: Optional[Dict[str, str]],
|
| 237 |
+
**kwargs: Any,
|
| 238 |
+
) -> List[Dict]:
|
| 239 |
+
"""Extract the raw data from YFinance."""
|
| 240 |
+
# pylint: disable=import-outside-toplevel
|
| 241 |
+
import asyncio # noqa
|
| 242 |
+
from curl_adapter import CurlCffiAdapter
|
| 243 |
+
from openbb_core.app.model.abstract.error import OpenBBError
|
| 244 |
+
from openbb_core.provider.utils.errors import EmptyDataError
|
| 245 |
+
from openbb_core.provider.utils.helpers import get_requests_session
|
| 246 |
+
from warnings import warn
|
| 247 |
+
from yfinance import Ticker
|
| 248 |
+
|
| 249 |
+
symbols = query.symbol.split(",")
|
| 250 |
+
results = []
|
| 251 |
+
fields = [
|
| 252 |
+
"symbol",
|
| 253 |
+
"marketCap",
|
| 254 |
+
"trailingPE",
|
| 255 |
+
"forwardPE",
|
| 256 |
+
"pegRatio",
|
| 257 |
+
"trailingPegRatio",
|
| 258 |
+
"earningsQuarterlyGrowth",
|
| 259 |
+
"earningsGrowth",
|
| 260 |
+
"revenuePerShare",
|
| 261 |
+
"revenueGrowth",
|
| 262 |
+
"cashPerShare",
|
| 263 |
+
"quickRatio",
|
| 264 |
+
"currentRatio",
|
| 265 |
+
"debtToEquity",
|
| 266 |
+
"grossMargins",
|
| 267 |
+
"ebitdaMargins",
|
| 268 |
+
"operatingMargins",
|
| 269 |
+
"profitMargins",
|
| 270 |
+
"returnOnAssets",
|
| 271 |
+
"returnOnEquity",
|
| 272 |
+
"dividendYield",
|
| 273 |
+
"fiveYearAvgDividendYield",
|
| 274 |
+
"payoutRatio",
|
| 275 |
+
"bookValue",
|
| 276 |
+
"priceToBook",
|
| 277 |
+
"enterpriseValue",
|
| 278 |
+
"enterpriseToRevenue",
|
| 279 |
+
"enterpriseToEbitda",
|
| 280 |
+
"overallRisk",
|
| 281 |
+
"auditRisk",
|
| 282 |
+
"boardRisk",
|
| 283 |
+
"compensationRisk",
|
| 284 |
+
"shareHolderRightsRisk",
|
| 285 |
+
"beta",
|
| 286 |
+
"52WeekChange",
|
| 287 |
+
"financialCurrency",
|
| 288 |
+
]
|
| 289 |
+
messages: list = []
|
| 290 |
+
session = get_requests_session()
|
| 291 |
+
session.mount("https://", CurlCffiAdapter())
|
| 292 |
+
session.mount("http://", CurlCffiAdapter())
|
| 293 |
+
|
| 294 |
+
async def get_one(symbol):
|
| 295 |
+
"""Get the data for one ticker symbol."""
|
| 296 |
+
result: dict = {}
|
| 297 |
+
ticker: dict = {}
|
| 298 |
+
try:
|
| 299 |
+
ticker = Ticker(
|
| 300 |
+
symbol,
|
| 301 |
+
session=session,
|
| 302 |
+
).get_info()
|
| 303 |
+
except Exception as e:
|
| 304 |
+
messages.append(
|
| 305 |
+
f"Error getting data for {symbol} -> {e.__class__.__name__}: {e}"
|
| 306 |
+
)
|
| 307 |
+
if not ticker:
|
| 308 |
+
messages.append(f"No data found for {symbol}")
|
| 309 |
+
elif ticker:
|
| 310 |
+
for field in fields:
|
| 311 |
+
if field in ticker:
|
| 312 |
+
result[field] = ticker.get(field, None)
|
| 313 |
+
if result and result.get("52WeekChange") is not None:
|
| 314 |
+
results.append(result)
|
| 315 |
+
|
| 316 |
+
tasks = [get_one(symbol) for symbol in symbols]
|
| 317 |
+
|
| 318 |
+
await asyncio.gather(*tasks)
|
| 319 |
+
|
| 320 |
+
if not results and not messages:
|
| 321 |
+
raise EmptyDataError("No data was returned for the given symbol(s).")
|
| 322 |
+
|
| 323 |
+
if not results and messages:
|
| 324 |
+
raise OpenBBError("\n".join(messages))
|
| 325 |
+
|
| 326 |
+
if results and messages:
|
| 327 |
+
for message in messages:
|
| 328 |
+
warn(message)
|
| 329 |
+
|
| 330 |
+
return results
|
| 331 |
+
|
| 332 |
+
@staticmethod
|
| 333 |
+
def transform_data(
|
| 334 |
+
query: YFinanceKeyMetricsQueryParams,
|
| 335 |
+
data: List[Dict],
|
| 336 |
+
**kwargs: Any,
|
| 337 |
+
) -> List[YFinanceKeyMetricsData]:
|
| 338 |
+
"""Transform the data."""
|
| 339 |
+
return [YFinanceKeyMetricsData.model_validate(d) for d in data]
|
openbb_platform/providers/yfinance/openbb_yfinance/models/losers.py
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Yahoo Finance Top Losers Model."""
|
| 2 |
+
|
| 3 |
+
# pylint: disable=unused-argument
|
| 4 |
+
|
| 5 |
+
from typing import Any, Optional
|
| 6 |
+
|
| 7 |
+
from openbb_core.provider.abstract.fetcher import Fetcher
|
| 8 |
+
from openbb_core.provider.standard_models.equity_performance import (
|
| 9 |
+
EquityPerformanceQueryParams,
|
| 10 |
+
)
|
| 11 |
+
from openbb_yfinance.utils.references import YFPredefinedScreenerData
|
| 12 |
+
from pydantic import Field
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
class YFLosersQueryParams(EquityPerformanceQueryParams):
|
| 16 |
+
"""Yahoo Finance Losers Query.
|
| 17 |
+
|
| 18 |
+
Source: https://finance.yahoo.com/screener/predefined/day_losers
|
| 19 |
+
"""
|
| 20 |
+
|
| 21 |
+
limit: Optional[int] = Field(
|
| 22 |
+
default=200,
|
| 23 |
+
description="Limit the number of results.",
|
| 24 |
+
)
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
class YFLosersData(YFPredefinedScreenerData):
|
| 28 |
+
"""Yahoo Finance Losers Data."""
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
class YFLosersFetcher(Fetcher[YFLosersQueryParams, list[YFLosersData]]):
|
| 32 |
+
"""Yahoo Finance Losers Fetcher."""
|
| 33 |
+
|
| 34 |
+
@staticmethod
|
| 35 |
+
def transform_query(params: dict[str, Any]) -> YFLosersQueryParams:
|
| 36 |
+
"""Transform query params."""
|
| 37 |
+
return YFLosersQueryParams(**params)
|
| 38 |
+
|
| 39 |
+
@staticmethod
|
| 40 |
+
async def aextract_data(
|
| 41 |
+
query: YFLosersQueryParams,
|
| 42 |
+
credentials: Optional[dict[str, str]],
|
| 43 |
+
**kwargs: Any,
|
| 44 |
+
) -> list[dict]:
|
| 45 |
+
"""Get data from YF."""
|
| 46 |
+
# pylint: disable=import-outside-toplevel
|
| 47 |
+
from openbb_yfinance.utils.helpers import get_custom_screener
|
| 48 |
+
|
| 49 |
+
body = {
|
| 50 |
+
"offset": 0,
|
| 51 |
+
"size": 250,
|
| 52 |
+
"sortField": "percentchange",
|
| 53 |
+
"sortType": "asc",
|
| 54 |
+
"quoteType": "equity",
|
| 55 |
+
"query": {
|
| 56 |
+
"operator": "and",
|
| 57 |
+
"operands": [
|
| 58 |
+
{"operator": "gt", "operands": ["intradaymarketcap", 500000000]},
|
| 59 |
+
{
|
| 60 |
+
"operator": "or",
|
| 61 |
+
"operands": [
|
| 62 |
+
{"operator": "eq", "operands": ["exchange", "NMS"]},
|
| 63 |
+
{"operator": "eq", "operands": ["exchange", "NYQ"]},
|
| 64 |
+
],
|
| 65 |
+
},
|
| 66 |
+
{"operator": "gt", "operands": ["percentchange", -3]},
|
| 67 |
+
{"operator": "gt", "operands": ["intradayprice", 5]},
|
| 68 |
+
],
|
| 69 |
+
},
|
| 70 |
+
"userId": "",
|
| 71 |
+
"userIdType": "guid",
|
| 72 |
+
}
|
| 73 |
+
|
| 74 |
+
return await get_custom_screener(body=body, limit=query.limit)
|
| 75 |
+
|
| 76 |
+
@staticmethod
|
| 77 |
+
def transform_data(
|
| 78 |
+
query: EquityPerformanceQueryParams,
|
| 79 |
+
data: list[dict],
|
| 80 |
+
**kwargs: Any,
|
| 81 |
+
) -> list[YFLosersData]:
|
| 82 |
+
"""Transform data."""
|
| 83 |
+
return [
|
| 84 |
+
YFLosersData.model_validate(d)
|
| 85 |
+
for d in sorted(
|
| 86 |
+
data,
|
| 87 |
+
key=lambda x: x["regularMarketChangePercent"],
|
| 88 |
+
reverse=query.sort == "desc",
|
| 89 |
+
)
|
| 90 |
+
]
|
openbb_platform/providers/yfinance/openbb_yfinance/models/options_chains.py
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""YFinance Options Chains Model."""
|
| 2 |
+
|
| 3 |
+
# pylint: disable=unused-argument
|
| 4 |
+
|
| 5 |
+
from datetime import datetime
|
| 6 |
+
from typing import Any, Dict, List, Optional, Union
|
| 7 |
+
|
| 8 |
+
from openbb_core.app.model.abstract.error import OpenBBError
|
| 9 |
+
from openbb_core.provider.abstract.annotated_result import AnnotatedResult
|
| 10 |
+
from openbb_core.provider.abstract.fetcher import Fetcher
|
| 11 |
+
from openbb_core.provider.standard_models.options_chains import (
|
| 12 |
+
OptionsChainsData,
|
| 13 |
+
OptionsChainsQueryParams,
|
| 14 |
+
)
|
| 15 |
+
from openbb_core.provider.utils.errors import EmptyDataError
|
| 16 |
+
from pydantic import Field
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
class YFinanceOptionsChainsQueryParams(OptionsChainsQueryParams):
|
| 20 |
+
"""YFinance Options Chains Query Parameters."""
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
class YFinanceOptionsChainsData(OptionsChainsData):
|
| 24 |
+
"""YFinance Options Chains Data."""
|
| 25 |
+
|
| 26 |
+
__doc__ = OptionsChainsData.__doc__
|
| 27 |
+
__alias_dict__ = {
|
| 28 |
+
"contract_symbol": "contractSymbol",
|
| 29 |
+
"last_trade_time": "lastTradeDate",
|
| 30 |
+
"last_trade_price": "lastPrice",
|
| 31 |
+
"change_percent": "percentChange",
|
| 32 |
+
"open_interest": "openInterest",
|
| 33 |
+
"implied_volatility": "impliedVolatility",
|
| 34 |
+
"in_the_money": "inTheMoney",
|
| 35 |
+
}
|
| 36 |
+
|
| 37 |
+
in_the_money: List[Union[bool, None]] = Field(
|
| 38 |
+
default_factory=list,
|
| 39 |
+
description="Whether the option is in the money.",
|
| 40 |
+
)
|
| 41 |
+
currency: List[Union[str, None]] = Field(
|
| 42 |
+
default_factory=list,
|
| 43 |
+
description="Currency of the option.",
|
| 44 |
+
)
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
class YFinanceOptionsChainsFetcher(
|
| 48 |
+
Fetcher[YFinanceOptionsChainsQueryParams, YFinanceOptionsChainsData]
|
| 49 |
+
):
|
| 50 |
+
"""YFinance Options Chains Fetcher."""
|
| 51 |
+
|
| 52 |
+
@staticmethod
|
| 53 |
+
def transform_query(params: Dict[str, Any]) -> YFinanceOptionsChainsQueryParams:
|
| 54 |
+
"""Transform the query."""
|
| 55 |
+
return YFinanceOptionsChainsQueryParams(**params)
|
| 56 |
+
|
| 57 |
+
@staticmethod
|
| 58 |
+
async def aextract_data(
|
| 59 |
+
query: YFinanceOptionsChainsQueryParams,
|
| 60 |
+
credentials: Optional[Dict[str, str]],
|
| 61 |
+
**kwargs: Any,
|
| 62 |
+
) -> Dict:
|
| 63 |
+
"""Extract the raw data from YFinance."""
|
| 64 |
+
# pylint: disable=import-outside-toplevel
|
| 65 |
+
import asyncio # noqa
|
| 66 |
+
from curl_adapter import CurlCffiAdapter
|
| 67 |
+
from openbb_core.provider.utils.helpers import get_requests_session
|
| 68 |
+
from pandas import concat
|
| 69 |
+
from yfinance import Ticker
|
| 70 |
+
from pytz import timezone
|
| 71 |
+
|
| 72 |
+
symbol = query.symbol.upper()
|
| 73 |
+
symbol = "^" + symbol if symbol in ["VIX", "RUT", "SPX", "NDX"] else symbol
|
| 74 |
+
session = get_requests_session()
|
| 75 |
+
session.mount("https://", CurlCffiAdapter())
|
| 76 |
+
session.mount("http://", CurlCffiAdapter())
|
| 77 |
+
ticker = Ticker(
|
| 78 |
+
symbol,
|
| 79 |
+
session=session,
|
| 80 |
+
)
|
| 81 |
+
expirations = list(ticker.options)
|
| 82 |
+
|
| 83 |
+
if not expirations or len(expirations) == 0:
|
| 84 |
+
raise OpenBBError(f"No options found for {symbol}")
|
| 85 |
+
|
| 86 |
+
chains_output: List = []
|
| 87 |
+
underlying = ticker.option_chain(expirations[0])[2]
|
| 88 |
+
underlying_output: Dict = {
|
| 89 |
+
"symbol": symbol,
|
| 90 |
+
"name": underlying.get("longName"),
|
| 91 |
+
"exchange": underlying.get("fullExchangeName"),
|
| 92 |
+
"exchange_tz": underlying.get("exchangeTimezoneName"),
|
| 93 |
+
"currency": underlying.get("currency"),
|
| 94 |
+
"bid": underlying.get("bid"),
|
| 95 |
+
"bid_size": underlying.get("bidSize"),
|
| 96 |
+
"ask": underlying.get("ask"),
|
| 97 |
+
"ask_size": underlying.get("askSize"),
|
| 98 |
+
"last_price": underlying.get(
|
| 99 |
+
"postMarketPrice", underlying.get("regularMarketPrice")
|
| 100 |
+
),
|
| 101 |
+
"open": underlying.get("regularMarketOpen"),
|
| 102 |
+
"high": underlying.get("regularMarketDayHigh"),
|
| 103 |
+
"low": underlying.get("regularMarketDayLow"),
|
| 104 |
+
"close": underlying.get("regularMarketPrice"),
|
| 105 |
+
"prev_close": underlying.get("regularMarketPreviousClose"),
|
| 106 |
+
"change": underlying.get("regularMarketChange"),
|
| 107 |
+
"change_percent": underlying.get("regularMarketChangePercent"),
|
| 108 |
+
"volume": underlying.get("regularMarketVolume"),
|
| 109 |
+
"dividend_yield": float(underlying.get("dividendYield", 0)) / 100,
|
| 110 |
+
"dividend_yield_ttm": underlying.get("trailingAnnualDividendYield"),
|
| 111 |
+
"year_high": underlying.get("fiftyTwoWeekHigh"),
|
| 112 |
+
"year_low": underlying.get("fiftyTwoWeekLow"),
|
| 113 |
+
"ma_50": underlying.get("fiftyDayAverage"),
|
| 114 |
+
"ma_200": underlying.get("twoHundredDayAverage"),
|
| 115 |
+
"volume_avg_10d": underlying.get("averageDailyVolume10Day"),
|
| 116 |
+
"volume_avg_3m": underlying.get("averageDailyVolume3Month"),
|
| 117 |
+
"market_cap": underlying.get("marketCap"),
|
| 118 |
+
"shares_outstanding": underlying.get("sharesOutstanding"),
|
| 119 |
+
}
|
| 120 |
+
tz = timezone(underlying_output.get("exchange_tz", "UTC"))
|
| 121 |
+
|
| 122 |
+
underlying_price = underlying_output.get("last_price")
|
| 123 |
+
|
| 124 |
+
async def get_chain(ticker, expiration, tz, underlying_price):
|
| 125 |
+
"""Get the data for one expiration."""
|
| 126 |
+
exp = datetime.strptime(expiration, "%Y-%m-%d").date()
|
| 127 |
+
now = datetime.now().date()
|
| 128 |
+
dte = (exp - now).days
|
| 129 |
+
calls = ticker.option_chain(expiration, tz=tz)[0]
|
| 130 |
+
calls["option_type"] = "call"
|
| 131 |
+
calls["expiration"] = expiration
|
| 132 |
+
puts = ticker.option_chain(expiration, tz=tz)[1]
|
| 133 |
+
puts["option_type"] = "put"
|
| 134 |
+
puts["expiration"] = expiration
|
| 135 |
+
chain = concat([calls, puts])
|
| 136 |
+
chain = (
|
| 137 |
+
chain.set_index(["strike", "option_type", "contractSymbol"])
|
| 138 |
+
.sort_index()
|
| 139 |
+
.reset_index()
|
| 140 |
+
)
|
| 141 |
+
chain = chain.drop(columns=["contractSize"])
|
| 142 |
+
chain["dte"] = dte
|
| 143 |
+
if underlying_price is not None:
|
| 144 |
+
chain["underlying_price"] = underlying_price
|
| 145 |
+
chain["underlying_symbol"] = symbol
|
| 146 |
+
chain["percentChange"] = chain["percentChange"] / 100
|
| 147 |
+
|
| 148 |
+
if len(chain) > 0:
|
| 149 |
+
chains_output.extend(
|
| 150 |
+
chain.fillna("N/A").replace("N/A", None).to_dict("records")
|
| 151 |
+
)
|
| 152 |
+
|
| 153 |
+
await asyncio.gather(
|
| 154 |
+
*[
|
| 155 |
+
get_chain(ticker, expiration, tz, underlying_price)
|
| 156 |
+
for expiration in expirations
|
| 157 |
+
]
|
| 158 |
+
)
|
| 159 |
+
|
| 160 |
+
if not chains_output:
|
| 161 |
+
raise EmptyDataError(f"No data was returned for {symbol}")
|
| 162 |
+
|
| 163 |
+
return {"underlying": underlying_output, "chains": chains_output}
|
| 164 |
+
|
| 165 |
+
@staticmethod
|
| 166 |
+
def transform_data(
|
| 167 |
+
query: YFinanceOptionsChainsQueryParams,
|
| 168 |
+
data: Dict,
|
| 169 |
+
**kwargs: Any,
|
| 170 |
+
) -> AnnotatedResult[YFinanceOptionsChainsData]:
|
| 171 |
+
"""Transform the data."""
|
| 172 |
+
# pylint: disable=import-outside-toplevel
|
| 173 |
+
from numpy import nan
|
| 174 |
+
from pandas import DataFrame
|
| 175 |
+
|
| 176 |
+
if not data:
|
| 177 |
+
raise EmptyDataError()
|
| 178 |
+
metadata = data.get("underlying", {})
|
| 179 |
+
records = data.get("chains", [])
|
| 180 |
+
output = DataFrame(records)
|
| 181 |
+
for col in ["volume", "openInterest"]:
|
| 182 |
+
output[col] = (
|
| 183 |
+
output[col].infer_objects(copy=False).replace({nan: 0}).astype("int64")
|
| 184 |
+
)
|
| 185 |
+
|
| 186 |
+
output = output.replace({nan: None})
|
| 187 |
+
|
| 188 |
+
return AnnotatedResult(
|
| 189 |
+
result=YFinanceOptionsChainsData.model_validate(output.to_dict("list")),
|
| 190 |
+
metadata=metadata,
|
| 191 |
+
)
|
openbb_platform/providers/yfinance/openbb_yfinance/models/price_target_consensus.py
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""YFinance Price Target Consensus Model."""
|
| 2 |
+
|
| 3 |
+
# pylint: disable=unused-argument
|
| 4 |
+
|
| 5 |
+
from typing import Any, Dict, List, Optional
|
| 6 |
+
|
| 7 |
+
from openbb_core.app.model.abstract.error import OpenBBError
|
| 8 |
+
from openbb_core.provider.abstract.fetcher import Fetcher
|
| 9 |
+
from openbb_core.provider.standard_models.price_target_consensus import (
|
| 10 |
+
PriceTargetConsensusData,
|
| 11 |
+
PriceTargetConsensusQueryParams,
|
| 12 |
+
)
|
| 13 |
+
from pydantic import Field, field_validator
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
class YFinancePriceTargetConsensusQueryParams(PriceTargetConsensusQueryParams):
|
| 17 |
+
"""YFinance Price Target Consensus Query."""
|
| 18 |
+
|
| 19 |
+
__json_schema_extra__ = {"symbol": {"multiple_items_allowed": True}}
|
| 20 |
+
|
| 21 |
+
@field_validator("symbol", mode="before", check_fields=False)
|
| 22 |
+
@classmethod
|
| 23 |
+
def check_symbol(cls, value):
|
| 24 |
+
"""Check the symbol."""
|
| 25 |
+
if not value:
|
| 26 |
+
raise OpenBBError("Error: Symbol is a required field for yFinance.")
|
| 27 |
+
return value
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
class YFinancePriceTargetConsensusData(PriceTargetConsensusData):
|
| 31 |
+
"""YFinance Price Target Consensus Data."""
|
| 32 |
+
|
| 33 |
+
__alias_dict__ = {
|
| 34 |
+
"target_high": "targetHighPrice",
|
| 35 |
+
"target_low": "targetLowPrice",
|
| 36 |
+
"target_consensus": "targetMeanPrice",
|
| 37 |
+
"target_median": "targetMedianPrice",
|
| 38 |
+
"recommendation": "recommendationKey",
|
| 39 |
+
"recommendation_mean": "recommendationMean",
|
| 40 |
+
"number_of_analysts": "numberOfAnalystOpinions",
|
| 41 |
+
"current_price": "currentPrice",
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
+
recommendation: Optional[str] = Field(
|
| 45 |
+
default=None,
|
| 46 |
+
description="Recommendation - buy, sell, etc.",
|
| 47 |
+
)
|
| 48 |
+
recommendation_mean: Optional[float] = Field(
|
| 49 |
+
default=None,
|
| 50 |
+
description="Mean recommendation score where 1 is strong buy and 5 is strong sell.",
|
| 51 |
+
)
|
| 52 |
+
number_of_analysts: Optional[int] = Field(
|
| 53 |
+
default=None, description="Number of analysts providing opinions."
|
| 54 |
+
)
|
| 55 |
+
current_price: Optional[float] = Field(
|
| 56 |
+
default=None,
|
| 57 |
+
description="Current price of the stock.",
|
| 58 |
+
)
|
| 59 |
+
currency: Optional[str] = Field(
|
| 60 |
+
default=None,
|
| 61 |
+
description="Currency the stock is priced in.",
|
| 62 |
+
)
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
class YFinancePriceTargetConsensusFetcher(
|
| 66 |
+
Fetcher[
|
| 67 |
+
YFinancePriceTargetConsensusQueryParams, List[YFinancePriceTargetConsensusData]
|
| 68 |
+
]
|
| 69 |
+
):
|
| 70 |
+
"""YFinance Price Target Consensus Fetcher."""
|
| 71 |
+
|
| 72 |
+
@staticmethod
|
| 73 |
+
def transform_query(
|
| 74 |
+
params: Dict[str, Any],
|
| 75 |
+
) -> YFinancePriceTargetConsensusQueryParams:
|
| 76 |
+
"""Transform the query."""
|
| 77 |
+
return YFinancePriceTargetConsensusQueryParams(**params)
|
| 78 |
+
|
| 79 |
+
@staticmethod
|
| 80 |
+
async def aextract_data(
|
| 81 |
+
query: YFinancePriceTargetConsensusQueryParams,
|
| 82 |
+
credentials: Optional[Dict[str, str]],
|
| 83 |
+
**kwargs: Any,
|
| 84 |
+
) -> List[Dict]:
|
| 85 |
+
"""Extract the raw data from YFinance."""
|
| 86 |
+
# pylint: disable=import-outside-toplevel
|
| 87 |
+
import asyncio # noqa
|
| 88 |
+
from curl_adapter import CurlCffiAdapter
|
| 89 |
+
from openbb_core.provider.utils.errors import EmptyDataError
|
| 90 |
+
from openbb_core.provider.utils.helpers import get_requests_session
|
| 91 |
+
from warnings import warn
|
| 92 |
+
from yfinance import Ticker
|
| 93 |
+
|
| 94 |
+
symbols = query.symbol.split(",") # type: ignore
|
| 95 |
+
results = []
|
| 96 |
+
fields = [
|
| 97 |
+
"symbol",
|
| 98 |
+
"currentPrice",
|
| 99 |
+
"currency",
|
| 100 |
+
"targetHighPrice",
|
| 101 |
+
"targetLowPrice",
|
| 102 |
+
"targetMeanPrice",
|
| 103 |
+
"targetMedianPrice",
|
| 104 |
+
"recommendationMean",
|
| 105 |
+
"recommendationKey",
|
| 106 |
+
"numberOfAnalystOpinions",
|
| 107 |
+
]
|
| 108 |
+
session = get_requests_session()
|
| 109 |
+
session.mount("https://", CurlCffiAdapter())
|
| 110 |
+
session.mount("http://", CurlCffiAdapter())
|
| 111 |
+
messages: list = []
|
| 112 |
+
|
| 113 |
+
async def get_one(symbol):
|
| 114 |
+
"""Get the data for one ticker symbol."""
|
| 115 |
+
result: dict = {}
|
| 116 |
+
ticker: dict = {}
|
| 117 |
+
try:
|
| 118 |
+
ticker = Ticker(
|
| 119 |
+
symbol,
|
| 120 |
+
session=session,
|
| 121 |
+
).get_info()
|
| 122 |
+
except Exception as e:
|
| 123 |
+
messages.append(
|
| 124 |
+
f"Error getting data for {symbol}: {e.__class__.__name__}: {e}"
|
| 125 |
+
)
|
| 126 |
+
if ticker:
|
| 127 |
+
for field in fields:
|
| 128 |
+
if field in ticker:
|
| 129 |
+
result[field] = ticker.get(field, None)
|
| 130 |
+
if result and result.get("numberOfAnalystOpinions") is not None:
|
| 131 |
+
results.append(result)
|
| 132 |
+
|
| 133 |
+
tasks = [get_one(symbol) for symbol in symbols]
|
| 134 |
+
|
| 135 |
+
await asyncio.gather(*tasks)
|
| 136 |
+
|
| 137 |
+
if not results and not messages:
|
| 138 |
+
raise EmptyDataError("No data was returned for the given symbol(s)")
|
| 139 |
+
|
| 140 |
+
if not results and messages:
|
| 141 |
+
raise OpenBBError("\n".join(messages))
|
| 142 |
+
|
| 143 |
+
if results and messages:
|
| 144 |
+
for message in messages:
|
| 145 |
+
warn(message)
|
| 146 |
+
|
| 147 |
+
return results
|
| 148 |
+
|
| 149 |
+
@staticmethod
|
| 150 |
+
def transform_data(
|
| 151 |
+
query: YFinancePriceTargetConsensusQueryParams,
|
| 152 |
+
data: List[Dict],
|
| 153 |
+
**kwargs: Any,
|
| 154 |
+
) -> List[YFinancePriceTargetConsensusData]:
|
| 155 |
+
"""Transform the data."""
|
| 156 |
+
return [YFinancePriceTargetConsensusData.model_validate(d) for d in data]
|
openbb_platform/providers/yfinance/openbb_yfinance/models/share_statistics.py
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""YFinance Share Statistics Model."""
|
| 2 |
+
|
| 3 |
+
# pylint: disable=unused-argument
|
| 4 |
+
|
| 5 |
+
from datetime import (
|
| 6 |
+
date as dateType,
|
| 7 |
+
datetime,
|
| 8 |
+
timezone,
|
| 9 |
+
)
|
| 10 |
+
from typing import Any, Dict, List, Optional
|
| 11 |
+
from warnings import warn
|
| 12 |
+
|
| 13 |
+
from openbb_core.provider.abstract.fetcher import Fetcher
|
| 14 |
+
from openbb_core.provider.standard_models.share_statistics import (
|
| 15 |
+
ShareStatisticsData,
|
| 16 |
+
ShareStatisticsQueryParams,
|
| 17 |
+
)
|
| 18 |
+
from pydantic import Field, field_validator
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
class YFinanceShareStatisticsQueryParams(ShareStatisticsQueryParams):
|
| 22 |
+
"""YFinance Share Statistics Query."""
|
| 23 |
+
|
| 24 |
+
__json_schema_extra__ = {"symbol": {"multiple_items_allowed": True}}
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
class YFinanceShareStatisticsData(ShareStatisticsData):
|
| 28 |
+
"""YFinance Share Statistics Data."""
|
| 29 |
+
|
| 30 |
+
__alias_dict__ = {
|
| 31 |
+
"outstanding_shares": "sharesOutstanding",
|
| 32 |
+
"float_shares": "floatShares",
|
| 33 |
+
"date": "dateShortInterest",
|
| 34 |
+
"implied_shares_outstanding": "impliedSharesOutstanding",
|
| 35 |
+
"short_interest": "sharesShort",
|
| 36 |
+
"short_percent_of_float": "shortPercentOfFloat",
|
| 37 |
+
"days_to_cover": "shortRatio",
|
| 38 |
+
"short_interest_prev_month": "sharesShortPriorMonth",
|
| 39 |
+
"short_interest_prev_date": "sharesShortPreviousMonthDate",
|
| 40 |
+
"insider_ownership": "heldPercentInsiders",
|
| 41 |
+
"institution_ownership": "heldPercentInstitutions",
|
| 42 |
+
"institution_float_ownership": "institutionsFloatPercentHeld",
|
| 43 |
+
"institutions_count": "institutionsCount",
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
implied_shares_outstanding: Optional[int] = Field(
|
| 47 |
+
default=None,
|
| 48 |
+
description="Implied Shares Outstanding of common equity,"
|
| 49 |
+
+ " assuming the conversion of all convertible subsidiary equity into common.",
|
| 50 |
+
)
|
| 51 |
+
short_interest: Optional[int] = Field(
|
| 52 |
+
default=None,
|
| 53 |
+
description="Number of shares that are reported short.",
|
| 54 |
+
)
|
| 55 |
+
short_percent_of_float: Optional[float] = Field(
|
| 56 |
+
default=None,
|
| 57 |
+
description="Percentage of shares that are reported short, as a normalized percent.",
|
| 58 |
+
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
|
| 59 |
+
)
|
| 60 |
+
days_to_cover: Optional[float] = Field(
|
| 61 |
+
default=None,
|
| 62 |
+
description="Number of days to repurchase the shares as a ratio of average daily volume",
|
| 63 |
+
)
|
| 64 |
+
short_interest_prev_month: Optional[int] = Field(
|
| 65 |
+
default=None,
|
| 66 |
+
description="Number of shares that were reported short in the previous month.",
|
| 67 |
+
)
|
| 68 |
+
short_interest_prev_date: Optional[dateType] = Field(
|
| 69 |
+
default=None,
|
| 70 |
+
description="Date of the previous month's report.",
|
| 71 |
+
)
|
| 72 |
+
insider_ownership: Optional[float] = Field(
|
| 73 |
+
default=None,
|
| 74 |
+
description="Percentage of shares held by insiders, as a normalized percent.",
|
| 75 |
+
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
|
| 76 |
+
)
|
| 77 |
+
institution_ownership: Optional[float] = Field(
|
| 78 |
+
default=None,
|
| 79 |
+
description="Percentage of shares held by institutions, as a normalized percent.",
|
| 80 |
+
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
|
| 81 |
+
)
|
| 82 |
+
institution_float_ownership: Optional[float] = Field(
|
| 83 |
+
default=None,
|
| 84 |
+
description="Percentage of float held by institutions, as a normalized percent.",
|
| 85 |
+
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
|
| 86 |
+
)
|
| 87 |
+
institutions_count: Optional[int] = Field(
|
| 88 |
+
default=None,
|
| 89 |
+
description="Number of institutions holding shares.",
|
| 90 |
+
)
|
| 91 |
+
|
| 92 |
+
@field_validator(
|
| 93 |
+
"date", "short_interest_prev_date", mode="before", check_fields=False
|
| 94 |
+
)
|
| 95 |
+
@classmethod
|
| 96 |
+
def validate_first_trade_date(cls, v):
|
| 97 |
+
"""Convert dates from UTC timestamp."""
|
| 98 |
+
return datetime.fromtimestamp(v, timezone.utc).date() if v else None
|
| 99 |
+
|
| 100 |
+
|
| 101 |
+
class YFinanceShareStatisticsFetcher(
|
| 102 |
+
Fetcher[YFinanceShareStatisticsQueryParams, List[YFinanceShareStatisticsData]]
|
| 103 |
+
):
|
| 104 |
+
"""YFinance Share Statistics Fetcher."""
|
| 105 |
+
|
| 106 |
+
@staticmethod
|
| 107 |
+
def transform_query(params: Dict[str, Any]) -> YFinanceShareStatisticsQueryParams:
|
| 108 |
+
"""Transform the query."""
|
| 109 |
+
return YFinanceShareStatisticsQueryParams(**params)
|
| 110 |
+
|
| 111 |
+
@staticmethod
|
| 112 |
+
async def aextract_data(
|
| 113 |
+
query: YFinanceShareStatisticsQueryParams,
|
| 114 |
+
credentials: Optional[Dict[str, str]],
|
| 115 |
+
**kwargs: Any,
|
| 116 |
+
) -> List[Dict]:
|
| 117 |
+
"""Extract the raw data from YFinance."""
|
| 118 |
+
# pylint: disable=import-outside-toplevel
|
| 119 |
+
import asyncio # noqa
|
| 120 |
+
from curl_adapter import CurlCffiAdapter
|
| 121 |
+
from openbb_core.app.model.abstract.error import OpenBBError
|
| 122 |
+
from openbb_core.provider.utils.errors import EmptyDataError
|
| 123 |
+
from openbb_core.provider.utils.helpers import get_requests_session
|
| 124 |
+
from yfinance import Ticker
|
| 125 |
+
|
| 126 |
+
symbols = query.symbol.split(",")
|
| 127 |
+
results = []
|
| 128 |
+
fields = [
|
| 129 |
+
"symbol",
|
| 130 |
+
"sharesOutstanding",
|
| 131 |
+
"floatShares",
|
| 132 |
+
"impliedSharesOutstanding",
|
| 133 |
+
"sharesShort",
|
| 134 |
+
"sharesShortPriorMonth",
|
| 135 |
+
"sharesShortPreviousMonthDate",
|
| 136 |
+
"shortRatio",
|
| 137 |
+
"shortPercentOfFloat",
|
| 138 |
+
"dateShortInterest",
|
| 139 |
+
"heldPercentInsiders",
|
| 140 |
+
"heldPercentInstitutions",
|
| 141 |
+
"institutionsFloatPercentHeld",
|
| 142 |
+
"institutionsCount",
|
| 143 |
+
]
|
| 144 |
+
session = get_requests_session()
|
| 145 |
+
session.mount("https://", CurlCffiAdapter())
|
| 146 |
+
session.mount("http://", CurlCffiAdapter())
|
| 147 |
+
messages: list = []
|
| 148 |
+
|
| 149 |
+
async def get_one(symbol):
|
| 150 |
+
"""Get the data for one ticker symbol."""
|
| 151 |
+
result: dict = {}
|
| 152 |
+
ticker: dict = {}
|
| 153 |
+
try:
|
| 154 |
+
_ticker = Ticker(
|
| 155 |
+
symbol,
|
| 156 |
+
session=session,
|
| 157 |
+
)
|
| 158 |
+
ticker = _ticker.get_info()
|
| 159 |
+
major_holders = _ticker.get_major_holders(as_dict=True).get("Value")
|
| 160 |
+
if major_holders:
|
| 161 |
+
ticker.update(major_holders) # type: ignore
|
| 162 |
+
except Exception as e:
|
| 163 |
+
messages.append(
|
| 164 |
+
f"Error getting data for {symbol} -> {e.__class__.__name__}: {e}"
|
| 165 |
+
)
|
| 166 |
+
if ticker:
|
| 167 |
+
for field in fields:
|
| 168 |
+
if field in ticker:
|
| 169 |
+
result[field] = ticker.get(field, None)
|
| 170 |
+
if result and result.get("sharesOutstanding") is not None:
|
| 171 |
+
results.append(result)
|
| 172 |
+
|
| 173 |
+
tasks = [get_one(symbol) for symbol in symbols]
|
| 174 |
+
|
| 175 |
+
await asyncio.gather(*tasks)
|
| 176 |
+
|
| 177 |
+
if not results and messages:
|
| 178 |
+
raise OpenBBError("\n".join(messages))
|
| 179 |
+
|
| 180 |
+
if not results and not messages:
|
| 181 |
+
raise EmptyDataError("No data was returned for the given symbol(s).")
|
| 182 |
+
|
| 183 |
+
if results and messages:
|
| 184 |
+
for message in messages:
|
| 185 |
+
warn(message)
|
| 186 |
+
|
| 187 |
+
return results
|
| 188 |
+
|
| 189 |
+
@staticmethod
|
| 190 |
+
def transform_data(
|
| 191 |
+
query: YFinanceShareStatisticsQueryParams,
|
| 192 |
+
data: List[Dict],
|
| 193 |
+
**kwargs: Any,
|
| 194 |
+
) -> List[YFinanceShareStatisticsData]:
|
| 195 |
+
"""Transform the data."""
|
| 196 |
+
return [YFinanceShareStatisticsData.model_validate(d) for d in data]
|
openbb_platform/providers/yfinance/openbb_yfinance/models/undervalued_growth_equities.py
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Yahoo Finance Asset Undervalued Growth Tech Equities Model."""
|
| 2 |
+
|
| 3 |
+
# pylint: disable=unused-argument
|
| 4 |
+
|
| 5 |
+
from typing import Any, Optional
|
| 6 |
+
|
| 7 |
+
from openbb_core.provider.abstract.fetcher import Fetcher
|
| 8 |
+
from openbb_core.provider.standard_models.equity_performance import (
|
| 9 |
+
EquityPerformanceQueryParams,
|
| 10 |
+
)
|
| 11 |
+
from openbb_yfinance.utils.references import YFPredefinedScreenerData
|
| 12 |
+
from pydantic import Field
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
class YFUndervaluedGrowthEquitiesQueryParams(EquityPerformanceQueryParams):
|
| 16 |
+
"""Yahoo Finance Undervalued Growth Stocks Query.
|
| 17 |
+
|
| 18 |
+
Source: https://finance.yahoo.com/screener/predefined/undervalued_growth_stocks
|
| 19 |
+
"""
|
| 20 |
+
|
| 21 |
+
limit: Optional[int] = Field(
|
| 22 |
+
default=200,
|
| 23 |
+
description="Limit the number of results.",
|
| 24 |
+
)
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
class YFUndervaluedGrowthEquitiesData(YFPredefinedScreenerData):
|
| 28 |
+
"""Yahoo Finance Undervalued Growth Stocks Data."""
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
class YFUndervaluedGrowthEquitiesFetcher(
|
| 32 |
+
Fetcher[
|
| 33 |
+
YFUndervaluedGrowthEquitiesQueryParams, list[YFUndervaluedGrowthEquitiesData]
|
| 34 |
+
]
|
| 35 |
+
):
|
| 36 |
+
"""Yahoo Finance Undervalued Growth Stocks Fetcher."""
|
| 37 |
+
|
| 38 |
+
@staticmethod
|
| 39 |
+
def transform_query(
|
| 40 |
+
params: dict[str, Any],
|
| 41 |
+
) -> YFUndervaluedGrowthEquitiesQueryParams:
|
| 42 |
+
"""Transform query params."""
|
| 43 |
+
return YFUndervaluedGrowthEquitiesQueryParams(**params)
|
| 44 |
+
|
| 45 |
+
@staticmethod
|
| 46 |
+
async def aextract_data(
|
| 47 |
+
query: YFUndervaluedGrowthEquitiesQueryParams,
|
| 48 |
+
credentials: Optional[dict[str, str]],
|
| 49 |
+
**kwargs: Any,
|
| 50 |
+
) -> list[dict]:
|
| 51 |
+
"""Get data from YF."""
|
| 52 |
+
# pylint: disable=import-outside-toplevel
|
| 53 |
+
from openbb_yfinance.utils.helpers import get_custom_screener
|
| 54 |
+
|
| 55 |
+
body = {
|
| 56 |
+
"offset": 0,
|
| 57 |
+
"size": 250,
|
| 58 |
+
"sortField": "eodvolume",
|
| 59 |
+
"sortType": "desc",
|
| 60 |
+
"quoteType": "equity",
|
| 61 |
+
"query": {
|
| 62 |
+
"operator": "and",
|
| 63 |
+
"operands": [
|
| 64 |
+
{"operator": "gt", "operands": ["intradaymarketcap", 500000000]},
|
| 65 |
+
{
|
| 66 |
+
"operator": "or",
|
| 67 |
+
"operands": [
|
| 68 |
+
{"operator": "eq", "operands": ["exchange", "NMS"]},
|
| 69 |
+
{"operator": "eq", "operands": ["exchange", "NYQ"]},
|
| 70 |
+
],
|
| 71 |
+
},
|
| 72 |
+
{
|
| 73 |
+
"operator": "btwn",
|
| 74 |
+
"operands": ["peratio.lasttwelvemonths", 0, 20],
|
| 75 |
+
},
|
| 76 |
+
{"operator": "lt", "operands": ["pegratio_5y", 1]},
|
| 77 |
+
{"operator": "gte", "operands": ["epsgrowth.lasttwelvemonths", 25]},
|
| 78 |
+
],
|
| 79 |
+
},
|
| 80 |
+
"userId": "",
|
| 81 |
+
"userIdType": "guid",
|
| 82 |
+
}
|
| 83 |
+
|
| 84 |
+
return await get_custom_screener(body=body, limit=query.limit)
|
| 85 |
+
|
| 86 |
+
@staticmethod
|
| 87 |
+
def transform_data(
|
| 88 |
+
query: EquityPerformanceQueryParams,
|
| 89 |
+
data: list[dict],
|
| 90 |
+
**kwargs: Any,
|
| 91 |
+
) -> list[YFUndervaluedGrowthEquitiesData]:
|
| 92 |
+
"""Transform data."""
|
| 93 |
+
return [
|
| 94 |
+
YFUndervaluedGrowthEquitiesData.model_validate(d)
|
| 95 |
+
for d in sorted(
|
| 96 |
+
data,
|
| 97 |
+
key=lambda x: x["regularMarketChangePercent"],
|
| 98 |
+
reverse=query.sort == "desc",
|
| 99 |
+
)
|
| 100 |
+
]
|
openbb_platform/providers/yfinance/openbb_yfinance/models/undervalued_large_caps.py
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Yahoo Finance Asset Undervalued Large Caps Model."""
|
| 2 |
+
|
| 3 |
+
# pylint: disable=unused-argument
|
| 4 |
+
|
| 5 |
+
from typing import Any, Optional
|
| 6 |
+
|
| 7 |
+
from openbb_core.provider.abstract.fetcher import Fetcher
|
| 8 |
+
from openbb_core.provider.standard_models.equity_performance import (
|
| 9 |
+
EquityPerformanceQueryParams,
|
| 10 |
+
)
|
| 11 |
+
from openbb_yfinance.utils.references import YFPredefinedScreenerData
|
| 12 |
+
from pydantic import Field
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
class YFUndervaluedLargeCapsQueryParams(EquityPerformanceQueryParams):
|
| 16 |
+
"""Yahoo Finance Undervalued Large Caps Query.
|
| 17 |
+
|
| 18 |
+
Source: https://finance.yahoo.com/screener/predefined/undervalued_large_caps
|
| 19 |
+
"""
|
| 20 |
+
|
| 21 |
+
limit: Optional[int] = Field(
|
| 22 |
+
default=200,
|
| 23 |
+
description="Limit the number of results.",
|
| 24 |
+
)
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
class YFUndervaluedLargeCapsData(YFPredefinedScreenerData):
|
| 28 |
+
"""Yahoo Finance Undervalued Large Caps Data."""
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
class YFUndervaluedLargeCapsFetcher(
|
| 32 |
+
Fetcher[YFUndervaluedLargeCapsQueryParams, list[YFUndervaluedLargeCapsData]]
|
| 33 |
+
):
|
| 34 |
+
"""Yahoo Finance Undervalued Large Caps Fetcher."""
|
| 35 |
+
|
| 36 |
+
@staticmethod
|
| 37 |
+
def transform_query(params: dict[str, Any]) -> YFUndervaluedLargeCapsQueryParams:
|
| 38 |
+
"""Transform query params."""
|
| 39 |
+
return YFUndervaluedLargeCapsQueryParams(**params)
|
| 40 |
+
|
| 41 |
+
@staticmethod
|
| 42 |
+
async def aextract_data(
|
| 43 |
+
query: YFUndervaluedLargeCapsQueryParams,
|
| 44 |
+
credentials: Optional[dict[str, str]],
|
| 45 |
+
**kwargs: Any,
|
| 46 |
+
) -> list[dict]:
|
| 47 |
+
"""Get data from YF."""
|
| 48 |
+
# pylint: disable=import-outside-toplevel
|
| 49 |
+
from openbb_yfinance.utils.helpers import get_custom_screener
|
| 50 |
+
|
| 51 |
+
body = {
|
| 52 |
+
"offset": 0,
|
| 53 |
+
"size": 250,
|
| 54 |
+
"sortField": "eodvolume",
|
| 55 |
+
"sortType": "desc",
|
| 56 |
+
"quoteType": "equity",
|
| 57 |
+
"query": {
|
| 58 |
+
"operator": "and",
|
| 59 |
+
"operands": [
|
| 60 |
+
{"operator": "gt", "operands": ["intradaymarketcap", 10000000000]},
|
| 61 |
+
{
|
| 62 |
+
"operator": "or",
|
| 63 |
+
"operands": [
|
| 64 |
+
{"operator": "eq", "operands": ["exchange", "NMS"]},
|
| 65 |
+
{"operator": "eq", "operands": ["exchange", "NYQ"]},
|
| 66 |
+
],
|
| 67 |
+
},
|
| 68 |
+
{
|
| 69 |
+
"operator": "btwn",
|
| 70 |
+
"operands": ["peratio.lasttwelvemonths", 0, 20],
|
| 71 |
+
},
|
| 72 |
+
{"operator": "lt", "operands": ["pegratio_5y", 1]},
|
| 73 |
+
{"operator": "gte", "operands": ["epsgrowth.lasttwelvemonths", 25]},
|
| 74 |
+
],
|
| 75 |
+
},
|
| 76 |
+
"userId": "",
|
| 77 |
+
"userIdType": "guid",
|
| 78 |
+
}
|
| 79 |
+
|
| 80 |
+
return await get_custom_screener(body=body, limit=query.limit)
|
| 81 |
+
|
| 82 |
+
@staticmethod
|
| 83 |
+
def transform_data(
|
| 84 |
+
query: EquityPerformanceQueryParams,
|
| 85 |
+
data: list[dict],
|
| 86 |
+
**kwargs: Any,
|
| 87 |
+
) -> list[YFUndervaluedLargeCapsData]:
|
| 88 |
+
"""Transform data."""
|
| 89 |
+
return [
|
| 90 |
+
YFUndervaluedLargeCapsData.model_validate(d)
|
| 91 |
+
for d in sorted(
|
| 92 |
+
data,
|
| 93 |
+
key=lambda x: x["regularMarketChangePercent"],
|
| 94 |
+
reverse=query.sort == "desc",
|
| 95 |
+
)
|
| 96 |
+
]
|
openbb_platform/providers/yfinance/openbb_yfinance/py.typed
ADDED
|
File without changes
|