crypticorn 2.14.0__py3-none-any.whl → 2.16.0__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.
@@ -14,14 +14,9 @@ dotenv.load_dotenv()
14
14
 
15
15
  logger = logging.getLogger(__name__)
16
16
 
17
- DOCKER_ENV = os.getenv("IS_DOCKER")
17
+ DOCKER_ENV = os.getenv("IS_DOCKER", "0")
18
18
  API_ENV = os.getenv("API_ENV")
19
19
 
20
- if not DOCKER_ENV:
21
- raise ValueError(
22
- "IS_DOCKER is not set. Please set it to '0' in .env and '1' in the docker-compose.yml file."
23
- )
24
-
25
20
  if not API_ENV:
26
21
  raise ValueError(
27
22
  "API_ENV is not set. Please set it to 'prod', 'dev' or 'local' in .env (of type ApiEnv)."
crypticorn/client.py CHANGED
@@ -37,7 +37,7 @@ class ApiClient:
37
37
  """The version of the client."""
38
38
 
39
39
  self._http_client = http_client
40
- self._owns_http_client = http_client is None # whether we own the http client
40
+ self._owns_http_client = http_client is None # whether we own the http client
41
41
  self._service_classes: dict[Service, type[SubClient]] = {
42
42
  Service.HIVE: HiveClient,
43
43
  Service.TRADE: TradeClient,
@@ -116,7 +116,9 @@ class ApiClient:
116
116
  headers={"User-Agent": f"crypticorn/python/{self.version}"},
117
117
  )
118
118
  for service in self._services.values():
119
- if hasattr(service, 'base_client') and hasattr(service.base_client, 'rest_client'):
119
+ if hasattr(service, "base_client") and hasattr(
120
+ service.base_client, "rest_client"
121
+ ):
120
122
  service.base_client.rest_client.pool_manager = self._http_client
121
123
 
122
124
  def _get_default_config(self, service, version=None):
@@ -5,15 +5,24 @@ try:
5
5
  except ImportError:
6
6
  from strenum import StrEnum
7
7
 
8
+ from enum import Enum
9
+ import warnings
10
+ import typing_extensions
8
11
  from crypticorn.common.mixins import ValidateEnumMixin
12
+ from crypticorn.common.warnings import CrypticornDeprecatedSince215
9
13
 
10
14
 
11
15
  class Exchange(ValidateEnumMixin, StrEnum):
12
- """Supported exchanges for trading"""
16
+ """All exchanges used in the crypticorn ecosystem. Refer to the APIs for support for a specific usecase (data, trading, etc.)."""
13
17
 
14
18
  KUCOIN = "kucoin"
15
19
  BINGX = "bingx"
20
+ BINANCE = "binance"
21
+ BYBIT = "bybit"
16
22
  HYPERLIQUID = "hyperliquid"
23
+ BITGET = "bitget"
24
+ GATEIO = "gateio"
25
+ BITSTAMP = "bitstamp"
17
26
 
18
27
 
19
28
  class InternalExchange(ValidateEnumMixin, StrEnum):
@@ -26,6 +35,14 @@ class InternalExchange(ValidateEnumMixin, StrEnum):
26
35
  HYPERLIQUID = "hyperliquid"
27
36
  BITGET = "bitget"
28
37
 
38
+ @classmethod
39
+ def __getattr__(cls, name):
40
+ warnings.warn(
41
+ "The `InternalExchange` enum is deprecated; use `Exchange` instead.",
42
+ category=CrypticornDeprecatedSince215,
43
+ )
44
+ return super().__getattr__(name)
45
+
29
46
 
30
47
  class MarketType(ValidateEnumMixin, StrEnum):
31
48
  """
@@ -1,6 +1,6 @@
1
1
  """Utilities for handling paginated API responses and cursor-based pagination."""
2
2
 
3
- from typing import Generic, Type, TypeVar, List, Optional, Literal
3
+ from typing import Generic, Type, TypeVar, Optional, Literal
4
4
  from pydantic import BaseModel, Field, model_validator
5
5
 
6
6
  T = TypeVar("T")
@@ -11,7 +11,7 @@ class PaginatedResponse(BaseModel, Generic[T]):
11
11
  >>> PaginatedResponse[ItemModel](data=items, total=total_items, page=1, size=10, prev=None, next=2)
12
12
  """
13
13
 
14
- data: List[T]
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
  size: int = Field(description="The number of items per page")
@@ -28,9 +28,7 @@ class PaginationParams(BaseModel, Generic[T]):
28
28
 
29
29
  page: int = Field(default=1, description="The current page number")
30
30
  size: int = Field(default=10, description="The number of items per page")
31
- order: Literal["asc", "desc"] = Field(
32
- default="asc", description="The order to sort by"
33
- )
31
+ order: Optional[Literal["asc", "desc"]] = Field(None, description="The order to sort by")
34
32
  sort: Optional[str] = Field(None, description="The field to sort by")
35
33
 
36
34
  @model_validator(mode="after")
@@ -48,4 +46,8 @@ class PaginationParams(BaseModel, Generic[T]):
48
46
  raise ValueError(
49
47
  f"Invalid sort field: '{self.sort}' — must be one of: {list(model.model_fields)}"
50
48
  )
49
+ if self.order and self.order not in ["asc", "desc"]:
50
+ raise ValueError(f"Invalid order: '{self.order}' — must be one of: ['asc', 'desc']")
51
+ if self.order and not self.sort or self.sort and not self.order:
52
+ raise ValueError("Sort and order must be provided together")
51
53
  return self
@@ -1,7 +1,7 @@
1
1
  """
2
2
  This module contains the status router for the API.
3
3
  It provides endpoints for checking the status of the API and get the server's time.
4
- SHOULD ALLOW ACCESS TO THIS ROUTER WITHOUT.
4
+ SHOULD ALLOW ACCESS TO THIS ROUTER WITHOUT AUTH.
5
5
  >>> app.include_router(status_router)
6
6
  """
7
7
 
@@ -16,6 +16,7 @@ class Scope(StrEnum):
16
16
 
17
17
  # Scopes that can be purchased - these actually exist in the jwt token
18
18
  READ_PREDICTIONS = "read:predictions"
19
+ READ_DEXSIGNALS = "read:dexsignals"
19
20
 
20
21
  # Hive scopes
21
22
  READ_HIVE_MODEL = "read:hive:model"
@@ -87,4 +88,5 @@ class Scope(StrEnum):
87
88
  cls.READ_METRICS_MARKETS,
88
89
  cls.READ_KLINES,
89
90
  cls.READ_SENTIMENT,
91
+ cls.READ_DEXSIGNALS,
90
92
  ]
