x10-python-trading-starknet 0.0.1__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.
- x10/__init__.py +0 -0
- x10/config.py +13 -0
- x10/errors.py +2 -0
- x10/perpetual/__init__.py +0 -0
- x10/perpetual/accounts.py +86 -0
- x10/perpetual/amounts.py +55 -0
- x10/perpetual/assets.py +71 -0
- x10/perpetual/balances.py +15 -0
- x10/perpetual/candles.py +18 -0
- x10/perpetual/configuration.py +86 -0
- x10/perpetual/fees.py +16 -0
- x10/perpetual/funding_rates.py +11 -0
- x10/perpetual/markets.py +125 -0
- x10/perpetual/order_object.py +201 -0
- x10/perpetual/orderbook.py +211 -0
- x10/perpetual/orderbooks.py +17 -0
- x10/perpetual/orders.py +179 -0
- x10/perpetual/positions.py +51 -0
- x10/perpetual/simple_client/simple_trading_client.py +216 -0
- x10/perpetual/stream_client/__init__.py +3 -0
- x10/perpetual/stream_client/perpetual_stream_connection.py +108 -0
- x10/perpetual/stream_client/stream_client.py +83 -0
- x10/perpetual/trades.py +38 -0
- x10/perpetual/trading_client/__init__.py +3 -0
- x10/perpetual/trading_client/account_module.py +209 -0
- x10/perpetual/trading_client/base_module.py +58 -0
- x10/perpetual/trading_client/info_module.py +13 -0
- x10/perpetual/trading_client/markets_information_module.py +76 -0
- x10/perpetual/trading_client/order_management_module.py +81 -0
- x10/perpetual/trading_client/testnet_module.py +68 -0
- x10/perpetual/trading_client/trading_client.py +123 -0
- x10/perpetual/transfer_object.py +80 -0
- x10/perpetual/transfers.py +39 -0
- x10/perpetual/user_client/__init__.py +0 -0
- x10/perpetual/user_client/l1_signing.py +0 -0
- x10/perpetual/user_client/onboarding.py +231 -0
- x10/perpetual/user_client/user_client.py +199 -0
- x10/perpetual/withdrawal_object.py +29 -0
- x10/perpetual/withdrawals.py +29 -0
- x10/utils/__init__.py +2 -0
- x10/utils/date.py +11 -0
- x10/utils/http.py +239 -0
- x10/utils/log.py +5 -0
- x10/utils/model.py +66 -0
- x10/utils/nonce.py +11 -0
- x10/utils/string.py +10 -0
- x10_python_trading_starknet-0.0.1.dist-info/LICENSE +21 -0
- x10_python_trading_starknet-0.0.1.dist-info/METADATA +413 -0
- x10_python_trading_starknet-0.0.1.dist-info/RECORD +50 -0
- x10_python_trading_starknet-0.0.1.dist-info/WHEEL +4 -0
x10/__init__.py
ADDED
|
File without changes
|
x10/config.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import importlib.metadata
|
|
2
|
+
|
|
3
|
+
TRADING_API_URL_DEV = "http://api.testnet.extended.exchange/api/v1"
|
|
4
|
+
STREAM_API_URL_DEV = "wss://api.testnet.extended.exchange/stream.extended.exchange/v1"
|
|
5
|
+
|
|
6
|
+
BTC_USD_MARKET = "BTC-USD"
|
|
7
|
+
SOL_USD_MARKET = "SOL-USD"
|
|
8
|
+
ADA_USD_MARKET = "ADA-USD"
|
|
9
|
+
ETH_USD_MARKET = "ETH-USD"
|
|
10
|
+
|
|
11
|
+
DEFAULT_REQUEST_TIMEOUT_SECONDS = 500
|
|
12
|
+
SDK_VERSION = importlib.metadata.version("x10-python-trading")
|
|
13
|
+
USER_AGENT = f"X10PythonTradingClient/{SDK_VERSION}"
|
x10/errors.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
from decimal import Decimal
|
|
2
|
+
from typing import Dict, List, Optional, Tuple
|
|
3
|
+
|
|
4
|
+
from fast_stark_crypto import sign
|
|
5
|
+
from pydantic import AliasChoices, Field
|
|
6
|
+
|
|
7
|
+
from x10.perpetual.balances import BalanceModel
|
|
8
|
+
from x10.perpetual.fees import TradingFeeModel
|
|
9
|
+
from x10.perpetual.orders import OpenOrderModel
|
|
10
|
+
from x10.perpetual.positions import PositionModel
|
|
11
|
+
from x10.perpetual.trades import AccountTradeModel
|
|
12
|
+
from x10.utils.model import X10BaseModel
|
|
13
|
+
from x10.utils.string import is_hex_string
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class StarkPerpetualAccount:
|
|
17
|
+
__vault: int
|
|
18
|
+
__private_key: int
|
|
19
|
+
__public_key: int
|
|
20
|
+
__trading_fee: Dict[str, TradingFeeModel]
|
|
21
|
+
|
|
22
|
+
def __init__(self, vault: int | str, private_key: str, public_key: str, api_key: str):
|
|
23
|
+
assert is_hex_string(private_key)
|
|
24
|
+
assert is_hex_string(public_key)
|
|
25
|
+
|
|
26
|
+
if isinstance(vault, str):
|
|
27
|
+
vault = int(vault)
|
|
28
|
+
elif isinstance(vault, int):
|
|
29
|
+
self.__vault = vault
|
|
30
|
+
else:
|
|
31
|
+
raise ValueError("Invalid vault type")
|
|
32
|
+
|
|
33
|
+
self.__vault = vault
|
|
34
|
+
self.__private_key = int(private_key, base=16)
|
|
35
|
+
self.__public_key = int(public_key, base=16)
|
|
36
|
+
self.__api_key = api_key
|
|
37
|
+
self.__trading_fee = {}
|
|
38
|
+
|
|
39
|
+
@property
|
|
40
|
+
def vault(self):
|
|
41
|
+
return self.__vault
|
|
42
|
+
|
|
43
|
+
@property
|
|
44
|
+
def public_key(self):
|
|
45
|
+
return self.__public_key
|
|
46
|
+
|
|
47
|
+
@property
|
|
48
|
+
def api_key(self):
|
|
49
|
+
return self.__api_key
|
|
50
|
+
|
|
51
|
+
@property
|
|
52
|
+
def trading_fee(self):
|
|
53
|
+
return self.__trading_fee
|
|
54
|
+
|
|
55
|
+
def sign(self, msg_hash: int) -> Tuple[int, int]:
|
|
56
|
+
return sign(private_key=self.__private_key, msg_hash=msg_hash)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class AccountStreamDataModel(X10BaseModel):
|
|
60
|
+
orders: Optional[List[OpenOrderModel]] = None
|
|
61
|
+
positions: Optional[List[PositionModel]] = None
|
|
62
|
+
trades: Optional[List[AccountTradeModel]] = None
|
|
63
|
+
balance: Optional[BalanceModel] = None
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class AccountLeverage(X10BaseModel):
|
|
67
|
+
market: str
|
|
68
|
+
leverage: Decimal
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class AccountModel(X10BaseModel):
|
|
72
|
+
id: int = Field(validation_alias=AliasChoices("accountId", "id"), serialization_alias="id")
|
|
73
|
+
description: str
|
|
74
|
+
account_index: int
|
|
75
|
+
status: str
|
|
76
|
+
l2_key: str
|
|
77
|
+
l2_vault: int
|
|
78
|
+
api_keys: Optional[List[str]] = None
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class ApiKeyResponseModel(X10BaseModel):
|
|
82
|
+
key: str
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class ApiKeyRequestModel(X10BaseModel):
|
|
86
|
+
description: str
|
x10/perpetual/amounts.py
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import decimal
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from decimal import Decimal
|
|
4
|
+
|
|
5
|
+
from x10.perpetual.assets import Asset
|
|
6
|
+
|
|
7
|
+
ROUNDING_SELL_CONTEXT = decimal.Context(rounding=decimal.ROUND_DOWN)
|
|
8
|
+
ROUNDING_BUY_CONTEXT = decimal.Context(rounding=decimal.ROUND_UP)
|
|
9
|
+
ROUNDING_FEE_CONTEXT = decimal.Context(rounding=decimal.ROUND_UP)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class HumanReadableAmount:
|
|
14
|
+
value: Decimal
|
|
15
|
+
asset: Asset
|
|
16
|
+
|
|
17
|
+
def to_l1_amount(self) -> "L1Amount":
|
|
18
|
+
converted_value = self.asset.convert_internal_quantity_to_l1_quantity(self.value)
|
|
19
|
+
return L1Amount(converted_value, self.asset)
|
|
20
|
+
|
|
21
|
+
def to_stark_amount(self, rounding_context: decimal.Context) -> "StarkAmount":
|
|
22
|
+
converted_value = self.asset.convert_human_readable_to_stark_quantity(self.value, rounding_context)
|
|
23
|
+
return StarkAmount(converted_value, self.asset)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass
|
|
27
|
+
class L1Amount:
|
|
28
|
+
value: int
|
|
29
|
+
asset: Asset
|
|
30
|
+
|
|
31
|
+
def to_internal_amount(self) -> HumanReadableAmount:
|
|
32
|
+
converted_value = self.asset.convert_l1_quantity_to_internal_quantity(self.value)
|
|
33
|
+
return HumanReadableAmount(converted_value, self.asset)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclass
|
|
37
|
+
class StarkAmount:
|
|
38
|
+
value: int
|
|
39
|
+
asset: Asset
|
|
40
|
+
|
|
41
|
+
def to_internal_amount(self) -> HumanReadableAmount:
|
|
42
|
+
converted_value = self.asset.convert_stark_to_internal_quantity(self.value)
|
|
43
|
+
return HumanReadableAmount(converted_value, self.asset)
|
|
44
|
+
|
|
45
|
+
def negate(self) -> "StarkAmount":
|
|
46
|
+
return StarkAmount(-self.value, self.asset)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@dataclass
|
|
50
|
+
class StarkOrderAmounts:
|
|
51
|
+
collateral_amount_internal: HumanReadableAmount
|
|
52
|
+
synthetic_amount_internal: HumanReadableAmount
|
|
53
|
+
fee_amount_internal: HumanReadableAmount
|
|
54
|
+
fee_rate: Decimal
|
|
55
|
+
rounding_context: decimal.Context
|
x10/perpetual/assets.py
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from decimal import Context, Decimal
|
|
3
|
+
from enum import Enum
|
|
4
|
+
from typing import Optional
|
|
5
|
+
|
|
6
|
+
from x10.utils.model import HexValue, X10BaseModel
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass
|
|
10
|
+
class Asset:
|
|
11
|
+
id: int
|
|
12
|
+
name: str
|
|
13
|
+
precision: int
|
|
14
|
+
active: bool
|
|
15
|
+
is_collateral: bool
|
|
16
|
+
settlement_external_id: str
|
|
17
|
+
settlement_resolution: int
|
|
18
|
+
l1_external_id: str
|
|
19
|
+
l1_resolution: int
|
|
20
|
+
|
|
21
|
+
def convert_human_readable_to_stark_quantity(self, internal: Decimal, rounding_context: Context) -> int:
|
|
22
|
+
return int(
|
|
23
|
+
rounding_context.multiply(internal, Decimal(self.settlement_resolution)).to_integral(
|
|
24
|
+
context=rounding_context
|
|
25
|
+
)
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
def convert_stark_to_internal_quantity(self, stark: int) -> Decimal:
|
|
29
|
+
return Decimal(stark) / Decimal(self.settlement_resolution)
|
|
30
|
+
|
|
31
|
+
def convert_l1_quantity_to_internal_quantity(self, l1: int) -> Decimal:
|
|
32
|
+
return Decimal(l1) / Decimal(self.l1_resolution)
|
|
33
|
+
|
|
34
|
+
def convert_internal_quantity_to_l1_quantity(self, internal: Decimal) -> int:
|
|
35
|
+
if not self.is_collateral:
|
|
36
|
+
raise ValueError("Only collateral assets have an L1 representation")
|
|
37
|
+
return int(internal * Decimal(self.l1_resolution))
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class AssetOperationType(str, Enum):
|
|
41
|
+
CLAIM = "CLAIM"
|
|
42
|
+
DEPOSIT = "DEPOSIT"
|
|
43
|
+
FAST_WITHDRAWAL = "FAST_WITHDRAWAL"
|
|
44
|
+
SLOW_WITHDRAWAL = "SLOW_WITHDRAWAL"
|
|
45
|
+
TRANSFER = "TRANSFER"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class AssetOperationStatus(Enum):
|
|
49
|
+
# Technical status
|
|
50
|
+
UNKNOWN = "UNKNOWN"
|
|
51
|
+
|
|
52
|
+
CREATED = "CREATED"
|
|
53
|
+
IN_PROGRESS = "IN_PROGRESS"
|
|
54
|
+
REJECTED = "REJECTED"
|
|
55
|
+
READY_FOR_CLAIM = "READY_FOR_CLAIM"
|
|
56
|
+
COMPLETED = "COMPLETED"
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class AssetOperationModel(X10BaseModel):
|
|
60
|
+
id: str
|
|
61
|
+
type: AssetOperationType
|
|
62
|
+
status: AssetOperationStatus
|
|
63
|
+
amount: Decimal
|
|
64
|
+
fee: Decimal
|
|
65
|
+
asset: int
|
|
66
|
+
time: int
|
|
67
|
+
account_id: int
|
|
68
|
+
|
|
69
|
+
# When operation type is `TRANSFER`
|
|
70
|
+
counterparty_account_id: Optional[int] = None
|
|
71
|
+
transaction_hash: Optional[HexValue] = None
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from decimal import Decimal
|
|
2
|
+
|
|
3
|
+
from x10.utils.model import X10BaseModel
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class BalanceModel(X10BaseModel):
|
|
7
|
+
collateral_name: str
|
|
8
|
+
balance: Decimal
|
|
9
|
+
equity: Decimal
|
|
10
|
+
available_for_trade: Decimal
|
|
11
|
+
available_for_withdrawal: Decimal
|
|
12
|
+
unrealised_pnl: Decimal
|
|
13
|
+
initial_margin: Decimal
|
|
14
|
+
margin_ratio: Decimal
|
|
15
|
+
updated_time: int
|
x10/perpetual/candles.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from decimal import Decimal
|
|
2
|
+
from typing import Literal
|
|
3
|
+
|
|
4
|
+
from pydantic import AliasChoices, Field
|
|
5
|
+
|
|
6
|
+
from x10.utils.model import X10BaseModel
|
|
7
|
+
|
|
8
|
+
CandleType = Literal["trades", "mark-prices", "index-prices"]
|
|
9
|
+
CandleInterval = Literal["PT1M", "PT5M", "PT15M", "PT30M", "PT1H", "PT2H", "PT4H", "P1D"]
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class CandleModel(X10BaseModel):
|
|
13
|
+
open: Decimal = Field(validation_alias=AliasChoices("open", "o"), serialization_alias="o")
|
|
14
|
+
low: Decimal = Field(validation_alias=AliasChoices("low", "l"), serialization_alias="l")
|
|
15
|
+
high: Decimal = Field(validation_alias=AliasChoices("high", "h"), serialization_alias="h")
|
|
16
|
+
close: Decimal = Field(validation_alias=AliasChoices("close", "c"), serialization_alias="c")
|
|
17
|
+
volume: Decimal = Field(validation_alias=AliasChoices("volume", "v"), serialization_alias="v")
|
|
18
|
+
timestamp: int = Field(validation_alias=AliasChoices("timestamp", "T"), serialization_alias="T")
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
@dataclass
|
|
5
|
+
class StarknetDomain:
|
|
6
|
+
name: str
|
|
7
|
+
version: str
|
|
8
|
+
chain_id: str
|
|
9
|
+
revision: str
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class EndpointConfig:
|
|
14
|
+
chain_rpc_url: str
|
|
15
|
+
api_base_url: str
|
|
16
|
+
stream_url: str
|
|
17
|
+
onboarding_url: str
|
|
18
|
+
signing_domain: str
|
|
19
|
+
collateral_asset_contract: str
|
|
20
|
+
asset_operations_contract: str
|
|
21
|
+
collateral_asset_on_chain_id: str
|
|
22
|
+
collateral_decimals: int
|
|
23
|
+
collateral_asset_id: str
|
|
24
|
+
starknet_domain: StarknetDomain
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
TESTNET_CONFIG = EndpointConfig(
|
|
28
|
+
chain_rpc_url="https://rpc.sepolia.org",
|
|
29
|
+
api_base_url="https://api.testnet.extended.exchange/api/v1",
|
|
30
|
+
stream_url="wss://api.testnet.extended.exchange/stream.extended.exchange/v1",
|
|
31
|
+
onboarding_url="https://api.testnet.extended.exchange",
|
|
32
|
+
signing_domain="testnet.extended.exchange",
|
|
33
|
+
collateral_asset_contract="0x0C9165046063B7bCD05C6924Bbe05ed535c140a1",
|
|
34
|
+
asset_operations_contract="0xe42bb60Fab4EA4905832AEbDf0f001c784dA271b",
|
|
35
|
+
collateral_asset_on_chain_id="0x31857064564ed0ff978e687456963cba09c2c6985d8f9300a1de4962fafa054",
|
|
36
|
+
collateral_decimals=6,
|
|
37
|
+
collateral_asset_id="0x1",
|
|
38
|
+
starknet_domain=StarknetDomain(name="Perpetuals", version="v0", chain_id="SN_SEPOLIA", revision="1"),
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
MAINNET_CONFIG = EndpointConfig(
|
|
42
|
+
chain_rpc_url="https://cloudflare-eth.com",
|
|
43
|
+
api_base_url="https://api.extended.exchange/api/v1",
|
|
44
|
+
stream_url="wss://api.extended.exchange/stream.extended.exchange/v1",
|
|
45
|
+
onboarding_url="https://api.extended.exchange",
|
|
46
|
+
signing_domain="extended.exchange",
|
|
47
|
+
collateral_asset_contract="0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
48
|
+
asset_operations_contract="0x1cE5D7f52A8aBd23551e91248151CA5A13353C65",
|
|
49
|
+
collateral_asset_on_chain_id="0x2893294412a4c8f915f75892b395ebbf6859ec246ec365c3b1f56f47c3a0a5d",
|
|
50
|
+
collateral_decimals=6,
|
|
51
|
+
collateral_asset_id="0x1",
|
|
52
|
+
starknet_domain=StarknetDomain(name="Perpetuals", version="v0", chain_id="SN_MAINNET", revision="1"),
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
"""
|
|
56
|
+
Identical to the MAINNET_CONFIG, but with a different signing domain.
|
|
57
|
+
Use it for accounts that were created before the signing domain was changed.
|
|
58
|
+
"""
|
|
59
|
+
MAINNET_CONFIG_LEGACY_SIGNING_DOMAIN = EndpointConfig(
|
|
60
|
+
chain_rpc_url="https://cloudflare-eth.com",
|
|
61
|
+
api_base_url="https://api.extended.exchange/api/v1",
|
|
62
|
+
stream_url="wss://api.extended.exchange/stream.extended.exchange/v1",
|
|
63
|
+
onboarding_url="https://api.extended.exchange",
|
|
64
|
+
signing_domain="x10.exchange",
|
|
65
|
+
collateral_asset_contract="0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
66
|
+
asset_operations_contract="0x1cE5D7f52A8aBd23551e91248151CA5A13353C65",
|
|
67
|
+
collateral_asset_on_chain_id="0x2893294412a4c8f915f75892b395ebbf6859ec246ec365c3b1f56f47c3a0a5d",
|
|
68
|
+
collateral_decimals=6,
|
|
69
|
+
collateral_asset_id="0x1",
|
|
70
|
+
starknet_domain=StarknetDomain(name="Perpetuals", version="v0", chain_id="SN_MAINNET", revision="1"),
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
STARKNET_TESTNET_CONFIG = EndpointConfig(
|
|
75
|
+
chain_rpc_url="https://rpc.sepolia.org",
|
|
76
|
+
api_base_url="https://api.starknet.sepolia.extended.exchange/api/v1",
|
|
77
|
+
stream_url="wss://api.starknet.sepolia.extended.exchange/stream.extended.exchange/v1",
|
|
78
|
+
onboarding_url="https://api.starknet.sepolia.extended.exchange",
|
|
79
|
+
signing_domain="starknet.sepolia.extended.exchange",
|
|
80
|
+
collateral_asset_contract="0x0C9165046063B7bCD05C6924Bbe05ed535c140a1",
|
|
81
|
+
asset_operations_contract="0xe42bb60Fab4EA4905832AEbDf0f001c784dA271b",
|
|
82
|
+
collateral_asset_on_chain_id="0x31857064564ed0ff978e687456963cba09c2c6985d8f9300a1de4962fafa054",
|
|
83
|
+
collateral_decimals=6,
|
|
84
|
+
starknet_domain=StarknetDomain(name="Perpetuals", version="v0", chain_id="SN_SEPOLIA", revision="1"),
|
|
85
|
+
collateral_asset_id="0x1",
|
|
86
|
+
)
|
x10/perpetual/fees.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from decimal import Decimal
|
|
2
|
+
|
|
3
|
+
from x10.utils.model import X10BaseModel
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class TradingFeeModel(X10BaseModel):
|
|
7
|
+
market: str
|
|
8
|
+
maker_fee_rate: Decimal
|
|
9
|
+
taker_fee_rate: Decimal
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
DEFAULT_FEES = TradingFeeModel(
|
|
13
|
+
market="BTC-USD",
|
|
14
|
+
maker_fee_rate=(Decimal("2") / Decimal("10000")),
|
|
15
|
+
taker_fee_rate=(Decimal("5") / Decimal("10000")),
|
|
16
|
+
)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from decimal import Decimal
|
|
2
|
+
|
|
3
|
+
from pydantic import AliasChoices, Field
|
|
4
|
+
|
|
5
|
+
from x10.utils.model import X10BaseModel
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class FundingRateModel(X10BaseModel):
|
|
9
|
+
market: str = Field(validation_alias=AliasChoices("market", "m"), serialization_alias="m")
|
|
10
|
+
funding_rate: Decimal = Field(validation_alias=AliasChoices("funding_rate", "f"), serialization_alias="f")
|
|
11
|
+
timestamp: int = Field(validation_alias=AliasChoices("timestamp", "T"), serialization_alias="T")
|
x10/perpetual/markets.py
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
from decimal import ROUND_CEILING, Decimal
|
|
2
|
+
from functools import cached_property
|
|
3
|
+
from typing import List
|
|
4
|
+
|
|
5
|
+
from x10.perpetual.assets import Asset
|
|
6
|
+
from x10.utils.model import X10BaseModel
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class RiskFactorConfig(X10BaseModel):
|
|
10
|
+
upper_bound: Decimal
|
|
11
|
+
risk_factor: Decimal
|
|
12
|
+
|
|
13
|
+
@cached_property
|
|
14
|
+
def max_leverage(self) -> Decimal:
|
|
15
|
+
return round(Decimal(1) / self.risk_factor, 2)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class MarketStatsModel(X10BaseModel):
|
|
19
|
+
daily_volume: Decimal
|
|
20
|
+
daily_volume_base: Decimal
|
|
21
|
+
daily_price_change: Decimal
|
|
22
|
+
daily_low: Decimal
|
|
23
|
+
daily_high: Decimal
|
|
24
|
+
last_price: Decimal
|
|
25
|
+
ask_price: Decimal
|
|
26
|
+
bid_price: Decimal
|
|
27
|
+
mark_price: Decimal
|
|
28
|
+
index_price: Decimal
|
|
29
|
+
funding_rate: Decimal
|
|
30
|
+
next_funding_rate: int
|
|
31
|
+
open_interest: Decimal
|
|
32
|
+
open_interest_base: Decimal
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class TradingConfigModel(X10BaseModel):
|
|
36
|
+
min_order_size: Decimal
|
|
37
|
+
min_order_size_change: Decimal
|
|
38
|
+
min_price_change: Decimal
|
|
39
|
+
max_market_order_value: Decimal
|
|
40
|
+
max_limit_order_value: Decimal
|
|
41
|
+
max_position_value: Decimal
|
|
42
|
+
max_leverage: Decimal
|
|
43
|
+
max_num_orders: int
|
|
44
|
+
limit_price_cap: Decimal
|
|
45
|
+
limit_price_floor: Decimal
|
|
46
|
+
risk_factor_config: List[RiskFactorConfig]
|
|
47
|
+
|
|
48
|
+
@cached_property
|
|
49
|
+
def price_precision(self) -> int:
|
|
50
|
+
return abs(int(self.min_price_change.log10().to_integral_exact(ROUND_CEILING)))
|
|
51
|
+
|
|
52
|
+
@cached_property
|
|
53
|
+
def quantity_precision(self) -> int:
|
|
54
|
+
return abs(int(self.min_order_size_change.log10().to_integral_exact(ROUND_CEILING)))
|
|
55
|
+
|
|
56
|
+
def max_leverage_for_position_value(self, position_value: Decimal) -> Decimal:
|
|
57
|
+
filtered = [x for x in self.risk_factor_config if x.upper_bound >= position_value]
|
|
58
|
+
return filtered[0].max_leverage if filtered else Decimal(0)
|
|
59
|
+
|
|
60
|
+
def max_position_value_for_leverage(self, leverage: Decimal) -> Decimal:
|
|
61
|
+
filtered = [x for x in self.risk_factor_config if x.max_leverage >= leverage]
|
|
62
|
+
return filtered[-1].upper_bound if filtered else Decimal(0)
|
|
63
|
+
|
|
64
|
+
def round_order_size(self, order_size: Decimal, rounding_direction: str = ROUND_CEILING) -> Decimal:
|
|
65
|
+
order_size = (order_size / self.min_order_size_change).to_integral_exact(
|
|
66
|
+
rounding_direction
|
|
67
|
+
) * self.min_order_size_change
|
|
68
|
+
return order_size
|
|
69
|
+
|
|
70
|
+
def calculate_order_size_from_value(
|
|
71
|
+
self, order_value: Decimal, order_price: Decimal, rounding_direction: str = ROUND_CEILING
|
|
72
|
+
) -> Decimal:
|
|
73
|
+
order_size = order_value / order_price
|
|
74
|
+
if order_size > 0:
|
|
75
|
+
return self.round_order_size(order_size, rounding_direction=rounding_direction)
|
|
76
|
+
else:
|
|
77
|
+
return Decimal(0)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class L2ConfigModel(X10BaseModel):
|
|
81
|
+
type: str
|
|
82
|
+
collateral_id: str
|
|
83
|
+
collateral_resolution: int
|
|
84
|
+
synthetic_id: str
|
|
85
|
+
synthetic_resolution: int
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class MarketModel(X10BaseModel):
|
|
89
|
+
name: str
|
|
90
|
+
asset_name: str
|
|
91
|
+
asset_precision: int
|
|
92
|
+
collateral_asset_name: str
|
|
93
|
+
collateral_asset_precision: int
|
|
94
|
+
active: bool
|
|
95
|
+
market_stats: MarketStatsModel
|
|
96
|
+
trading_config: TradingConfigModel
|
|
97
|
+
l2_config: L2ConfigModel
|
|
98
|
+
|
|
99
|
+
@cached_property
|
|
100
|
+
def synthetic_asset(self) -> Asset:
|
|
101
|
+
return Asset(
|
|
102
|
+
id=1,
|
|
103
|
+
name=self.asset_name,
|
|
104
|
+
precision=self.asset_precision,
|
|
105
|
+
active=self.active,
|
|
106
|
+
is_collateral=False,
|
|
107
|
+
settlement_external_id=self.l2_config.synthetic_id,
|
|
108
|
+
settlement_resolution=self.l2_config.synthetic_resolution,
|
|
109
|
+
l1_external_id="",
|
|
110
|
+
l1_resolution=0,
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
@cached_property
|
|
114
|
+
def collateral_asset(self) -> Asset:
|
|
115
|
+
return Asset(
|
|
116
|
+
id=2,
|
|
117
|
+
name=self.collateral_asset_name,
|
|
118
|
+
precision=self.collateral_asset_precision,
|
|
119
|
+
active=self.active,
|
|
120
|
+
is_collateral=True,
|
|
121
|
+
settlement_external_id=self.l2_config.collateral_id,
|
|
122
|
+
settlement_resolution=self.l2_config.collateral_resolution,
|
|
123
|
+
l1_external_id="",
|
|
124
|
+
l1_resolution=0,
|
|
125
|
+
)
|