CatPtain commited on
Commit
2665ecc
·
verified ·
1 Parent(s): 8ae3767

Upload 111 files

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. openbb_platform/providers/README.md +29 -0
  2. openbb_platform/providers/__init__.py +1 -0
  3. openbb_platform/providers/wsj/README.md +13 -0
  4. openbb_platform/providers/wsj/__init__.py +1 -0
  5. openbb_platform/providers/wsj/openbb_wsj/__init__.py +25 -0
  6. openbb_platform/providers/wsj/openbb_wsj/models/active.py +104 -0
  7. openbb_platform/providers/wsj/openbb_wsj/models/gainers.py +110 -0
  8. openbb_platform/providers/wsj/openbb_wsj/models/losers.py +109 -0
  9. openbb_platform/providers/wsj/poetry.lock +0 -0
  10. openbb_platform/providers/wsj/pyproject.toml +19 -0
  11. openbb_platform/providers/wsj/tests/record/http/test_wsj_fetchers/test_wsj_active_fetcher_urllib3_v1.yaml +139 -0
  12. openbb_platform/providers/wsj/tests/record/http/test_wsj_fetchers/test_wsj_active_fetcher_urllib3_v2.yaml +125 -0
  13. openbb_platform/providers/wsj/tests/record/http/test_wsj_fetchers/test_wsj_gainers_fetcher_urllib3_v1.yaml +139 -0
  14. openbb_platform/providers/wsj/tests/record/http/test_wsj_fetchers/test_wsj_gainers_fetcher_urllib3_v2.yaml +125 -0
  15. openbb_platform/providers/wsj/tests/record/http/test_wsj_fetchers/test_wsj_losers_fetcher_urllib3_v1.yaml +141 -0
  16. openbb_platform/providers/wsj/tests/record/http/test_wsj_fetchers/test_wsj_losers_fetcher_urllib3_v2.yaml +125 -0
  17. openbb_platform/providers/wsj/tests/test_wsj_fetchers.py +52 -0
  18. openbb_platform/providers/yfinance/README.md +13 -0
  19. openbb_platform/providers/yfinance/__init__.py +1 -0
  20. openbb_platform/providers/yfinance/openbb_yfinance/__init__.py +79 -0
  21. openbb_platform/providers/yfinance/openbb_yfinance/models/__init__.py +1 -0
  22. openbb_platform/providers/yfinance/openbb_yfinance/models/active.py +90 -0
  23. openbb_platform/providers/yfinance/openbb_yfinance/models/aggressive_small_caps.py +89 -0
  24. openbb_platform/providers/yfinance/openbb_yfinance/models/available_indices.py +68 -0
  25. openbb_platform/providers/yfinance/openbb_yfinance/models/balance_sheet.py +124 -0
  26. openbb_platform/providers/yfinance/openbb_yfinance/models/cash_flow.py +123 -0
  27. openbb_platform/providers/yfinance/openbb_yfinance/models/company_news.py +119 -0
  28. openbb_platform/providers/yfinance/openbb_yfinance/models/crypto_historical.py +137 -0
  29. openbb_platform/providers/yfinance/openbb_yfinance/models/currency_historical.py +135 -0
  30. openbb_platform/providers/yfinance/openbb_yfinance/models/equity_historical.py +193 -0
  31. openbb_platform/providers/yfinance/openbb_yfinance/models/equity_profile.py +211 -0
  32. openbb_platform/providers/yfinance/openbb_yfinance/models/equity_quote.py +146 -0
  33. openbb_platform/providers/yfinance/openbb_yfinance/models/equity_screener.py +250 -0
  34. openbb_platform/providers/yfinance/openbb_yfinance/models/etf_info.py +332 -0
  35. openbb_platform/providers/yfinance/openbb_yfinance/models/futures_curve.py +69 -0
  36. openbb_platform/providers/yfinance/openbb_yfinance/models/futures_historical.py +151 -0
  37. openbb_platform/providers/yfinance/openbb_yfinance/models/gainers.py +90 -0
  38. openbb_platform/providers/yfinance/openbb_yfinance/models/growth_tech_equities.py +96 -0
  39. openbb_platform/providers/yfinance/openbb_yfinance/models/historical_dividends.py +78 -0
  40. openbb_platform/providers/yfinance/openbb_yfinance/models/income_statement.py +125 -0
  41. openbb_platform/providers/yfinance/openbb_yfinance/models/index_historical.py +154 -0
  42. openbb_platform/providers/yfinance/openbb_yfinance/models/key_executives.py +95 -0
  43. openbb_platform/providers/yfinance/openbb_yfinance/models/key_metrics.py +339 -0
  44. openbb_platform/providers/yfinance/openbb_yfinance/models/losers.py +90 -0
  45. openbb_platform/providers/yfinance/openbb_yfinance/models/options_chains.py +191 -0
  46. openbb_platform/providers/yfinance/openbb_yfinance/models/price_target_consensus.py +156 -0
  47. openbb_platform/providers/yfinance/openbb_yfinance/models/share_statistics.py +196 -0
  48. openbb_platform/providers/yfinance/openbb_yfinance/models/undervalued_growth_equities.py +100 -0
  49. openbb_platform/providers/yfinance/openbb_yfinance/models/undervalued_large_caps.py +96 -0
  50. 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