@@ -56,6 +56,13 @@ class CrypticornDeprecatedSince28(CrypticornDeprecationWarning):
56
56
  super().__init__(message, *args, since=(2, 8), expected_removal=(3, 0))
57
57
 
58
58
 
59
+ class CrypticornDeprecatedSince215(CrypticornDeprecationWarning):
60
+ """A specific `CrypticornDeprecationWarning` subclass defining functionality deprecated since Crypticorn 2.15."""
61
+
62
+ def __init__(self, message: str, *args: object) -> None:
63
+ super().__init__(message, *args, since=(2, 15), expected_removal=(3, 0))
64
+
65
+
59
66
  class CrypticornExperimentalWarning(Warning):
60
67
  """A Crypticorn specific experimental functionality warning.
61
68
 
@@ -215,9 +215,7 @@ class Configuration:
215
215
  debug: Optional[bool] = None,
216
216
  ) -> None:
217
217
  """Constructor"""
218
- self._base_path = (
219
- "https://api.crypticorn.dev/v1/metrics" if host is None else host
220
- )
218
+ self._base_path = "http://localhost/v1/metrics" if host is None else host
221
219
  """Default Base url
222
220
  """
223
221
  self.server_index = 0 if server_index is None and host is None else server_index
@@ -559,7 +557,7 @@ class Configuration:
559
557
  """
560
558
  return [
561
559
  {
562
- "url": "https://api.crypticorn.dev/v1/metrics",
560
+ "url": "http://localhost/v1/metrics",
563
561
  "description": "No description provided",
564
562
  }
565
563
  ]
@@ -28,15 +28,17 @@ class ApiErrorIdentifier(str, Enum):
28
28
  """
29
29
  ALLOCATION_BELOW_CURRENT_EXPOSURE = "allocation_below_current_exposure"
30
30
  ALLOCATION_BELOW_MIN_AMOUNT = "allocation_below_min_amount"
31
+ ALLOCATION_LIMIT_EXCEEDED = "allocation_limit_exceeded"
31
32
  BLACK_SWAN = "black_swan"
32
33
  BOT_ALREADY_DELETED = "bot_already_deleted"
33
- BOT_DISABLED = "bot_disabled"
34
34
  BOT_STOPPING_COMPLETED = "bot_stopping_completed"
35
35
  BOT_STOPPING_STARTED = "bot_stopping_started"
36
36
  CANCELLED_OPEN_ORDER = "cancelled_open_order"
37
37
  CLIENT_ORDER_ID_ALREADY_EXISTS = "client_order_id_already_exists"
38
38
  INVALID_CONTENT_TYPE = "invalid_content_type"
39
39
  DELETE_BOT_ERROR = "delete_bot_error"
40
+ EXCHANGE_HTTP_REQUEST_ERROR = "exchange_http_request_error"
41
+ EXCHANGE_INVALID_PARAMETER = "exchange_invalid_parameter"
40
42
  EXCHANGE_INVALID_SIGNATURE = "exchange_invalid_signature"
41
43
  EXCHANGE_INVALID_TIMESTAMP = "exchange_invalid_timestamp"
42
44
  EXCHANGE_IP_ADDRESS_IS_NOT_AUTHORIZED = "exchange_ip_address_is_not_authorized"
@@ -56,10 +58,9 @@ class ApiErrorIdentifier(str, Enum):
56
58
  EXCHANGE_USER_ACCOUNT_IS_FROZEN = "exchange_user_account_is_frozen"
57
59
  API_KEY_EXPIRED = "api_key_expired"
58
60
  BEARER_TOKEN_EXPIRED = "bearer_token_expired"
59
- OPEN_ORDER_EXPIRED = "open_order_expired"
61
+ FAILED_OPEN_ORDER = "failed_open_order"
60
62
  FORBIDDEN = "forbidden"
61
63
  HEDGE_MODE_NOT_ACTIVE = "hedge_mode_not_active"
62
- HTTP_REQUEST_ERROR = "http_request_error"
63
64
  INSUFFICIENT_BALANCE = "insufficient_balance"
64
65
  INSUFFICIENT_MARGIN = "insufficient_margin"
65
66
  INSUFFICIENT_SCOPES = "insufficient_scopes"
@@ -68,15 +69,15 @@ class ApiErrorIdentifier(str, Enum):
68
69
  INVALID_DATA = "invalid_data"
69
70
  INVALID_DATA_RESPONSE = "invalid_data_response"
70
71
  INVALID_EXCHANGE_KEY = "invalid_exchange_key"
71
- INVALID_MARGIN_MODE = "invalid_margin_mode"
72
72
  INVALID_MODEL_NAME = "invalid_model_name"
73
- INVALID_PARAMETER_PROVIDED = "exchange_invalid_parameter"
74
73
  LEVERAGE_LIMIT_EXCEEDED = "leverage_limit_exceeded"
75
74
  ORDER_VIOLATES_LIQUIDATION_PRICE_CONSTRAINTS = (
76
75
  "order_violates_liquidation_price_constraints"
77
76
  )
78
77
  MARGIN_MODE_CLASH = "margin_mode_clash"
79
78
  NAME_NOT_UNIQUE = "name_not_unique"
79
+ NO_API_KEY = "no_api_key"
80
+ NO_BEARER = "no_bearer"
80
81
  NO_CREDENTIALS = "no_credentials"
81
82
  NOW_API_DOWN = "now_api_down"
82
83
  OBJECT_ALREADY_EXISTS = "object_already_exists"
@@ -102,14 +103,13 @@ class ApiErrorIdentifier(str, Enum):
102
103
  RISK_LIMIT_EXCEEDED = "risk_limit_exceeded"
103
104
  RPC_TIMEOUT = "rpc_timeout"
104
105
  SYSTEM_SETTLEMENT_IN_PROCESS = "system_settlement_in_process"
105
- STRATEGY_ALREADY_EXISTS = "strategy_already_exists"
106
106
  STRATEGY_DISABLED = "strategy_disabled"
107
107
  STRATEGY_LEVERAGE_MISMATCH = "strategy_leverage_mismatch"
108
108
  STRATEGY_NOT_SUPPORTING_EXCHANGE = "strategy_not_supporting_exchange"
109
109
  SUCCESS = "success"
110
110
  SYMBOL_DOES_NOT_EXIST = "symbol_does_not_exist"
111
111
  TRADING_ACTION_EXPIRED = "trading_action_expired"
112
- TRADING_ACTION_SKIPPED_BOT_STOPPING = "TRADING_ACTION_SKIPPED_BOT_STOPPING"
112
+ TRADING_ACTION_SKIPPED_BOT_STOPPING = "trading_action_skipped_bot_stopping"
113
113
  TRADING_HAS_BEEN_LOCKED = "trading_has_been_locked"
114
114
  TRADING_IS_SUSPENDED = "trading_is_suspended"
115
115
  UNKNOWN_ERROR_OCCURRED = "unknown_error_occurred"
@@ -28,15 +28,17 @@ class ApiErrorIdentifier(str, Enum):
28
28
  """
