crypticorn 2.17.0rc1__py3-none-any.whl → 2.17.0rc3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- crypticorn/__init__.py +2 -2
- crypticorn/auth/client/api/admin_api.py +415 -13
- crypticorn/auth/client/api/auth_api.py +2622 -113
- crypticorn/auth/client/api/service_api.py +258 -7
- crypticorn/auth/client/api/user_api.py +2485 -270
- crypticorn/auth/client/api/wallet_api.py +1518 -77
- crypticorn/auth/client/models/create_api_key_request.py +2 -1
- crypticorn/auth/client/models/get_api_keys200_response_inner.py +2 -1
- crypticorn/auth/client/rest.py +23 -4
- crypticorn/auth/main.py +8 -5
- crypticorn/client.py +227 -59
- crypticorn/common/__init__.py +0 -1
- crypticorn/common/auth.py +2 -1
- crypticorn/common/metrics.py +4 -6
- crypticorn/common/middleware.py +10 -5
- crypticorn/common/pagination.py +137 -18
- crypticorn/common/router/admin_router.py +1 -1
- crypticorn/common/utils.py +2 -1
- crypticorn/common/warnings.py +1 -0
- crypticorn/hive/client/api/admin_api.py +1234 -51
- crypticorn/hive/client/api/data_api.py +517 -13
- crypticorn/hive/client/api/models_api.py +1657 -83
- crypticorn/hive/client/api/status_api.py +415 -13
- crypticorn/hive/client/models/api_error_identifier.py +1 -1
- crypticorn/hive/client/rest.py +23 -4
- crypticorn/hive/main.py +99 -25
- crypticorn/klines/client/api/admin_api.py +1234 -51
- crypticorn/klines/client/api/change_in_timeframe_api.py +278 -7
- crypticorn/klines/client/api/funding_rates_api.py +324 -7
- crypticorn/klines/client/api/ohlcv_data_api.py +399 -7
- crypticorn/klines/client/api/status_api.py +415 -13
- crypticorn/klines/client/api/symbols_api.py +225 -7
- crypticorn/klines/client/api/udf_api.py +1393 -120
- crypticorn/klines/client/models/api_error_identifier.py +3 -1
- crypticorn/klines/client/rest.py +23 -4
- crypticorn/klines/main.py +89 -12
- crypticorn/metrics/client/api/admin_api.py +1234 -51
- crypticorn/metrics/client/api/exchanges_api.py +1405 -140
- crypticorn/metrics/client/api/indicators_api.py +640 -13
- crypticorn/metrics/client/api/logs_api.py +305 -7
- crypticorn/metrics/client/api/marketcap_api.py +1240 -60
- crypticorn/metrics/client/api/markets_api.py +352 -7
- crypticorn/metrics/client/api/quote_currencies_api.py +237 -7
- crypticorn/metrics/client/api/status_api.py +415 -13
- crypticorn/metrics/client/api/tokens_api.py +400 -13
- crypticorn/metrics/client/configuration.py +4 -2
- crypticorn/metrics/client/rest.py +23 -4
- crypticorn/metrics/main.py +113 -19
- crypticorn/pay/client/api/admin_api.py +1720 -126
- crypticorn/pay/client/api/now_payments_api.py +1013 -42
- crypticorn/pay/client/api/payments_api.py +580 -13
- crypticorn/pay/client/api/products_api.py +915 -25
- crypticorn/pay/client/api/status_api.py +415 -13
- crypticorn/pay/client/configuration.py +2 -2
- crypticorn/pay/client/models/api_error_identifier.py +7 -7
- crypticorn/pay/client/models/scope.py +1 -0
- crypticorn/pay/client/rest.py +23 -4
- crypticorn/pay/main.py +10 -6
- crypticorn/trade/client/__init__.py +2 -1
- crypticorn/trade/client/api/__init__.py +0 -1
- crypticorn/trade/client/api/admin_api.py +1718 -123
- crypticorn/trade/client/api/api_keys_api.py +1596 -103
- crypticorn/trade/client/api/bots_api.py +1106 -47
- crypticorn/trade/client/api/exchanges_api.py +592 -19
- crypticorn/trade/client/api/notifications_api.py +1340 -112
- crypticorn/trade/client/api/orders_api.py +240 -7
- crypticorn/trade/client/api/status_api.py +415 -13
- crypticorn/trade/client/api/strategies_api.py +1170 -69
- crypticorn/trade/client/api/trading_actions_api.py +650 -19
- crypticorn/trade/client/models/__init__.py +2 -0
- crypticorn/trade/client/models/exchange.py +6 -1
- crypticorn/trade/client/models/exchange_key_balance.py +111 -0
- crypticorn/trade/client/models/futures_balance.py +27 -25
- crypticorn/trade/client/models/spot_balance.py +110 -0
- crypticorn/trade/client/models/strategy.py +5 -3
- crypticorn/trade/client/models/strategy_create.py +6 -4
- crypticorn/trade/client/models/strategy_exchange_info.py +16 -4
- crypticorn/trade/client/models/strategy_update.py +2 -2
- crypticorn/trade/client/rest.py +23 -4
- crypticorn/trade/main.py +15 -12
- {crypticorn-2.17.0rc1.dist-info → crypticorn-2.17.0rc3.dist-info}/METADATA +64 -20
- {crypticorn-2.17.0rc1.dist-info → crypticorn-2.17.0rc3.dist-info}/RECORD +86 -85
- crypticorn/trade/client/api/futures_trading_panel_api.py +0 -1285
- {crypticorn-2.17.0rc1.dist-info → crypticorn-2.17.0rc3.dist-info}/WHEEL +0 -0
- {crypticorn-2.17.0rc1.dist-info → crypticorn-2.17.0rc3.dist-info}/entry_points.txt +0 -0
- {crypticorn-2.17.0rc1.dist-info → crypticorn-2.17.0rc3.dist-info}/licenses/LICENSE +0 -0
- {crypticorn-2.17.0rc1.dist-info → crypticorn-2.17.0rc3.dist-info}/top_level.txt +0 -0
crypticorn/common/pagination.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
"""Utilities for handling paginated API responses
|
1
|
+
"""Utilities for handling paginated API responses, cursor-based pagination, filtering, sorting, etc."""
|
2
2
|
|
3
|
-
from typing import Generic, Type, TypeVar, Optional, Literal
|
3
|
+
from typing import Annotated, Any, Generic, Type, TypeVar, Optional, Literal
|
4
4
|
from pydantic import BaseModel, Field, model_validator
|
5
5
|
|
6
6
|
T = TypeVar("T")
|
@@ -14,40 +14,159 @@ class PaginatedResponse(BaseModel, Generic[T]):
|
|
14
14
|
data: list[T]
|
15
15
|
total: int = Field(description="The total number of items")
|
16
16
|
page: int = Field(description="The current page number")
|
17
|
-
|
17
|
+
page_size: int = Field(description="The number of items per page")
|
18
18
|
prev: Optional[int] = Field(None, description="The previous page number")
|
19
19
|
next: Optional[int] = Field(None, description="The next page number")
|
20
20
|
|
21
21
|
|
22
22
|
class PaginationParams(BaseModel, Generic[T]):
|
23
23
|
"""Standard pagination parameters for usage in API endpoints. Check the [fastapi docs](https://fastapi.tiangolo.com/tutorial/query-param-models/?h=qu#query-parameters-with-a-pydantic-model) for usage examples.
|
24
|
-
|
25
|
-
|
26
|
-
>>>
|
24
|
+
Can only be used isolated, not in combination with other parameters. If you need to combine with other parameters, use `FilterComboParams` or refer to this [workaround](https://github.com/fastapi/fastapi/discussions/13448#discussioncomment-12440374).
|
25
|
+
Usage:
|
26
|
+
>>> @router.get("", operation_id="getOrders")
|
27
|
+
>>> async def get_orders(
|
28
|
+
>>> paginate: Annotated[PaginationParams[Order], Query()],
|
29
|
+
>>> ) -> PaginatedResponse[Order]:
|
30
|
+
>>> ...
|
31
|
+
|
32
|
+
The default size is 10 items per page and there is a `HeavyPaginationParams` class with 100 items per page. You can override this default:
|
33
|
+
>>> class LightPaginationParams(PaginationParams[T]):
|
34
|
+
>>> page_size: int = Field(default=5, description="The number of items per page")
|
35
|
+
"""
|
36
|
+
|
37
|
+
page: Optional[int] = Field(default=1, description="The current page number")
|
38
|
+
page_size: Annotated[int, Field(ge=1, le=100)] = Field(
|
39
|
+
10, description="The number of items per page. Default is 10, max is 100."
|
40
|
+
)
|
41
|
+
|
42
|
+
|
43
|
+
class HeavyPaginationParams(PaginationParams[T]):
|
44
|
+
"""Pagination parameters with a higher default size. Refer to `PaginationParams` for usage examples."""
|
45
|
+
|
46
|
+
page_size: Annotated[int, Field(ge=1, le=1000)] = Field(
|
47
|
+
100, description="The number of items per page. Default is 100, max is 1000."
|
48
|
+
)
|
49
|
+
|
50
|
+
|
51
|
+
class SortParams(BaseModel, Generic[T]):
|
52
|
+
"""Standard sort parameters for usage in API endpoints. Check the [fastapi docs](https://fastapi.tiangolo.com/tutorial/query-param-models/?h=qu#query-parameters-with-a-pydantic-model) for usage examples.
|
53
|
+
Can only be used isolated, not in combination with other parameters. If you need to combine with other parameters, use `FilterComboParams` or refer to this [workaround](https://github.com/fastapi/fastapi/discussions/13448#discussioncomment-12440374).
|
54
|
+
Usage:
|
55
|
+
>>> @router.get("", operation_id="getOrders")
|
56
|
+
>>> async def get_orders(
|
57
|
+
>>> sort: Annotated[SortParams[Order], Query()],
|
58
|
+
>>> ) -> PaginatedResponse[Order]:
|
59
|
+
>>> ...
|
27
60
|
"""
|
28
61
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
62
|
+
sort_order: Optional[Literal["asc", "desc"]] = Field(
|
63
|
+
None, description="The order to sort by"
|
64
|
+
)
|
65
|
+
sort_by: Optional[str] = Field(None, description="The field to sort by")
|
33
66
|
|
34
67
|
@model_validator(mode="after")
|
35
|
-
def
|
68
|
+
def validate_sort(self):
|
36
69
|
# Extract the generic argument type
|
37
70
|
args: tuple = self.__pydantic_generic_metadata__.get("args")
|
38
71
|
if not args or not issubclass(args[0], BaseModel):
|
39
72
|
raise TypeError(
|
40
73
|
"PaginationParams must be used with a Pydantic BaseModel as a generic parameter"
|
41
74
|
)
|
42
|
-
if self.
|
75
|
+
if self.sort_by:
|
43
76
|
# check if the sort field is valid
|
44
77
|
model: Type[BaseModel] = args[0]
|
45
|
-
if self.
|
78
|
+
if self.sort_by not in model.model_fields:
|
46
79
|
raise ValueError(
|
47
|
-
f"Invalid
|
80
|
+
f"Invalid field: '{self.sort_by}'. Must be one of: {list(model.model_fields)}"
|
48
81
|
)
|
49
|
-
if self.
|
50
|
-
raise ValueError(
|
51
|
-
|
52
|
-
|
82
|
+
if self.sort_order and self.sort_order not in ["asc", "desc"]:
|
83
|
+
raise ValueError(
|
84
|
+
f"Invalid order: '{self.sort_order}' — must be one of: ['asc', 'desc']"
|
85
|
+
)
|
86
|
+
if self.sort_order and self.sort_by is None or self.sort_by and self.sort_order is None:
|
87
|
+
raise ValueError("sort_order and sort_by must be provided together")
|
53
88
|
return self
|
89
|
+
|
90
|
+
|
91
|
+
class FilterParams(BaseModel, Generic[T]):
|
92
|
+
"""Standard filter parameters for usage in API endpoints. Check the [fastapi docs](https://fastapi.tiangolo.com/tutorial/query-param-models/?h=qu#query-parameters-with-a-pydantic-model) for usage examples.
|
93
|
+
Can only be used isolated, not in combination with other parameters. If you need to combine with other parameters, use `FilterComboParams` or refer to this [workaround](https://github.com/fastapi/fastapi/discussions/13448#discussioncomment-12440374).
|
94
|
+
Usage:
|
95
|
+
>>> @router.get("", operation_id="getOrders")
|
96
|
+
>>> async def get_orders(
|
97
|
+
>>> filter: Annotated[FilterParams[Order], Query()],
|
98
|
+
>>> ) -> PaginatedResponse[Order]:
|
99
|
+
>>> ...
|
100
|
+
"""
|
101
|
+
|
102
|
+
filter_by: Optional[str] = Field(None, description="The field to filter by")
|
103
|
+
filter_value: Optional[Any] = Field(None, description="The value to filter with")
|
104
|
+
|
105
|
+
@model_validator(mode="after")
|
106
|
+
def validate_filter(self):
|
107
|
+
if self.filter_by and not self.filter_value:
|
108
|
+
raise ValueError("filter_by and filter_value must be provided together")
|
109
|
+
if self.filter_by:
|
110
|
+
# Extract the generic argument type
|
111
|
+
args: tuple = self.__pydantic_generic_metadata__.get("args")
|
112
|
+
if not args or not issubclass(args[0], BaseModel):
|
113
|
+
raise TypeError(
|
114
|
+
"FilterParams must be used with a Pydantic BaseModel as a generic parameter"
|
115
|
+
)
|
116
|
+
# check if the filter field is valid
|
117
|
+
model: Type[BaseModel] = args[0]
|
118
|
+
if self.filter_by not in model.model_fields:
|
119
|
+
raise ValueError(
|
120
|
+
f"Invalid field: '{self.filter_by}'. Must be one of: {list(model.model_fields)}"
|
121
|
+
)
|
122
|
+
self.filter_value = _enforce_field_type(model, self.filter_by, self.filter_value)
|
123
|
+
return self
|
124
|
+
|
125
|
+
|
126
|
+
class FilterComboParams(PaginationParams[T], SortParams[T], FilterParams[T], ):
|
127
|
+
"""Combines pagination, filter, and sort parameters.
|
128
|
+
Usage:
|
129
|
+
>>> @router.get("", operation_id="getOrders")
|
130
|
+
>>> async def get_orders(
|
131
|
+
>>> filter_combo: Annotated[FilterComboParams[Order], Query()],
|
132
|
+
>>> ) -> PaginatedResponse[Order]:
|
133
|
+
>>> ...
|
134
|
+
"""
|
135
|
+
|
136
|
+
pass
|
137
|
+
|
138
|
+
|
139
|
+
class HeavyFilterComboParams(HeavyPaginationParams[T], FilterParams[T], SortParams[T]):
|
140
|
+
"""Combines pagination, filter, and sort parameters.
|
141
|
+
Usage:
|
142
|
+
>>> @router.get("", operation_id="getOrders")
|
143
|
+
>>> async def get_orders(
|
144
|
+
>>> filter_combo: Annotated[HeavyFilterComboParams[Order], Query()],
|
145
|
+
>>> ) -> PaginatedResponse[Order]:
|
146
|
+
>>> ...
|
147
|
+
"""
|
148
|
+
|
149
|
+
pass
|
150
|
+
|
151
|
+
|
152
|
+
def _enforce_field_type(model: Type[BaseModel], field_name: str, value: Any) -> Any:
|
153
|
+
"""
|
154
|
+
Coerce or validate `value` to match the type of `field_name` on the given `model`. Should be used after checking that the field is valid.
|
155
|
+
|
156
|
+
:param model: The Pydantic model.
|
157
|
+
:param field_name: The name of the field to match.
|
158
|
+
:param value: The value to validate or coerce.
|
159
|
+
|
160
|
+
:return: The value cast to the expected type.
|
161
|
+
|
162
|
+
:raises: ValueError: If the field doesn't exist or coercion fails.
|
163
|
+
"""
|
164
|
+
expected_type = model.model_fields[field_name].annotation
|
165
|
+
|
166
|
+
if isinstance(value, expected_type):
|
167
|
+
return value
|
168
|
+
|
169
|
+
try:
|
170
|
+
return expected_type(value)
|
171
|
+
except Exception as e:
|
172
|
+
raise ValueError(f"Expected {expected_type} for field {field_name}, got {type(value)}")
|
crypticorn/common/utils.py
CHANGED
@@ -1,10 +1,11 @@
|
|
1
1
|
"""General utility functions and helper methods used across the codebase."""
|
2
2
|
|
3
3
|
from datetime import datetime
|
4
|
-
from typing import Any, Union
|
4
|
+
from typing import Any, Type, Union
|
5
5
|
from decimal import Decimal
|
6
6
|
import string
|
7
7
|
import random
|
8
|
+
from pydantic import BaseModel
|
8
9
|
import typing_extensions
|
9
10
|
import warnings
|
10
11
|
|
crypticorn/common/warnings.py
CHANGED
@@ -62,6 +62,7 @@ class CrypticornDeprecatedSince215(CrypticornDeprecationWarning):
|
|
62
62
|
def __init__(self, message: str, *args: object) -> None:
|
63
63
|
super().__init__(message, *args, since=(2, 15), expected_removal=(3, 0))
|
64
64
|
|
65
|
+
|
65
66
|
class CrypticornDeprecatedSince217(CrypticornDeprecationWarning):
|
66
67
|
"""A specific `CrypticornDeprecationWarning` subclass defining functionality deprecated since Crypticorn 2.17."""
|
67
68
|
|