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.
Files changed (105) hide show
  1. walrasquant/__init__.py +7 -0
  2. walrasquant/aggregation.py +449 -0
  3. walrasquant/backends/__init__.py +5 -0
  4. walrasquant/backends/db.py +109 -0
  5. walrasquant/backends/db_memory.py +61 -0
  6. walrasquant/backends/db_postgresql.py +321 -0
  7. walrasquant/backends/db_sqlite.py +310 -0
  8. walrasquant/base/__init__.py +24 -0
  9. walrasquant/base/api_client.py +46 -0
  10. walrasquant/base/connector.py +863 -0
  11. walrasquant/base/ems.py +794 -0
  12. walrasquant/base/exchange.py +213 -0
  13. walrasquant/base/oms.py +428 -0
  14. walrasquant/base/retry.py +220 -0
  15. walrasquant/base/sms.py +545 -0
  16. walrasquant/base/ws_client.py +408 -0
  17. walrasquant/config.py +284 -0
  18. walrasquant/constants.py +413 -0
  19. walrasquant/core/__init__.py +0 -0
  20. walrasquant/core/cache.py +688 -0
  21. walrasquant/core/clock.py +59 -0
  22. walrasquant/core/connection.py +41 -0
  23. walrasquant/core/entity.py +504 -0
  24. walrasquant/core/nautilius_core.py +103 -0
  25. walrasquant/core/registry.py +41 -0
  26. walrasquant/engine.py +745 -0
  27. walrasquant/error.py +34 -0
  28. walrasquant/exchange/__init__.py +13 -0
  29. walrasquant/exchange/base_factory.py +172 -0
  30. walrasquant/exchange/binance/__init__.py +30 -0
  31. walrasquant/exchange/binance/connector.py +1093 -0
  32. walrasquant/exchange/binance/constants.py +934 -0
  33. walrasquant/exchange/binance/ems.py +140 -0
  34. walrasquant/exchange/binance/error.py +48 -0
  35. walrasquant/exchange/binance/exchange.py +144 -0
  36. walrasquant/exchange/binance/factory.py +115 -0
  37. walrasquant/exchange/binance/oms.py +1807 -0
  38. walrasquant/exchange/binance/rest_api.py +1653 -0
  39. walrasquant/exchange/binance/schema.py +1063 -0
  40. walrasquant/exchange/binance/websockets.py +389 -0
  41. walrasquant/exchange/bitget/__init__.py +28 -0
  42. walrasquant/exchange/bitget/connector.py +578 -0
  43. walrasquant/exchange/bitget/constants.py +392 -0
  44. walrasquant/exchange/bitget/ems.py +202 -0
  45. walrasquant/exchange/bitget/error.py +36 -0
  46. walrasquant/exchange/bitget/exchange.py +128 -0
  47. walrasquant/exchange/bitget/factory.py +135 -0
  48. walrasquant/exchange/bitget/oms.py +1619 -0
  49. walrasquant/exchange/bitget/rest_api.py +610 -0
  50. walrasquant/exchange/bitget/schema.py +885 -0
  51. walrasquant/exchange/bitget/websockets.py +753 -0
  52. walrasquant/exchange/bybit/__init__.py +32 -0
  53. walrasquant/exchange/bybit/connector.py +819 -0
  54. walrasquant/exchange/bybit/constants.py +479 -0
  55. walrasquant/exchange/bybit/ems.py +93 -0
  56. walrasquant/exchange/bybit/error.py +36 -0
  57. walrasquant/exchange/bybit/exchange.py +108 -0
  58. walrasquant/exchange/bybit/factory.py +128 -0
  59. walrasquant/exchange/bybit/oms.py +1195 -0
  60. walrasquant/exchange/bybit/rest_api.py +570 -0
  61. walrasquant/exchange/bybit/schema.py +867 -0
  62. walrasquant/exchange/bybit/websockets.py +307 -0
  63. walrasquant/exchange/hyperliquid/__init__.py +28 -0
  64. walrasquant/exchange/hyperliquid/connector.py +370 -0
  65. walrasquant/exchange/hyperliquid/constants.py +371 -0
  66. walrasquant/exchange/hyperliquid/ems.py +156 -0
  67. walrasquant/exchange/hyperliquid/error.py +48 -0
  68. walrasquant/exchange/hyperliquid/exchange.py +120 -0
  69. walrasquant/exchange/hyperliquid/factory.py +135 -0
  70. walrasquant/exchange/hyperliquid/oms.py +1081 -0
  71. walrasquant/exchange/hyperliquid/rest_api.py +348 -0
  72. walrasquant/exchange/hyperliquid/schema.py +583 -0
  73. walrasquant/exchange/hyperliquid/websockets.py +592 -0
  74. walrasquant/exchange/okx/__init__.py +25 -0
  75. walrasquant/exchange/okx/connector.py +931 -0
  76. walrasquant/exchange/okx/constants.py +518 -0
  77. walrasquant/exchange/okx/ems.py +144 -0
  78. walrasquant/exchange/okx/error.py +66 -0
  79. walrasquant/exchange/okx/exchange.py +102 -0
  80. walrasquant/exchange/okx/factory.py +138 -0
  81. walrasquant/exchange/okx/oms.py +1199 -0
  82. walrasquant/exchange/okx/rest_api.py +799 -0
  83. walrasquant/exchange/okx/schema.py +1449 -0
  84. walrasquant/exchange/okx/websockets.py +420 -0
  85. walrasquant/exchange/registry.py +201 -0
  86. walrasquant/execution/__init__.py +24 -0
  87. walrasquant/execution/algorithm.py +968 -0
  88. walrasquant/execution/algorithms/__init__.py +3 -0
  89. walrasquant/execution/algorithms/twap.py +392 -0
  90. walrasquant/execution/config.py +34 -0
  91. walrasquant/execution/constants.py +27 -0
  92. walrasquant/execution/schema.py +62 -0
  93. walrasquant/indicator.py +382 -0
  94. walrasquant/push.py +77 -0
  95. walrasquant/schema.py +755 -0
  96. walrasquant/strategy.py +1805 -0
  97. walrasquant/tools/__init__.py +0 -0
  98. walrasquant/tools/pm2_wrapper.py +1016 -0
  99. walrasquant/web/__init__.py +26 -0
  100. walrasquant/web/app.py +157 -0
  101. walrasquant/web/server.py +92 -0
  102. walrasquant_lib-0.4.20.dist-info/METADATA +162 -0
  103. walrasquant_lib-0.4.20.dist-info/RECORD +105 -0
  104. walrasquant_lib-0.4.20.dist-info/WHEEL +4 -0
  105. 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
+ )