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.
Files changed (87) hide show
  1. crypticorn/__init__.py +2 -2
  2. crypticorn/auth/client/api/admin_api.py +415 -13
  3. crypticorn/auth/client/api/auth_api.py +2622 -113
  4. crypticorn/auth/client/api/service_api.py +258 -7
  5. crypticorn/auth/client/api/user_api.py +2485 -270
  6. crypticorn/auth/client/api/wallet_api.py +1518 -77
  7. crypticorn/auth/client/models/create_api_key_request.py +2 -1
  8. crypticorn/auth/client/models/get_api_keys200_response_inner.py +2 -1
  9. crypticorn/auth/client/rest.py +23 -4
  10. crypticorn/auth/main.py +8 -5
  11. crypticorn/client.py +227 -59
  12. crypticorn/common/__init__.py +0 -1
  13. crypticorn/common/auth.py +2 -1
  14. crypticorn/common/metrics.py +4 -6
  15. crypticorn/common/middleware.py +10 -5
  16. crypticorn/common/pagination.py +137 -18
  17. crypticorn/common/router/admin_router.py +1 -1
  18. crypticorn/common/utils.py +2 -1
  19. crypticorn/common/warnings.py +1 -0
  20. crypticorn/hive/client/api/admin_api.py +1234 -51
  21. crypticorn/hive/client/api/data_api.py +517 -13
  22. crypticorn/hive/client/api/models_api.py +1657 -83
  23. crypticorn/hive/client/api/status_api.py +415 -13
  24. crypticorn/hive/client/models/api_error_identifier.py +1 -1
  25. crypticorn/hive/client/rest.py +23 -4
  26. crypticorn/hive/main.py +99 -25
  27. crypticorn/klines/client/api/admin_api.py +1234 -51
  28. crypticorn/klines/client/api/change_in_timeframe_api.py +278 -7
  29. crypticorn/klines/client/api/funding_rates_api.py +324 -7
  30. crypticorn/klines/client/api/ohlcv_data_api.py +399 -7
  31. crypticorn/klines/client/api/status_api.py +415 -13
  32. crypticorn/klines/client/api/symbols_api.py +225 -7
  33. crypticorn/klines/client/api/udf_api.py +1393 -120
  34. crypticorn/klines/client/models/api_error_identifier.py +3 -1
  35. crypticorn/klines/client/rest.py +23 -4
  36. crypticorn/klines/main.py +89 -12
  37. crypticorn/metrics/client/api/admin_api.py +1234 -51
  38. crypticorn/metrics/client/api/exchanges_api.py +1405 -140
  39. crypticorn/metrics/client/api/indicators_api.py +640 -13
  40. crypticorn/metrics/client/api/logs_api.py +305 -7
  41. crypticorn/metrics/client/api/marketcap_api.py +1240 -60
  42. crypticorn/metrics/client/api/markets_api.py +352 -7
  43. crypticorn/metrics/client/api/quote_currencies_api.py +237 -7
  44. crypticorn/metrics/client/api/status_api.py +415 -13
  45. crypticorn/metrics/client/api/tokens_api.py +400 -13
  46. crypticorn/metrics/client/configuration.py +4 -2
  47. crypticorn/metrics/client/rest.py +23 -4
  48. crypticorn/metrics/main.py +113 -19
  49. crypticorn/pay/client/api/admin_api.py +1720 -126
  50. crypticorn/pay/client/api/now_payments_api.py +1013 -42
  51. crypticorn/pay/client/api/payments_api.py +580 -13
  52. crypticorn/pay/client/api/products_api.py +915 -25
  53. crypticorn/pay/client/api/status_api.py +415 -13
  54. crypticorn/pay/client/configuration.py +2 -2
  55. crypticorn/pay/client/models/api_error_identifier.py +7 -7
  56. crypticorn/pay/client/models/scope.py +1 -0
  57. crypticorn/pay/client/rest.py +23 -4
  58. crypticorn/pay/main.py +10 -6
  59. crypticorn/trade/client/__init__.py +2 -1
  60. crypticorn/trade/client/api/__init__.py +0 -1
  61. crypticorn/trade/client/api/admin_api.py +1718 -123
  62. crypticorn/trade/client/api/api_keys_api.py +1596 -103
  63. crypticorn/trade/client/api/bots_api.py +1106 -47
  64. crypticorn/trade/client/api/exchanges_api.py +592 -19
  65. crypticorn/trade/client/api/notifications_api.py +1340 -112
  66. crypticorn/trade/client/api/orders_api.py +240 -7
  67. crypticorn/trade/client/api/status_api.py +415 -13
  68. crypticorn/trade/client/api/strategies_api.py +1170 -69
  69. crypticorn/trade/client/api/trading_actions_api.py +650 -19
  70. crypticorn/trade/client/models/__init__.py +2 -0
  71. crypticorn/trade/client/models/exchange.py +6 -1
  72. crypticorn/trade/client/models/exchange_key_balance.py +111 -0
  73. crypticorn/trade/client/models/futures_balance.py +27 -25
  74. crypticorn/trade/client/models/spot_balance.py +110 -0
  75. crypticorn/trade/client/models/strategy.py +5 -3
  76. crypticorn/trade/client/models/strategy_create.py +6 -4
  77. crypticorn/trade/client/models/strategy_exchange_info.py +16 -4
  78. crypticorn/trade/client/models/strategy_update.py +2 -2
  79. crypticorn/trade/client/rest.py +23 -4
  80. crypticorn/trade/main.py +15 -12
  81. {crypticorn-2.17.0rc1.dist-info → crypticorn-2.17.0rc3.dist-info}/METADATA +64 -20
  82. {crypticorn-2.17.0rc1.dist-info → crypticorn-2.17.0rc3.dist-info}/RECORD +86 -85
  83. crypticorn/trade/client/api/futures_trading_panel_api.py +0 -1285
  84. {crypticorn-2.17.0rc1.dist-info → crypticorn-2.17.0rc3.dist-info}/WHEEL +0 -0
  85. {crypticorn-2.17.0rc1.dist-info → crypticorn-2.17.0rc3.dist-info}/entry_points.txt +0 -0
  86. {crypticorn-2.17.0rc1.dist-info → crypticorn-2.17.0rc3.dist-info}/licenses/LICENSE +0 -0
  87. {crypticorn-2.17.0rc1.dist-info → crypticorn-2.17.0rc3.dist-info}/top_level.txt +0 -0
