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,108 @@
1
+ import ccxt
2
+ import msgspec
3
+ from typing import Any, Dict
4
+ from walrasquant.base import ExchangeManager
5
+ from walrasquant.exchange.bybit.schema import BybitMarket
6
+ from walrasquant.exchange.bybit.constants import BybitAccountType
7
+ from walrasquant.constants import AccountType, ConfigType
8
+ from walrasquant.schema import InstrumentId
9
+ from walrasquant.error import EngineBuildError
10
+
11
+
12
+ class BybitExchangeManager(ExchangeManager):
13
+ api: ccxt.bybit
14
+ market: Dict[str, BybitMarket]
15
+ market_id: Dict[str, str]
16
+
17
+ def __init__(self, config: ConfigType | None = None):
18
+ resolved_config: ConfigType = {
19
+ "apiKey": config.get("apiKey", "") if config else "",
20
+ "secret": config.get("secret", "") if config else "",
21
+ "exchange_id": config.get("exchange_id", "bybit") if config else "bybit",
22
+ "sandbox": config.get("sandbox", False) if config else False,
23
+ }
24
+ if config and "password" in config:
25
+ resolved_config["password"] = config["password"]
26
+ super().__init__(resolved_config)
27
+
28
+ def load_markets(self, reload: bool = False) -> None:
29
+ market = self.api.load_markets(reload=reload)
30
+ for symbol, mkt in market.items():
31
+ try:
32
+ mkt_json = msgspec.json.encode(mkt)
33
+ mkt = msgspec.json.decode(mkt_json, type=BybitMarket)
34
+ if mkt.spot or mkt.linear or mkt.inverse or mkt.future:
35
+ symbol = self._parse_symbol(mkt, exchange_suffix="BYBIT")
36
+ mkt.symbol = symbol
37
+ self.market[symbol] = mkt
38
+ if mkt.type.value == "spot":
39
+ self.market_id[f"{mkt.id}_spot"] = symbol
40
+ elif mkt.option:
41
+ self.market_id[f"{mkt.id}_option"] = symbol
42
+ elif mkt.linear:
43
+ self.market_id[f"{mkt.id}_linear"] = symbol
44
+ elif mkt.inverse:
45
+ self.market_id[f"{mkt.id}_inverse"] = symbol
46
+
47
+ except msgspec.ValidationError as ve:
48
+ self._log.warning(f"Symbol Format Error: {ve}, {symbol}, {mkt}")
49
+ continue
50
+
51
+ def validate_public_connector_config(
52
+ self, account_type: AccountType, basic_config: Any
53
+ ) -> None:
54
+ """Validate public connector configuration for Bybit exchange"""
55
+ if not isinstance(account_type, BybitAccountType):
56
+ raise EngineBuildError(
57
+ f"Expected BybitAccountType, got {type(account_type)}"
58
+ )
59
+
60
+ if (
61
+ account_type == BybitAccountType.UNIFIED
62
+ or account_type == BybitAccountType.UNIFIED_TESTNET
63
+ ):
64
+ raise EngineBuildError(
65
+ f"{account_type} is not supported for public connector."
66
+ )
67
+
68
+ if basic_config.testnet != account_type.is_testnet:
69
+ raise EngineBuildError(
70
+ f"The `testnet` setting of Bybit is not consistent with the public connector's account type `{account_type}`."
71
+ )
72
+
73
+ def validate_public_connector_limits(
74
+ self, existing_connectors: Dict[AccountType, Any]
75
+ ) -> None:
76
+ """Validate public connector limits for Bybit exchange"""
77
+ # Bybit has no specific connector limits
78
+ pass
79
+
80
+ def instrument_id_to_account_type(self, instrument_id: InstrumentId) -> AccountType:
81
+ """Convert an instrument ID to the appropriate account type for Bybit exchange"""
82
+ if instrument_id.is_spot:
83
+ return (
84
+ BybitAccountType.SPOT_TESTNET
85
+ if self.is_testnet
86
+ else BybitAccountType.SPOT
87
+ )
88
+ elif instrument_id.is_linear:
89
+ return (
90
+ BybitAccountType.LINEAR_TESTNET
91
+ if self.is_testnet
92
+ else BybitAccountType.LINEAR
93
+ )
94
+ elif instrument_id.is_inverse:
95
+ return (
96
+ BybitAccountType.INVERSE_TESTNET
97
+ if self.is_testnet
98
+ else BybitAccountType.INVERSE
99
+ )
100
+ else:
101
+ raise ValueError(f"Unsupported instrument type: {instrument_id.type}")
102
+
103
+
104
+ if __name__ == "__main__":
105
+ exchange = BybitExchangeManager()
106
+ exchange.load_markets()
107
+ # print(exchange.market)
108
+ # print(exchange.market_id)
@@ -0,0 +1,128 @@
1
+ """
2
+ Bybit 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.bybit.exchange import BybitExchangeManager
22
+ from walrasquant.exchange.bybit.connector import (
23
+ BybitPublicConnector,
24
+ BybitPrivateConnector,
25
+ )
26
+ from walrasquant.exchange.bybit.ems import BybitExecutionManagementSystem
27
+ from walrasquant.exchange.bybit.constants import BybitAccountType
28
+ from walrasquant.exchange.bybit.schema import BybitMarket
29
+
30
+
31
+ class BybitFactory(ExchangeFactory):
32
+ """Factory for creating Bybit exchange components."""
33
+
34
+ @property
35
+ def exchange_type(self) -> ExchangeType:
36
+ return ExchangeType.BYBIT
37
+
38
+ def create_manager(self, basic_config: BasicConfig) -> ExchangeManager:
39
+ """Create BybitExchangeManager."""
40
+ ccxt_config: ConfigType = {
41
+ "apiKey": basic_config.api_key or "",
42
+ "secret": basic_config.secret or "",
43
+ "exchange_id": "bybit",
44
+ "sandbox": basic_config.testnet,
45
+ }
46
+ if basic_config.passphrase:
47
+ ccxt_config["password"] = basic_config.passphrase
48
+
49
+ return BybitExchangeManager(ccxt_config)
50
+
51
+ def create_public_connector(
52
+ self,
53
+ config: PublicConnectorConfig,
54
+ exchange: ExchangeManager,
55
+ context: BuildContext,
56
+ ) -> PublicConnector:
57
+ """Create BybitPublicConnector."""
58
+ if not isinstance(config.account_type, BybitAccountType):
59
+ raise TypeError("Bybit public connector requires BybitAccountType")
60
+ return BybitPublicConnector(
61
+ account_type=config.account_type,
62
+ exchange=cast(BybitExchangeManager, 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
+ def create_private_connector(
73
+ self,
74
+ config: PrivateConnectorConfig,
75
+ exchange: ExchangeManager,
76
+ context: BuildContext,
77
+ account_type: AccountType | None = None,
78
+ ) -> PrivateConnector:
79
+ """Create BybitPrivateConnector."""
80
+ # Use provided account_type or determine from exchange testnet status
81
+ if account_type:
82
+ final_account_type = account_type
83
+ else:
84
+ final_account_type = self.get_private_account_type(exchange, config)
85
+
86
+ if not isinstance(final_account_type, BybitAccountType):
87
+ raise TypeError("Bybit private connector requires BybitAccountType")
88
+ return BybitPrivateConnector(
89
+ exchange=cast(BybitExchangeManager, exchange),
90
+ account_type=final_account_type,
91
+ cache=context.cache,
92
+ clock=context.clock,
93
+ msgbus=context.msgbus,
94
+ registry=context.registry,
95
+ enable_rate_limit=config.enable_rate_limit,
96
+ task_manager=context.task_manager,
97
+ max_retries=config.max_retries,
98
+ delay_initial_ms=config.delay_initial_ms,
99
+ delay_max_ms=config.delay_max_ms,
100
+ backoff_factor=config.backoff_factor,
101
+ max_subscriptions_per_client=config.max_subscriptions_per_client,
102
+ max_clients=config.max_clients,
103
+ order_query_config=context.order_query_config,
104
+ )
105
+
106
+ def create_ems(
107
+ self, exchange: ExchangeManager, context: BuildContext
108
+ ) -> ExecutionManagementSystem:
109
+ """Create BybitExecutionManagementSystem."""
110
+ return BybitExecutionManagementSystem(
111
+ market=cast(dict[str, BybitMarket], exchange.market),
112
+ cache=context.cache,
113
+ msgbus=context.msgbus,
114
+ clock=context.clock,
115
+ task_manager=context.task_manager,
116
+ registry=context.registry,
117
+ queue_maxsize=context.queue_maxsize,
118
+ )
119
+
120
+ def get_private_account_type(
121
+ self, exchange: ExchangeManager, config: PrivateConnectorConfig
122
+ ) -> AccountType:
123
+ """Determine account type based on testnet status."""
124
+ return (
125
+ BybitAccountType.UNIFIED_TESTNET
126
+ if exchange.is_testnet
127
+ else BybitAccountType.UNIFIED
128
+ )