29
29
  ALLOCATION_BELOW_CURRENT_EXPOSURE = "allocation_below_current_exposure"
30
30
  ALLOCATION_BELOW_MIN_AMOUNT = "allocation_below_min_amount"
31
+ ALLOCATION_LIMIT_EXCEEDED = "allocation_limit_exceeded"
31
32
  BLACK_SWAN = "black_swan"
32
33
  BOT_ALREADY_DELETED = "bot_already_deleted"
33
- BOT_DISABLED = "bot_disabled"
34
34
  BOT_STOPPING_COMPLETED = "bot_stopping_completed"
35
35
  BOT_STOPPING_STARTED = "bot_stopping_started"
36
36
  CANCELLED_OPEN_ORDER = "cancelled_open_order"
37
37
  CLIENT_ORDER_ID_ALREADY_EXISTS = "client_order_id_already_exists"
38
38
  INVALID_CONTENT_TYPE = "invalid_content_type"
39
39
  DELETE_BOT_ERROR = "delete_bot_error"
40
+ EXCHANGE_HTTP_REQUEST_ERROR = "exchange_http_request_error"
41
+ EXCHANGE_INVALID_PARAMETER = "exchange_invalid_parameter"
40
42
  EXCHANGE_INVALID_SIGNATURE = "exchange_invalid_signature"
41
43
  EXCHANGE_INVALID_TIMESTAMP = "exchange_invalid_timestamp"
42
44
  EXCHANGE_IP_ADDRESS_IS_NOT_AUTHORIZED = "exchange_ip_address_is_not_authorized"
@@ -56,10 +58,9 @@ class ApiErrorIdentifier(str, Enum):
56
58
  EXCHANGE_USER_ACCOUNT_IS_FROZEN = "exchange_user_account_is_frozen"
57
59
  API_KEY_EXPIRED = "api_key_expired"
58
60
  BEARER_TOKEN_EXPIRED = "bearer_token_expired"
59
- OPEN_ORDER_EXPIRED = "open_order_expired"
61
+ FAILED_OPEN_ORDER = "failed_open_order"
60
62
  FORBIDDEN = "forbidden"
61
63
  HEDGE_MODE_NOT_ACTIVE = "hedge_mode_not_active"
62
- HTTP_REQUEST_ERROR = "http_request_error"
63
64
  INSUFFICIENT_BALANCE = "insufficient_balance"
64
65
  INSUFFICIENT_MARGIN = "insufficient_margin"
65
66
  INSUFFICIENT_SCOPES = "insufficient_scopes"
@@ -68,15 +69,15 @@ class ApiErrorIdentifier(str, Enum):
68
69
  INVALID_DATA = "invalid_data"
69
70
  INVALID_DATA_RESPONSE = "invalid_data_response"
70
71
  INVALID_EXCHANGE_KEY = "invalid_exchange_key"
71
- INVALID_MARGIN_MODE = "invalid_margin_mode"
72
72
  INVALID_MODEL_NAME = "invalid_model_name"
73
- INVALID_PARAMETER_PROVIDED = "exchange_invalid_parameter"
74
73
  LEVERAGE_LIMIT_EXCEEDED = "leverage_limit_exceeded"
75
74
  ORDER_VIOLATES_LIQUIDATION_PRICE_CONSTRAINTS = (
76
75
  "order_violates_liquidation_price_constraints"
77
76
  )
78
77
  MARGIN_MODE_CLASH = "margin_mode_clash"
79
78
  NAME_NOT_UNIQUE = "name_not_unique"
79
+ NO_API_KEY = "no_api_key"
80
+ NO_BEARER = "no_bearer"
80
81
  NO_CREDENTIALS = "no_credentials"
81
82
  NOW_API_DOWN = "now_api_down"
82
83
  OBJECT_ALREADY_EXISTS = "object_already_exists"
@@ -102,14 +103,13 @@ class ApiErrorIdentifier(str, Enum):
102
103
  RISK_LIMIT_EXCEEDED = "risk_limit_exceeded"
103
104
  RPC_TIMEOUT = "rpc_timeout"
104
105
  SYSTEM_SETTLEMENT_IN_PROCESS = "system_settlement_in_process"