@@ -24,6 +24,7 @@ from crypticorn.trade.client.models.bot_update import BotUpdate
24
24
  from crypticorn.trade.client.models.exception_detail import ExceptionDetail
25
25
  from crypticorn.trade.client.models.exchange import Exchange
26
26
  from crypticorn.trade.client.models.exchange_key import ExchangeKey
27
+ from crypticorn.trade.client.models.exchange_key_balance import ExchangeKeyBalance
27
28
  from crypticorn.trade.client.models.exchange_key_create import ExchangeKeyCreate
28
29
  from crypticorn.trade.client.models.exchange_key_update import ExchangeKeyUpdate
29
30
  from crypticorn.trade.client.models.execution_ids import ExecutionIds
@@ -41,6 +42,7 @@ from crypticorn.trade.client.models.notification_update import NotificationUpdat
41
42
  from crypticorn.trade.client.models.order import Order
42
43
  from crypticorn.trade.client.models.order_status import OrderStatus
43
44
  from crypticorn.trade.client.models.post_futures_action import PostFuturesAction
45
+ from crypticorn.trade.client.models.spot_balance import SpotBalance
44
46
  from crypticorn.trade.client.models.spot_trading_action_create import (
45
47
  SpotTradingActionCreate,
46
48
  )
@@ -20,7 +20,7 @@ from typing_extensions import Self
20
20
 
