summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDanglewood <85772166+deeleeramone@users.noreply.github.com>2024-02-05 11:53:17 -0800
committerGitHub <noreply@github.com>2024-02-05 19:53:17 +0000
commit7c953d5964b1a5b5427dc2b2d188ccaed66acbdb (patch)
tree2e82be46fd095cf790ba88d3f541e1a31ac9e524
parent16f2bec65f15e38bffcf910d0c9686343a3d6c96 (diff)
[BugFix] - Adds a timezone to the Unix timestamp conversion to correct localization errors. (#6023)
* fix: remove specific imports * make polygon historical tz-aware * undo including that commit * that PR wasn't supposed to be there. * pylint unused argument * fix polygon crypto historical intervals * pylint * new cassette * fix the rest of the polygon historical price query params * black * pylint.. * integration test param * appease daylight savings handling * paginate for crypto currency and index * black * raise EmptyDataError when no results instead of returning an empty list * ruff --------- Co-authored-by: Diogo Sousa <montezdesousa@gmail.com>
-rw-r--r--openbb_platform/extensions/crypto/integration/test_crypto_api.py8
-rw-r--r--openbb_platform/extensions/crypto/integration/test_crypto_python.py8
-rw-r--r--openbb_platform/extensions/currency/integration/test_currency_api.py8
-rw-r--r--openbb_platform/extensions/currency/integration/test_currency_python.py8
-rw-r--r--openbb_platform/extensions/index/integration/test_index_api.py10
-rw-r--r--openbb_platform/extensions/index/integration/test_index_python.py10
-rw-r--r--openbb_platform/providers/polygon/openbb_polygon/models/crypto_historical.py110
-rw-r--r--openbb_platform/providers/polygon/openbb_polygon/models/currency_historical.py110
-rw-r--r--openbb_platform/providers/polygon/openbb_polygon/models/equity_historical.py18
-rw-r--r--openbb_platform/providers/polygon/openbb_polygon/models/index_historical.py117
-rw-r--r--openbb_platform/providers/polygon/openbb_polygon/models/market_snapshots.py30
-rw-r--r--openbb_platform/providers/polygon/tests/record/http/test_polygon_fetchers/test_polygon_crypto_historical_fetcher.yaml30
-rw-r--r--openbb_platform/providers/polygon/tests/record/http/test_polygon_fetchers/test_polygon_currency_historical_fetcher.yaml26
-rw-r--r--openbb_platform/providers/polygon/tests/record/http/test_polygon_fetchers/test_polygon_index_historical_fetcher.yaml88
14 files changed, 383 insertions, 198 deletions
diff --git a/openbb_platform/extensions/crypto/integration/test_crypto_api.py b/openbb_platform/extensions/crypto/integration/test_crypto_api.py
index 111f9d08a0b..97ff10da864 100644
--- a/openbb_platform/extensions/crypto/integration/test_crypto_api.py
+++ b/openbb_platform/extensions/crypto/integration/test_crypto_api.py
@@ -71,11 +71,9 @@ def test_crypto_search(params, headers):
),
(
{
- "multiplier": 1,
- "timespan": "minute",
+ "interval": "1m",
"sort": "desc",
"limit": 49999,
- "adjusted": True,
"provider": "polygon",
"symbol": "BTCUSD",
"start_date": "2023-01-01",
@@ -84,11 +82,9 @@ def test_crypto_search(params, headers):
),
(
{
- "multiplier": 1,
- "timespan": "day",
+ "interval": "1d",
"sort": "desc",
"limit": 49999,
- "adjusted": True,
"provider": "polygon",
"symbol": "BTCUSD",
"start_date": "2023-01-01",
diff --git a/openbb_platform/extensions/crypto/integration/test_crypto_python.py b/openbb_platform/extensions/crypto/integration/test_crypto_python.py
index 5a46fa9e988..e8d3ba097a5 100644
--- a/openbb_platform/extensions/crypto/integration/test_crypto_python.py
+++ b/openbb_platform/extensions/crypto/integration/test_crypto_python.py
@@ -65,11 +65,9 @@ def test_crypto_search(params, obb):
),
(
{
- "multiplier": 1,
- "timespan": "minute",
+ "interval": "1m",
"sort": "desc",
"limit": 49999,
- "adjusted": True,
"provider": "polygon",
"symbol": "BTCUSD",
"start_date": "2023-01-01",
@@ -78,11 +76,9 @@ def test_crypto_search(params, obb):
),
(
{
- "multiplier": 1,
- "timespan": "day",
+ "interval": "1d",
"sort": "desc",
"limit": 49999,
- "adjusted": True,
"provider": "polygon",
"symbol": "BTCUSD",
"start_date": "2023-01-01",
diff --git a/openbb_platform/extensions/currency/integration/test_currency_api.py b/openbb_platform/extensions/currency/integration/test_currency_api.py
index 574fd9cac62..d5dea2303af 100644
--- a/openbb_platform/extensions/currency/integration/test_currency_api.py
+++ b/openbb_platform/extensions/currency/integration/test_currency_api.py
@@ -81,11 +81,9 @@ def test_currency_search(params, headers):
),
(
{
- "multiplier": 1,
- "timespan": "minute",
+ "interval": "1m",
"sort": "desc",
"limit": 49999,
- "adjusted": True,
"provider": "polygon",
"symbol": "EURUSD",
"start_date": "2023-01-01",
@@ -94,11 +92,9 @@ def test_currency_search(params, headers):
),
(
{
- "multiplier": 1,
- "timespan": "day",
+ "interval": "1d",
"sort": "desc",
"limit": 49999,
- "adjusted": True,
"provider": "polygon",
"symbol": "EURUSD",
"start_date": "2023-01-01",
diff --git a/openbb_platform/extensions/currency/integration/test_currency_python.py b/openbb_platform/extensions/currency/integration/test_currency_python.py
index 223b0b1ed60..329f017eb2c 100644
--- a/openbb_platform/extensions/currency/integration/test_currency_python.py
+++ b/openbb_platform/extensions/currency/integration/test_currency_python.py
@@ -76,11 +76,9 @@ def test_currency_search(params, obb):
),
(
{
- "multiplier": 1,
- "timespan": "minute",
+ "interval": "1m",
"sort": "desc",
"limit": 49999,
- "adjusted": True,
"provider": "polygon",
"symbol": "EURUSD",
"start_date": "2023-01-01",
@@ -89,11 +87,9 @@ def test_currency_search(params, obb):
),
(
{
- "multiplier": 1,
- "timespan": "day",
+ "interval": "1d",
"sort": "desc",
"limit": 49999,
- "adjusted": True,
"provider": "polygon",
"symbol": "EURUSD",
"start_date": "2023-01-01",
diff --git a/openbb_platform/extensions/index/integration/test_index_api.py b/openbb_platform/extensions/index/integration/test_index_api.py
index 9b811f561c1..dff0265d57a 100644
--- a/openbb_platform/extensions/index/integration/test_index_api.py
+++ b/openbb_platform/extensions/index/integration/test_index_api.py
@@ -90,30 +90,24 @@ def test_index_constituents(params, headers):
),
(
{
- "timespan": "minute",
"sort": "desc",
"limit": 49999,
- "adjusted": True,
- "multiplier": 1,
+ "interval": "1m",
"provider": "polygon",
"symbol": "NDX",
"start_date": "2023-01-01",
"end_date": "2023-06-06",
- "interval": None,
}
),
(
{
- "timespan": "day",
+ "interval": "1d",
"sort": "desc",
"limit": 49999,
- "adjusted": True,
- "multiplier": 1,
"provider": "polygon",
"symbol": "NDX",
"start_date": "2023-01-01",
"end_date": "2023-06-06",
- "interval": None,
}
),
(
diff --git a/openbb_platform/extensions/index/integration/test_index_python.py b/openbb_platform/extensions/index/integration/test_index_python.py
index 30e4631dbc6..600a1279d7e 100644
--- a/openbb_platform/extensions/index/integration/test_index_python.py
+++ b/openbb_platform/extensions/index/integration/test_index_python.py
@@ -86,30 +86,24 @@ def test_index_constituents(params, obb):
),
(
{
- "timespan": "minute",
+ "interval": "1m",
"sort": "desc",
"limit": 49999,
- "adjusted": True,
- "multiplier": 1,
"provider": "polygon",
"symbol": "NDX",
"start_date": "2023-01-01",
"end_date": "2023-06-06",
- "interval": None,
}
),
(
{
- "timespan": "day",
+ "interval": "1d",
"sort": "desc",
"limit": 49999,
- "adjusted": True,
- "multiplier": 1,
"provider": "polygon",
"symbol": "NDX",
"start_date": "2023-01-01",
"end_date": "2023-06-06",
- "interval": None,
}
),
(
diff --git a/openbb_platform/providers/polygon/openbb_polygon/models/crypto_historical.py b/openbb_platform/providers/polygon/openbb_polygon/models/crypto_historical.py
index c7ae932f62c..96f2918b4d1 100644
--- a/openbb_platform/providers/polygon/openbb_polygon/models/crypto_historical.py
+++ b/openbb_platform/providers/polygon/openbb_polygon/models/crypto_historical.py
@@ -1,5 +1,8 @@
"""Polygon Crypto Historical Price Model."""
+# pylint: disable=unused-argument,protected-access,line-too-long
+
+import warnings
from datetime import datetime
from typing import Any, Dict, List, Literal, Optional
@@ -10,8 +13,21 @@ from openbb_core.provider.standard_models.crypto_historical import (
CryptoHistoricalQueryParams,
)
from openbb_core.provider.utils.descriptions import QUERY_DESCRIPTIONS
-from openbb_polygon.utils.helpers import get_data_many
-from pydantic import Field, PositiveInt
+from openbb_core.provider.utils.errors import EmptyDataError
+from openbb_core.provider.utils.helpers import (
+ ClientResponse,
+ ClientSession,
+ amake_requests,
+)
+from pydantic import (
+ Field,
+ PositiveInt,
+ PrivateAttr,
+ model_validator,
+)
+from pytz import timezone
+
+_warn = warnings.warn
class PolygonCryptoHistoricalQueryParams(CryptoHistoricalQueryParams):
@@ -20,11 +36,8 @@ class PolygonCryptoHistoricalQueryParams(CryptoHistoricalQueryParams):
Source: https://polygon.io/docs/crypto/get_v2_aggs_ticker__cryptoticker__range__multiplier___timespan___from___to
"""
- multiplier: PositiveInt = Field(
- default=1, description="Multiplier of the timespan."
- )
- timespan: Literal["minute", "hour", "day", "week", "month", "quarter", "year"] = (
- Field(default="day", description="Timespan of the data.")
+ interval: str = Field(
+ default="1d", description=QUERY_DESCRIPTIONS.get("interval", "")
)
sort: Literal["asc", "desc"] = Field(
default="desc", description="Sort order of the data."
@@ -32,7 +45,28 @@ class PolygonCryptoHistoricalQueryParams(CryptoHistoricalQueryParams):
limit: PositiveInt = Field(
default=49999, description=QUERY_DESCRIPTIONS.get("limit", "")
)
- adjusted: bool = Field(default=True, description="Whether the data is adjusted.")
+ _multiplier: PositiveInt = PrivateAttr(default=None)
+ _timespan: str = PrivateAttr(default=None)
+
+ @model_validator(mode="after")
+ @classmethod
+ def get_api_interval_params(cls, values: "PolygonCryptoHistoricalQueryParams"):
+ """Get the multiplier and timespan parameters for the Polygon API."""
+ intervals = {
+ "s": "second",
+ "m": "minute",
+ "h": "hour",
+ "d": "day",
+ "W": "week",
+ "M": "month",
+ "Q": "quarter",
+ "Y": "year",
+ }
+
+ values._multiplier = int(values.interval[:-1])
+ values._timespan = intervals[values.interval[-1]]
+
+ return values
class PolygonCryptoHistoricalData(CryptoHistoricalData):
@@ -84,28 +118,56 @@ class PolygonCryptoHistoricalFetcher(
query: PolygonCryptoHistoricalQueryParams,
credentials: Optional[Dict[str, str]],
**kwargs: Any,
- ) -> dict:
+ ) -> List[Dict]:
"""Extract raw data from the Polygon endpoint."""
api_key = credentials.get("polygon_api_key") if credentials else ""
- request_url = (
- f"https://api.polygon.io/v2/aggs/ticker/"
- f"X:{query.symbol}/range/{query.multiplier}/{query.timespan}/"
- f"{query.start_date}/{query.end_date}?adjusted={query.adjusted}"
- f"&sort={query.sort}&limit={query.limit}&apiKey={api_key}"
- )
- data = await get_data_many(request_url, "results", **kwargs)
-
- for d in data:
- d["t"] = datetime.fromtimestamp(d["t"] / 1000)
- if query.timespan not in ["minute", "hour"]:
- d["t"] = d["t"].date()
-
- return data
+ urls = [
+ (
+ "https://api.polygon.io/v2/aggs/ticker/"
+ f"X:{symbol.upper()}/range/{query._multiplier}/{query._timespan}/"
+ f"{query.start_date}/{query.end_date}?"
+ f"&sort={query.sort}&limit={query.limit}&apiKey={api_key}"
+ )
+ for symbol in query.symbol.split(",")
+ ]
+
+ async def callback(
+ response: ClientResponse, session: ClientSession
+ ) -> List[Dict]:
+ data = await response.json()
+
+ symbol = response.url.parts[4]
+ next_url = data.get("next_url", None)
+ results: list = data.get("results", [])
+
+ while next_url:
+ url = f"{next_url}&apiKey={api_key}"
+ data = await session.get_json(url)
+ results.extend(data.get("results", []))
+ next_url = data.get("next_url", None)
+
+ for r in results:
+ r["t"] = datetime.fromtimestamp(r["t"] / 1000, tz=timezone("UTC"))
+ if query._timespan not in ["second", "minute", "hour"]:
+ r["t"] = r["t"].date()
+ else:
+ r["t"] = r["t"].strftime("%Y-%m-%dT%H:%M:%S%z")
+ if "," in query.symbol:
+ r["symbol"] = symbol.replace("X:", "")
+
+ if results == []:
+ _warn(f"Symbol Error: No data found for {symbol.replace('X:', '')}")
+
+ return results
+
+ return await amake_requests(urls, callback, **kwargs)
@staticmethod
def transform_data(
- query: PolygonCryptoHistoricalQueryParams, data: dict, **kwargs: Any
+ query: PolygonCryptoHistoricalQueryParams, data: List[Dict], **kwargs: Any
) -> List[PolygonCryptoHistoricalData]:
"""Transform the data."""
+ if not data:
+ raise EmptyDataError()
return [PolygonCryptoHistoricalData.model_validate(d) for d in data]
diff --git a/openbb_platform/providers/polygon/openbb_polygon/models/currency_historical.py b/openbb_platform/providers/polygon/openbb_polygon/models/currency_historical.py
index b0b9611e1f2..4b5c0acb436 100644
--- a/openbb_platform/providers/polygon/openbb_polygon/models/currency_historical.py
+++ b/openbb_platform/providers/polygon/openbb_polygon/models/currency_historical.py
@@ -1,5 +1,8 @@
"""Polygon Currency Historical Price Model."""
+# pylint: disable=unused-argument,protected-access,line-too-long
+
+import warnings
from datetime import datetime
from typing import Any, Dict, List, Literal, Optional
@@ -10,8 +13,21 @@ from openbb_core.provider.standard_models.currency_historical import (
CurrencyHistoricalQueryParams,
)
from openbb_core.provider.utils.descriptions import QUERY_DESCRIPTIONS
-from openbb_polygon.utils.helpers import get_data_many
-from pydantic import Field, PositiveInt
+from openbb_core.provider.utils.errors import EmptyDataError
+from openbb_core.provider.utils.helpers import (
+ ClientResponse,
+ ClientSession,
+ amake_requests,
+)
+from pydantic import (
+ Field,
+ PositiveInt,
+ PrivateAttr,
+ model_validator,
+)
+from pytz import timezone
+
+_warn = warnings.warn
class PolygonCurrencyHistoricalQueryParams(CurrencyHistoricalQueryParams):
@@ -20,11 +36,8 @@ class PolygonCurrencyHistoricalQueryParams(CurrencyHistoricalQueryParams):
Source: https://polygon.io/docs/forex/get_v2_aggs_ticker__forexticker__range__multiplier___timespan___from___to
"""
- multiplier: PositiveInt = Field(
- default=1, description="Multiplier of the timespan."
- )
- timespan: Literal["minute", "hour", "day", "week", "month", "quarter", "year"] = (
- Field(default="day", description="Timespan of the data.")
+ interval: str = Field(
+ default="1d", description=QUERY_DESCRIPTIONS.get("interval", "")
)
sort: Literal["asc", "desc"] = Field(
default="desc", description="Sort order of the data."
@@ -32,7 +45,28 @@ class PolygonCurrencyHistoricalQueryParams(CurrencyHistoricalQueryParams):
limit: PositiveInt = Field(
default=49999, description=QUERY_DESCRIPTIONS.get("limit", "")
)
- adjusted: bool = Field(default=True, description="Whether the data is adjusted.")
+ _multiplier: PositiveInt = PrivateAttr(default=None)
+ _timespan: str = PrivateAttr(default=None)
+
+ @model_validator(mode="after")
+ @classmethod
+ def get_api_interval_params(cls, values: "PolygonCurrencyHistoricalQueryParams"):
+ """Get the multiplier and timespan parameters for the Polygon API."""
+ intervals = {
+ "s": "second",
+ "m": "minute",
+ "h": "hour",
+ "d": "day",
+ "W": "week",
+ "M": "month",
+ "Q": "quarter",
+ "Y": "year",
+ }
+
+ values._multiplier = int(values.interval[:-1])
+ values._timespan = intervals[values.interval[-1]]
+
+ return values
class PolygonCurrencyHistoricalData(CurrencyHistoricalData):
@@ -81,28 +115,56 @@ class PolygonCurrencyHistoricalFetcher(
query: PolygonCurrencyHistoricalQueryParams,
credentials: Optional[Dict[str, str]],
**kwargs: Any,
- ) -> dict:
+ ) -> List[Dict]:
"""Return the raw data from the polygon endpoint."""
api_key = credentials.get("polygon_api_key") if credentials else ""
- request_url = (
- f"https://api.polygon.io/v2/aggs/ticker/"
- f"C:{query.symbol}/range/{query.multiplier}/{query.timespan}/"
- f"{query.start_date}/{query.end_date}?adjusted={query.adjusted}"
- f"&sort={query.sort}&limit={query.limit}&apiKey={api_key}"
- )
- data = await get_data_many(request_url, "results", **kwargs)
-
- for d in data:
- d["t"] = datetime.fromtimestamp(d["t"] / 1000)
- if query.timespan not in ["minute", "hour"]:
- d["t"] = d["t"].date()
-
- return data
+ urls = [
+ (
+ "https://api.polygon.io/v2/aggs/ticker/"
+ f"C:{symbol.upper()}/range/{query._multiplier}/{query._timespan}/"
+ f"{query.start_date}/{query.end_date}?"
+ f"&sort={query.sort}&limit={query.limit}&apiKey={api_key}"
+ )
+ for symbol in query.symbol.split(",")
+ ]
+
+ async def callback(
+ response: ClientResponse, session: ClientSession
+ ) -> List[Dict]:
+ data = await response.json()
+
+ symbol = response.url.parts[4]
+ next_url = data.get("next_url", None)
+ results: list = data.get("results", [])
+
+ while next_url:
+ url = f"{next_url}&apiKey={api_key}"
+ data = await session.get_json(url)
+ results.extend(data.get("results", []))
+ next_url = data.get("next_url", None)
+
+ for r in results:
+ r["t"] = datetime.fromtimestamp(r["t"] / 1000, tz=timezone("UTC"))
+ if query._timespan not in ["second", "minute", "hour"]:
+ r["t"] = r["t"].date()
+ else:
+ r["t"] = r["t"].strftime("%Y-%m-%dT%H:%M:%S%z")
+ if "," in query.symbol:
+ r["symbol"] = symbol
+
+ if results == []:
+ _warn(f"Symbol Error: No data found for {symbol.replace('C:', '')}")
+
+ return results
+
+ return await amake_requests(urls, callback, **kwargs)
@staticmethod
def transform_data(
- query: PolygonCurrencyHistoricalQueryParams, data: dict, **kwargs: Any
+ query: PolygonCurrencyHistoricalQueryParams, data: List[Dict], **kwargs: Any
) -> List[PolygonCurrencyHistoricalData]:
"""Return the transformed data."""
+ if not data:
+ raise EmptyDataError()
return [PolygonCurrencyHistoricalData.model_validate(d) for d in data]
diff --git a/openbb_platform/providers/polygon/openbb_polygon/models/equity_historical.py b/openbb_platform/providers/polygon/openbb_polygon/models/equity_historical.py
index 38980590b6f..a74aac6f64f 100644
--- a/openbb_platform/providers/polygon/openbb_polygon/models/equity_historical.py
+++ b/openbb_platform/providers/polygon/openbb_polygon/models/equity_historical.py
@@ -1,5 +1,8 @@
"""Polygon Equity Historical Price Model."""
+# pylint: disable=unused-argument
+
+import warnings
from datetime import datetime
from typing import Any, Dict, List, Literal, Optional
@@ -10,6 +13,7 @@ from openbb_core.provider.standard_models.equity_historical import (
EquityHistoricalQueryParams,
)
from openbb_core.provider.utils.descriptions import QUERY_DESCRIPTIONS
+from openbb_core.provider.utils.errors import EmptyDataError
from openbb_core.provider.utils.helpers import (
ClientResponse,
ClientSession,
@@ -21,6 +25,9 @@ from pydantic import (
PrivateAttr,
model_validator,
)
+from pytz import timezone
+
+_warn = warnings.warn
class PolygonEquityHistoricalQueryParams(EquityHistoricalQueryParams):
@@ -143,12 +150,19 @@ class PolygonEquityHistoricalFetcher(
next_url = data.get("next_url", None)
for r in results:
- r["t"] = datetime.fromtimestamp(r["t"] / 1000)
+ r["t"] = datetime.fromtimestamp(
+ r["t"] / 1000, tz=timezone("America/New_York")
+ )
if query._timespan not in ["second", "minute", "hour"]:
r["t"] = r["t"].date()
+ else:
+ r["t"] = r["t"].strftime("%Y-%m-%dT%H:%M:%S")
if "," in query.symbol:
r["symbol"] = symbol
+ if results == []:
+ _warn(f"Symbol Error: No data found for {symbol}")
+
return results
return await amake_requests(urls, callback, **kwargs)
@@ -160,4 +174,6 @@ class PolygonEquityHistoricalFetcher(
**kwargs: Any,
) -> List[PolygonEquityHistoricalData]:
"""Transform the data from the Polygon endpoint."""
+ if not data:
+ raise EmptyDataError()
return [PolygonEquityHistoricalData.model_validate(d) for d in data]
diff --git a/openbb_platform/providers/polygon/openbb_polygon/models/index_historical.py b/openbb_platform/providers/polygon/openbb_polygon/models/index_historical.py
index 31a38b5261c..adc57bfa2c6 100644
--- a/openbb_platform/providers/polygon/openbb_polygon/models/index_historical.py
+++ b/openbb_platform/providers/polygon/openbb_polygon/models/index_historical.py
@@ -1,5 +1,8 @@
"""Polygon Index Historical Model."""
+# pylint: disable=unused-argument,protected-access,line-too-long
+
+import warnings
from datetime import datetime
from typing import Any, Dict, List, Literal, Optional
@@ -9,8 +12,22 @@ from openbb_core.provider.standard_models.index_historical import (
IndexHistoricalData,
IndexHistoricalQueryParams,
)
-from openbb_polygon.utils.helpers import get_data_many
-from pydantic import Field, PositiveInt
+from openbb_core.provider.utils.descriptions import QUERY_DESCRIPTIONS
+from openbb_core.provider.utils.errors import EmptyDataError
+from openbb_core.provider.utils.helpers import (
+ ClientResponse,
+ ClientSession,
+ amake_requests,
+)
+from pydantic import (
+ Field,
+ PositiveInt,
+ PrivateAttr,
+ model_validator,
+)
+from pytz import timezone
+
+_warn = warnings.warn
class PolygonIndexHistoricalQueryParams(IndexHistoricalQueryParams):
@@ -19,13 +36,37 @@ class PolygonIndexHistoricalQueryParams(IndexHistoricalQueryParams):
Source: https://polygon.io/docs/indices/getting-started
"""
- timespan: Literal["minute", "hour", "day", "week", "month", "quarter", "year"] = (
- Field(default="day", description="Timespan of the data.")
+ interval: str = Field(
+ default="1d", description=QUERY_DESCRIPTIONS.get("interval", "")
)
- adjusted: bool = Field(default=True, description="Whether the data is adjusted.")
- multiplier: PositiveInt = Field(
- default=1, description="Multiplier of the