105
- STRATEGY_ALREADY_EXISTS = "strategy_already_exists"
106
106
  STRATEGY_DISABLED = "strategy_disabled"
107
107
  STRATEGY_LEVERAGE_MISMATCH = "strategy_leverage_mismatch"
108
108
  STRATEGY_NOT_SUPPORTING_EXCHANGE = "strategy_not_supporting_exchange"
109
109
  SUCCESS = "success"
110
110
  SYMBOL_DOES_NOT_EXIST = "symbol_does_not_exist"
111
111
  TRADING_ACTION_EXPIRED = "trading_action_expired"
112
- TRADING_ACTION_SKIPPED_BOT_STOPPING = "TRADING_ACTION_SKIPPED_BOT_STOPPING"
112
+ TRADING_ACTION_SKIPPED_BOT_STOPPING = "trading_action_skipped_bot_stopping"
113
113
  TRADING_HAS_BEEN_LOCKED = "trading_has_been_locked"
114
114
  TRADING_IS_SUSPENDED = "trading_is_suspended"
115
115
  UNKNOWN_ERROR_OCCURRED = "unknown_error_occurred"
@@ -33,8 +33,12 @@ class FuturesTradingAction(BaseModel):
33
33
  Model for futures trading actions
34
34
  """ # noqa: E501
35
35
 
36
- leverage: Optional[Annotated[int, Field(strict=True, ge=1)]]
37
- margin_mode: Optional[MarginMode] = None
36
+ leverage: Optional[Annotated[int, Field(strict=True, ge=1)]] = Field(
37
+ default=1, description="Leverage to use for futures trades. Default is 1."
38
+ )
39
+ margin_mode: Optional[MarginMode] = Field(
40
+ default=None, description="Margin mode for futures trades. Default is isolated."
41
+ )
38
42
  created_at: Optional[StrictInt] = Field(
39
43
  default=None, description="Timestamp of creation"
40
44
  )
@@ -60,8 +64,6 @@ class FuturesTradingAction(BaseModel):
60
64
  take_profit: Optional[List[TPSL]] = None
61
65
  stop_loss: Optional[List[TPSL]] = None
62
66
  expiry_timestamp: Optional[StrictInt] = None
63
- client_order_id: Optional[StrictStr] = None
64
- position_id: Optional[StrictStr] = None
65
67
  __properties: ClassVar[List[str]] = [
66
68
  "leverage",
67
69
  "margin_mode",
@@ -80,8 +82,6 @@ class FuturesTradingAction(BaseModel):
80
82
  "take_profit",
81
83
  "stop_loss",
82
84
  "expiry_timestamp",
83
- "client_order_id",
84
- "position_id",
85
85
  ]
86
86
 
87
87
  model_config = ConfigDict(
@@ -135,16 +135,6 @@ class FuturesTradingAction(BaseModel):
135
135
  if _item_stop_loss:
136
136
  _items.append(_item_stop_loss.to_dict())
137
137
  _dict["stop_loss"] = _items
138
- # set to None if leverage (nullable) is None
139
- # and model_fields_set contains the field
140
- if self.leverage is None and "leverage" in self.model_fields_set:
141
- _dict["leverage"] = None
142
-
143
- # set to None if margin_mode (nullable) is None
144
- # and model_fields_set contains the field
145
- if self.margin_mode is None and "margin_mode" in self.model_fields_set:
146
- _dict["margin_mode"] = None
147
-
148
138
  # set to None if execution_id (nullable) is None
149
139
  # and model_fields_set contains the field
150
140
  if self.execution_id is None and "execution_id" in self.model_fields_set:
@@ -186,16 +176,6 @@ class FuturesTradingAction(BaseModel):
186
176
  ):
187
177
  _dict["expiry_timestamp"] = None
188
178
 
189
- # set to None if client_order_id (nullable) is None
190
- # and model_fields_set contains the field
191
- if self.client_order_id is None and "client_order_id" in self.model_fields_set:
192
- _dict["client_order_id"] = None
193
-
194
- # set to None if position_id (nullable) is None
195
- # and model_fields_set contains the field
196
- if self.position_id is None and "position_id" in self.model_fields_set:
197
- _dict["position_id"] = None
198
-
199
179
  return _dict
200
180
 
201
181
  @classmethod
@@ -236,8 +216,6 @@ class FuturesTradingAction(BaseModel):
236
216
  else None
237
217
  ),
238
218
  "expiry_timestamp": obj.get("expiry_timestamp"),
239
- "client_order_id": obj.get("client_order_id"),
240
- "position_id": obj.get("position_id"),
241
219
  }
242
220
  )
243
221
  return _obj
@@ -33,12 +33,19 @@ class FuturesTradingActionCreate(BaseModel):
33
33
  Model for sending futures trading actions
34
34
  """ # noqa: E501
35
35
 
36
- leverage: Optional[Annotated[int, Field(strict=True, ge=1)]]
37
- margin_mode: Optional[MarginMode] = None
36
+ leverage: Optional[Annotated[int, Field(strict=True, ge=1)]] = Field(
37
+ default=1, description="Leverage to use for futures trades. Default is 1."
38
+ )
39
+ margin_mode: Optional[MarginMode] = Field(
40
+ default=None, description="Margin mode for futures trades. Default is isolated."
41
+ )
38
42
  execution_id: Optional[StrictStr] = None
39
43
  open_order_execution_id: Optional[StrictStr] = None
40
44
  action_type: TradingActionType = Field(description="The type of action.")
41
- market_type: MarketType = Field(description="The type of market the action is for.")
45
+ market_type: Optional[MarketType] = Field(
46
+ default=None,
47
+ description="The type of market the action is for. Must be set to futures.",
48
+ )
42
49
  strategy_id: StrictStr = Field(description="UID for the strategy.")