21
21
  class Exchange(str, Enum):
22
22
  """
23
- Supported exchanges for trading
23
+ All exchanges used in the crypticorn ecosystem. Refer to the APIs for support for a specific usecase (data, trading, etc.).
24
24
  """
25
25
 
26
26
  """
@@ -28,7 +28,12 @@ class Exchange(str, Enum):
28
28
  """
29
29
  KUCOIN = "kucoin"
30
30
  BINGX = "bingx"
31
+ BINANCE = "binance"
32
+ BYBIT = "bybit"
31
33
  HYPERLIQUID = "hyperliquid"
34
+ BITGET = "bitget"
35
+ GATEIO = "gateio"
36
+ BITSTAMP = "bitstamp"
32
37
 
33
38
  @classmethod
34
39
  def from_json(cls, json_str: str) -> Self:
@@ -0,0 +1,111 @@
1
+ # coding: utf-8
2
+
3
+ """
4
+ Trading API
5
+
6
+ API for automated trading and exchange interface. This API is used to trade on the exchange and manage bots, API keys, orders, and more.
7
+
8
+ The version of the OpenAPI document: 1.0.0
9
+ Generated by OpenAPI Generator (https://openapi-generator.tech)
10
+
11
+ Do not edit the class manually.
12
+ """ # noqa: E501
13
+
14
+
15
+ from __future__ import annotations
16
+ import pprint
17
+ import re # noqa: F401
18
+ import json
19
+
20
+ from pydantic import BaseModel, ConfigDict, Field, StrictStr
21
+ from typing import Any, ClassVar, Dict, List
22
+ from crypticorn.trade.client.models.futures_balance import FuturesBalance
23
+ from crypticorn.trade.client.models.spot_balance import SpotBalance
24
+ from typing import Optional, Set
25
+ from typing_extensions import Self
26
+
27
+
28
+ class ExchangeKeyBalance(BaseModel):
29
+ """
30
+ ExchangeKeyBalance
31
+ """ # noqa: E501
32
+
33
+ api_key_id: StrictStr = Field(description="API key ID.")
34
+ futures: FuturesBalance = Field(description="Futures balance information.")
35
+ spot: List[SpotBalance] = Field(description="Spot balance information.")
36
+ __properties: ClassVar[List[str]] = ["api_key_id", "futures", "spot"]
37
+
38
+ model_config = ConfigDict(
39
+ populate_by_name=True,
40
+ validate_assignment=True,
41
+ protected_namespaces=(),
42
+ )
43
+
44
+ def to_str(self) -> str:
45
+ """Returns the string representation of the model using alias"""
46
+ return pprint.pformat(self.model_dump(by_alias=True))
47
+
48
+ def to_json(self) -> str:
49
+ """Returns the JSON representation of the model using alias"""
50
+ # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead
51
+ return json.dumps(self.to_dict())
52
+
53
+ @classmethod
54
+ def from_json(cls, json_str: str) -> Optional[Self]:
55
+ """Create an instance of ExchangeKeyBalance from a JSON string"""
56
+ return cls.from_dict(json.loads(json_str))
57
+
58
+ def to_dict(self) -> Dict[str, Any]:
59
+ """Return the dictionary representation of the model using alias.
60
+
61
+ This has the following differences from calling pydantic's
62
+ `self.model_dump(by_alias=True)`:
63
+
64
+ * `None` is only added to the output dict for nullable fields that
65
+ were set at model initialization. Other fields with value `None`
66
+ are ignored.
67
+ """
68
+ excluded_fields: Set[str] = set([])
69
+
70
+ _dict = self.model_dump(
71
+ by_alias=True,
72
+ exclude=excluded_fields,
73
+ exclude_none=True,
74
+ )
75
+ # override the default output from pydantic by calling `to_dict()` of futures
76
+ if self.futures:
77
+ _dict["futures"] = self.futures.to_dict()
78
+ # override the default output from pydantic by calling `to_dict()` of each item in spot (list)
79
+ _items = []
80
+ if self.spot:
81
+ for _item_spot in self.spot:
82
+ if _item_spot:
83
+ _items.append(_item_spot.to_dict())
84
+ _dict["spot"] = _items
85
+ return _dict
86
+
87
+ @classmethod
88
+ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]:
89
+ """Create an instance of ExchangeKeyBalance from a dict"""
90
+ if obj is None:
91
+ return None
92
+
93
+ if not isinstance(obj, dict):
94
+ return cls.model_validate(obj)
95
+
96
+ _obj = cls.model_validate(
97
+ {
98
+ "api_key_id": obj.get("api_key_id"),
99
+ "futures": (
100
+ FuturesBalance.from_dict(obj["futures"])
101
+ if obj.get("futures") is not None
102
+ else None
103
+ ),
104
+ "spot": (
105
+ [SpotBalance.from_dict(_item) for _item in obj["spot"]]
106
+ if obj.get("spot") is not None
107
+ else None
108
+ ),
109
+ }
110
+ )
111
+ return _obj
@@ -28,29 +28,35 @@ class FuturesBalance(BaseModel):
28
28
  Model for futures balance
