crypticorn 2.15.0__py3-none-any.whl → 2.17.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.
- crypticorn/__init__.py +2 -2
- crypticorn/auth/client/api/admin_api.py +397 -13
- crypticorn/auth/client/api/auth_api.py +3610 -341
- crypticorn/auth/client/api/service_api.py +249 -7
- crypticorn/auth/client/api/user_api.py +2295 -179
- crypticorn/auth/client/api/wallet_api.py +1468 -81
- crypticorn/auth/client/configuration.py +2 -2
- 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/cli/init.py +1 -1
- crypticorn/cli/templates/.env.docker.temp +3 -0
- crypticorn/cli/templates/.env.example.temp +4 -0
- crypticorn/cli/templates/Dockerfile +5 -2
- crypticorn/client.py +226 -59
- crypticorn/common/__init__.py +1 -0
- crypticorn/common/auth.py +45 -14
- crypticorn/common/decorators.py +1 -2
- crypticorn/common/enums.py +0 -2
- crypticorn/common/errors.py +10 -0
- crypticorn/common/metrics.py +30 -0
- crypticorn/common/middleware.py +94 -1
- crypticorn/common/pagination.py +252 -18
- crypticorn/common/router/admin_router.py +2 -2
- crypticorn/common/router/status_router.py +40 -2
- crypticorn/common/scopes.py +2 -0
- crypticorn/common/warnings.py +8 -0
- crypticorn/dex/__init__.py +6 -0
- crypticorn/dex/client/__init__.py +49 -0
- crypticorn/dex/client/api/__init__.py +6 -0
- crypticorn/dex/client/api/admin_api.py +2986 -0
- crypticorn/dex/client/api/signals_api.py +1798 -0
- crypticorn/dex/client/api/status_api.py +892 -0
- crypticorn/dex/client/api_client.py +758 -0
- crypticorn/dex/client/api_response.py +20 -0
- crypticorn/dex/client/configuration.py +620 -0
- crypticorn/dex/client/exceptions.py +220 -0
- crypticorn/dex/client/models/__init__.py +30 -0
- crypticorn/dex/client/models/api_error_identifier.py +121 -0
- crypticorn/dex/client/models/api_error_level.py +37 -0
- crypticorn/dex/client/models/api_error_type.py +37 -0
- crypticorn/dex/client/models/exception_detail.py +117 -0
- crypticorn/dex/client/models/log_level.py +38 -0
- crypticorn/dex/client/models/paginated_response_signal_with_token.py +134 -0
- crypticorn/dex/client/models/risk.py +86 -0
- crypticorn/dex/client/models/signal_overview_stats.py +158 -0
- crypticorn/dex/client/models/signal_volume.py +84 -0
- crypticorn/dex/client/models/signal_with_token.py +163 -0
- crypticorn/dex/client/models/token_data.py +127 -0
- crypticorn/dex/client/models/token_detail.py +116 -0
- crypticorn/dex/client/py.typed +0 -0
- crypticorn/dex/client/rest.py +217 -0
- crypticorn/dex/main.py +1 -0
- crypticorn/hive/client/api/admin_api.py +1173 -47
- crypticorn/hive/client/api/data_api.py +499 -17
- crypticorn/hive/client/api/models_api.py +1595 -87
- crypticorn/hive/client/api/status_api.py +397 -16
- crypticorn/hive/client/api_client.py +0 -5
- crypticorn/hive/client/models/api_error_identifier.py +1 -1
- crypticorn/hive/client/models/coin_info.py +1 -1
- crypticorn/hive/client/models/exception_detail.py +1 -1
- crypticorn/hive/client/models/target_info.py +1 -1
- crypticorn/hive/client/rest.py +23 -4
- crypticorn/hive/main.py +99 -25
- crypticorn/hive/utils.py +2 -2
- crypticorn/klines/client/api/admin_api.py +1173 -47
- crypticorn/klines/client/api/change_in_timeframe_api.py +269 -11
- crypticorn/klines/client/api/funding_rates_api.py +315 -11
- crypticorn/klines/client/api/ohlcv_data_api.py +390 -11
- crypticorn/klines/client/api/status_api.py +397 -16
- crypticorn/klines/client/api/symbols_api.py +216 -11
- crypticorn/klines/client/api/udf_api.py +1268 -51
- crypticorn/klines/client/api_client.py +0 -5
- crypticorn/klines/client/models/api_error_identifier.py +3 -1
- crypticorn/klines/client/models/exception_detail.py +1 -1
- crypticorn/klines/client/models/ohlcv.py +1 -1
- crypticorn/klines/client/models/symbol_group.py +1 -1
- crypticorn/klines/client/models/udf_config.py +1 -1
- crypticorn/klines/client/rest.py +23 -4
- crypticorn/klines/main.py +89 -12
- crypticorn/metrics/client/api/admin_api.py +1173 -47
- crypticorn/metrics/client/api/exchanges_api.py +1370 -145
- crypticorn/metrics/client/api/indicators_api.py +622 -17
- crypticorn/metrics/client/api/logs_api.py +296 -11
- crypticorn/metrics/client/api/marketcap_api.py +1207 -67
- crypticorn/metrics/client/api/markets_api.py +343 -11
- crypticorn/metrics/client/api/quote_currencies_api.py +228 -11
- crypticorn/metrics/client/api/status_api.py +397 -16
- crypticorn/metrics/client/api/tokens_api.py +382 -15
- crypticorn/metrics/client/api_client.py +0 -5
- crypticorn/metrics/client/configuration.py +4 -2
- crypticorn/metrics/client/models/exception_detail.py +1 -1
- crypticorn/metrics/client/models/exchange_mapping.py +1 -1
- crypticorn/metrics/client/models/marketcap_ranking.py +1 -1
- crypticorn/metrics/client/models/marketcap_symbol_ranking.py +1 -1
- crypticorn/metrics/client/models/ohlcv.py +1 -1
- crypticorn/metrics/client/rest.py +23 -4
- crypticorn/metrics/main.py +113 -19
- crypticorn/pay/client/api/admin_api.py +1585 -57
- crypticorn/pay/client/api/now_payments_api.py +961 -39
- crypticorn/pay/client/api/payments_api.py +562 -17
- crypticorn/pay/client/api/products_api.py +880 -30
- crypticorn/pay/client/api/status_api.py +397 -16
- crypticorn/pay/client/api_client.py +0 -5
- crypticorn/pay/client/configuration.py +2 -2
- crypticorn/pay/client/models/api_error_identifier.py +7 -7
- crypticorn/pay/client/models/exception_detail.py +1 -1
- crypticorn/pay/client/models/now_create_invoice_req.py +1 -1
- crypticorn/pay/client/models/now_create_invoice_res.py +1 -1
- crypticorn/pay/client/models/product.py +1 -1
- crypticorn/pay/client/models/product_create.py +1 -1
- crypticorn/pay/client/models/product_update.py +1 -1
- 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 +11 -1
- crypticorn/trade/client/api/__init__.py +0 -1
- crypticorn/trade/client/api/admin_api.py +1184 -55
- crypticorn/trade/client/api/api_keys_api.py +1678 -162
- crypticorn/trade/client/api/bots_api.py +7563 -187
- crypticorn/trade/client/api/exchanges_api.py +565 -19
- crypticorn/trade/client/api/notifications_api.py +1290 -116
- crypticorn/trade/client/api/orders_api.py +393 -55
- crypticorn/trade/client/api/status_api.py +397 -13
- crypticorn/trade/client/api/strategies_api.py +1133 -77
- crypticorn/trade/client/api/trading_actions_api.py +786 -65
- crypticorn/trade/client/models/__init__.py +11 -0
- crypticorn/trade/client/models/actions_count.py +88 -0
- crypticorn/trade/client/models/api_error_identifier.py +8 -7
- crypticorn/trade/client/models/bot.py +7 -18
- crypticorn/trade/client/models/bot_create.py +17 -1
- crypticorn/trade/client/models/bot_update.py +17 -1
- crypticorn/trade/client/models/exchange.py +6 -1
- crypticorn/trade/client/models/exchange_key.py +1 -1
- crypticorn/trade/client/models/exchange_key_balance.py +111 -0
- crypticorn/trade/client/models/exchange_key_create.py +17 -1
- crypticorn/trade/client/models/exchange_key_update.py +17 -1
- crypticorn/trade/client/models/execution_ids.py +1 -1
- crypticorn/trade/client/models/futures_balance.py +27 -25
- crypticorn/trade/client/models/futures_trading_action.py +6 -28
- crypticorn/trade/client/models/futures_trading_action_create.py +10 -13
- crypticorn/trade/client/models/notification.py +17 -1
- crypticorn/trade/client/models/notification_create.py +18 -2
- crypticorn/trade/client/models/notification_update.py +17 -1
- crypticorn/trade/client/models/order.py +2 -14
- crypticorn/trade/client/models/orders_count.py +88 -0
- crypticorn/trade/client/models/paginated_response_futures_trading_action.py +134 -0
- crypticorn/trade/client/models/paginated_response_order.py +134 -0
- crypticorn/trade/client/models/pn_l.py +95 -0
- crypticorn/trade/client/models/post_futures_action.py +1 -1
- crypticorn/trade/client/models/spot_balance.py +109 -0
- crypticorn/trade/client/models/spot_trading_action_create.py +4 -1
- crypticorn/trade/client/models/strategy.py +22 -4
- crypticorn/trade/client/models/strategy_create.py +23 -5
- crypticorn/trade/client/models/strategy_exchange_info.py +16 -4
- crypticorn/trade/client/models/strategy_update.py +19 -3
- crypticorn/trade/client/models/tpsl.py +4 -27
- crypticorn/trade/client/models/tpsl_create.py +6 -19
- crypticorn/trade/client/rest.py +23 -4
- crypticorn/trade/main.py +15 -12
- {crypticorn-2.15.0.dist-info → crypticorn-2.17.0.dist-info}/METADATA +65 -20
- {crypticorn-2.15.0.dist-info → crypticorn-2.17.0.dist-info}/RECORD +167 -132
- crypticorn/trade/client/api/futures_trading_panel_api.py +0 -1285
- {crypticorn-2.15.0.dist-info → crypticorn-2.17.0.dist-info}/WHEEL +0 -0
- {crypticorn-2.15.0.dist-info → crypticorn-2.17.0.dist-info}/entry_points.txt +0 -0
- {crypticorn-2.15.0.dist-info → crypticorn-2.17.0.dist-info}/licenses/LICENSE +0 -0
- {crypticorn-2.15.0.dist-info → crypticorn-2.17.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,95 @@
|
|
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
|
21
|
+
from typing import Any, ClassVar, Dict, List, Union
|
22
|
+
from typing import Optional, Set
|
23
|
+
from typing_extensions import Self
|
24
|
+
|
25
|
+
|
26
|
+
class PnL(BaseModel):
|
27
|
+
"""
|
28
|
+
The profit and loss of a bot by timestamp. In the case of sampling, the PnL is the sum of the PnLs between the prior timestamp and the current timestamp.
|
29
|
+
""" # noqa: E501
|
30
|
+
|
31
|
+
timestamp: StrictInt = Field(description="Timestamp of the order")
|
32
|
+
pnl: Union[StrictFloat, StrictInt] = Field(
|
33
|
+
description="The profit and loss of the order"
|
34
|
+
)
|
35
|
+
cum_pnl: Union[StrictFloat, StrictInt] = Field(
|
36
|
+
description="The cumulative profit and loss of the bot until the order (inclusive)"
|
37
|
+
)
|
38
|
+
__properties: ClassVar[List[str]] = ["timestamp", "pnl", "cum_pnl"]
|
39
|
+
|
40
|
+
model_config = ConfigDict(
|
41
|
+
populate_by_name=True,
|
42
|
+
validate_assignment=True,
|
43
|
+
protected_namespaces=(),
|
44
|
+
)
|
45
|
+
|
46
|
+
def to_str(self) -> str:
|
47
|
+
"""Returns the string representation of the model using alias"""
|
48
|
+
return pprint.pformat(self.model_dump(by_alias=True))
|
49
|
+
|
50
|
+
def to_json(self) -> str:
|
51
|
+
"""Returns the JSON representation of the model using alias"""
|
52
|
+
# TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead
|
53
|
+
return json.dumps(self.to_dict())
|
54
|
+
|
55
|
+
@classmethod
|
56
|
+
def from_json(cls, json_str: str) -> Optional[Self]:
|
57
|
+
"""Create an instance of PnL from a JSON string"""
|
58
|
+
return cls.from_dict(json.loads(json_str))
|
59
|
+
|
60
|
+
def to_dict(self) -> Dict[str, Any]:
|
61
|
+
"""Return the dictionary representation of the model using alias.
|
62
|
+
|
63
|
+
This has the following differences from calling pydantic's
|
64
|
+
`self.model_dump(by_alias=True)`:
|
65
|
+
|
66
|
+
* `None` is only added to the output dict for nullable fields that
|
67
|
+
were set at model initialization. Other fields with value `None`
|
68
|
+
are ignored.
|
69
|
+
"""
|
70
|
+
excluded_fields: Set[str] = set([])
|
71
|
+
|
72
|
+
_dict = self.model_dump(
|
73
|
+
by_alias=True,
|
74
|
+
exclude=excluded_fields,
|
75
|
+
exclude_none=True,
|
76
|
+
)
|
77
|
+
return _dict
|
78
|
+
|
79
|
+
@classmethod
|
80
|
+
def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]:
|
81
|
+
"""Create an instance of PnL from a dict"""
|
82
|
+
if obj is None:
|
83
|
+
return None
|
84
|
+
|
85
|
+
if not isinstance(obj, dict):
|
86
|
+
return cls.model_validate(obj)
|
87
|
+
|
88
|
+
_obj = cls.model_validate(
|
89
|
+
{
|
90
|
+
"timestamp": obj.get("timestamp"),
|
91
|
+
"pnl": obj.get("pnl"),
|
92
|
+
"cum_pnl": obj.get("cum_pnl"),
|
93
|
+
}
|
94
|
+
)
|
95
|
+
return _obj
|
@@ -0,0 +1,109 @@
|
|
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]] = Field(
|
40
|
+
default=0, description="Allocated balance for bots. Added on runtime."
|
41
|
+
)
|
42
|
+
__properties: ClassVar[List[str]] = [
|
43
|
+
"asset",
|
44
|
+
"balance",
|
45
|
+
"available",
|
46
|
+
"frozen",
|
47
|
+
"allocated",
|
48
|
+
]
|
49
|
+
|
50
|
+
model_config = ConfigDict(
|
51
|
+
populate_by_name=True,
|
52
|
+
validate_assignment=True,
|
53
|
+
protected_namespaces=(),
|
54
|
+
)
|
55
|
+
|
56
|
+
def to_str(self) -> str:
|
57
|
+
"""Returns the string representation of the model using alias"""
|
58
|
+
return pprint.pformat(self.model_dump(by_alias=True))
|
59
|
+
|
60
|
+
def to_json(self) -> str:
|
61
|
+
"""Returns the JSON representation of the model using alias"""
|
62
|
+
# TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead
|
63
|
+
return json.dumps(self.to_dict())
|
64
|
+
|
65
|
+
@classmethod
|
66
|
+
def from_json(cls, json_str: str) -> Optional[Self]:
|
67
|
+
"""Create an instance of SpotBalance from a JSON string"""
|
68
|
+
return cls.from_dict(json.loads(json_str))
|
69
|
+
|
70
|
+
def to_dict(self) -> Dict[str, Any]:
|
71
|
+
"""Return the dictionary representation of the model using alias.
|
72
|
+
|
73
|
+
This has the following differences from calling pydantic's
|
74
|
+
`self.model_dump(by_alias=True)`:
|
75
|
+
|
76
|
+
* `None` is only added to the output dict for nullable fields that
|
77
|
+
were set at model initialization. Other fields with value `None`
|
78
|
+
are ignored.
|
79
|
+
"""
|
80
|
+
excluded_fields: Set[str] = set([])
|
81
|
+
|
82
|
+
_dict = self.model_dump(
|
83
|
+
by_alias=True,
|
84
|
+
exclude=excluded_fields,
|
85
|
+
exclude_none=True,
|
86
|
+
)
|
87
|
+
return _dict
|
88
|
+
|
89
|
+
@classmethod
|
90
|
+
def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]:
|
91
|
+
"""Create an instance of SpotBalance from a dict"""
|
92
|
+
if obj is None:
|
93
|
+
return None
|
94
|
+
|
95
|
+
if not isinstance(obj, dict):
|
96
|
+
return cls.model_validate(obj)
|
97
|
+
|
98
|
+
_obj = cls.model_validate(
|
99
|
+
{
|
100
|
+
"asset": obj.get("asset"),
|
101
|
+
"balance": obj.get("balance"),
|
102
|
+
"available": obj.get("available"),
|
103
|
+
"frozen": obj.get("frozen"),
|
104
|
+
"allocated": (
|
105
|
+
obj.get("allocated") if obj.get("allocated") is not None else 0
|
106
|
+
),
|
107
|
+
}
|
108
|
+
)
|
109
|
+
return _obj
|
@@ -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(
|
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)"
|
@@ -50,15 +50,18 @@ 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:
|
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")
|
64
|
+
additional_properties: Dict[str, Any] = {}
|
62
65
|
__properties: ClassVar[List[str]] = [
|
63
66
|
"created_at",
|
64
67
|
"updated_at",
|
@@ -103,8 +106,13 @@ class Strategy(BaseModel):
|
|
103
106
|
* `None` is only added to the output dict for nullable fields that
|
104
107
|
were set at model initialization. Other fields with value `None`
|
105
108
|
are ignored.
|
109
|
+
* Fields in `self.additional_properties` are added to the output dict.
|
106
110
|
"""
|
107
|
-
excluded_fields: Set[str] = set(
|
111
|
+
excluded_fields: Set[str] = set(
|
112
|
+
[
|
113
|
+
"additional_properties",
|
114
|
+
]
|
115
|
+
)
|
108
116
|
|
109
117
|
_dict = self.model_dump(
|
110
118
|
by_alias=True,
|
@@ -118,6 +126,11 @@ class Strategy(BaseModel):
|
|
118
126
|
if _item_exchanges:
|
119
127
|
_items.append(_item_exchanges.to_dict())
|
120
128
|
_dict["exchanges"] = _items
|
129
|
+
# puts key-value pairs in additional_properties in the top level
|
130
|
+
if self.additional_properties is not None:
|
131
|
+
for _key, _value in self.additional_properties.items():
|
132
|
+
_dict[_key] = _value
|
133
|
+
|
121
134
|
# set to None if margin_mode (nullable) is None
|
122
135
|
# and model_fields_set contains the field
|
123
136
|
if self.margin_mode is None and "margin_mode" in self.model_fields_set:
|
@@ -157,4 +170,9 @@ class Strategy(BaseModel):
|
|
157
170
|
"market_type": obj.get("market_type"),
|
158
171
|
}
|
159
172
|
)
|
173
|
+
# store additional fields in additional_properties
|
174
|
+
for _key in obj.keys():
|
175
|
+
if _key not in cls.__properties:
|
176
|
+
_obj.additional_properties[_key] = obj.get(_key)
|
177
|
+
|
160
178
|
return _obj
|
@@ -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,
|
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,15 +41,18 @@ 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:
|
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")
|
55
|
+
additional_properties: Dict[str, Any] = {}
|
53
56
|
__properties: ClassVar[List[str]] = [
|
54
57
|
"name",
|
55
58
|
"description",
|
@@ -91,8 +94,13 @@ class StrategyCreate(BaseModel):
|
|
91
94
|
* `None` is only added to the output dict for nullable fields that
|
92
95
|
were set at model initialization. Other fields with value `None`
|
93
96
|
are ignored.
|
97
|
+
* Fields in `self.additional_properties` are added to the output dict.
|
94
98
|
"""
|
95
|
-
excluded_fields: Set[str] = set(
|
99
|
+
excluded_fields: Set[str] = set(
|
100
|
+
[
|
101
|
+
"additional_properties",
|
102
|
+
]
|
103
|
+
)
|
96
104
|
|
97
105
|
_dict = self.model_dump(
|
98
106
|
by_alias=True,
|
@@ -106,6 +114,11 @@ class StrategyCreate(BaseModel):
|
|
106
114
|
if _item_exchanges:
|
107
115
|
_items.append(_item_exchanges.to_dict())
|
108
116
|
_dict["exchanges"] = _items
|
117
|
+
# puts key-value pairs in additional_properties in the top level
|
118
|
+
if self.additional_properties is not None:
|
119
|
+
for _key, _value in self.additional_properties.items():
|
120
|
+
_dict[_key] = _value
|
121
|
+
|
109
122
|
# set to None if margin_mode (nullable) is None
|
110
123
|
# and model_fields_set contains the field
|
111
124
|
if self.margin_mode is None and "margin_mode" in self.model_fields_set:
|
@@ -142,4 +155,9 @@ class StrategyCreate(BaseModel):
|
|
142
155
|
"market_type": obj.get("market_type"),
|
143
156
|
}
|
144
157
|
)
|
158
|
+
# store additional fields in additional_properties
|
159
|
+
for _key in obj.keys():
|
160
|
+
if _key not in cls.__properties:
|
161
|
+
_obj.additional_properties[_key] = obj.get(_key)
|
162
|
+
|
145
163
|
return _obj
|
@@ -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
|
-
{
|
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,10 +36,11 @@ 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
|
+
additional_properties: Dict[str, Any] = {}
|
43
44
|
__properties: ClassVar[List[str]] = [
|
44
45
|
"name",
|
45
46
|
"description",
|
@@ -77,8 +78,13 @@ class StrategyUpdate(BaseModel):
|
|
77
78
|
* `None` is only added to the output dict for nullable fields that
|
78
79
|
were set at model initialization. Other fields with value `None`
|
79
80
|
are ignored.
|
81
|
+
* Fields in `self.additional_properties` are added to the output dict.
|
80
82
|
"""
|
81
|
-
excluded_fields: Set[str] = set(
|
83
|
+
excluded_fields: Set[str] = set(
|
84
|
+
[
|
85
|
+
"additional_properties",
|
86
|
+
]
|
87
|
+
)
|
82
88
|
|
83
89
|
_dict = self.model_dump(
|
84
90
|
by_alias=True,
|
@@ -92,6 +98,11 @@ class StrategyUpdate(BaseModel):
|
|
92
98
|
if _item_exchanges:
|
93
99
|
_items.append(_item_exchanges.to_dict())
|
94
100
|
_dict["exchanges"] = _items
|
101
|
+
# puts key-value pairs in additional_properties in the top level
|
102
|
+
if self.additional_properties is not None:
|
103
|
+
for _key, _value in self.additional_properties.items():
|
104
|
+
_dict[_key] = _value
|
105
|
+
|
95
106
|
# set to None if name (nullable) is None
|
96
107
|
# and model_fields_set contains the field
|
97
108
|
if self.name is None and "name" in self.model_fields_set:
|
@@ -144,4 +155,9 @@ class StrategyUpdate(BaseModel):
|
|
144
155
|
"performance_fee": obj.get("performance_fee"),
|
145
156
|
}
|
146
157
|
)
|
158
|
+
# store additional fields in additional_properties
|
159
|
+
for _key in obj.keys():
|
160
|
+
if _key not in cls.__properties:
|
161
|
+
_obj.additional_properties[_key] = obj.get(_key)
|
162
|
+
|
147
163
|
return _obj
|
@@ -28,20 +28,14 @@ class TPSL(BaseModel):
|
|
28
28
|
Runtime fields for take profit and stop loss
|
29
29
|
""" # noqa: E501
|
30
30
|
|
31
|
-
price_delta:
|
32
|
-
|
31
|
+
price_delta: StrictStr = Field(
|
32
|
+
description="The price delta to calculate the limit price from the current market price, e.g. 1.01 for a TP of 1% on long"
|
33
|
+
)
|
33
34
|
allocation: StrictStr = Field(
|
34
35
|
description="Percentage of the open order to sell. All allocations must sum up to 1. Use this allocation again when closing the order."
|
35
36
|
)
|
36
37
|
execution_id: Optional[StrictStr] = None
|
37
|
-
|
38
|
-
__properties: ClassVar[List[str]] = [
|
39
|
-
"price_delta",
|
40
|
-
"price",
|
41
|
-
"allocation",
|
42
|
-
"execution_id",
|
43
|
-
"client_order_id",
|
44
|
-
]
|
38
|
+
__properties: ClassVar[List[str]] = ["price_delta", "allocation", "execution_id"]
|
45
39
|
|
46
40
|
model_config = ConfigDict(
|
47
41
|
populate_by_name=True,
|
@@ -80,26 +74,11 @@ class TPSL(BaseModel):
|
|
80
74
|
exclude=excluded_fields,
|
81
75
|
exclude_none=True,
|
82
76
|
)
|
83
|
-
# set to None if price_delta (nullable) is None
|
84
|
-
# and model_fields_set contains the field
|
85
|
-
if self.price_delta is None and "price_delta" in self.model_fields_set:
|
86
|
-
_dict["price_delta"] = None
|
87
|
-
|
88
|
-
# set to None if price (nullable) is None
|
89
|
-
# and model_fields_set contains the field
|
90
|
-
if self.price is None and "price" in self.model_fields_set:
|
91
|
-
_dict["price"] = None
|
92
|
-
|
93
77
|
# set to None if execution_id (nullable) is None
|
94
78
|
# and model_fields_set contains the field
|
95
79
|
if self.execution_id is None and "execution_id" in self.model_fields_set:
|
96
80
|
_dict["execution_id"] = None
|
97
81
|
|
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
82
|
return _dict
|
104
83
|
|
105
84
|
@classmethod
|
@@ -114,10 +93,8 @@ class TPSL(BaseModel):
|
|
114
93
|
_obj = cls.model_validate(
|
115
94
|
{
|
116
95
|
"price_delta": obj.get("price_delta"),
|
117
|
-
"price": obj.get("price"),
|
118
96
|
"allocation": obj.get("allocation"),
|
119
97
|
"execution_id": obj.get("execution_id"),
|
120
|
-
"client_order_id": obj.get("client_order_id"),
|
121
98
|
}
|
122
99
|
)
|
123
100
|
return _obj
|
@@ -18,7 +18,7 @@ import re # noqa: F401
|
|
18
18
|
import json
|
19
19
|
|
20
20
|
from pydantic import BaseModel, ConfigDict, Field, StrictStr
|
21
|
-
from typing import Any, ClassVar, Dict, List
|
21
|
+
from typing import Any, ClassVar, Dict, List
|
22
22
|
from typing import Optional, Set
|
23
23
|
from typing_extensions import Self
|
24
24
|
|
@@ -28,12 +28,13 @@ class TPSLCreate(BaseModel):
|
|
28
28
|
Model for take profit and stop loss
|
29
29
|
""" # noqa: E501
|
30
30
|
|
31
|
-
price_delta:
|
32
|
-
|
31
|
+
price_delta: StrictStr = Field(
|
32
|
+
description="The price delta to calculate the limit price from the current market price, e.g. 1.01 for a TP of 1% on long"
|
33
|
+
)
|
33
34
|
allocation: StrictStr = Field(
|
34
35
|
description="Percentage of the open order to sell. All allocations must sum up to 1. Use this allocation again when closing the order."
|
35
36
|
)
|
36
|
-
__properties: ClassVar[List[str]] = ["price_delta", "
|
37
|
+
__properties: ClassVar[List[str]] = ["price_delta", "allocation"]
|
37
38
|
|
38
39
|
model_config = ConfigDict(
|
39
40
|
populate_by_name=True,
|
@@ -72,16 +73,6 @@ class TPSLCreate(BaseModel):
|
|
72
73
|
exclude=excluded_fields,
|
73
74
|
exclude_none=True,
|
74
75
|
)
|
75
|
-
# set to None if price_delta (nullable) is None
|
76
|
-
# and model_fields_set contains the field
|
77
|
-
if self.price_delta is None and "price_delta" in self.model_fields_set:
|
78
|
-
_dict["price_delta"] = None
|
79
|
-
|
80
|
-
# set to None if price (nullable) is None
|
81
|
-
# and model_fields_set contains the field
|
82
|
-
if self.price is None and "price" in self.model_fields_set:
|
83
|
-
_dict["price"] = None
|
84
|
-
|
85
76
|
return _dict
|
86
77
|
|
87
78
|
@classmethod
|
@@ -94,10 +85,6 @@ class TPSLCreate(BaseModel):
|
|
94
85
|
return cls.model_validate(obj)
|
95
86
|
|
96
87
|
_obj = cls.model_validate(
|
97
|
-
{
|
98
|
-
"price_delta": obj.get("price_delta"),
|
99
|
-
"price": obj.get("price"),
|
100
|
-
"allocation": obj.get("allocation"),
|
101
|
-
}
|
88
|
+
{"price_delta": obj.get("price_delta"), "allocation": obj.get("allocation")}
|
102
89
|
)
|
103
90
|
return _obj
|
crypticorn/trade/client/rest.py
CHANGED
@@ -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
|
-
#
|
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
|
-
|
197
|
-
|
198
|
-
|
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,
|
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.
|
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)
|