43
50
  symbol: StrictStr = Field(
44
51
  description="Trading symbol or asset pair in format: 'symbol/quote_currency' (see market service for valid symbols)"
@@ -119,16 +126,6 @@ class FuturesTradingActionCreate(BaseModel):
119
126
  if _item_stop_loss:
120
127
  _items.append(_item_stop_loss.to_dict())
121
128
  _dict["stop_loss"] = _items
122
- # set to None if leverage (nullable) is None
123
- # and model_fields_set contains the field
124
- if self.leverage is None and "leverage" in self.model_fields_set:
125
- _dict["leverage"] = None
126
-
127
- # set to None if margin_mode (nullable) is None
128
- # and model_fields_set contains the field
129
- if self.margin_mode is None and "margin_mode" in self.model_fields_set:
130
- _dict["margin_mode"] = None
131
-
132
129
  # set to None if execution_id (nullable) is None
133
130
  # and model_fields_set contains the field
134
131
  if self.execution_id is None and "execution_id" in self.model_fields_set:
@@ -65,7 +65,7 @@ class Order(BaseModel):
65
65
  sent_qty: Optional[StrictStr] = None
66
66
  fee: Optional[StrictStr] = None
67
67
  leverage: Optional[StrictInt] = None
68
- order_details: Optional[Any] = Field(
68
+ order_details: Optional[Dict[str, Any]] = Field(
69
69
  default=None, description="Exchange specific details of the order"
70
70
  )
71
71
  pnl: Optional[StrictStr] = None
@@ -138,9 +138,6 @@ class Order(BaseModel):
138
138
  exclude=excluded_fields,
139
139
  exclude_none=True,
140
140
  )
141
- # override the default output from pydantic by calling `to_dict()` of order_details
142
- if self.order_details:
143
- _dict["order_details"] = self.order_details.to_dict()
144
141
  # set to None if trading_action_id (nullable) is None
145
142
  # and model_fields_set contains the field
146
143
  if (
@@ -257,11 +254,6 @@ class Order(BaseModel):
257
254
  if self.leverage is None and "leverage" in self.model_fields_set:
258
255
  _dict["leverage"] = None
259
256
 
260
- # set to None if order_details (nullable) is None
261
- # and model_fields_set contains the field
262
- if self.order_details is None and "order_details" in self.model_fields_set:
263
- _dict["order_details"] = None
264
-
265
257
  # set to None if pnl (nullable) is None
266
258
  # and model_fields_set contains the field
267
259
  if self.pnl is None and "pnl" in self.model_fields_set:
@@ -310,11 +302,7 @@ class Order(BaseModel):
310
302
  "sent_qty": obj.get("sent_qty"),
311
303
  "fee": obj.get("fee"),
312
304
  "leverage": obj.get("leverage"),
313
- "order_details": (
314
- AnyOf.from_dict(obj["order_details"])
315
- if obj.get("order_details") is not None
316
- else None
317
- ),
305
+ "order_details": obj.get("order_details"),
318
306
  "pnl": obj.get("pnl"),
319
307
  "order_time": obj.get("order_time"),
320
308
  }
@@ -34,7 +34,10 @@ class SpotTradingActionCreate(BaseModel):
34
34
  execution_id: Optional[StrictStr] = None
35
35
  open_order_execution_id: Optional[StrictStr] = None
36
36
  action_type: TradingActionType = Field(description="The type of action.")
37
- market_type: MarketType = Field(description="The type of market the action is for.")
37
+ market_type: Optional[MarketType] = Field(
38
+ default=None,
39
+ description="The type of market the action is for. Must be set to spot.",
40
+ )
38
41
  strategy_id: StrictStr = Field(description="UID for the strategy.")
39
42
  symbol: StrictStr = Field(
40
43
  description="Trading symbol or asset pair in format: 'symbol/quote_currency' (see market service for valid symbols)"
@@ -34,13 +34,11 @@ class TPSL(BaseModel):
34
34
  description="Percentage of the open order to sell. All allocations must sum up to 1. Use this allocation again when closing the order."
35
35
  )
36
36
  execution_id: Optional[StrictStr] = None
37
- client_order_id: Optional[StrictStr] = None
38
37
  __properties: ClassVar[List[str]] = [
39
38
  "price_delta",
40
39
  "price",
41
40
  "allocation",
42
41
  "execution_id",
43
- "client_order_id",
44
42
  ]
45
43
 
46
44
  model_config = ConfigDict(
@@ -95,11 +93,6 @@ class TPSL(BaseModel):
95
93
  if self.execution_id is None and "execution_id" in self.model_fields_set:
96
94
  _dict["execution_id"] = None
97
95
 
98
- # set to None if client_order_id (nullable) is None
99
- # and model_fields_set contains the field
100
- if self.client_order_id is None and "client_order_id" in self.model_fields_set:
101
- _dict["client_order_id"] = None
102
-
103
96
  return _dict
104
97
 
105
98
  @classmethod
@@ -117,7 +110,6 @@ class TPSL(BaseModel):
117
110
  "price": obj.get("price"),
118
111
  "allocation": obj.get("allocation"),
119
112
  "execution_id": obj.get("execution_id"),
120
- "client_order_id": obj.get("client_order_id"),
121
113
  }
122
114
  )
123
115
  return _obj
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: crypticorn
3
- Version: 2.14.0
3
+ Version: 2.16.0
4
4
  Summary: Maximise Your Crypto Trading Profits with Machine Learning
5
5
  Author-email: Crypticorn <timon@crypticorn.com>
6
6
  License-Expression: MIT
@@ -1,5 +1,5 @@
1
1
  crypticorn/__init__.py,sha256=ctrwe5CQtYhnetHYPgSmC0CIHa4xbDsLZvpY38tfEow,423
2
- crypticorn/client.py,sha256=JoS1cTA9cKo_7zgmSdjy3GbjEUxE8pYWN4XAXtujX1M,6023
2
+ crypticorn/client.py,sha256=eGEk-AuoVFCuXdygMXqCivj6G56O_I_JH14C8lKW3Dk,6062
3
3
  crypticorn/auth/__init__.py,sha256=JAl1tBLK9pYLr_-YKaj581c-c94PWLoqnatTIVAVvMM,81
