[Feature] Add Nasdaq To Equity Screener (#6521)

* nasdaq equity screener

* commit the limit param

* missing test params

* the other missing param

* sector mapping

* fix biztoc
This commit is contained in:
Danglewood
2024-06-20 01:34:23 -07:00
committed by GitHub
parent 3e2d8b53d1
commit 050b15696b
14 changed files with 1014 additions and 1218 deletions

View File

@@ -1462,6 +1462,19 @@ def test_equity_search(params, headers):
"provider": "fmp",
}
),
(
{
"sector": "consumer_staples,consumer_discretionary",
"exchange": "all",
"exsubcategory": "all",
"region": "all",
"country": "all",
"mktcap": "large",
"recommendation": "all",
"limit": None,
"provider": "nasdaq",
}
),
],
)
@pytest.mark.integration

View File

@@ -1396,6 +1396,19 @@ def test_equity_search(params, obb):
"provider": "fmp",
}
),
(
{
"sector": "consumer_staples,consumer_discretionary",
"exchange": "all",
"exsubcategory": "all",
"region": "all",
"country": "all",
"mktcap": "large",
"recommendation": "all",
"limit": None,
"provider": "nasdaq",
}
),
],
)
@pytest.mark.integration

View File

@@ -73,10 +73,8 @@ def headers():
(
{
"provider": "biztoc",
"filter": "tag",
"tag": "federalreserve",
"source": "bloomberg",
"term": "MSFT",
"source": None,
"term": "microsoft",
"start_date": None,
"end_date": None,
}

View File

@@ -69,10 +69,8 @@ def obb(pytestconfig): # pylint: disable=inconsistent-return-statements
(
{
"provider": "biztoc",
"filter": "tag",
"tag": "federalreserve",
"source": "bloomberg",
"term": "MSFT",
"source": None,
"term": "microsoft",
"start_date": None,
"end_date": None,
}

View File

@@ -1,59 +1,47 @@
"""Biztoc World News Model."""
import warnings
from datetime import datetime, timedelta
from typing import Any, Dict, List, Literal, Optional
# pylint: disable=unused-argument
from openbb_biztoc.utils.helpers import get_news
from typing import Any, Dict, List, Optional
from warnings import warn
from openbb_core.app.model.abstract.error import OpenBBError
from openbb_core.provider.abstract.fetcher import Fetcher
from openbb_core.provider.standard_models.world_news import (
WorldNewsData,
WorldNewsQueryParams,
)
from openbb_core.provider.utils.helpers import make_request
from pandas import to_datetime
from pydantic import Field, field_validator
_warn = warnings.warn
class BiztocWorldNewsQueryParams(WorldNewsQueryParams):
"""Biztoc World News Query."""
filter: Literal["crypto", "hot", "latest", "main", "media", "source", "tag"] = (
Field(default="latest", description="Filter by type of news.")
)
source: str = Field(
description="Filter by a specific publisher. Only valid when filter is set to source.",
default="bloomberg",
)
tag: Optional[str] = Field(
description="Tag, topic, to filter articles by. Only valid when filter is set to tag.",
default=None,
)
term: Optional[str] = Field(
description="Search term to filter articles by. This overrides all other filters.",
default=None,
)
source: Optional[str] = Field(
description="Filter by a specific publisher. Only valid when filter is set to source.",
default=None,
)
class BiztocWorldNewsData(WorldNewsData):
"""Biztoc World News Data."""
__alias_dict__ = {
"date": "created",
"date": "published",
"text": "body",
"site": "domain",
"images": "img",
}
images: Optional[Dict[str, str]] = Field(
description="Images for the article.", alias="images", default=None
)
favicon: Optional[str] = Field(
description="Icon image for the source of the article.", default=None
)
tags: Optional[List[str]] = Field(description="Tags for the article.", default=None)
id: Optional[str] = Field(description="Unique Article ID.", default=None)
score: Optional[float] = Field(
description="Search relevance score for the article.", default=None
)
@@ -64,6 +52,12 @@ class BiztocWorldNewsData(WorldNewsData):
"""Return formatted datetime."""
return to_datetime(v).strftime("%Y-%m-%d %H:%M:%S")
@field_validator("title")
@classmethod
def title_validate(cls, v):
"""Strip empty title text."""
return v.strip() if v else None
class BiztocWorldNewsFetcher(
Fetcher[
@@ -77,7 +71,7 @@ class BiztocWorldNewsFetcher(
def transform_query(params: Dict[str, Any]) -> BiztocWorldNewsQueryParams:
"""Transform the query."""
if params.get("start_date") or params.get("end_date"):
_warn("start_date and end_date are not supported for this endpoint.")
warn("start_date and end_date are not supported for this endpoint.")
return BiztocWorldNewsQueryParams(**params)
@staticmethod
@@ -88,29 +82,61 @@ class BiztocWorldNewsFetcher(
) -> List[Dict]:
"""Extract the data from the Biztoc endpoint."""
api_key = credentials.get("biztoc_api_key") if credentials else ""
data = get_news(
api_key=api_key, filter_=query.filter, source=query.source, tag=query.tag, term=query.term # type: ignore
)
if query.filter == "hot":
data = [post for sublist in data for post in sublist["posts"]]
times = {"2 Hours Ago": 2, "4 Hours Ago": 4}
# Drop 'body_preview' because it is always nan, empty string, or empty string with space.
for _, item in enumerate(data):
item.pop("body_preview", None) # Removes 'body_preview' if it exists
# Adjust 'created' time if necessary
if item.get("created") in times:
item["created"] = datetime.now() - timedelta(
hours=times[item["created"]]
headers = {
"X-RapidAPI-Key": f"{api_key}",
"X-RapidAPI-Host": "biztoc.p.rapidapi.com",
"Accept": "application/json",
"Accept-Encoding": "gzip",
}
base_url = "https://biztoc.p.rapidapi.com/"
url = ""
response: List = []
if query.term:
query.term = query.term.replace(" ", "%20")
url = base_url + f"search?q={query.term}"
response = make_request(url, headers=headers).json()
if query.source is not None:
sources_response = make_request(
"https://biztoc.p.rapidapi.com/sources",
headers=headers,
).json()
sources = [source["id"] for source in sources_response]
if query.source.lower() not in sources:
raise OpenBBError(
f"{query.source} not a valid source. Valid sources: {sources}"
)
url = base_url + f"news/source/{query.source.lower()}"
response = make_request(url, headers=headers).json()
else:
url1 = base_url + "news/latest"
url2 = base_url + "news/topics"
response = make_request(url1, headers=headers).json()
response2 = make_request(url2, headers=headers).json()
if response2:
for topic in response2:
stories = topic.get("stories", [])
if stories:
response.extend(
{
"text" if k == "body_preview" else k: v
for k, v in story.items()
}
for story in stories
)
return sorted(data, key=lambda x: x["created"], reverse=True)
return response
# pylint: disable=unused-argument
@staticmethod
def transform_data(
query: BiztocWorldNewsQueryParams, data: List[Dict], **kwargs: Any
) -> List[BiztocWorldNewsData]:
"""Transform the data to the standard format."""
return [BiztocWorldNewsData.model_validate(d) for d in data[: query.limit]]
results: List[BiztocWorldNewsData] = []
for item in data:
item.pop("id", None)
item.pop("uid", None)
item.pop("body_preview", None)
item.pop("site", None)
item.pop("domain", None)
results.append(BiztocWorldNewsData.model_validate(item))
return results

View File

@@ -1,144 +0,0 @@
"""Biztoc Helpers."""
from datetime import timedelta
from typing import Dict, List, Literal
import requests
import requests_cache
from openbb_core.app.model.abstract.error import OpenBBError
from openbb_core.app.utils import get_user_cache_directory
# pylint: disable=C0325
cache_dir = get_user_cache_directory()
biztoc_session_tags = requests_cache.CachedSession(
f"{cache_dir}/http/biztoc_tags", expire_after=timedelta(days=1)
)
biztoc_session_sources = requests_cache.CachedSession(
f"{cache_dir}/http/biztoc_sources", expire_after=timedelta(days=3)
)
def get_sources(api_key: str) -> List[Dict]:
"""Get valid sources for Biztoc queries."""
headers = {
"X-RapidAPI-Key": f"{api_key}",
"X-RapidAPI-Host": "biztoc.p.rapidapi.com",
"Accept": "application/json",
"Accept-Encoding": "gzip",
}
sources = biztoc_session_sources.get(
"https://biztoc.p.rapidapi.com/sources", headers=headers, timeout=10
)
return sources.json()
def get_pages(api_key: str) -> List[str]:
"""Get valid pages for Biztoc queries."""
headers = {
"X-RapidAPI-Key": f"{api_key}",
"X-RapidAPI-Host": "biztoc.p.rapidapi.com",
"Accept": "application/json",
"Accept-Encoding": "gzip",
}
pages = biztoc_session_sources.get(
"https://biztoc.p.rapidapi.com/pages", headers=headers, timeout=10
)
if pages.status_code != 200:
msg = pages.json().get("message") or pages.text
raise OpenBBError(f"HTTP error - > {msg}")
return pages.json()
def get_tags_by_page(page_id: str, api_key: str) -> List[str]:
"""Get valid tags required for Biztoc queries."""
headers = {
"X-RapidAPI-Key": f"{api_key}",
"X-RapidAPI-Host": "biztoc.p.rapidapi.com",
"Accept": "application/json",
"Accept-Encoding": "gzip",
}
tags = biztoc_session_tags.get(
f"https://biztoc.p.rapidapi.com/tags/{page_id}", headers=headers, timeout=10
)
if tags.status_code != 200:
msg = tags.json().get("message") or tags.text
raise OpenBBError(f"HTTP error - > {msg}")
return tags.json()
def get_all_tags(api_key) -> Dict[str, List[str]]:
"""Get all tags for all pages."""
tags: Dict[str, List[str]] = {}
pages = get_pages(api_key)
for page in pages:
page_tags = get_tags_by_page(page, api_key)
tags.update({page: [x["tag"] for x in page_tags]}) # type: ignore
return tags
def get_news(
api_key: str,
filter_: Literal[
"crypto", "hot", "latest", "main", "media", "source", "tag"
] = "latest",
source: str = "bloomberg",
tag: str = "",
term: str = "",
) -> List[Dict]:
"""Call the BizToc API and returns the data."""
results = []
term = term.replace(" ", "%20") if term else ""
_tags = get_all_tags(api_key)
pages = get_pages(api_key)
tags = []
tag = tag.lower() if tag else ""
for page in pages:
tags.extend(_tags[page][:])
_sources = get_sources(api_key)
sources = sorted([i["id"] for i in _sources])
headers = {
"X-RapidAPI-Key": f"{api_key}",
"X-RapidAPI-Host": "biztoc.p.rapidapi.com",
"Accept": "application/json",
"Accept-Encoding": "gzip",
}
filter_dict = {
"hot": "news/hot",
"latest": "news/latest",
"crypto": "news/latest/crypto",
"main": "news/latest/main",
"media": "news/latest/media",
"source": f"news/source/{source.lower()}",
"tag": f"tag/{tag}",
}
if filter_ == "source" and source.lower() not in sources:
raise OpenBBError(f"{source} not a valid source. Valid sources: {sources}")
if filter_ == "tag" and tag.lower().replace(" ", "") not in tags:
raise OpenBBError(f"{tag} not a valid tag. Valid tags: {tags}")
url = (
f"https://biztoc.p.rapidapi.com/search?q={term}"
if term
else f"https://biztoc.p.rapidapi.com/{filter_dict[filter_]}"
)
r = requests.get(url, headers=headers, timeout=5)
if r.status_code != 200:
raise OpenBBError(f"HTTP error - > {r.text}")
try:
results = r.json()
except Exception as e:
raise (e)
return results

View File

@@ -187,31 +187,6 @@ files = [
{file = "backoff-2.2.1.tar.gz", hash = "sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba"},
]
[[package]]
name = "cattrs"
version = "23.2.3"
description = "Composable complex class support for attrs and dataclasses."
optional = false
python-versions = ">=3.8"
files = [
{file = "cattrs-23.2.3-py3-none-any.whl", hash = "sha256:0341994d94971052e9ee70662542699a3162ea1e0c62f7ce1b4a57f563685108"},
{file = "cattrs-23.2.3.tar.gz", hash = "sha256:a934090d95abaa9e911dac357e3a8699e0b4b14f8529bcc7d2b1ad9d51672b9f"},
]
[package.dependencies]
attrs = ">=23.1.0"
exceptiongroup = {version = ">=1.1.1", markers = "python_version < \"3.11\""}
typing-extensions = {version = ">=4.1.0,<4.6.3 || >4.6.3", markers = "python_version < \"3.11\""}
[package.extras]
bson = ["pymongo (>=4.4.0)"]
cbor2 = ["cbor2 (>=5.4.6)"]
msgpack = ["msgpack (>=1.0.5)"]
orjson = ["orjson (>=3.9.2)"]
pyyaml = ["pyyaml (>=6.0)"]
tomlkit = ["tomlkit (>=0.11.8)"]
ujson = ["ujson (>=5.7.0)"]
[[package]]
name = "certifi"
version = "2024.6.2"
@@ -369,13 +344,13 @@ wmi = ["wmi (>=1.5.1)"]
[[package]]
name = "email-validator"
version = "2.1.1"
version = "2.1.2"
description = "A robust email address syntax and deliverability validation library."
optional = false
python-versions = ">=3.8"
files = [
{file = "email_validator-2.1.1-py3-none-any.whl", hash = "sha256:97d882d174e2a65732fb43bfce81a3a834cbc1bde8bf419e30ef5ea976370a05"},
{file = "email_validator-2.1.1.tar.gz", hash = "sha256:200a70680ba08904be6d1eef729205cc0d687634399a5924d842533efb824b84"},
{file = "email_validator-2.1.2-py3-none-any.whl", hash = "sha256:d89f6324e13b1e39889eab7f9ca2f91dc9aebb6fa50a6d8bd4329ab50f251115"},
{file = "email_validator-2.1.2.tar.gz", hash = "sha256:14c0f3d343c4beda37400421b39fa411bbe33a75df20825df73ad53e06a9f04c"},
]
[package.dependencies]
@@ -951,47 +926,56 @@ files = [
[[package]]
name = "numpy"
version = "1.26.4"
version = "2.0.0"
description = "Fundamental package for array computing in Python"
optional = false
python-versions = ">=3.9"
files = [
{file = "numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0"},
{file = "numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a"},
{file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4"},
{file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f"},
{file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a"},
{file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2"},
{file = "numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07"},
{file = "numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5"},
{file = "numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71"},
{file = "numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef"},
{file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e"},
{file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5"},
{file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a"},
{file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a"},
{file = "numpy-1.26.4-cp311-cp311-win32.whl", hash = "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20"},
{file = "numpy-1.26.4-cp311-cp311-win_amd64.whl", hash = "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2"},
{file = "numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218"},
{file = "numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b"},
{file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b"},
{file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed"},
{file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a"},
{file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0"},
{file = "numpy-1.26.4-cp312-cp312-win32.whl", hash = "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110"},
{file = "numpy-1.26.4-cp312-cp312-win_amd64.whl", hash = "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818"},
{file = "numpy-1.26.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7349ab0fa0c429c82442a27a9673fc802ffdb7c7775fad780226cb234965e53c"},
{file = "numpy-1.26.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:52b8b60467cd7dd1e9ed082188b4e6bb35aa5cdd01777621a1658910745b90be"},
{file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5241e0a80d808d70546c697135da2c613f30e28251ff8307eb72ba696945764"},
{file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f870204a840a60da0b12273ef34f7051e98c3b5961b61b0c2c1be6dfd64fbcd3"},
{file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:679b0076f67ecc0138fd2ede3a8fd196dddc2ad3254069bcb9faf9a79b1cebcd"},
{file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:47711010ad8555514b434df65f7d7b076bb8261df1ca9bb78f53d3b2db02e95c"},
{file = "numpy-1.26.4-cp39-cp39-win32.whl", hash = "sha256:a354325ee03388678242a4d7ebcd08b5c727033fcff3b2f536aea978e15ee9e6"},
{file = "numpy-1.26.4-cp39-cp39-win_amd64.whl", hash = "sha256:3373d5d70a5fe74a2c1bb6d2cfd9609ecf686d47a2d7b1d37a8f3b6bf6003aea"},
{file = "numpy-1.26.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:afedb719a9dcfc7eaf2287b839d8198e06dcd4cb5d276a3df279231138e83d30"},
{file = "numpy-1.26.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95a7476c59002f2f6c590b9b7b998306fba6a5aa646b1e22ddfeaf8f78c3a29c"},
{file = "numpy-1.26.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7e50d0a0cc3189f9cb0aeb3a6a6af18c16f59f004b866cd2be1c14b36134a4a0"},
{file = "numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010"},
{file = "numpy-2.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:04494f6ec467ccb5369d1808570ae55f6ed9b5809d7f035059000a37b8d7e86f"},
{file = "numpy-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2635dbd200c2d6faf2ef9a0d04f0ecc6b13b3cad54f7c67c61155138835515d2"},
{file = "numpy-2.0.0-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:0a43f0974d501842866cc83471bdb0116ba0dffdbaac33ec05e6afed5b615238"},
{file = "numpy-2.0.0-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:8d83bb187fb647643bd56e1ae43f273c7f4dbcdf94550d7938cfc32566756514"},
{file = "numpy-2.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79e843d186c8fb1b102bef3e2bc35ef81160ffef3194646a7fdd6a73c6b97196"},
{file = "numpy-2.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d7696c615765091cc5093f76fd1fa069870304beaccfd58b5dcc69e55ef49c1"},
{file = "numpy-2.0.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b4c76e3d4c56f145d41b7b6751255feefae92edbc9a61e1758a98204200f30fc"},
{file = "numpy-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:acd3a644e4807e73b4e1867b769fbf1ce8c5d80e7caaef0d90dcdc640dfc9787"},
{file = "numpy-2.0.0-cp310-cp310-win32.whl", hash = "sha256:cee6cc0584f71adefe2c908856ccc98702baf95ff80092e4ca46061538a2ba98"},
{file = "numpy-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:ed08d2703b5972ec736451b818c2eb9da80d66c3e84aed1deeb0c345fefe461b"},
{file = "numpy-2.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ad0c86f3455fbd0de6c31a3056eb822fc939f81b1618f10ff3406971893b62a5"},
{file = "numpy-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e7f387600d424f91576af20518334df3d97bc76a300a755f9a8d6e4f5cadd289"},
{file = "numpy-2.0.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:34f003cb88b1ba38cb9a9a4a3161c1604973d7f9d5552c38bc2f04f829536609"},
{file = "numpy-2.0.0-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:b6f6a8f45d0313db07d6d1d37bd0b112f887e1369758a5419c0370ba915b3871"},
{file = "numpy-2.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f64641b42b2429f56ee08b4f427a4d2daf916ec59686061de751a55aafa22e4"},
{file = "numpy-2.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a7039a136017eaa92c1848152827e1424701532ca8e8967fe480fe1569dae581"},
{file = "numpy-2.0.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:46e161722e0f619749d1cd892167039015b2c2817296104487cd03ed4a955995"},
{file = "numpy-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0e50842b2295ba8414c8c1d9d957083d5dfe9e16828b37de883f51fc53c4016f"},
{file = "numpy-2.0.0-cp311-cp311-win32.whl", hash = "sha256:2ce46fd0b8a0c947ae047d222f7136fc4d55538741373107574271bc00e20e8f"},
{file = "numpy-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:fbd6acc766814ea6443628f4e6751d0da6593dae29c08c0b2606164db026970c"},
{file = "numpy-2.0.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:354f373279768fa5a584bac997de6a6c9bc535c482592d7a813bb0c09be6c76f"},
{file = "numpy-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4d2f62e55a4cd9c58c1d9a1c9edaedcd857a73cb6fda875bf79093f9d9086f85"},
{file = "numpy-2.0.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:1e72728e7501a450288fc8e1f9ebc73d90cfd4671ebbd631f3e7857c39bd16f2"},
{file = "numpy-2.0.0-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:84554fc53daa8f6abf8e8a66e076aff6ece62de68523d9f665f32d2fc50fd66e"},
{file = "numpy-2.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c73aafd1afca80afecb22718f8700b40ac7cab927b8abab3c3e337d70e10e5a2"},
{file = "numpy-2.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49d9f7d256fbc804391a7f72d4a617302b1afac1112fac19b6c6cec63fe7fe8a"},
{file = "numpy-2.0.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0ec84b9ba0654f3b962802edc91424331f423dcf5d5f926676e0150789cb3d95"},
{file = "numpy-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:feff59f27338135776f6d4e2ec7aeeac5d5f7a08a83e80869121ef8164b74af9"},
{file = "numpy-2.0.0-cp312-cp312-win32.whl", hash = "sha256:c5a59996dc61835133b56a32ebe4ef3740ea5bc19b3983ac60cc32be5a665d54"},
{file = "numpy-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:a356364941fb0593bb899a1076b92dfa2029f6f5b8ba88a14fd0984aaf76d0df"},
{file = "numpy-2.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e61155fae27570692ad1d327e81c6cf27d535a5d7ef97648a17d922224b216de"},
{file = "numpy-2.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4554eb96f0fd263041baf16cf0881b3f5dafae7a59b1049acb9540c4d57bc8cb"},
{file = "numpy-2.0.0-cp39-cp39-macosx_14_0_arm64.whl", hash = "sha256:903703372d46bce88b6920a0cd86c3ad82dae2dbef157b5fc01b70ea1cfc430f"},
{file = "numpy-2.0.0-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:3e8e01233d57639b2e30966c63d36fcea099d17c53bf424d77f088b0f4babd86"},
{file = "numpy-2.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cde1753efe513705a0c6d28f5884e22bdc30438bf0085c5c486cdaff40cd67a"},
{file = "numpy-2.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:821eedb7165ead9eebdb569986968b541f9908979c2da8a4967ecac4439bae3d"},
{file = "numpy-2.0.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9a1712c015831da583b21c5bfe15e8684137097969c6d22e8316ba66b5baabe4"},
{file = "numpy-2.0.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9c27f0946a3536403efb0e1c28def1ae6730a72cd0d5878db38824855e3afc44"},
{file = "numpy-2.0.0-cp39-cp39-win32.whl", hash = "sha256:63b92c512d9dbcc37f9d81b123dec99fdb318ba38c8059afc78086fe73820275"},
{file = "numpy-2.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:3f6bed7f840d44c08ebdb73b1825282b801799e325bcbdfa6bc5c370e5aecc65"},
{file = "numpy-2.0.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9416a5c2e92ace094e9f0082c5fd473502c91651fb896bc17690d6fc475128d6"},
{file = "numpy-2.0.0-pp39-pypy39_pp73-macosx_14_0_x86_64.whl", hash = "sha256:17067d097ed036636fa79f6a869ac26df7db1ba22039d962422506640314933a"},
{file = "numpy-2.0.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38ecb5b0582cd125f67a629072fed6f83562d9dd04d7e03256c9829bdec027ad"},
{file = "numpy-2.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:cef04d068f5fb0518a77857953193b6bb94809a806bd0a14983a8f12ada060c9"},
{file = "numpy-2.0.0.tar.gz", hash = "sha256:cf5d1c9e6837f8af9f92b6bd3e86d513cdc11f60fd62185cc49ec7d1aba34864"},
]
[[package]]
@@ -1024,57 +1008,57 @@ websockets = ">=12.0,<13.0"
[[package]]
name = "orjson"
version = "3.10.3"
version = "3.10.5"
description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy"
optional = false
python-versions = ">=3.8"
files = [
{file = "orjson-3.10.3-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9fb6c3f9f5490a3eb4ddd46fc1b6eadb0d6fc16fb3f07320149c3286a1409dd8"},
{file = "orjson-3.10.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:252124b198662eee80428f1af8c63f7ff077c88723fe206a25df8dc57a57b1fa"},
{file = "orjson-3.10.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9f3e87733823089a338ef9bbf363ef4de45e5c599a9bf50a7a9b82e86d0228da"},
{file = "orjson-3.10.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c8334c0d87103bb9fbbe59b78129f1f40d1d1e8355bbed2ca71853af15fa4ed3"},
{file = "orjson-3.10.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1952c03439e4dce23482ac846e7961f9d4ec62086eb98ae76d97bd41d72644d7"},
{file = "orjson-3.10.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c0403ed9c706dcd2809f1600ed18f4aae50be263bd7112e54b50e2c2bc3ebd6d"},
{file = "orjson-3.10.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:382e52aa4270a037d41f325e7d1dfa395b7de0c367800b6f337d8157367bf3a7"},
{file = "orjson-3.10.3-cp310-none-win32.whl", hash = "sha256:be2aab54313752c04f2cbaab4515291ef5af8c2256ce22abc007f89f42f49109"},
{file = "orjson-3.10.3-cp310-none-win_amd64.whl", hash = "sha256:416b195f78ae461601893f482287cee1e3059ec49b4f99479aedf22a20b1098b"},
{file = "orjson-3.10.3-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:73100d9abbbe730331f2242c1fc0bcb46a3ea3b4ae3348847e5a141265479700"},
{file = "orjson-3.10.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:544a12eee96e3ab828dbfcb4d5a0023aa971b27143a1d35dc214c176fdfb29b3"},
{file = "orjson-3.10.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:520de5e2ef0b4ae546bea25129d6c7c74edb43fc6cf5213f511a927f2b28148b"},
{file = "orjson-3.10.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ccaa0a401fc02e8828a5bedfd80f8cd389d24f65e5ca3954d72c6582495b4bcf"},
{file = "orjson-3.10.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a7bc9e8bc11bac40f905640acd41cbeaa87209e7e1f57ade386da658092dc16"},
{file = "orjson-3.10.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3582b34b70543a1ed6944aca75e219e1192661a63da4d039d088a09c67543b08"},
{file = "orjson-3.10.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1c23dfa91481de880890d17aa7b91d586a4746a4c2aa9a145bebdbaf233768d5"},
{file = "orjson-3.10.3-cp311-none-win32.whl", hash = "sha256:1770e2a0eae728b050705206d84eda8b074b65ee835e7f85c919f5705b006c9b"},
{file = "orjson-3.10.3-cp311-none-win_amd64.whl", hash = "sha256:93433b3c1f852660eb5abdc1f4dd0ced2be031ba30900433223b28ee0140cde5"},
{file = "orjson-3.10.3-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a39aa73e53bec8d410875683bfa3a8edf61e5a1c7bb4014f65f81d36467ea098"},
{file = "orjson-3.10.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0943a96b3fa09bee1afdfccc2cb236c9c64715afa375b2af296c73d91c23eab2"},
{file = "orjson-3.10.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e852baafceff8da3c9defae29414cc8513a1586ad93e45f27b89a639c68e8176"},
{file = "orjson-3.10.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18566beb5acd76f3769c1d1a7ec06cdb81edc4d55d2765fb677e3eaa10fa99e0"},
{file = "orjson-3.10.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bd2218d5a3aa43060efe649ec564ebedec8ce6ae0a43654b81376216d5ebd42"},
{file = "orjson-3.10.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:cf20465e74c6e17a104ecf01bf8cd3b7b252565b4ccee4548f18b012ff2f8069"},
{file = "orjson-3.10.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ba7f67aa7f983c4345eeda16054a4677289011a478ca947cd69c0a86ea45e534"},
{file = "orjson-3.10.3-cp312-none-win32.whl", hash = "sha256:17e0713fc159abc261eea0f4feda611d32eabc35708b74bef6ad44f6c78d5ea0"},
{file = "orjson-3.10.3-cp312-none-win_amd64.whl", hash = "sha256:4c895383b1ec42b017dd2c75ae8a5b862fc489006afde06f14afbdd0309b2af0"},
{file = "orjson-3.10.3-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:be2719e5041e9fb76c8c2c06b9600fe8e8584e6980061ff88dcbc2691a16d20d"},
{file = "orjson-3.10.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0175a5798bdc878956099f5c54b9837cb62cfbf5d0b86ba6d77e43861bcec2"},
{file = "orjson-3.10.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:978be58a68ade24f1af7758626806e13cff7748a677faf95fbb298359aa1e20d"},
{file = "orjson-3.10.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16bda83b5c61586f6f788333d3cf3ed19015e3b9019188c56983b5a299210eb5"},
{file = "orjson-3.10.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ad1f26bea425041e0a1adad34630c4825a9e3adec49079b1fb6ac8d36f8b754"},
{file = "orjson-3.10.3-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:9e253498bee561fe85d6325ba55ff2ff08fb5e7184cd6a4d7754133bd19c9195"},
{file = "orjson-3.10.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0a62f9968bab8a676a164263e485f30a0b748255ee2f4ae49a0224be95f4532b"},
{file = "orjson-3.10.3-cp38-none-win32.whl", hash = "sha256:8d0b84403d287d4bfa9bf7d1dc298d5c1c5d9f444f3737929a66f2fe4fb8f134"},
{file = "orjson-3.10.3-cp38-none-win_amd64.whl", hash = "sha256:8bc7a4df90da5d535e18157220d7915780d07198b54f4de0110eca6b6c11e290"},
{file = "orjson-3.10.3-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9059d15c30e675a58fdcd6f95465c1522b8426e092de9fff20edebfdc15e1cb0"},
{file = "orjson-3.10.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d40c7f7938c9c2b934b297412c067936d0b54e4b8ab916fd1a9eb8f54c02294"},
{file = "orjson-3.10.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d4a654ec1de8fdaae1d80d55cee65893cb06494e124681ab335218be6a0691e7"},
{file = "orjson-3.10.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:831c6ef73f9aa53c5f40ae8f949ff7681b38eaddb6904aab89dca4d85099cb78"},
{file = "orjson-3.10.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99b880d7e34542db89f48d14ddecbd26f06838b12427d5a25d71baceb5ba119d"},
{file = "orjson-3.10.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2e5e176c994ce4bd434d7aafb9ecc893c15f347d3d2bbd8e7ce0b63071c52e25"},
{file = "orjson-3.10.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b69a58a37dab856491bf2d3bbf259775fdce262b727f96aafbda359cb1d114d8"},
{file = "orjson-3.10.3-cp39-none-win32.whl", hash = "sha256:b8d4d1a6868cde356f1402c8faeb50d62cee765a1f7ffcfd6de732ab0581e063"},
{file = "orjson-3.10.3-cp39-none-win_amd64.whl", hash = "sha256:5102f50c5fc46d94f2033fe00d392588564378260d64377aec702f21a7a22912"},
{file = "orjson-3.10.3.tar.gz", hash = "sha256:2b166507acae7ba2f7c315dcf185a9111ad5e992ac81f2d507aac39193c2c818"},
{file = "orjson-3.10.5-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:545d493c1f560d5ccfc134803ceb8955a14c3fcb47bbb4b2fee0232646d0b932"},
{file = "orjson-3.10.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4324929c2dd917598212bfd554757feca3e5e0fa60da08be11b4aa8b90013c1"},
{file = "orjson-3.10.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8c13ca5e2ddded0ce6a927ea5a9f27cae77eee4c75547b4297252cb20c4d30e6"},
{file = "orjson-3.10.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b6c8e30adfa52c025f042a87f450a6b9ea29649d828e0fec4858ed5e6caecf63"},
{file = "orjson-3.10.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:338fd4f071b242f26e9ca802f443edc588fa4ab60bfa81f38beaedf42eda226c"},
{file = "orjson-3.10.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6970ed7a3126cfed873c5d21ece1cd5d6f83ca6c9afb71bbae21a0b034588d96"},
{file = "orjson-3.10.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:235dadefb793ad12f7fa11e98a480db1f7c6469ff9e3da5e73c7809c700d746b"},
{file = "orjson-3.10.5-cp310-none-win32.whl", hash = "sha256:be79e2393679eda6a590638abda16d167754393f5d0850dcbca2d0c3735cebe2"},
{file = "orjson-3.10.5-cp310-none-win_amd64.whl", hash = "sha256:c4a65310ccb5c9910c47b078ba78e2787cb3878cdded1702ac3d0da71ddc5228"},
{file = "orjson-3.10.5-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:cdf7365063e80899ae3a697def1277c17a7df7ccfc979990a403dfe77bb54d40"},
{file = "orjson-3.10.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b68742c469745d0e6ca5724506858f75e2f1e5b59a4315861f9e2b1df77775a"},
{file = "orjson-3.10.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7d10cc1b594951522e35a3463da19e899abe6ca95f3c84c69e9e901e0bd93d38"},
{file = "orjson-3.10.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dcbe82b35d1ac43b0d84072408330fd3295c2896973112d495e7234f7e3da2e1"},
{file = "orjson-3.10.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10c0eb7e0c75e1e486c7563fe231b40fdd658a035ae125c6ba651ca3b07936f5"},
{file = "orjson-3.10.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:53ed1c879b10de56f35daf06dbc4a0d9a5db98f6ee853c2dbd3ee9d13e6f302f"},
{file = "orjson-3.10.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:099e81a5975237fda3100f918839af95f42f981447ba8f47adb7b6a3cdb078fa"},
{file = "orjson-3.10.5-cp311-none-win32.whl", hash = "sha256:1146bf85ea37ac421594107195db8bc77104f74bc83e8ee21a2e58596bfb2f04"},
{file = "orjson-3.10.5-cp311-none-win_amd64.whl", hash = "sha256:36a10f43c5f3a55c2f680efe07aa93ef4a342d2960dd2b1b7ea2dd764fe4a37c"},
{file = "orjson-3.10.5-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:68f85ecae7af14a585a563ac741b0547a3f291de81cd1e20903e79f25170458f"},
{file = "orjson-3.10.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28afa96f496474ce60d3340fe8d9a263aa93ea01201cd2bad844c45cd21f5268"},
{file = "orjson-3.10.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9cd684927af3e11b6e754df80b9ffafd9fb6adcaa9d3e8fdd5891be5a5cad51e"},
{file = "orjson-3.10.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d21b9983da032505f7050795e98b5d9eee0df903258951566ecc358f6696969"},
{file = "orjson-3.10.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ad1de7fef79736dde8c3554e75361ec351158a906d747bd901a52a5c9c8d24b"},
{file = "orjson-3.10.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2d97531cdfe9bdd76d492e69800afd97e5930cb0da6a825646667b2c6c6c0211"},
{file = "orjson-3.10.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d69858c32f09c3e1ce44b617b3ebba1aba030e777000ebdf72b0d8e365d0b2b3"},
{file = "orjson-3.10.5-cp312-none-win32.whl", hash = "sha256:64c9cc089f127e5875901ac05e5c25aa13cfa5dbbbd9602bda51e5c611d6e3e2"},
{file = "orjson-3.10.5-cp312-none-win_amd64.whl", hash = "sha256:b2efbd67feff8c1f7728937c0d7f6ca8c25ec81373dc8db4ef394c1d93d13dc5"},
{file = "orjson-3.10.5-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:03b565c3b93f5d6e001db48b747d31ea3819b89abf041ee10ac6988886d18e01"},
{file = "orjson-3.10.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:584c902ec19ab7928fd5add1783c909094cc53f31ac7acfada817b0847975f26"},
{file = "orjson-3.10.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5a35455cc0b0b3a1eaf67224035f5388591ec72b9b6136d66b49a553ce9eb1e6"},
{file = "orjson-3.10.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1670fe88b116c2745a3a30b0f099b699a02bb3482c2591514baf5433819e4f4d"},
{file = "orjson-3.10.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:185c394ef45b18b9a7d8e8f333606e2e8194a50c6e3c664215aae8cf42c5385e"},
{file = "orjson-3.10.5-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:ca0b3a94ac8d3886c9581b9f9de3ce858263865fdaa383fbc31c310b9eac07c9"},
{file = "orjson-3.10.5-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:dfc91d4720d48e2a709e9c368d5125b4b5899dced34b5400c3837dadc7d6271b"},
{file = "orjson-3.10.5-cp38-none-win32.whl", hash = "sha256:c05f16701ab2a4ca146d0bca950af254cb7c02f3c01fca8efbbad82d23b3d9d4"},
{file = "orjson-3.10.5-cp38-none-win_amd64.whl", hash = "sha256:8a11d459338f96a9aa7f232ba95679fc0c7cedbd1b990d736467894210205c09"},
{file = "orjson-3.10.5-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:85c89131d7b3218db1b24c4abecea92fd6c7f9fab87441cfc342d3acc725d807"},
{file = "orjson-3.10.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb66215277a230c456f9038d5e2d84778141643207f85336ef8d2a9da26bd7ca"},
{file = "orjson-3.10.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:51bbcdea96cdefa4a9b4461e690c75ad4e33796530d182bdd5c38980202c134a"},
{file = "orjson-3.10.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dbead71dbe65f959b7bd8cf91e0e11d5338033eba34c114f69078d59827ee139"},
{file = "orjson-3.10.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5df58d206e78c40da118a8c14fc189207fffdcb1f21b3b4c9c0c18e839b5a214"},
{file = "orjson-3.10.5-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:c4057c3b511bb8aef605616bd3f1f002a697c7e4da6adf095ca5b84c0fd43595"},
{file = "orjson-3.10.5-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b39e006b00c57125ab974362e740c14a0c6a66ff695bff44615dcf4a70ce2b86"},
{file = "orjson-3.10.5-cp39-none-win32.whl", hash = "sha256:eded5138cc565a9d618e111c6d5c2547bbdd951114eb822f7f6309e04db0fb47"},
{file = "orjson-3.10.5-cp39-none-win_amd64.whl", hash = "sha256:cc28e90a7cae7fcba2493953cff61da5a52950e78dc2dacfe931a317ee3d8de7"},
{file = "orjson-3.10.5.tar.gz", hash = "sha256:7a5baef8a4284405d96c90c7c62b755e9ef1ada84c2406c24a9ebec86b89f46d"},
]
[[package]]
@@ -1114,8 +1098,8 @@ files = [
[package.dependencies]
numpy = [
{version = ">=1.20.3", markers = "python_version < \"3.10\""},
{version = ">=1.23.2", markers = "python_version >= \"3.11\""},
{version = ">=1.21.0", markers = "python_version >= \"3.10\" and python_version < \"3.11\""},
{version = ">=1.23.2", markers = "python_version >= \"3.11\""},
]
python-dateutil = ">=2.8.2"
pytz = ">=2020.1"
@@ -1144,22 +1128,6 @@ sql-other = ["SQLAlchemy (>=1.4.16)"]
test = ["hypothesis (>=6.34.2)", "pytest (>=7.3.2)", "pytest-asyncio (>=0.17.0)", "pytest-xdist (>=2.2.0)"]
xml = ["lxml (>=4.6.3)"]
[[package]]
name = "platformdirs"
version = "4.2.2"
description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`."
optional = false
python-versions = ">=3.8"
files = [
{file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"},
{file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"},
]
[package.extras]
docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"]
test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"]
type = ["mypy (>=1.8)"]
[[package]]
name = "posthog"
version = "3.5.0"
@@ -1185,13 +1153,13 @@ test = ["coverage", "flake8", "freezegun (==0.3.15)", "mock (>=2.0.0)", "pylint"
[[package]]
name = "pydantic"
version = "2.7.3"
version = "2.7.4"
description = "Data validation using Python type hints"
optional = false
python-versions = ">=3.8"
files = [
{file = "pydantic-2.7.3-py3-none-any.whl", hash = "sha256:ea91b002777bf643bb20dd717c028ec43216b24a6001a280f83877fd2655d0b4"},
{file = "pydantic-2.7.3.tar.gz", hash = "sha256:c46c76a40bb1296728d7a8b99aa73dd70a48c3510111ff290034f860c99c419e"},
{file = "pydantic-2.7.4-py3-none-any.whl", hash = "sha256:ee8538d41ccb9c0a9ad3e0e5f07bf15ed8015b481ced539a1759d8cc89ae90d0"},
{file = "pydantic-2.7.4.tar.gz", hash = "sha256:0c84efd9548d545f63ac0060c1e4d39bb9b14db8b3c0652338aecc07b5adec52"},
]
[package.dependencies]
@@ -1458,36 +1426,6 @@ urllib3 = ">=1.21.1,<3"
socks = ["PySocks (>=1.5.6,!=1.5.7)"]
use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
[[package]]
name = "requests-cache"
version = "1.2.0"
description = "A persistent cache for python requests"
optional = false
python-versions = ">=3.8"
files = [
{file = "requests_cache-1.2.0-py3-none-any.whl", hash = "sha256:490324301bf0cb924ff4e6324bd2613453e7e1f847353928b08adb0fdfb7f722"},
{file = "requests_cache-1.2.0.tar.gz", hash = "sha256:db1c709ca343cc1cd5b6c8b1a5387298eceed02306a6040760db538c885e3838"},
]
[package.dependencies]
attrs = ">=21.2"
cattrs = ">=22.2"
platformdirs = ">=2.5"
requests = ">=2.22"
url-normalize = ">=1.4"
urllib3 = ">=1.25.5"
[package.extras]
all = ["boto3 (>=1.15)", "botocore (>=1.18)", "itsdangerous (>=2.0)", "pymongo (>=3)", "pyyaml (>=6.0.1)", "redis (>=3)", "ujson (>=5.4)"]
bson = ["bson (>=0.5)"]
docs = ["furo (>=2023.3,<2024.0)", "linkify-it-py (>=2.0,<3.0)", "myst-parser (>=1.0,<2.0)", "sphinx (>=5.0.2,<6.0.0)", "sphinx-autodoc-typehints (>=1.19)", "sphinx-automodapi (>=0.14)", "sphinx-copybutton (>=0.5)", "sphinx-design (>=0.2)", "sphinx-notfound-page (>=0.8)", "sphinxcontrib-apidoc (>=0.3)", "sphinxext-opengraph (>=0.9)"]
dynamodb = ["boto3 (>=1.15)", "botocore (>=1.18)"]
json = ["ujson (>=5.4)"]
mongodb = ["pymongo (>=3)"]
redis = ["redis (>=3)"]
security = ["itsdangerous (>=2.0)"]
yaml = ["pyyaml (>=6.0.1)"]
[[package]]
name = "rich"
version = "13.7.1"
@@ -1509,28 +1447,28 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"]
[[package]]
name = "ruff"
version = "0.4.7"
version = "0.4.9"
description = "An extremely fast Python linter and code formatter, written in Rust."
optional = false
python-versions = ">=3.7"
files = [
{file = "ruff-0.4.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:e089371c67892a73b6bb1525608e89a2aca1b77b5440acf7a71dda5dac958f9e"},
{file = "ruff-0.4.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:10f973d521d910e5f9c72ab27e409e839089f955be8a4c8826601a6323a89753"},
{file = "ruff-0.4.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59c3d110970001dfa494bcd95478e62286c751126dfb15c3c46e7915fc49694f"},
{file = "ruff-0.4.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa9773c6c00f4958f73b317bc0fd125295110c3776089f6ef318f4b775f0abe4"},
{file = "ruff-0.4.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07fc80bbb61e42b3b23b10fda6a2a0f5a067f810180a3760c5ef1b456c21b9db"},
{file = "ruff-0.4.7-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:fa4dafe3fe66d90e2e2b63fa1591dd6e3f090ca2128daa0be33db894e6c18648"},
{file = "ruff-0.4.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a7c0083febdec17571455903b184a10026603a1de078428ba155e7ce9358c5f6"},
{file = "ruff-0.4.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ad1b20e66a44057c326168437d680a2166c177c939346b19c0d6b08a62a37589"},
{file = "ruff-0.4.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cbf5d818553add7511c38b05532d94a407f499d1a76ebb0cad0374e32bc67202"},
{file = "ruff-0.4.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:50e9651578b629baec3d1513b2534de0ac7ed7753e1382272b8d609997e27e83"},
{file = "ruff-0.4.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8874a9df7766cb956b218a0a239e0a5d23d9e843e4da1e113ae1d27ee420877a"},
{file = "ruff-0.4.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:b9de9a6e49f7d529decd09381c0860c3f82fa0b0ea00ea78409b785d2308a567"},
{file = "ruff-0.4.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:13a1768b0691619822ae6d446132dbdfd568b700ecd3652b20d4e8bc1e498f78"},
{file = "ruff-0.4.7-py3-none-win32.whl", hash = "sha256:769e5a51df61e07e887b81e6f039e7ed3573316ab7dd9f635c5afaa310e4030e"},
{file = "ruff-0.4.7-py3-none-win_amd64.whl", hash = "sha256:9e3ab684ad403a9ed1226894c32c3ab9c2e0718440f6f50c7c5829932bc9e054"},
{file = "ruff-0.4.7-py3-none-win_arm64.whl", hash = "sha256:10f2204b9a613988e3484194c2c9e96a22079206b22b787605c255f130db5ed7"},
{file = "ruff-0.4.7.tar.gz", hash = "sha256:2331d2b051dc77a289a653fcc6a42cce357087c5975738157cd966590b18b5e1"},
{file = "ruff-0.4.9-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b262ed08d036ebe162123170b35703aaf9daffecb698cd367a8d585157732991"},
{file = "ruff-0.4.9-py3-none-macosx_11_0_arm64.whl", hash = "sha256:98ec2775fd2d856dc405635e5ee4ff177920f2141b8e2d9eb5bd6efd50e80317"},
{file = "ruff-0.4.9-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4555056049d46d8a381f746680db1c46e67ac3b00d714606304077682832998e"},
{file = "ruff-0.4.9-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e91175fbe48f8a2174c9aad70438fe9cb0a5732c4159b2a10a3565fea2d94cde"},
{file = "ruff-0.4.9-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e8e7b95673f22e0efd3571fb5b0cf71a5eaaa3cc8a776584f3b2cc878e46bff"},
{file = "ruff-0.4.9-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:2d45ddc6d82e1190ea737341326ecbc9a61447ba331b0a8962869fcada758505"},
{file = "ruff-0.4.9-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:78de3fdb95c4af084087628132336772b1c5044f6e710739d440fc0bccf4d321"},
{file = "ruff-0.4.9-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:06b60f91bfa5514bb689b500a25ba48e897d18fea14dce14b48a0c40d1635893"},
{file = "ruff-0.4.9-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88bffe9c6a454bf8529f9ab9091c99490578a593cc9f9822b7fc065ee0712a06"},
{file = "ruff-0.4.9-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:673bddb893f21ab47a8334c8e0ea7fd6598ecc8e698da75bcd12a7b9d0a3206e"},
{file = "ruff-0.4.9-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8c1aff58c31948cc66d0b22951aa19edb5af0a3af40c936340cd32a8b1ab7438"},
{file = "ruff-0.4.9-py3-none-musllinux_1_2_i686.whl", hash = "sha256:784d3ec9bd6493c3b720a0b76f741e6c2d7d44f6b2be87f5eef1ae8cc1d54c84"},
{file = "ruff-0.4.9-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:732dd550bfa5d85af8c3c6cbc47ba5b67c6aed8a89e2f011b908fc88f87649db"},
{file = "ruff-0.4.9-py3-none-win32.whl", hash = "sha256:8064590fd1a50dcf4909c268b0e7c2498253273309ad3d97e4a752bb9df4f521"},
{file = "ruff-0.4.9-py3-none-win_amd64.whl", hash = "sha256:e0a22c4157e53d006530c902107c7f550b9233e9706313ab57b892d7197d8e52"},
{file = "ruff-0.4.9-py3-none-win_arm64.whl", hash = "sha256:5d5460f789ccf4efd43f265a58538a2c24dbce15dbf560676e430375f20a8198"},
{file = "ruff-0.4.9.tar.gz", hash = "sha256:f1cb0828ac9533ba0135d148d214e284711ede33640465e706772645483427e3"},
]
[[package]]
@@ -1603,13 +1541,13 @@ typing-extensions = ">=3.7.4.3"
[[package]]
name = "typing-extensions"
version = "4.12.1"
version = "4.12.2"
description = "Backported and Experimental Type Hints for Python 3.8+"
optional = false
python-versions = ">=3.8"
files = [
{file = "typing_extensions-4.12.1-py3-none-any.whl", hash = "sha256:6024b58b69089e5a89c347397254e35f1bf02a907728ec7fee9bf0fe837d203a"},
{file = "typing_extensions-4.12.1.tar.gz", hash = "sha256:915f5e35ff76f56588223f15fdd5938f9a1cf9195c0de25130c627e4d597f6d1"},
{file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"},
{file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"},
]
[[package]]
@@ -1710,29 +1648,15 @@ files = [
{file = "ujson-5.10.0.tar.gz", hash = "sha256:b3cd8f3c5d8c7738257f1018880444f7b7d9b66232c64649f562d7ba86ad4bc1"},
]
[[package]]
name = "url-normalize"
version = "1.4.3"
description = "URL normalization for Python"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
files = [
{file = "url-normalize-1.4.3.tar.gz", hash = "sha256:d23d3a070ac52a67b83a1c59a0e68f8608d1cd538783b401bc9de2c0fac999b2"},
{file = "url_normalize-1.4.3-py2.py3-none-any.whl", hash = "sha256:ec3c301f04e5bb676d333a7fa162fa977ad2ca04b7e652bfc9fac4e405728eed"},
]
[package.dependencies]
six = "*"
[[package]]
name = "urllib3"
version = "2.2.1"
version = "2.2.2"
description = "HTTP library with thread-safe connection pooling, file post, and more."
optional = false
python-versions = ">=3.8"
files = [
{file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"},
{file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"},
{file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"},
{file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"},
]
[package.extras]
@@ -2122,4 +2046,4 @@ test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools",
[metadata]
lock-version = "2.0"
python-versions = "^3.8"
content-hash = "b43271976f223a951406fc8e66b4b8203bad9390dd44856a1c1009ad803958cc"
content-hash = "c60f83ca7ea8f013136da295af84119795cfabf177acaf348eec39cad38b35a2"

View File

@@ -10,7 +10,6 @@ packages = [{ include = "openbb_biztoc" }]
[tool.poetry.dependencies]
python = "^3.8"
openbb-core = "^1.2.5"
requests-cache = "^1.1.0"
[build-system]
requires = ["poetry-core"]

View File

@@ -4,14 +4,16 @@ import pytest
from openbb_biztoc.models.world_news import BiztocWorldNewsFetcher
from openbb_core.app.service.user_service import UserService
test_credentials = UserService().default_user_settings.credentials.dict()
test_credentials = UserService().default_user_settings.credentials.model_dump(
mode="json"
)
@pytest.fixture(scope="module")
def vcr_config():
"""VCR configuration."""
return {
"filter_headers": [("User-Agent", None), ("X-RapidAPI-Key", "MOCK_API_KEY")],
"filter_headers": [("X-RapidAPI-Key", "MOCK_API_KEY")],
"filter_query_parameters": [
("apikey", "MOCK_API_KEY"),
],
@@ -21,7 +23,7 @@ def vcr_config():
@pytest.mark.record_http
def test_biztoc_world_news_fetcher(credentials=test_credentials):
"""Test the Biztoc World News fetcher."""
params = {"term": "earnings"}
params = {"source": "bloomberg"}
fetcher = BiztocWorldNewsFetcher()
result = fetcher.test(params, credentials)

View File

@@ -8,6 +8,7 @@ from openbb_nasdaq.models.cot import NasdaqCotFetcher
from openbb_nasdaq.models.cot_search import NasdaqCotSearchFetcher
from openbb_nasdaq.models.economic_calendar import NasdaqEconomicCalendarFetcher
from openbb_nasdaq.models.equity_search import NasdaqEquitySearchFetcher
from openbb_nasdaq.models.equity_screener import NasdaqEquityScreenerFetcher
from openbb_nasdaq.models.historical_dividends import NasdaqHistoricalDividendsFetcher
from openbb_nasdaq.models.lbma_fixing import NasdaqLbmaFixingFetcher
from openbb_nasdaq.models.sp500_multiples import NasdaqSP500MultiplesFetcher
@@ -28,6 +29,7 @@ unmatched technology, insights and markets expertise.""",
"COTSearch": NasdaqCotSearchFetcher,
"EconomicCalendar": NasdaqEconomicCalendarFetcher,
"EquitySearch": NasdaqEquitySearchFetcher,
"EquityScreener": NasdaqEquityScreenerFetcher,
"HistoricalDividends": NasdaqHistoricalDividendsFetcher,
"LbmaFixing": NasdaqLbmaFixingFetcher,
"SP500Multiples": NasdaqSP500MultiplesFetcher,

View File

@@ -0,0 +1,490 @@
"""Nasdaq Equity Screener Model."""
# pylint: disable=unused-argument
from typing import Any, Dict, List, Literal, Optional, Union
from warnings import warn
from openbb_core.provider.abstract.fetcher import Fetcher
from openbb_core.provider.standard_models.equity_screener import (
EquityScreenerData,
EquityScreenerQueryParams,
)
from openbb_core.provider.utils.errors import EmptyDataError
from openbb_core.provider.utils.helpers import get_querystring, make_request
from openbb_nasdaq.utils.helpers import HEADERS
from pydantic import Field, field_validator
EXCHANGE_CHOICES = ["all", "nasdaq", "nyse", "amex"]
EXSUBCATEGORY_CHOICES = ["all", "ngs", "ngm", "ncm", "adr"]
MKT_CAP_CHOICES = ["all", "mega", "large", "mid", "small", "micro"]
RECOMMENDATION_CHOICES = ["all", "strong_buy", "buy", "hold", "sell", "strong_sell"]
SECTOR_CHOICES = [
"all",
"energy",
"basic_materials",
"industrials",
"consumer_staples",
"consumer_discretionary",
"health_care",
"financial_services",
"technology",
"communication_services",
"utilities",
"real_estate",
]
REGION_CHOICES = [
"all",
"africa",
"asia",
"australia_and_south_pacific",
"caribbean",
"europe",
"middle_east",
"north_america",
"south_america",
]
COUNTRY_CHOICES = [
"all",
"argentina",
"armenia",
"australia",
"austria",
"belgium",
"bermuda",
"brazil",
"canada",
"cayman_islands",
"chile",
"colombia",
"costa_rica",
"curacao",
"cyprus",
"denmark",
"finland",
"france",
"germany",
"greece",
"guernsey",
"hong_kong",
"india",
"indonesia",
"ireland",
"isle_of_man",
"israel",
"italy",
"japan",
"jersey",
"luxembourg",
"macau",
"mexico",
"monaco",
"netherlands",
"norway",
"panama",
"peru",
"philippines",
"puerto_rico",
"russia",
"singapore",
"south_africa",
"south_korea",
"spain",
"sweden",
"switzerland",
"taiwan",
"turkey",
"united_kingdom",
"united_states",
"usa",
]
class NasdaqEquityScreenerQueryParams(EquityScreenerQueryParams):
"""Nasdaq Equity Screener Query Params."""
__alias_dict__ = {
"mktcap": "marketcap",
}
__json_schema_extra__ = {
"exchange": {"multiple_items_allowed": True},
"exsubcategory": {"multiple_items_allowed": True},
"mktcap": {"multiple_items_allowed": True},
"recommendation": {"multiple_items_allowed": True},
"sector": {"multiple_items_allowed": True},
"region": {"multiple_items_allowed": True},
"country": {"multiple_items_allowed": True},
}
exchange: Union[Literal["all", "nasdaq", "nyse", "amex"], str] = Field(
default="all",
description="Filter by exchange.",
json_schema_extra={"choices": EXCHANGE_CHOICES},
)
exsubcategory: Union[Literal["all", "ngs", "ngm", "ncm", "adr"], str] = Field(
default="all",
description="Filter by exchange subcategory."
"\n NGS - Nasdaq Global Select Market"
"\n NGM - Nasdaq Global Market"
"\n NCM - Nasdaq Capital Market"
"\n ADR - American Depository Receipt\n",
json_schema_extra={"choices": EXSUBCATEGORY_CHOICES},
)
mktcap: Union[Literal["all", "mega", "large", "mid", "small", "micro"], str] = (
Field(
default="all",
description="Filter by market cap."
"\n Mega - > 200B"
"\n Large - 10B - 200B"
"\n Mid - 2B - 10B"
"\n Small - 300M - 2B"
"\n Micro - 50M - 300M\n",
json_schema_extra={"choices": MKT_CAP_CHOICES},
)
)
recommendation: Union[
Literal["all", "strong_buy", "buy", "hold", "sell", "strong_sell"], str
] = Field(
default="all",
description="Filter by consensus analyst action.",
json_schema_extra={"choices": RECOMMENDATION_CHOICES},
)
sector: Union[
Literal[
"all",
"energy",
"basic_materials",
"industrials",
"consumer_staples",
"consumer_discretionary",
"health_care",
"financial_services",
"technology",
"communication_services",
"utilities",
"real_estate",
],
str,
] = Field(
default="all",
description="Filter by sector.",
json_schema_extra={"choices": SECTOR_CHOICES},
)
region: Union[
Literal[
"all",
"africa",
"asia",
"australia_and_south_pacific",
"caribbean",
"europe",
"middle_east",
"north_america",
"south_america",
],
str,
] = Field(
default="all",
description="Filter by region.",
json_schema_extra={"choices": REGION_CHOICES},
)
country: Union[
Literal[
"all",
"argentina",
"armenia",
"australia",
"austria",
"belgium",
"bermuda",
"brazil",
"canada",
"cayman_islands",
"chile",
"colombia",
"costa_rica",
"curacao",
"cyprus",
"denmark",
"finland",
"france",
"germany",
"greece",
"guernsey",
"hong_kong",
"india",
"indonesia",
"ireland",
"isle_of_man",
"israel",
"italy",
"japan",
"jersey",
"luxembourg",
"macau",
"mexico",
"monaco",
"netherlands",
"norway",
"panama",
"peru",
"philippines",
"puerto_rico",
"russia",
"singapore",
"south_africa",
"south_korea",
"spain",
"sweden",
"switzerland",
"taiwan",
"turkey",
"united_kingdom",
"united_states",
"usa",
],
str,
] = Field(
default="all",
description="Filter by country.",
json_schema_extra={"choices": COUNTRY_CHOICES},
)
limit: Optional[int] = Field(
default=None,
description="Limit the number of results to return.",
)
@field_validator("exchange", mode="before", check_fields=False)
@classmethod
def validate_exchange(cls, v):
"""Validate exchange."""
v = v.split(",")
new_items = []
for item in v:
if item == "all":
continue
if item in EXCHANGE_CHOICES:
new_items.append(item)
else:
warn(f"Invalid exchange: {item}")
return ",".join(new_items) if new_items else "all"
@field_validator("exsubcategory", mode="before", check_fields=False)
@classmethod
def validate_exsubcategory(cls, v):
"""Validate exsubcategory."""
v = v.split(",")
new_items = []
for item in v:
if item == "all":
continue
if item in EXSUBCATEGORY_CHOICES:
new_items.append(item)
else:
warn(f"Invalid exsubcategory: {item}")
return ",".join(new_items) if new_items else "all"
@field_validator("mktcap", mode="before", check_fields=False)
@classmethod
def validate_mktcap(cls, v):
"""Validate market cap."""
v = v.split(",")
new_items = []
for item in v:
if item == "all":
continue
if item in MKT_CAP_CHOICES:
new_items.append(item)
else:
warn(f"Invalid market cap: {item}")
return ",".join(new_items) if new_items else "all"
@field_validator("recommendation", mode="before", check_fields=False)
@classmethod
def validate_recommendation(cls, v):
"""Validate recommendation."""
v = v.split(",")
new_items = []
for item in v:
if item == "all":
continue
if item in RECOMMENDATION_CHOICES:
new_items.append(item)
else:
warn(f"Invalid recommendation: {item}")
return ",".join(new_items) if new_items else "all"
@field_validator("sector", mode="before", check_fields=False)
@classmethod
def validate_sector(cls, v):
"""Validate sector."""
v = v.split(",")
new_items = []
for item in v:
if item == "all":
continue
if item in SECTOR_CHOICES:
new_items.append(item)
else:
warn(f"Invalid sector: {item}")
return ",".join(new_items) if new_items else "all"
@field_validator("region", mode="before", check_fields=False)
@classmethod
def validate_region(cls, v):
"""Validate region."""
v = v.split(",")
new_items = []
for item in v:
if item == "all":
continue
if item in REGION_CHOICES:
new_items.append(item)
else:
warn(f"Invalid region: {item}")
return ",".join(new_items) if new_items else "all"
@field_validator("country", mode="before", check_fields=False)
@classmethod
def validate_country(cls, v):
"""Validate country."""
v = v.split(",")
new_items = []
for item in v:
if item == "all":
continue
if item in COUNTRY_CHOICES:
new_items.append(item)
else:
warn(f"Invalid country: {item}")
return ",".join(new_items) if new_items else "all"
class NasdaqEquityScreenerData(EquityScreenerData):
"""Nasdaq Equity Screener Data."""
__alias_dict__ = {
"last_price": "lastsale",
"change": "netchange",
"change_percent": "pctchange",
"market_cap": "marketCap",
}
last_price: float = Field(
description="Last sale price.",
json_schema_extra={"x-unit_measurement": "currency"},
)
change: Optional[float] = Field(
default=None,
description="1-day change in price.",
json_schema_extra={"x-unit_measurement": "currency"},
)
change_percent: Optional[float] = Field(
default=None,
description="1-day percent change in price.",
json_schema_extra={"x-unit_measurement": "percent", "x-frontend_multiply": 100},
)
market_cap: Optional[int] = Field(
default=None,
description="Market cap.",
json_schema_extra={"x-unit_measurement": "currency"},
)
@field_validator(
"last_price",
"change",
"change_percent",
"market_cap",
mode="before",
check_fields=False,
)
@classmethod
def validate_numbers(cls, v):
"""Validate numbers."""
if "%" in v:
v = v.replace("%", "")
return float(v) / 100
v = (
v.replace("$", "")
.replace(",", "")
.replace("UNCH", "")
.replace("--", "")
.replace("NA", "")
)
return v if v else None
class NasdaqEquityScreenerFetcher(
Fetcher[
NasdaqEquityScreenerQueryParams,
List[NasdaqEquityScreenerData],
]
):
"""Nasdaq Equity Screener Fetcher."""
require_credentials = False
@staticmethod
def transform_query(params: Dict[str, Any]) -> NasdaqEquityScreenerQueryParams:
"""Transform query."""
return NasdaqEquityScreenerQueryParams(**params)
@staticmethod
def extract_data(
query: NasdaqEquityScreenerQueryParams,
credentials: Optional[Dict[str, str]],
**kwargs: Any,
) -> Dict:
"""Extract data from the Nasdaq Equity Screener."""
base_url = f"https://api.nasdaq.com/api/screener/stocks?tableonly=true&limit={query.limit if query.limit else 10000}&"
exchange = query.exchange.split(",")
exsubcategory = query.exsubcategory.split(",")
marketcap = query.mktcap.split(",")
recommendation = query.recommendation.split(",")
sector = (
query.sector.replace("communications_services", "telecommunications")
.replace("financial_services", "finance")
.split(",")
)
region = query.region.split(",")
country = query.country.split(",")
params = dict(
exchange=None if "all" in exchange else "|".join(exchange).upper(),
exsubcategory=(
None if "all" in exsubcategory else "|".join(exsubcategory).upper()
),
marketcap=None if "all" in marketcap else "|".join(marketcap),
recommendation=(
None if "all" in recommendation else "|".join(recommendation)
),
sector=None if "all" in sector else "|".join(sector),
region=None if "all" in region else "|".join(region),
country=None if "all" in country else "|".join(country),
)
querystring = get_querystring(params, [])
querystring = "&" + querystring if querystring else ""
url = f"{base_url}{querystring}"
try:
response = make_request(url, headers=HEADERS)
except response.status_code != 200:
raise response.raise_for_status()
return response.json()
@staticmethod
def transform_data(
query: NasdaqEquityScreenerQueryParams,
data: Dict,
**kwargs: Any,
) -> List[NasdaqEquityScreenerData]:
"""Transform data."""
if not data:
raise EmptyDataError("The request was returned empty.")
rows = data.get("data", {}).get("table", {}).get("rows")
if not rows:
raise EmptyDataError("No results were found.")
results: NasdaqEquityScreenerData = []
for row in sorted(rows, key=lambda x: x["pctchange"], reverse=True):
row.pop("url", None)
results.append(NasdaqEquityScreenerData.model_validate(row))
return results

View File

@@ -0,0 +1,101 @@
interactions:
- request:
body: null
headers:
Accept:
- text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Encoding:
- gzip
Accept-Language:
- en-CA,en-US;q=0.7,en;q=0.3
Connection:
- keep-alive
Host:
- api.nasdaq.com
method: GET
uri: https://api.nasdaq.com/api/screener/stocks?limit=10000&marketcap=large&sector=consumer_staples&tableonly=true
response:
body:
string: !!binary |
H4sIAAAAAAAAAI1ZXXObSBb9K13Uzlamqk3oDxrIm75sJ7ZsV+QkM7u1Dy3UllgBTQFyxknlv+8F
2SvUzQg9uAQtZJ1z+9x7z239dFayls6Hn85TktaqrJwP+S5NsVPLZaqadVndP70tbpRctc/8dKqX
bKlT54Oz2F9gJ5cZfMC5a16wk8qqrmTzL5xbuESL5hoeUnW8kfm6fVLVaLK/wU4RH9747bCcyXKr
6oksYHneXqPm5hd2Sv0dgPy7g+Ry/scBxqXOVF5rNIt1rrMk1miu/kpimWu0cEfu2EUrhSbuVxdN
dJbpHC1qHW+Pgf+DeKFLuQH7wnM9YSBu1nz/NwMwCQQORISDyMOCEXh3VzZA3+8fupBxnTwn9cv7
qvnu6v1T9hcw6zAaf5keGI3yjdpVqrwY76p4gz7mS/WMFiO0KHRe6VKt0Gj6Gb0bq3Sd7LLfDSp+
5JIeJn4Pk4iFFhOP4oAFOOQCE8YHmSx3q2Mmi/u7Pw9UFjp/QVel3hUQ/bLQpawT2IJRpspmi9BU
FbpKalm+oMVGlqpCBpsgcgUz2RCXRyYb4oYsMNlEAY5Cjn1GsUe8QTIVoD1mM5/e/uvAZq7zlUrV
D9gSyKC85SJTDLcxqAtgV2h0SmVCuCQwyICeTC6eS5gwqYQhDvwAB1RgSocVlq3SH8dUJl8XByZw
g66VTOvN0b6cwk5cj/QkSGjLijBioof88CE3mOdjj9JB9PFzdQx+Ors/gJ8mcq00KtL4ZErTyGWR
iZi6zEIM0vGseAcUU0EwBemIYDjeK6UN5dwtHo+UU4Fk0Fg9qxLQd6Nu4Oahy6kd6BaDAZtzqxL5
HuYc/kIPh96w4jOAZeTvY0fwkxZ2mu7VMS5lvqre5P73kaeCucI3KBCXmQw8SOPIJMBDTDnBAaGN
WoZTtjZkfjN9OOC/UbsyWaNpiR5UUUD8h6Azvi8tRvCpZ6vcD6xyAzUz8Bn8wSsfxr5dFQb268kB
++NGoZtSPtWQqEn+o0FdSKimp9BTV5j1pUHv2+gD34o8ixqlY0IJFm3fG0C/iY/RX33sFJgrlYPQ
UzRP0vQMyUBlFMKqjCywFOP7VsuCbiUgS0PqYZ8P15Z1YtSWm88dwZR6DTJ5i/U72ITfT+H2iRuF
Fu6IGbihwlDag9snDDPGodMOV5hteQz7evHnsViuwbBt1Ms5QiEhdQOrrULAaU81D6wiwwSOQOes
zdNhpWwqo6s+zK4O0B92yzSJ0UKVz0ms0KxprUWZVOrVN4B0XmslGJ8TnALmUjN1QfsmIyDpWe0J
CPkex4wEmNNhQoVaG7318euoWzXLWj3LHtWb7gbkE/aUG9KTsDS0hQ8WTQjsgcMRfLjWx/WzNGBP
Zg9d2LG8mOhUotmu1IWMkyfYlwdZ1pDKVdtu78tVkh/Mmr0DxEwG6FK0x6sFzE4HhgWkMPHglQ+n
Qxwro3qOxrOvHROdta4ZJoBTbvPdTILDLlUBNzBFJPkakf/vWPOI6a7p3vF0KX65m1ybDC9McuCq
BZjRgIGjGCYnAbtBbjrvcCvjDYwHU5knKq0u5skqhc58TuLDcBD1uLi+DsGJ5YkodAjoDi2T1loN
sFhlRqXtdmZwFbl+lifRhuDMrJQWpoEjUKMsqI2SIOh+iNkZGb09Bvq4uOvU1heYC9Cl1ud4H993
idnImmD2BDj0rBZMIMAMEDdyIcOtrK5yw3TedAzEPIZCBAPxFv3zTRr47Goqwp65CzBbPCC/KbN4
hFBHwUoIhiM+PERmW8NK3NxfHtcl1NalS5VV0pzqT6Q3RspObw99yZMaUr99r7lGMdjcBLgj/YRY
04kSKA3jtxrRpJb/tnr7umrWhdB3uW1gAm51nzCwoxW07bSxvDwajtZWPxlO4PPtIVrXsOcq3cv1
3NmOvVot0w2Y9qvZbI9b8AUWPgzY4GP8M0S7gXePu9Ds9rqz21DRkl2FrnUKvWZ9jneExm/WZO6G
5nlHCAOInXAcnHqEGbTQ0B8OfazSjXFw03Ey4x18E7pNsqSbXH2dEkqTy/sOaUy9NGvUjjhEupmk
mwInhrvJ0nAr38Yds/JNputSqRwErzWkwChNE5nHajjuRLjE7CXgfc24C5fZBzPge8MICl0z5Z1R
nr8vTecyuuoWiHy0LuX582nYOyZ5pmtvnJhnn/VBpnrguvwIe+QMyUjTKz6MO9BlViyhFaLF/ohs
sIFzvo+wcYRklmVATiyXC8g5pTgKmyONM/xisTxGfvvtAPxWZkv0TcGj+alUNQ1vyPft2ziT4Vbo
YT6xk5ViSgMoNDCg+uEg/vS7cbLxqWOhmoHpkzt30SLbxdvOyHdK8IS6zDzX8PZHekbwfcveEoIj
GsKUyrDX0h041/iv4Z1Gk48dA5guVVmDLalecSeqOv8YkkT7CdWcOnqKj4jsE2ICxSfAAZT7gA7r
H8YIw1uNOjPHXKdV25s0TBidw7HXvWi5jE/6LTCvPUeqPVvCbD15sBU+5mDKxRlHTXXzc8R/fmGn
1rVMSwVGalU5H8CrObKCnvz6+wfMrjDGytZMfNrlCLwQoh5ECj6ZqaqSDaD9ryxVLetd+yNLOdEr
WKaeh51lcz0/fnIFoUl1ocqj9V+//geFI65S1hkAAA==
headers:
Cache-Control:
- max-age=0, no-cache, no-store
Connection:
- keep-alive
Content-Encoding:
- gzip
Content-Length:
- '1969'
Content-Type:
- application/json; charset=utf-8
Date:
- Wed, 19 Jun 2024 21:55:28 GMT
Expires:
- Wed, 19 Jun 2024 21:55:28 GMT
Pragma:
- no-cache
Server:
- Kestrel
Set-Cookie:
- akaalb_ALB_Default=~op=ao_api__east1:ao_api_east1|~rv=98~m=ao_api_east1:0|~os=ff51b6e767de05e2054c5c99e232919a~id=203108838c99ea4e479f8c21471f25fe;
path=/; HttpOnly; Secure; SameSite=None
- ak_bmsc=E6D38917F0ADD7E1FEC6DEB849E81462~000000000000000000000000000000~YAAQ2pUeuIHX0fGPAQAAWmZ/Mhgd4GwWL1iu1lcNC+ekK/2jK4SfejWlzn1pCiQrEx5PJBdlAGLH7NlNZy1GhbZeM8xXf2pzkRP0G8sxmeUHhvg8/38JieSuk/NMj4jUXV2LbthNx6fgo/vE4PLaqaZXDW+6kwELDdJSMfmx8A8njFSEJlNd+mtJEcmX+BmyA/RU+V4nfRXRRMUfGUOg2ydok/CurJkTqD7QejqHu5uCYmf+3Mho5oSoN9s4qsvQfCWx7lUhVw5Q+XwLBpm2m4HQvMzImK4ypPfA6rQnZWEUaqyQuxF4PMKk9rHxIthxOqbF2J3gofseH3amzYCYFQ8143QskbuHlbASaQ5shF6W372ZmUvVB9wrdsU2XxZf67bceEmboBGGV5L/h8UCQ5aiL5U4mYQ=;
Domain=.nasdaq.com; Path=/; Expires=Wed, 19 Jun 2024 23:55:28 GMT; Max-Age=7200;
HttpOnly
Strict-Transport-Security:
- max-age=86400
Vary:
- Accept-Encoding
X-EdgeConnect-MidMile-RTT:
- '5'
- '56'
X-EdgeConnect-Origin-MEX-Latency:
- '50'
- '50'
gid:
- '203'
lang:
- en
rid:
- 8b115157-6b55-4aa1-b3da-3aaaecba8876
srctype:
- default
status:
code: 200
message: OK
version: 1

View File

@@ -10,6 +10,7 @@ from openbb_nasdaq.models.calendar_ipo import NasdaqCalendarIpoFetcher
from openbb_nasdaq.models.cot import NasdaqCotFetcher
from openbb_nasdaq.models.cot_search import NasdaqCotSearchFetcher
from openbb_nasdaq.models.economic_calendar import NasdaqEconomicCalendarFetcher
from openbb_nasdaq.models.equity_screener import NasdaqEquityScreenerFetcher
from openbb_nasdaq.models.equity_search import NasdaqEquitySearchFetcher
from openbb_nasdaq.models.historical_dividends import NasdaqHistoricalDividendsFetcher
from openbb_nasdaq.models.lbma_fixing import NasdaqLbmaFixingFetcher
@@ -157,3 +158,13 @@ def test_nasdaq_lbma_fixing_fetcher(credentials=test_credentials):
fetcher = NasdaqLbmaFixingFetcher()
result = fetcher.test(params, credentials)
assert result is None
@pytest.mark.record_http
def test_nasdaq_equity_screener_fetcher(credentials=test_credentials):
"""Test the Nasdaq Equity Screener fetcher."""
params = {"mktcap": "large", "sector": "consumer_staples"}
fetcher = NasdaqEquityScreenerFetcher()
result = fetcher.test(params, credentials)
assert result is None