29
29
  """ # noqa: E501
30
30
 
31
- api_key_id: StrictStr = Field(description="API key ID", alias="apiKeyId")
32
- asset: StrictStr = Field(description="Asset/Currency code")
33
- balance: Union[StrictFloat, StrictInt] = Field(description="Total balance/equity")
31
+ asset: StrictStr = Field(description="Asset the balance values are in")
32
+ equity: Union[StrictFloat, StrictInt] = Field(
33
+ description="Net asset value including unrealized profit and loss"
34
+ )
35
+ balance: Union[StrictFloat, StrictInt] = Field(
36
+ description="Actual account balance (equity - unrealized)"
37
+ )
34
38
  available: Union[StrictFloat, StrictInt] = Field(
35
39
  description="Available balance for trading/withdrawal"
36
40
  )
37
- unrealized_pnl: Union[StrictFloat, StrictInt] = Field(
38
- description="Unrealized profit and loss", alias="unrealizedPnl"
41
+ unrealized: Union[StrictFloat, StrictInt] = Field(
42
+ description="Unrealized profit and loss"
39
43
  )
40
- used_margin: Optional[Union[StrictFloat, StrictInt]] = Field(
41
- default=None, alias="usedMargin"
44
+ used: Union[StrictFloat, StrictInt] = Field(
45
+ description="Margin used in open positions"
42
46
  )
43
- frozen_amount: Optional[Union[StrictFloat, StrictInt]] = Field(
44
- default=None, alias="frozenAmount"
47
+ frozen: Union[StrictFloat, StrictInt] = Field(
48
+ description="Frozen funds not available for use"
45
49
  )
50
+ allocated: Optional[Union[StrictFloat, StrictInt]] = None
46
51
  __properties: ClassVar[List[str]] = [
47
- "apiKeyId",
48
52
  "asset",
53
+ "equity",
49
54
  "balance",
50
55
  "available",
51
- "unrealizedPnl",
52
- "usedMargin",
53
- "frozenAmount",
56
+ "unrealized",
57
+ "used",
58
+ "frozen",
59
+ "allocated",
54
60
  ]
55
61
 
56
62
  model_config = ConfigDict(
@@ -90,15 +96,10 @@ class FuturesBalance(BaseModel):
90
96
  exclude=excluded_fields,
91
97
  exclude_none=True,
92
98
  )
93
- # set to None if used_margin (nullable) is None
94
- # and model_fields_set contains the field
95
- if self.used_margin is None and "used_margin" in self.model_fields_set:
96
- _dict["usedMargin"] = None
97
-
98
- # set to None if frozen_amount (nullable) is None
99
+ # set to None if allocated (nullable) is None
99
100
  # and model_fields_set contains the field
100
- if self.frozen_amount is None and "frozen_amount" in self.model_fields_set:
101
- _dict["frozenAmount"] = None
101
+ if self.allocated is None and "allocated" in self.model_fields_set:
102
+ _dict["allocated"] = None
102
103
 
103
104
  return _dict
104
105
 
@@ -113,13 +114,14 @@ class FuturesBalance(BaseModel):
113
114
 
114
115
  _obj = cls.model_validate(
115
116
  {
116
- "apiKeyId": obj.get("apiKeyId"),
117
117
  "asset": obj.get("asset"),
118
+ "equity": obj.get("equity"),
118
119
  "balance": obj.get("balance"),
119
120
  "available": obj.get("available"),
120
- "unrealizedPnl": obj.get("unrealizedPnl"),
121
- "usedMargin": obj.get("usedMargin"),
122
- "frozenAmount": obj.get("frozenAmount"),
121
+ "unrealized": obj.get("unrealized"),
122
+ "used": obj.get("used"),
123
+ "frozen": obj.get("frozen"),
124
+ "allocated": obj.get("allocated"),
123
125
  }
124
126
  )
125
127
  return _obj
@@ -0,0 +1,110 @@
1
+ # coding: utf-8
2
+
3
+ """
4
+ Trading API
5
+
6
+ API for automated trading and exchange interface. This API is used to trade on the exchange and manage bots, API keys, orders, and more.
7
+
8
+ The version of the OpenAPI document: 1.0.0
9
+ Generated by OpenAPI Generator (https://openapi-generator.tech)
10
+
11
+ Do not edit the class manually.
12
+ """ # noqa: E501
13
+
14
+
15
+ from __future__ import annotations
16
+ import pprint
17
+ import re # noqa: F401
18
+ import json
19
+
20
+ from pydantic import BaseModel, ConfigDict, Field, StrictFloat, StrictInt, StrictStr
21
+ from typing import Any, ClassVar, Dict, List, Optional, Union
22
+ from typing import Optional, Set
23
+ from typing_extensions import Self
24
+
25
+
26
+ class SpotBalance(BaseModel):
27
+ """
28
+ Model for spot balance
29
+ """ # noqa: E501
30
+
31
+ asset: StrictStr = Field(description="Asset/Currency code")
32
+ balance: Union[StrictFloat, StrictInt] = Field(description="Balance of the asset")
33
+ available: Union[StrictFloat, StrictInt] = Field(
34
+ description="Available balance for trading/withdrawal"
35
+ )
36
+ frozen: Union[StrictFloat, StrictInt] = Field(
37
+ description="Frozen funds not available for use"
38
+ )
39
+ allocated: Optional[Union[StrictFloat, StrictInt]] = None
40
+ __properties: ClassVar[List[str]] = [
41
+ "asset",
42
+ "balance",
43
+ "available",
44
+ "frozen",
45
+ "allocated",
46
+ ]
47
+
48
+ model_config = ConfigDict(
49
+ populate_by_name=True,
50
+ validate_assignment=True,
51
+ protected_namespaces=(),
52
+ )
53
+
54
+ def to_str(self) -> str:
55
+ """Returns the string representation of the model using alias"""
56
+ return pprint.pformat(self.model_dump(by_alias=True))
57
+
58
+ def to_json(self) -> str:
59
+ """Returns the JSON representation of the model using alias"""
60
+ # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead
61
+ return json.dumps(self.to_dict())
62
+
63
+ @classmethod
64
+ def from_json(cls, json_str: str) -> Optional[Self]:
65
+ """Create an instance of SpotBalance from a JSON string"""
66
+ return cls.from_dict(json.loads(json_str))
67
+
68
+ def to_dict(self) -> Dict[str, Any]:
69
+ """Return the dictionary representation of the model using alias.
70
+
71
+ This has the following differences from calling pydantic's
72
+ `self.model_dump(by_alias=True)`:
73
+
74
+ * `None` is only added to the output dict for nullable fields that
75
+ were set at model initialization. Other fields with value `None`
76
+ are ignored.
77
+ """
78
+ excluded_fields: Set[str] = set([])
79
+
80
+ _dict = self.model_dump(
81
+ by_alias=True,
82
+ exclude=excluded_fields,
83
+ exclude_none=True,
84
+ )
85
+ # set to None if allocated (nullable) is None
86
+ # and model_fields_set contains the field
87
+ if self.allocated is None and "allocated" in self.model_fields_set:
88
+ _dict["allocated"] = None
89
+
90
+ return _dict
91
+
92
+ @classmethod
93
+ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]:
94
+ """Create an instance of SpotBalance from a dict"""
95
+ if obj is None:
96
+ return None
97
+
98
+ if not isinstance(obj, dict):
99
+ return cls.model_validate(obj)
100
+
101
+ _obj = cls.model_validate(
102
+ {
103
+ "asset": obj.get("asset"),
104
+ "balance": obj.get("balance"),
105
+ "available": obj.get("available"),
106
+ "frozen": obj.get("frozen"),
107
+ "allocated": obj.get("allocated"),
108
+ }
109
+ )
110
+ return _obj
@@ -50,14 +50,16 @@ class Strategy(BaseModel):
50
50
  description="Whether the strategy is enabled. If false, no bots will be created or updated for this strategy, open trades will be rejected. Existing bots will be marked as stopping."
51
51
  )
52
52
  performance_fee: Union[
53
- Annotated[float, Field(le=1.0, strict=True)],
54
- Annotated[int, Field(le=1, strict=True)],
53
+ Annotated[float, Field(le=1.0, strict=True, ge=0.0)],
54
+ Annotated[int, Field(le=1, strict=True, ge=0)],
55
55
  ] = Field(description="Performance fee for the strategy")
56
56
  identifier: StrictStr = Field(
57
57
  description="Unique human readable identifier for the strategy e.g. 'daily_trend_momentum'"
58
58
  )
59
59
  margin_mode: Optional[MarginMode] = None
60
- leverage: StrictInt = Field(description="Leverage for the strategy")
60
+ leverage: Annotated[int, Field(strict=True, ge=1)] = Field(
61
+ description="Leverage for the strategy"
62
+ )
61
63
  market_type: MarketType = Field(description="Market of operation of the strategy")
62
64
  __properties: ClassVar[List[str]] = [
63
65
  "created_at",
@@ -17,7 +17,7 @@ import pprint
17
17
  import re # noqa: F401
18
18
  import json
19
19
 
20
- from pydantic import BaseModel, ConfigDict, Field, StrictBool, StrictInt, StrictStr
20
+ from pydantic import BaseModel, ConfigDict, Field, StrictBool, StrictStr
21
21
  from typing import Any, ClassVar, Dict, List, Optional, Union
22
22
  from typing_extensions import Annotated
23
23
  from crypticorn.trade.client.models.margin_mode import MarginMode
@@ -41,14 +41,16 @@ class StrategyCreate(BaseModel):
41
41
  description="Whether the strategy is enabled. If false, no bots will be created or updated for this strategy, open trades will be rejected. Existing bots will be marked as stopping."
42
42
  )
43
43
  performance_fee: Union[
44
- Annotated[float, Field(le=1.0, strict=True)],
45
- Annotated[int, Field(le=1, strict=True)],
44
+ Annotated[float, Field(le=1.0, strict=True, ge=0.0)],
45
+ Annotated[int, Field(le=1, strict=True, ge=0)],
46
46
  ] = Field(description="Performance fee for the strategy")
47
47
  identifier: StrictStr = Field(
48
48
  description="Unique human readable identifier for the strategy e.g. 'daily_trend_momentum'"
49
49
  )
50
50
  margin_mode: Optional[MarginMode] = None
51
- leverage: StrictInt = Field(description="Leverage for the strategy")
51
+ leverage: Annotated[int, Field(strict=True, ge=1)] = Field(
52
+ description="Leverage for the strategy"
53
+ )
52
54
  market_type: MarketType = Field(description="Market of operation of the strategy")
53
55
  __properties: ClassVar[List[str]] = [
54
56
  "name",
@@ -17,8 +17,8 @@ import pprint
17
17
  import re # noqa: F401
18
18
  import json
19
19
 
20
- from pydantic import BaseModel, ConfigDict, Field, StrictInt
21
- from typing import Any, ClassVar, Dict, List
20
+ from pydantic import BaseModel, ConfigDict, Field, StrictInt, StrictStr
21
+ from typing import Any, ClassVar, Dict, List, Optional
22
22
  from crypticorn.trade.client.models.exchange import Exchange
23
23
  from typing import Optional, Set
24
24
  from typing_extensions import Self
@@ -30,10 +30,14 @@ class StrategyExchangeInfo(BaseModel):
30
30
  """ # noqa: E501
31
31
 
32
32
  exchange: Exchange = Field(description="Exchange name. Of type Exchange")
33
+ base_asset: Optional[StrictStr] = Field(
34
+ default="USDT",
35
+ description="Base asset for the strategy. This is the asset that will be used to trade with. Default is USDT.",
36
+ )
33
37
  min_amount: StrictInt = Field(
34
38
  description="Minimum amount for the strategy on the exchange"
35
39
  )
36
- __properties: ClassVar[List[str]] = ["exchange", "min_amount"]
40
+ __properties: ClassVar[List[str]] = ["exchange", "base_asset", "min_amount"]
37
41
 
38
42
  model_config = ConfigDict(
39
43
  populate_by_name=True,
@@ -84,6 +88,14 @@ class StrategyExchangeInfo(BaseModel):
84
88
  return cls.model_validate(obj)
85
89
 
86
90
  _obj = cls.model_validate(
87
- {"exchange": obj.get("exchange"), "min_amount": obj.get("min_amount")}
91
+ {
92
+ "exchange": obj.get("exchange"),
93
+ "base_asset": (
94
+ obj.get("base_asset")
95
+ if obj.get("base_asset") is not None
96
+ else "USDT"
97
+ ),
98
+ "min_amount": obj.get("min_amount"),
99
+ }
88
100
  )
89
101
  return _obj
@@ -36,8 +36,8 @@ class StrategyUpdate(BaseModel):
36
36
  enabled: Optional[StrictBool] = None
37
37
  performance_fee: Optional[
38
38
  Union[
39
- Annotated[float, Field(le=1.0, strict=True)],
40
- Annotated[int, Field(le=1, strict=True)],
39
+ Annotated[float, Field(le=1.0, strict=True, ge=0.0)],
40
+ Annotated[int, Field(le=1, strict=True, ge=0)],
41
41
  ]
42
42
  ] = None
43
43
  __properties: ClassVar[List[str]] = [
@@ -77,6 +77,7 @@ class RESTClientObject:
77
77
 
78
78
  self.pool_manager: Optional[aiohttp.ClientSession] = None
79
79
  self.retry_client: Optional[aiohttp_retry.RetryClient] = None
80
+ self.is_sync: bool = False # Track whether this is sync or async mode
80
81
 
81
82
  async def close(self) -> None:
82
83
  if self.pool_manager:
@@ -170,7 +171,9 @@ class RESTClientObject:
170
171
 
171
172
  pool_manager: Union[aiohttp.ClientSession, aiohttp_retry.RetryClient]
172
173
 
173
- # https pool manager
174
+ # For sync operations, always use a fresh session
175
+ should_close_session = False
176
+
174
177
  if self.pool_manager is None:
175
178
  self.pool_manager = aiohttp.ClientSession(
176
179
  connector=aiohttp.TCPConnector(
@@ -178,6 +181,9 @@ class RESTClientObject:
178
181
  ),
179
182
  trust_env=True,
180
183
  )
184
+ # Only close session automatically in sync mode
185
+ should_close_session = self.is_sync
186
+
181
187
  pool_manager = self.pool_manager
182
188
 
183
189
  if self.retries is not None and method in ALLOW_RETRY_METHODS:
@@ -193,6 +199,19 @@ class RESTClientObject:
193
199
  )
194
200
  pool_manager = self.retry_client
195
201
 
196
- r = await pool_manager.request(**args)
197
-
198
- return RESTResponse(r)
202
+ try:
203
+ r = await pool_manager.request(**args)
204
+ # For sessions we're about to close, read the data immediately
205
+ if should_close_session:
206
+ response = RESTResponse(r)
207
+ await response.read() # Read data before closing session
208
+ return response
209
+ else:
210
+ return RESTResponse(r)
211
+ finally:
212
+ if should_close_session:
213
+ if self.retry_client is not None:
214
+ await self.retry_client.close()
215
+ self.retry_client = None
216
+ await self.pool_manager.close()
217
+ self.pool_manager = None
crypticorn/trade/main.py CHANGED
@@ -1,12 +1,11 @@
1
1
  from __future__ import annotations
2
2
  from typing import TYPE_CHECKING, Optional
3
- from crypticorn.trade import (
3
+ from crypticorn.trade.client import (
4
4
  ApiClient,
5
5
  APIKeysApi,
6
6
  BotsApi,
7
7
  Configuration,
8
8
  ExchangesApi,
9
- FuturesTradingPanelApi,
10
9
  NotificationsApi,
11
10
  OrdersApi,
12
11
  StatusApi,
@@ -26,19 +25,23 @@ class TradeClient:
26
25
  config_class = Configuration
27
26
 
28
27
  def __init__(
29
- self, config: Configuration, http_client: Optional[ClientSession] = None
28
+ self,
29
+ config: Configuration,
30
+ http_client: Optional[ClientSession] = None,
31
+ is_sync: bool = False,
30
32
  ):
31
33
  self.config = config
32
34
  self.base_client = ApiClient(configuration=self.config)
33
35
  if http_client is not None:
34
36
  self.base_client.rest_client.pool_manager = http_client
37
+ # Pass sync context to REST client for proper session management
38
+ self.base_client.rest_client.is_sync = is_sync
35
39
  # Instantiate all the endpoint clients
36
- self.bots = BotsApi(self.base_client)
37
- self.exchanges = ExchangesApi(self.base_client)
38
- self.notifications = NotificationsApi(self.base_client)
39
- self.orders = OrdersApi(self.base_client)
40
- self.status = StatusApi(self.base_client)
41
- self.strategies = StrategiesApi(self.base_client)
42
- self.actions = TradingActionsApi(self.base_client)
43
- self.futures = FuturesTradingPanelApi(self.base_client)
44
- self.keys = APIKeysApi(self.base_client)
40
+ self.bots = BotsApi(self.base_client, is_sync=is_sync)
41
+ self.exchanges = ExchangesApi(self.base_client, is_sync=is_sync)
42
+ self.notifications = NotificationsApi(self.base_client, is_sync=is_sync)
43
+ self.orders = OrdersApi(self.base_client, is_sync=is_sync)
44
+ self.status = StatusApi(self.base_client, is_sync=is_sync)
45
+ self.strategies = StrategiesApi(self.base_client, is_sync=is_sync)
46
+ self.actions = TradingActionsApi(self.base_client, is_sync=is_sync)
47
+ self.keys = APIKeysApi(self.base_client, is_sync=is_sync)