4
4
  crypticorn/auth/main.py,sha256=FHLsAbp2mXDlmcPmLKc29qaD1dBev65V3DNKLyfz4Tw,1012
5
5
  crypticorn/auth/client/__init__.py,sha256=do16xS84uXvVoJuWERjb9RwlOaLy4UF4uKBZWczFC3c,5291
@@ -61,7 +61,7 @@ crypticorn/cli/init.py,sha256=-ZVXbrHb_Yg8a4bKyflgi9Om5GHVYo3vX91X44b90ZA,4100
61
61
  crypticorn/cli/version.py,sha256=OVDxeL80eMgZsFgw2cDSzFfuaRToDfnYAVOQTpkoMWs,206
62
62
  crypticorn/cli/templates/Dockerfile,sha256=89KlphaXJH51L7Vs4B928WmwYcMtpvLmKGyoDAhOcMw,726
63
63
  crypticorn/cli/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
64
- crypticorn/cli/templates/auth.py,sha256=Q1TxlA7qzhjvrqp1xz1aV2vGnj3DKFNN-VSl3o0B-dI,983
64
+ crypticorn/cli/templates/auth.py,sha256=i27-Ts-Eiyv6_WRshOp7NV5OYUNbw2-kiN5Ll0k2UOA,839
65
65
  crypticorn/cli/templates/dependabot.yml,sha256=ct5ieB8KAV1KLzoYKUNm6dZ9wKG_P_JQHgRjZUfT54w,861
66
66
  crypticorn/cli/templates/merge-env.sh,sha256=BNPrDTihII0yG2gehBkWwWj0GqHmb6xwmrAgwFpl8dA,554
67
67
  crypticorn/cli/templates/ruff.yml,sha256=gWicFFTzC4nToSmRkIIGipos8CZ447YG0kebBCJhtJE,319
@@ -69,20 +69,20 @@ crypticorn/common/__init__.py,sha256=DXEuUU_kaLBSBcvpiFie_ROuK5XEZuTMIfsg-BZE0iE
69
69
  crypticorn/common/ansi_colors.py,sha256=-tMlUTE8NI7TPv7uj0kGRe-SI2hGaUNPKBFI_dfiZy0,1392
70
70
  crypticorn/common/auth.py,sha256=b7jhR8k7bQFfgokI_Eqji0MpfiyD4EhCoddefUSqs6Y,9925
71
71
  crypticorn/common/decorators.py,sha256=t5Y3vSJ-gt0n2vOYYjYN0dtzNXvZxrJs2SEItpzG8oo,1127
72
- crypticorn/common/enums.py,sha256=YE7ObydyWAKO8MOSQBwk9M1PzzaPvlnxc6Dbpu78QMk,787
72
+ crypticorn/common/enums.py,sha256=x-2xe0NUI8kB6DNg9GLjqAjYOsFAGwP-jdM1YgIt9AI,1381
73
73
  crypticorn/common/errors.py,sha256=qPCYpmxw0uZNmQw9RYj9vUsg-_R5aRn7fYE8qxTAnyA,30129
74
74
  crypticorn/common/exceptions.py,sha256=4oT58wcL9zQuqYU8op_36uZ1Kzt7JRCccu-o_usgqtU,6392
75
75
  crypticorn/common/logging.py,sha256=n-qaYreRNFVAFRUd91hzYoaTExNLysd9cgEXm-v6eJY,4440
76
76
  crypticorn/common/middleware.py,sha256=O7XiXPimNYUhF9QTv6yFUTVlb91-SK-3CfTrWMNP6Ck,1011
77
77
  crypticorn/common/mixins.py,sha256=l7XQrBISaee6fDZXy96k0HnQ18XYocjTUXlNpVxhaOY,2206
78
78
  crypticorn/common/openapi.py,sha256=D8bCpCVVzYQptHrJ7SYOgCxI3R_d0cjW9KMOBq-x0xk,279
79
- crypticorn/common/pagination.py,sha256=BYMNB4JUW9eeiTw1q3CyHXaT_-hk_BrSXAOqvif08Ek,2334
80
- crypticorn/common/scopes.py,sha256=H-ow744XhArgN2Zb7SESP5ZZ6fNjA5oY1eHuZsuF4Gw,2827
79
+ crypticorn/common/pagination.py,sha256=eD4S8t-NHECWYCFTu3taeDv07_AzPBbopoKSZRBSiCM,2621
80
+ crypticorn/common/scopes.py,sha256=ZreqkW_XshlyLJjciAfaIFmcy4HO6RnCsbbkjCw-fxc,2900
81
81
  crypticorn/common/urls.py,sha256=v23H2gevTNZ6HMRXDPiuc8znBbNdNqj0JTAdm5Hhms8,1223
82
82
  crypticorn/common/utils.py,sha256=LcWudhcjZtULg87yYghh5muTYdHvk3UmkEAXmX7xgLk,3073
83
- crypticorn/common/warnings.py,sha256=TNDiIbwFYh18zs20QnrYHYL9pB20Ip1tTzv5KlnDSUE,2426
83
+ crypticorn/common/warnings.py,sha256=YU7JKIJpVAoBlqCXaaP61t7jzLibB1G0yjQ1NHUJ6xU,2755
84
84
  crypticorn/common/router/admin_router.py,sha256=x81s1gxhH7nLf7txqAIjVxrNgQmXsA1YG7g9v9KJwHA,3740
85
- crypticorn/common/router/status_router.py,sha256=it6kfvx_Pn4Rv06fmViwhwr-m6f4WuSgcZwc6VTaMz4,868
85
+ crypticorn/common/router/status_router.py,sha256=NpLaCF2a1jApo95iolKzzUKuyHbpf4Xwc0sM7dxQ9DE,873
86
86
  crypticorn/hive/__init__.py,sha256=hRfTlEzEql4msytdUC_04vfaHzVKG5CGZle1M-9QFgY,81
