walrasquant-lib 0.4.20__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.
- walrasquant/__init__.py +7 -0
- walrasquant/aggregation.py +449 -0
- walrasquant/backends/__init__.py +5 -0
- walrasquant/backends/db.py +109 -0
- walrasquant/backends/db_memory.py +61 -0
- walrasquant/backends/db_postgresql.py +321 -0
- walrasquant/backends/db_sqlite.py +310 -0
- walrasquant/base/__init__.py +24 -0
- walrasquant/base/api_client.py +46 -0
- walrasquant/base/connector.py +863 -0
- walrasquant/base/ems.py +794 -0
- walrasquant/base/exchange.py +213 -0
- walrasquant/base/oms.py +428 -0
- walrasquant/base/retry.py +220 -0
- walrasquant/base/sms.py +545 -0
- walrasquant/base/ws_client.py +408 -0
- walrasquant/config.py +284 -0
- walrasquant/constants.py +413 -0
- walrasquant/core/__init__.py +0 -0
- walrasquant/core/cache.py +688 -0
- walrasquant/core/clock.py +59 -0
- walrasquant/core/connection.py +41 -0
- walrasquant/core/entity.py +504 -0
- walrasquant/core/nautilius_core.py +103 -0
- walrasquant/core/registry.py +41 -0
- walrasquant/engine.py +745 -0
- walrasquant/error.py +34 -0
- walrasquant/exchange/__init__.py +13 -0
- walrasquant/exchange/base_factory.py +172 -0
- walrasquant/exchange/binance/__init__.py +30 -0
- walrasquant/exchange/binance/connector.py +1093 -0
- walrasquant/exchange/binance/constants.py +934 -0
- walrasquant/exchange/binance/ems.py +140 -0
- walrasquant/exchange/binance/error.py +48 -0
- walrasquant/exchange/binance/exchange.py +144 -0
- walrasquant/exchange/binance/factory.py +115 -0
- walrasquant/exchange/binance/oms.py +1807 -0
- walrasquant/exchange/binance/rest_api.py +1653 -0
- walrasquant/exchange/binance/schema.py +1063 -0
- walrasquant/exchange/binance/websockets.py +389 -0
- walrasquant/exchange/bitget/__init__.py +28 -0
- walrasquant/exchange/bitget/connector.py +578 -0
- walrasquant/exchange/bitget/constants.py +392 -0
- walrasquant/exchange/bitget/ems.py +202 -0
- walrasquant/exchange/bitget/error.py +36 -0
- walrasquant/exchange/bitget/exchange.py +128 -0
- walrasquant/exchange/bitget/factory.py +135 -0
- walrasquant/exchange/bitget/oms.py +1619 -0
- walrasquant/exchange/bitget/rest_api.py +610 -0
- walrasquant/exchange/bitget/schema.py +885 -0
- walrasquant/exchange/bitget/websockets.py +753 -0
- walrasquant/exchange/bybit/__init__.py +32 -0
- walrasquant/exchange/bybit/connector.py +819 -0
- walrasquant/exchange/bybit/constants.py +479 -0
- walrasquant/exchange/bybit/ems.py +93 -0
- walrasquant/exchange/bybit/error.py +36 -0
- walrasquant/exchange/bybit/exchange.py +108 -0
- walrasquant/exchange/bybit/factory.py +128 -0
- walrasquant/exchange/bybit/oms.py +1195 -0
- walrasquant/exchange/bybit/rest_api.py +570 -0
- walrasquant/exchange/bybit/schema.py +867 -0
- walrasquant/exchange/bybit/websockets.py +307 -0
- walrasquant/exchange/hyperliquid/__init__.py +28 -0
- walrasquant/exchange/hyperliquid/connector.py +370 -0
- walrasquant/exchange/hyperliquid/constants.py +371 -0
- walrasquant/exchange/hyperliquid/ems.py +156 -0
- walrasquant/exchange/hyperliquid/error.py +48 -0
- walrasquant/exchange/hyperliquid/exchange.py +120 -0
- walrasquant/exchange/hyperliquid/factory.py +135 -0
- walrasquant/exchange/hyperliquid/oms.py +1081 -0
- walrasquant/exchange/hyperliquid/rest_api.py +348 -0
- walrasquant/exchange/hyperliquid/schema.py +583 -0
- walrasquant/exchange/hyperliquid/websockets.py +592 -0
- walrasquant/exchange/okx/__init__.py +25 -0
- walrasquant/exchange/okx/connector.py +931 -0
- walrasquant/exchange/okx/constants.py +518 -0
- walrasquant/exchange/okx/ems.py +144 -0
- walrasquant/exchange/okx/error.py +66 -0
- walrasquant/exchange/okx/exchange.py +102 -0
- walrasquant/exchange/okx/factory.py +138 -0
- walrasquant/exchange/okx/oms.py +1199 -0
- walrasquant/exchange/okx/rest_api.py +799 -0
- walrasquant/exchange/okx/schema.py +1449 -0
- walrasquant/exchange/okx/websockets.py +420 -0
- walrasquant/exchange/registry.py +201 -0
- walrasquant/execution/__init__.py +24 -0
- walrasquant/execution/algorithm.py +968 -0
- walrasquant/execution/algorithms/__init__.py +3 -0
- walrasquant/execution/algorithms/twap.py +392 -0
- walrasquant/execution/config.py +34 -0
- walrasquant/execution/constants.py +27 -0
- walrasquant/execution/schema.py +62 -0
- walrasquant/indicator.py +382 -0
- walrasquant/push.py +77 -0
- walrasquant/schema.py +755 -0
- walrasquant/strategy.py +1805 -0
- walrasquant/tools/__init__.py +0 -0
- walrasquant/tools/pm2_wrapper.py +1016 -0
- walrasquant/web/__init__.py +26 -0
- walrasquant/web/app.py +157 -0
- walrasquant/web/server.py +92 -0
- walrasquant_lib-0.4.20.dist-info/METADATA +162 -0
- walrasquant_lib-0.4.20.dist-info/RECORD +105 -0
- walrasquant_lib-0.4.20.dist-info/WHEEL +4 -0
- walrasquant_lib-0.4.20.dist-info/entry_points.txt +3 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import ccxt
|
|
2
|
+
import msgspec
|
|
3
|
+
from typing import Any, Dict
|
|
4
|
+
from decimal import Decimal
|
|
5
|
+
from walrasquant.base import ExchangeManager
|
|
6
|
+
from walrasquant.schema import InstrumentId
|
|
7
|
+
from walrasquant.error import EngineBuildError
|
|
8
|
+
from walrasquant.config import BasicConfig
|
|
9
|
+
from walrasquant.constants import AccountType, ConfigType
|
|
10
|
+
from walrasquant.exchange.bitget.schema import BitgetMarket
|
|
11
|
+
from walrasquant.exchange.bitget.constants import BitgetAccountType
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class BitgetExchangeManager(ExchangeManager):
|
|
15
|
+
api: ccxt.bitget
|
|
16
|
+
market: Dict[str, BitgetMarket]
|
|
17
|
+
market_id: Dict[str, str]
|
|
18
|
+
|
|
19
|
+
def __init__(self, config: ConfigType | None = None):
|
|
20
|
+
resolved_config: ConfigType = {
|
|
21
|
+
"apiKey": config.get("apiKey", "") if config else "",
|
|
22
|
+
"secret": config.get("secret", "") if config else "",
|
|
23
|
+
"exchange_id": config.get("exchange_id", "bitget") if config else "bitget",
|
|
24
|
+
"sandbox": config.get("sandbox", False) if config else False,
|
|
25
|
+
}
|
|
26
|
+
if config and "password" in config:
|
|
27
|
+
resolved_config["password"] = config["password"]
|
|
28
|
+
super().__init__(resolved_config)
|
|
29
|
+
self.passphrase = resolved_config.get("password")
|
|
30
|
+
self._public_conn_account_type: BitgetAccountType | None = None
|
|
31
|
+
|
|
32
|
+
def load_markets(self, reload: bool = False) -> None:
|
|
33
|
+
market = self.api.load_markets(reload=reload)
|
|
34
|
+
for symbol, mkt in market.items():
|
|
35
|
+
try:
|
|
36
|
+
mkt_json = msgspec.json.encode(mkt)
|
|
37
|
+
mkt = msgspec.json.decode(mkt_json, type=BitgetMarket)
|
|
38
|
+
|
|
39
|
+
if (
|
|
40
|
+
mkt.spot or mkt.linear or mkt.inverse or mkt.future
|
|
41
|
+
) and not mkt.option:
|
|
42
|
+
symbol = self._parse_symbol(mkt, exchange_suffix="BITGET")
|
|
43
|
+
mkt.symbol = symbol
|
|
44
|
+
|
|
45
|
+
if (
|
|
46
|
+
(mkt.linear or mkt.inverse)
|
|
47
|
+
and mkt.info.priceEndStep is not None
|
|
48
|
+
and mkt.info.pricePlace is not None
|
|
49
|
+
):
|
|
50
|
+
mkt.info.priceEndStep = str(
|
|
51
|
+
Decimal(mkt.info.priceEndStep)
|
|
52
|
+
/ (Decimal("10") ** Decimal(mkt.info.pricePlace))
|
|
53
|
+
)
|
|
54
|
+
self.market[symbol] = mkt
|
|
55
|
+
if mkt.type.value == "spot":
|
|
56
|
+
self.market_id[f"{mkt.id}_spot"] = symbol
|
|
57
|
+
elif mkt.linear:
|
|
58
|
+
self.market_id[f"{mkt.id}_linear"] = symbol
|
|
59
|
+
elif mkt.inverse:
|
|
60
|
+
self.market_id[f"{mkt.id}_inverse"] = symbol
|
|
61
|
+
|
|
62
|
+
except msgspec.ValidationError as ve:
|
|
63
|
+
self._log.warning(f"Symbol Format Error: {ve}, {symbol}, {mkt}")
|
|
64
|
+
continue
|
|
65
|
+
|
|
66
|
+
def validate_public_connector_config(
|
|
67
|
+
self, account_type: AccountType, basic_config: BasicConfig | None = None
|
|
68
|
+
) -> None:
|
|
69
|
+
"""Validate public connector configuration for this exchange"""
|
|
70
|
+
if basic_config is None:
|
|
71
|
+
raise EngineBuildError("basic_config is required for Bitget")
|
|
72
|
+
if not isinstance(account_type, BitgetAccountType):
|
|
73
|
+
raise EngineBuildError(
|
|
74
|
+
f"Expected BitgetAccountType, got {type(account_type)}"
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
if basic_config.testnet != account_type.is_testnet:
|
|
78
|
+
raise EngineBuildError(
|
|
79
|
+
f"The `testnet` setting of Bitget is not consistent with the public connector's account type `{account_type}`."
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
def validate_public_connector_limits(
|
|
83
|
+
self, existing_connectors: Dict[AccountType, Any]
|
|
84
|
+
) -> None:
|
|
85
|
+
"""Validate public connector limits for this exchange"""
|
|
86
|
+
bitget_connectors = [
|
|
87
|
+
c
|
|
88
|
+
for c in existing_connectors.values()
|
|
89
|
+
if hasattr(c, "account_type")
|
|
90
|
+
and isinstance(c.account_type, BitgetAccountType)
|
|
91
|
+
]
|
|
92
|
+
if len(bitget_connectors) > 1:
|
|
93
|
+
raise EngineBuildError(
|
|
94
|
+
"Only one public connector is supported for Bitget, please remove the extra public connector config."
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
def set_public_connector_account_type(
|
|
98
|
+
self, account_type: BitgetAccountType
|
|
99
|
+
) -> None:
|
|
100
|
+
"""Set the account type for public connector configuration"""
|
|
101
|
+
self._public_conn_account_type = account_type
|
|
102
|
+
|
|
103
|
+
def instrument_id_to_account_type(self, instrument_id: InstrumentId) -> AccountType:
|
|
104
|
+
"""Convert an instrument ID to the appropriate account type for this exchange"""
|
|
105
|
+
if self._public_conn_account_type is None:
|
|
106
|
+
raise EngineBuildError(
|
|
107
|
+
"Public connector account type not set for Bitget. Please add Bitget in public_conn_config."
|
|
108
|
+
)
|
|
109
|
+
return self._public_conn_account_type
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def main():
|
|
113
|
+
# Example usage
|
|
114
|
+
exchange = BitgetExchangeManager(
|
|
115
|
+
config={
|
|
116
|
+
"apiKey": "",
|
|
117
|
+
"secret": "",
|
|
118
|
+
"exchange_id": "bitget",
|
|
119
|
+
"sandbox": False,
|
|
120
|
+
}
|
|
121
|
+
)
|
|
122
|
+
exchange.load_markets()
|
|
123
|
+
|
|
124
|
+
# print(exchange.market)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
if __name__ == "__main__":
|
|
128
|
+
main()
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Bitget exchange factory implementation.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import cast
|
|
6
|
+
|
|
7
|
+
from walrasquant.constants import ExchangeType, AccountType, ConfigType
|
|
8
|
+
from walrasquant.config import (
|
|
9
|
+
PublicConnectorConfig,
|
|
10
|
+
PrivateConnectorConfig,
|
|
11
|
+
BasicConfig,
|
|
12
|
+
)
|
|
13
|
+
from walrasquant.exchange.base_factory import ExchangeFactory, BuildContext
|
|
14
|
+
from walrasquant.base import (
|
|
15
|
+
ExchangeManager,
|
|
16
|
+
PublicConnector,
|
|
17
|
+
PrivateConnector,
|
|
18
|
+
ExecutionManagementSystem,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
from walrasquant.exchange.bitget.exchange import BitgetExchangeManager
|
|
22
|
+
from walrasquant.exchange.bitget.constants import BitgetAccountType
|
|
23
|
+
from walrasquant.exchange.bitget.connector import (
|
|
24
|
+
BitgetPublicConnector,
|
|
25
|
+
BitgetPrivateConnector,
|
|
26
|
+
)
|
|
27
|
+
from walrasquant.exchange.bitget.ems import BitgetExecutionManagementSystem
|
|
28
|
+
from walrasquant.exchange.bitget.schema import BitgetMarket
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class BitgetFactory(ExchangeFactory):
|
|
32
|
+
"""Factory for creating Bitget exchange components."""
|
|
33
|
+
|
|
34
|
+
@property
|
|
35
|
+
def exchange_type(self) -> ExchangeType:
|
|
36
|
+
return ExchangeType.BITGET
|
|
37
|
+
|
|
38
|
+
def create_manager(self, basic_config: BasicConfig) -> ExchangeManager:
|
|
39
|
+
"""Create BitgetExchangeManager."""
|
|
40
|
+
ccxt_config: ConfigType = {
|
|
41
|
+
"apiKey": basic_config.api_key or "",
|
|
42
|
+
"secret": basic_config.secret or "",
|
|
43
|
+
"exchange_id": "bitget",
|
|
44
|
+
"sandbox": basic_config.testnet,
|
|
45
|
+
}
|
|
46
|
+
if basic_config.passphrase:
|
|
47
|
+
ccxt_config["password"] = basic_config.passphrase
|
|
48
|
+
|
|
49
|
+
return BitgetExchangeManager(ccxt_config)
|
|
50
|
+
|
|
51
|
+
def create_public_connector(
|
|
52
|
+
self,
|
|
53
|
+
config: PublicConnectorConfig,
|
|
54
|
+
exchange: ExchangeManager,
|
|
55
|
+
context: BuildContext,
|
|
56
|
+
) -> PublicConnector:
|
|
57
|
+
"""Create BitgetPublicConnector."""
|
|
58
|
+
if not isinstance(config.account_type, BitgetAccountType):
|
|
59
|
+
raise TypeError("Bitget public connector requires BitgetAccountType")
|
|
60
|
+
connector = BitgetPublicConnector(
|
|
61
|
+
account_type=config.account_type,
|
|
62
|
+
exchange=cast(BitgetExchangeManager, exchange),
|
|
63
|
+
msgbus=context.msgbus,
|
|
64
|
+
clock=context.clock,
|
|
65
|
+
task_manager=context.task_manager,
|
|
66
|
+
enable_rate_limit=config.enable_rate_limit,
|
|
67
|
+
custom_url=config.custom_url,
|
|
68
|
+
max_subscriptions_per_client=config.max_subscriptions_per_client,
|
|
69
|
+
max_clients=config.max_clients,
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
# Bitget needs post-creation setup
|
|
73
|
+
self.setup_public_connector(
|
|
74
|
+
cast(BitgetExchangeManager, exchange), connector, config.account_type
|
|
75
|
+
)
|
|
76
|
+
return connector
|
|
77
|
+
|
|
78
|
+
def create_private_connector(
|
|
79
|
+
self,
|
|
80
|
+
config: PrivateConnectorConfig,
|
|
81
|
+
exchange: ExchangeManager,
|
|
82
|
+
context: BuildContext,
|
|
83
|
+
account_type: AccountType | None = None,
|
|
84
|
+
) -> PrivateConnector:
|
|
85
|
+
"""Create BitgetPrivateConnector."""
|
|
86
|
+
# Use provided account_type or fall back to config
|
|
87
|
+
final_account_type = account_type or config.account_type
|
|
88
|
+
if not isinstance(final_account_type, BitgetAccountType):
|
|
89
|
+
raise TypeError("Bitget private connector requires BitgetAccountType")
|
|
90
|
+
|
|
91
|
+
return BitgetPrivateConnector(
|
|
92
|
+
exchange=cast(BitgetExchangeManager, exchange),
|
|
93
|
+
account_type=final_account_type,
|
|
94
|
+
cache=context.cache,
|
|
95
|
+
msgbus=context.msgbus,
|
|
96
|
+
clock=context.clock,
|
|
97
|
+
registry=context.registry,
|
|
98
|
+
enable_rate_limit=config.enable_rate_limit,
|
|
99
|
+
task_manager=context.task_manager,
|
|
100
|
+
max_retries=config.max_retries,
|
|
101
|
+
delay_initial_ms=config.delay_initial_ms,
|
|
102
|
+
delay_max_ms=config.delay_max_ms,
|
|
103
|
+
backoff_factor=config.backoff_factor,
|
|
104
|
+
max_slippage=config.max_slippage,
|
|
105
|
+
max_subscriptions_per_client=config.max_subscriptions_per_client,
|
|
106
|
+
max_clients=config.max_clients,
|
|
107
|
+
order_query_config=context.order_query_config,
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
def create_ems(
|
|
111
|
+
self, exchange: ExchangeManager, context: BuildContext
|
|
112
|
+
) -> ExecutionManagementSystem:
|
|
113
|
+
"""Create BitgetExecutionManagementSystem."""
|
|
114
|
+
return BitgetExecutionManagementSystem(
|
|
115
|
+
market=cast(dict[str, BitgetMarket], exchange.market),
|
|
116
|
+
cache=context.cache,
|
|
117
|
+
msgbus=context.msgbus,
|
|
118
|
+
clock=context.clock,
|
|
119
|
+
task_manager=context.task_manager,
|
|
120
|
+
registry=context.registry,
|
|
121
|
+
queue_maxsize=context.queue_maxsize,
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
def setup_public_connector(
|
|
125
|
+
self,
|
|
126
|
+
exchange: ExchangeManager,
|
|
127
|
+
connector: PublicConnector,
|
|
128
|
+
account_type: AccountType,
|
|
129
|
+
) -> None:
|
|
130
|
+
"""Setup public connector account type."""
|
|
131
|
+
if not isinstance(account_type, BitgetAccountType):
|
|
132
|
+
raise TypeError("Bitget public connector requires BitgetAccountType")
|
|
133
|
+
cast(BitgetExchangeManager, exchange).set_public_connector_account_type(
|
|
134
|
+
account_type
|
|
135
|
+
)
|