87
87
  crypticorn/hive/main.py,sha256=bSOY2PBi4VV_mvN1c0FQAI-En28f8HnmoZDm3g9X6lU,3103
88
88
  crypticorn/hive/utils.py,sha256=5T2GYnIFazXgAdUlO03xWqcMWhWkM82cfWvwsO8geHE,2040
@@ -164,7 +164,7 @@ crypticorn/metrics/main.py,sha256=hVOlPsn2_1rrAGBxUO95KO2rpVlJUmIdzdUAs0ORgXw,35
164
164
  crypticorn/metrics/client/__init__.py,sha256=bu9LdJkG7vljgmLVKhoSY08ooOuEo0AhoHt7-zDVyek,2720
165
165
  crypticorn/metrics/client/api_client.py,sha256=pGWJuO-mgxlUdhJGwkScf7CviGzjDrmUAiU0LXasQY4,26934
166
166
  crypticorn/metrics/client/api_response.py,sha256=WhxwYDSMm6wPixp9CegO8dJzjFxDz3JF1yCq9s0ZqKE,639
167
- crypticorn/metrics/client/configuration.py,sha256=BQNgtkCNhI3uB8qCV54TMf85b3sFzQYvXGMbhar5KSM,19202
167
+ crypticorn/metrics/client/configuration.py,sha256=wA1hBWEINMM_AlZg-DAv1AelmkjBERB5d2gA66aEwSM,19158
168
168
  crypticorn/metrics/client/exceptions.py,sha256=UegnYftFlQDXAQv8BmD20yRzTtWpjTHcuOymTBWmgeE,6421
169
169
  crypticorn/metrics/client/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
170
170
  crypticorn/metrics/client/rest.py,sha256=pWeYnpTfTV7L5U6Kli3b7i8VrmqdG8sskqSnTHPIoQo,7025
@@ -179,7 +179,7 @@ crypticorn/metrics/client/api/quote_currencies_api.py,sha256=H4c3zOp5eTTUrRMlMH-
179
179
  crypticorn/metrics/client/api/status_api.py,sha256=_Ou_EGmjPyv32G-S4QKfRemdpGG6FUsgOkbGDfYaFp0,19633
180
180
  crypticorn/metrics/client/api/tokens_api.py,sha256=x5a-YAeAgFJm-pN4K3-lOM-WPVYAxoBr-AYb-oxhysM,19522
181
181
  crypticorn/metrics/client/models/__init__.py,sha256=hPPLh2kQc3KNHCLeOT7AS2HXu04LEeSp_G4ME7BCmW4,1475
182
- crypticorn/metrics/client/models/api_error_identifier.py,sha256=HIkojVyK4-6AEPduX54epI2JfggecaGu1tnqIozOtmk,5242
182
+ crypticorn/metrics/client/models/api_error_identifier.py,sha256=S55hnVdp469PkfdWCcpKlpEfO9_gLzQZPuY-gKwOuBA,5238
183
183
  crypticorn/metrics/client/models/api_error_level.py,sha256=soxtGExzlBja-Jux_jguro-1taLAGcKmGSHlxTKTYR4,772
184
184
  crypticorn/metrics/client/models/api_error_type.py,sha256=Y8pCAtdcS4zfTetTZj9x60BFJwSXD6ro-8NU38aEDyQ,811
185
185
  crypticorn/metrics/client/models/exception_detail.py,sha256=GIIa_LNZrtXlWCMLh1OAgEO6UoBo3m0kiCk788wMbuA,3853
@@ -246,7 +246,7 @@ crypticorn/trade/client/api/status_api.py,sha256=YtSZh7xuOl_vq9xfPqdnfuOEA9EpftO
246
246
  crypticorn/trade/client/api/strategies_api.py,sha256=L3BlHboDnDtsWf70_kGBU839zy4lGveF-NP1h0bogJQ,53156
247
247
  crypticorn/trade/client/api/trading_actions_api.py,sha256=9S5KbugtgkJAuGjujgs-ZvE5MV-4orhABkbIJxKcjsw,32735
248
248
  crypticorn/trade/client/models/__init__.py,sha256=KoJgwPzyY4gzwmIZuwmKyQx0Enn2-qcy7J0ahBjXIAc,2789
249
- crypticorn/trade/client/models/api_error_identifier.py,sha256=0jsIFXqmm_BQmPeLJ21dnvxDONU1uGbUMSK1GEOuiiA,5286
249
+ crypticorn/trade/client/models/api_error_identifier.py,sha256=vOnj2swcFn3qU5brKTmd4BJEOkBLaeE07iMzLnFLogE,5282
250
250
  crypticorn/trade/client/models/api_error_level.py,sha256=8Sau6q3K29fg-EIGnex0yrw7gXJZLmyGTicYIXcehmY,816
251
251
  crypticorn/trade/client/models/api_error_type.py,sha256=34NBgCP296pQx8doVpeqlhopPfBfRH1I-135YVtN19Y,855
252
252
  crypticorn/trade/client/models/bot.py,sha256=8A6rbg_HW4NWhXknOyHmHEHxzfhRC1TqODOLh6BHrm0,5513
@@ -260,28 +260,28 @@ crypticorn/trade/client/models/exchange_key_create.py,sha256=n6yiomatf9FGB5GQpB2
260
260
  crypticorn/trade/client/models/exchange_key_update.py,sha256=6rBpzKIAzRq7Wg_P-nNSOJKoljTm3Lq3B8cVkZtM50E,3725
261
261
  crypticorn/trade/client/models/execution_ids.py,sha256=0MHzL43PX-aMjKxCkcLTPbszG5q0KruZPwlColEZW58,2963
262
262
  crypticorn/trade/client/models/futures_balance.py,sha256=BePXd2bngkHjcKtZButMFcsU4RS7MU1QkvzF46q8X6U,4191
263
- crypticorn/trade/client/models/futures_trading_action.py,sha256=xoLIn29jzLJ8G1zw0MGFZ2erKevDcEL07e5jxcnVJg0,9528
264
- crypticorn/trade/client/models/futures_trading_action_create.py,sha256=OFfA5N21Caen4aKN5VwReAFI79S-bo_C6yho4fZe0jQ,8325
263
+ crypticorn/trade/client/models/futures_trading_action.py,sha256=5zGeKBTwHsHC3aQkM7fDE98yH__bC9kw-PtALhrumKI,8551
264
+ crypticorn/trade/client/models/futures_trading_action_create.py,sha256=Aho_Do2h-Wegaq-tCWJGD3kRpueISnfkdp5c9KvTi9A,8149
265
265
  crypticorn/trade/client/models/log_level.py,sha256=Fv2WlajyWZTiXBRNyFQ1x0xqh5nrzKL_0k7fyt92bHA,813
266
266
  crypticorn/trade/client/models/margin_mode.py,sha256=kG1ElFka5OYQI6YQOH1JhGY3qo-ZQhdMTqIYDpqPlgs,777
267
267
  crypticorn/trade/client/models/market_type.py,sha256=OkEE5lAPw2FAudw_98BLcyglEXQhb2uhFeP82W7WFPM,755
268
268
  crypticorn/trade/client/models/notification.py,sha256=QbvFkWGjolyba7trkG8SdJE1gfzdUftfywCMUe1IDwQ,4416
269
269
  crypticorn/trade/client/models/notification_create.py,sha256=iXkqvDhFYGoKW4yCWkJP7_5q-DPpJpE0FqWKqK21CE8,3749
270
270
  crypticorn/trade/client/models/notification_update.py,sha256=a1tpTjKWA2kOqBJxxeyheMdIOZ_2NtAYu9X7R5h1M3c,3034
271
- crypticorn/trade/client/models/order.py,sha256=bMFpJTrK6kYsTSk0iYNA3rgImJo6BD27saUV5fhAxOo,12439
271
+ crypticorn/trade/client/models/order.py,sha256=AQu1qV9GkbLqckgY8AW9m_hd6YDpStAIFJCEUmMewZw,11883
272
272
  crypticorn/trade/client/models/order_status.py,sha256=dQkbXtPSW4bVGQOVL0M9-Q_fMpAUa4v3uwuuUwXNcnY,852
273
273
  crypticorn/trade/client/models/post_futures_action.py,sha256=KBI3lPayQQNfQP8Attow9tkuo9zimJk19cKQQb9JK-w,3123
274
- crypticorn/trade/client/models/spot_trading_action_create.py,sha256=UTj86pfeb_ryKGeFJy88Z-AsOUeY_flg9YUy9K6bBhU,7426
274
+ crypticorn/trade/client/models/spot_trading_action_create.py,sha256=g6Puv6BTLhgtTYqeE-QhC9WqM4wfoNQOLFsqEXZC0aQ,7494
275
275
  crypticorn/trade/client/models/strategy.py,sha256=Jlgt4oxseebVkbhthQDH3ayOLO2muYnyP6Kg23W02ic,5797
276
276
  crypticorn/trade/client/models/strategy_create.py,sha256=jtr1lUcafFzqYQKiuSXWEqfhLiT4WPj9yw63jtVW6kI,5285
277
277
  crypticorn/trade/client/models/strategy_exchange_info.py,sha256=xRUX4vY4HTZP2hq6joaCvO01MVRbZul_qYGrvzdZpr4,2801
278
278
  crypticorn/trade/client/models/strategy_update.py,sha256=f7UsKSlNardj5h6uqHYbacjWWQscXkFDqcLOoefa28M,5048
279
- crypticorn/trade/client/models/tpsl.py,sha256=lLPVSvLETgLMFqH9wEBUTQXY6aaydMifEt47mYbfw-A,4111
279
+ crypticorn/trade/client/models/tpsl.py,sha256=iuHwBxHaUdtJn7NhcTE6Skzv4nmoSF8jEnf7_bTglwk,3730
280
280
  crypticorn/trade/client/models/tpsl_create.py,sha256=nX4i2BGWv5rmu3SLgRngfvEMFOWa3CIy0G3fyoxI-e4,3351
281
281
  crypticorn/trade/client/models/trading_action_type.py,sha256=BysUEOl85zs79EA2zOcDN1EExcpQdABaJ4Jz08_z8VU,857
282
- crypticorn-2.14.0.dist-info/licenses/LICENSE,sha256=HonAVvzFXkP2C1d7D3ByIKPwjGH8NcHTAQvKH7uvOHQ,1856
283
- crypticorn-2.14.0.dist-info/METADATA,sha256=h1yBg54-gCo7aHAwg7Tvp0O_dq0ZsaJ6Ipq2ZO3F0n0,9993
284
- crypticorn-2.14.0.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
285
- crypticorn-2.14.0.dist-info/entry_points.txt,sha256=d_xHsGvUTebPveVUK0SrpDFQ5ZRSjlI7lNCc11sn2PM,59
286
- crypticorn-2.14.0.dist-info/top_level.txt,sha256=EP3NY216qIBYfmvGl0L2Zc9ItP0DjGSkiYqd9xJwGcM,11
287
- crypticorn-2.14.0.dist-info/RECORD,,
282
+ crypticorn-2.16.0.dist-info/licenses/LICENSE,sha256=HonAVvzFXkP2C1d7D3ByIKPwjGH8NcHTAQvKH7uvOHQ,1856
283
+ crypticorn-2.16.0.dist-info/METADATA,sha256=nNS1g853y575ebtgifCqxjGYtwmi1tkBp2g_IcmC450,9993
284
+ crypticorn-2.16.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
285
+ crypticorn-2.16.0.dist-info/entry_points.txt,sha256=d_xHsGvUTebPveVUK0SrpDFQ5ZRSjlI7lNCc11sn2PM,59
286
+ crypticorn-2.16.0.dist-info/top_level.txt,sha256=EP3NY216qIBYfmvGl0L2Zc9ItP0DjGSkiYqd9xJwGcM,11
287
+ crypticorn-2.16.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.8.0)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5