walrasquant-lib 0.4.24__tar.gz → 0.4.26__tar.gz
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_lib-0.4.24 → walrasquant_lib-0.4.26}/PKG-INFO +3 -3
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/pyproject.toml +3 -3
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/aggregation.py +5 -2
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/backends/db_postgresql.py +9 -8
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/backends/db_sqlite.py +9 -8
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/base/connector.py +18 -8
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/base/ems.py +10 -7
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/base/oms.py +26 -17
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/base/retry.py +11 -5
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/base/sms.py +5 -5
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/base/ws_client.py +19 -12
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/config.py +14 -4
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/core/cache.py +17 -12
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/core/entity.py +12 -8
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/core/nautilius_core.py +3 -0
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/core/registry.py +5 -5
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/engine.py +51 -24
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/exchange/binance/connector.py +40 -18
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/exchange/binance/exchange.py +8 -5
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/exchange/binance/oms.py +44 -29
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/exchange/binance/rest_api.py +4 -4
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/exchange/binance/websockets.py +3 -3
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/exchange/bitget/connector.py +4 -4
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/exchange/bitget/exchange.py +1 -1
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/exchange/bitget/oms.py +65 -38
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/exchange/bitget/rest_api.py +8 -4
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/exchange/bitget/websockets.py +2 -2
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/exchange/bybit/connector.py +3 -3
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/exchange/bybit/exchange.py +1 -1
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/exchange/bybit/oms.py +41 -25
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/exchange/bybit/rest_api.py +4 -4
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/exchange/bybit/websockets.py +10 -8
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/exchange/hyperliquid/connector.py +1 -1
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/exchange/hyperliquid/exchange.py +5 -2
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/exchange/hyperliquid/oms.py +42 -22
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/exchange/hyperliquid/rest_api.py +4 -4
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/exchange/hyperliquid/websockets.py +2 -2
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/exchange/okx/connector.py +5 -5
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/exchange/okx/exchange.py +1 -1
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/exchange/okx/oms.py +43 -24
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/exchange/okx/rest_api.py +8 -4
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/exchange/okx/websockets.py +2 -2
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/exchange/registry.py +5 -2
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/execution/algorithm.py +43 -21
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/execution/algorithms/twap.py +48 -23
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/push.py +1 -1
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/strategy.py +56 -16
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/tools/pm2_wrapper.py +1 -1
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/README.md +0 -0
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/__init__.py +0 -0
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/backends/__init__.py +0 -0
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/backends/db.py +0 -0
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/backends/db_memory.py +0 -0
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/base/__init__.py +0 -0
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/base/api_client.py +0 -0
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/base/exchange.py +0 -0
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/constants.py +0 -0
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/core/__init__.py +0 -0
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/core/clock.py +0 -0
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/core/connection.py +0 -0
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/error.py +0 -0
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/exchange/__init__.py +0 -0
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/exchange/base_factory.py +0 -0
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/exchange/binance/__init__.py +0 -0
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/exchange/binance/constants.py +0 -0
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/exchange/binance/ems.py +0 -0
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/exchange/binance/error.py +0 -0
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/exchange/binance/factory.py +0 -0
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/exchange/binance/schema.py +0 -0
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/exchange/bitget/__init__.py +0 -0
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/exchange/bitget/constants.py +0 -0
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/exchange/bitget/ems.py +0 -0
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/exchange/bitget/error.py +0 -0
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/exchange/bitget/factory.py +0 -0
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/exchange/bitget/schema.py +0 -0
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/exchange/bybit/__init__.py +0 -0
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/exchange/bybit/constants.py +0 -0
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/exchange/bybit/ems.py +0 -0
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/exchange/bybit/error.py +0 -0
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/exchange/bybit/factory.py +0 -0
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/exchange/bybit/schema.py +0 -0
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/exchange/hyperliquid/__init__.py +0 -0
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/exchange/hyperliquid/constants.py +0 -0
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/exchange/hyperliquid/ems.py +0 -0
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/exchange/hyperliquid/error.py +0 -0
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/exchange/hyperliquid/factory.py +0 -0
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/exchange/hyperliquid/schema.py +0 -0
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/exchange/hyperliquid/signing.py +0 -0
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/exchange/okx/__init__.py +0 -0
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/exchange/okx/constants.py +0 -0
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/exchange/okx/ems.py +0 -0
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/exchange/okx/error.py +0 -0
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/exchange/okx/factory.py +0 -0
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/exchange/okx/schema.py +0 -0
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/execution/__init__.py +0 -0
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/execution/algorithms/__init__.py +0 -0
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/execution/config.py +0 -0
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/execution/constants.py +0 -0
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/execution/schema.py +0 -0
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/indicator.py +0 -0
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/schema.py +0 -0
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/tools/__init__.py +0 -0
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/web/__init__.py +0 -0
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/web/app.py +0 -0
- {walrasquant_lib-0.4.24 → walrasquant_lib-0.4.26}/src/walrasquant/web/server.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: walrasquant-lib
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.26
|
|
4
4
|
Summary: fastest python trading bot
|
|
5
5
|
Author: River-Shi
|
|
6
6
|
Author-email: River-Shi <nachuan.shi.quant@gmail.com>
|
|
@@ -23,9 +23,9 @@ Requires-Dist: dynaconf[redis]>=3.2.12
|
|
|
23
23
|
Requires-Dist: aiohttp>=3.13.3
|
|
24
24
|
Requires-Dist: rich>=14.0.0
|
|
25
25
|
Requires-Dist: picows>=2.1.1
|
|
26
|
-
Requires-Dist: nexuscore-lib>=0.2.
|
|
26
|
+
Requires-Dist: nexuscore-lib>=0.2.1
|
|
27
27
|
Requires-Dist: flashduty-lib>=0.2.0
|
|
28
|
-
Requires-Dist: nexuslog-lib>=0.
|
|
28
|
+
Requires-Dist: nexuslog-lib>=0.8.0
|
|
29
29
|
Requires-Dist: msgspec>=0.21.1
|
|
30
30
|
Requires-Dist: pandas<3.0.0
|
|
31
31
|
Requires-Dist: numpy>=2.2.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "walrasquant-lib"
|
|
3
|
-
version = "0.4.
|
|
3
|
+
version = "0.4.26"
|
|
4
4
|
description = "fastest python trading bot"
|
|
5
5
|
authors = [
|
|
6
6
|
{name = "River-Shi",email = "nachuan.shi.quant@gmail.com"}
|
|
@@ -27,9 +27,9 @@ dependencies = [
|
|
|
27
27
|
"aiohttp>=3.13.3",
|
|
28
28
|
"rich>=14.0.0",
|
|
29
29
|
"picows>=2.1.1",
|
|
30
|
-
"nexuscore-lib>=0.2.
|
|
30
|
+
"nexuscore-lib>=0.2.1",
|
|
31
31
|
"flashduty-lib>=0.2.0",
|
|
32
|
-
"nexuslog-lib>=0.
|
|
32
|
+
"nexuslog-lib>=0.8.0",
|
|
33
33
|
"msgspec>=0.21.1",
|
|
34
34
|
"pandas<3.0.0",
|
|
35
35
|
"numpy>=2.2.0",
|
|
@@ -396,7 +396,10 @@ class TimeKlineAggregator(KlineAggregator):
|
|
|
396
396
|
callback=self._build_bar,
|
|
397
397
|
)
|
|
398
398
|
self._log.debug(
|
|
399
|
-
|
|
399
|
+
"Timer set: %s, start=%s, interval=%s",
|
|
400
|
+
self._timer_name,
|
|
401
|
+
start_time,
|
|
402
|
+
interval_td,
|
|
400
403
|
)
|
|
401
404
|
|
|
402
405
|
def _get_start_time(self):
|
|
@@ -446,4 +449,4 @@ class TimeKlineAggregator(KlineAggregator):
|
|
|
446
449
|
|
|
447
450
|
# # Build and send kline (only if trades were received)
|
|
448
451
|
self._build_and_send(ts_event, ts_init)
|
|
449
|
-
self._log.debug(
|
|
452
|
+
self._log.debug("Kline built: ts_event=%s, ts_init=%s", ts_event, ts_init)
|
|
@@ -139,7 +139,8 @@ class PostgreSQLBackend(StorageBackend):
|
|
|
139
139
|
str(symbol),
|
|
140
140
|
)
|
|
141
141
|
self._log.debug(
|
|
142
|
-
|
|
142
|
+
"Deleted %s stale positions from database",
|
|
143
|
+
len(positions_to_delete),
|
|
143
144
|
)
|
|
144
145
|
|
|
145
146
|
for symbol, position in mem_positions.copy().items():
|
|
@@ -219,7 +220,7 @@ class PostgreSQLBackend(StorageBackend):
|
|
|
219
220
|
return order
|
|
220
221
|
return None
|
|
221
222
|
except psycopg2.DatabaseError as error:
|
|
222
|
-
self._log.error(
|
|
223
|
+
self._log.error("Error getting order from PostgreSQL: %s", error)
|
|
223
224
|
return None
|
|
224
225
|
|
|
225
226
|
def get_symbol_orders(self, symbol: str) -> Set[str]:
|
|
@@ -277,9 +278,9 @@ class PostgreSQLBackend(StorageBackend):
|
|
|
277
278
|
serialized_value,
|
|
278
279
|
)
|
|
279
280
|
except msgspec.EncodeError as e:
|
|
280
|
-
self._log.error(
|
|
281
|
+
self._log.error("Error serializing parameter %s: %s", key, e)
|
|
281
282
|
except Exception as e:
|
|
282
|
-
self._log.error(
|
|
283
|
+
self._log.error("Error storing parameter %s: %s", key, e)
|
|
283
284
|
|
|
284
285
|
def get_param(self, key: str, default: Any = None) -> Any:
|
|
285
286
|
import msgspec
|
|
@@ -296,11 +297,11 @@ class PostgreSQLBackend(StorageBackend):
|
|
|
296
297
|
try:
|
|
297
298
|
return self._decode_param(row[0])
|
|
298
299
|
except msgspec.DecodeError as e:
|
|
299
|
-
self._log.error(
|
|
300
|
+
self._log.error("Error deserializing parameter %s: %s", key, e)
|
|
300
301
|
return default
|
|
301
302
|
return default
|
|
302
303
|
except Exception as e:
|
|
303
|
-
self._log.error(
|
|
304
|
+
self._log.error("Error getting parameter %s: %s", key, e)
|
|
304
305
|
return default
|
|
305
306
|
|
|
306
307
|
def get_all_params(self) -> Dict[str, Any]:
|
|
@@ -314,8 +315,8 @@ class PostgreSQLBackend(StorageBackend):
|
|
|
314
315
|
try:
|
|
315
316
|
params[row[0]] = self._decode_param(row[1])
|
|
316
317
|
except msgspec.DecodeError as e:
|
|
317
|
-
self._log.error(
|
|
318
|
+
self._log.error("Error deserializing parameter %s: %s", row[0], e)
|
|
318
319
|
cursor.close()
|
|
319
320
|
except Exception as e:
|
|
320
|
-
self._log.error(
|
|
321
|
+
self._log.error("Error getting all parameters: %s", e)
|
|
321
322
|
return params
|
|
@@ -142,7 +142,8 @@ class SQLiteBackend(StorageBackend):
|
|
|
142
142
|
[(symbol,) for symbol in positions_to_delete],
|
|
143
143
|
)
|
|
144
144
|
self._log.debug(
|
|
145
|
-
|
|
145
|
+
"Deleted %s stale positions from database",
|
|
146
|
+
len(positions_to_delete),
|
|
146
147
|
)
|
|
147
148
|
|
|
148
149
|
for symbol, position in mem_positions.copy().items():
|
|
@@ -217,7 +218,7 @@ class SQLiteBackend(StorageBackend):
|
|
|
217
218
|
return None
|
|
218
219
|
|
|
219
220
|
except sqlite3.Error as e:
|
|
220
|
-
self._log.error(
|
|
221
|
+
self._log.error("Error getting order from SQLite: %s", e)
|
|
221
222
|
return None
|
|
222
223
|
|
|
223
224
|
def get_symbol_orders(self, symbol: str) -> Set[str]:
|
|
@@ -269,9 +270,9 @@ class SQLiteBackend(StorageBackend):
|
|
|
269
270
|
(key, serialized_value),
|
|
270
271
|
)
|
|
271
272
|
except msgspec.EncodeError as e:
|
|
272
|
-
self._log.error(
|
|
273
|
+
self._log.error("Error serializing parameter %s: %s", key, e)
|
|
273
274
|
except sqlite3.Error as e:
|
|
274
|
-
self._log.error(
|
|
275
|
+
self._log.error("Error storing parameter %s: %s", key, e)
|
|
275
276
|
await self._async_conn.commit()
|
|
276
277
|
|
|
277
278
|
def get_param(self, key: str, default: Any = None) -> Any:
|
|
@@ -286,11 +287,11 @@ class SQLiteBackend(StorageBackend):
|
|
|
286
287
|
try:
|
|
287
288
|
return self._decode_param(row[0])
|
|
288
289
|
except msgspec.DecodeError as e:
|
|
289
|
-
self._log.error(
|
|
290
|
+
self._log.error("Error deserializing parameter %s: %s", key, e)
|
|
290
291
|
return default
|
|
291
292
|
return default
|
|
292
293
|
except sqlite3.Error as e:
|
|
293
|
-
self._log.error(
|
|
294
|
+
self._log.error("Error getting parameter %s: %s", key, e)
|
|
294
295
|
return default
|
|
295
296
|
|
|
296
297
|
def get_all_params(self) -> Dict[str, Any]:
|
|
@@ -304,7 +305,7 @@ class SQLiteBackend(StorageBackend):
|
|
|
304
305
|
try:
|
|
305
306
|
params[row[0]] = self._decode_param(row[1])
|
|
306
307
|
except msgspec.DecodeError as e:
|
|
307
|
-
self._log.error(
|
|
308
|
+
self._log.error("Error deserializing parameter %s: %s", row[0], e)
|
|
308
309
|
except sqlite3.Error as e:
|
|
309
|
-
self._log.error(
|
|
310
|
+
self._log.error("Error getting all parameters: %s", e)
|
|
310
311
|
return params
|
|
@@ -223,7 +223,7 @@ class PublicConnector(ABC):
|
|
|
223
223
|
self._add_aggregator(symbol, aggregator)
|
|
224
224
|
|
|
225
225
|
self._log.info(
|
|
226
|
-
|
|
226
|
+
"Time kline aggregator created for %s with interval %s", symbol, interval
|
|
227
227
|
)
|
|
228
228
|
|
|
229
229
|
def unsubscribe_kline_aggregator(
|
|
@@ -247,7 +247,9 @@ class PublicConnector(ABC):
|
|
|
247
247
|
aggregator.stop()
|
|
248
248
|
aggregators.remove(aggregator)
|
|
249
249
|
self._log.info(
|
|
250
|
-
|
|
250
|
+
"Time kline aggregator stopped for %s with interval %s",
|
|
251
|
+
symbol,
|
|
252
|
+
interval,
|
|
251
253
|
)
|
|
252
254
|
if not aggregators:
|
|
253
255
|
self._aggregators.pop(symbol, None)
|
|
@@ -275,7 +277,10 @@ class PublicConnector(ABC):
|
|
|
275
277
|
self._add_aggregator(symbol, aggregator)
|
|
276
278
|
|
|
277
279
|
self._log.info(
|
|
278
|
-
|
|
280
|
+
"Volume kline aggregator created for %s with threshold %s and type %s",
|
|
281
|
+
symbol,
|
|
282
|
+
volume_threshold,
|
|
283
|
+
volume_type,
|
|
279
284
|
)
|
|
280
285
|
|
|
281
286
|
def unsubscribe_volume_kline_aggregator(
|
|
@@ -296,7 +301,10 @@ class PublicConnector(ABC):
|
|
|
296
301
|
):
|
|
297
302
|
aggregators.remove(aggregator)
|
|
298
303
|
self._log.info(
|
|
299
|
-
|
|
304
|
+
"Volume kline aggregator stopped for %s with threshold %s and type %s",
|
|
305
|
+
symbol,
|
|
306
|
+
volume_threshold,
|
|
307
|
+
volume_type,
|
|
300
308
|
)
|
|
301
309
|
if not aggregators:
|
|
302
310
|
self._aggregators.pop(symbol, None)
|
|
@@ -690,7 +698,7 @@ class MockLinearConnector:
|
|
|
690
698
|
)
|
|
691
699
|
return order
|
|
692
700
|
except OrderError as e:
|
|
693
|
-
self._log.error(
|
|
701
|
+
self._log.error("Error creating order: %s", e)
|
|
694
702
|
return Order(
|
|
695
703
|
exchange=self._exchange_id,
|
|
696
704
|
timestamp=self._clock.timestamp_ms(),
|
|
@@ -726,7 +734,8 @@ class MockLinearConnector:
|
|
|
726
734
|
book = self._cache.bookl1(symbol)
|
|
727
735
|
if not book:
|
|
728
736
|
self._log.warning(
|
|
729
|
-
|
|
737
|
+
"Please subscribe to the `bookl1` data for %s or data not ready",
|
|
738
|
+
symbol,
|
|
730
739
|
)
|
|
731
740
|
continue
|
|
732
741
|
notional += float(position.amount) * book.mid
|
|
@@ -739,7 +748,8 @@ class MockLinearConnector:
|
|
|
739
748
|
book = self._cache.bookl1(symbol)
|
|
740
749
|
if not book:
|
|
741
750
|
self._log.warning(
|
|
742
|
-
|
|
751
|
+
"Please subscribe to the `bookl1` data for %s or data not ready",
|
|
752
|
+
symbol,
|
|
743
753
|
)
|
|
744
754
|
return
|
|
745
755
|
|
|
@@ -851,7 +861,7 @@ class MockLinearConnector:
|
|
|
851
861
|
self._apply_fee(order)
|
|
852
862
|
|
|
853
863
|
async def connect(self):
|
|
854
|
-
self._log.debug(
|
|
864
|
+
self._log.debug("Starting mock connector for %s", self._account_type)
|
|
855
865
|
await self._init_position()
|
|
856
866
|
await self._init_balance()
|
|
857
867
|
|
|
@@ -138,7 +138,7 @@ class ExecutionManagementSystem(ABC):
|
|
|
138
138
|
def _do_put(self, queue: PriorityOrderQueue, item: Any) -> None:
|
|
139
139
|
# Always runs on the loop thread: queue access + bookkeeping are serialized.
|
|
140
140
|
if self._shutdown:
|
|
141
|
-
self._log.warning(
|
|
141
|
+
self._log.warning("EMS is shutting down; order submit rejected: %s", item)
|
|
142
142
|
return
|
|
143
143
|
priority = _item_priority(item)
|
|
144
144
|
|
|
@@ -161,7 +161,7 @@ class ExecutionManagementSystem(ABC):
|
|
|
161
161
|
return
|
|
162
162
|
exc = task.exception()
|
|
163
163
|
if exc:
|
|
164
|
-
self._log.error(
|
|
164
|
+
self._log.error("Queue put failed: %r", exc)
|
|
165
165
|
|
|
166
166
|
def _build(self, private_connectors: Dict[AccountType, PrivateConnector]):
|
|
167
167
|
self._private_connectors = private_connectors
|
|
@@ -440,7 +440,7 @@ class ExecutionManagementSystem(ABC):
|
|
|
440
440
|
SubmitType.CANCEL_BATCH: self._cancel_batch_orders,
|
|
441
441
|
}
|
|
442
442
|
|
|
443
|
-
self._log.debug(
|
|
443
|
+
self._log.debug("Handling orders for account type: %s", account_type)
|
|
444
444
|
# Each dequeued item runs as its own task so one in-flight REST round
|
|
445
445
|
# trip does not block the rest of the queue. The semaphore throttles
|
|
446
446
|
# the dequeue itself: excess items stay in the priority queue (where
|
|
@@ -452,7 +452,7 @@ class ExecutionManagementSystem(ABC):
|
|
|
452
452
|
queue_item = await queue.get()
|
|
453
453
|
await semaphore.acquire()
|
|
454
454
|
(order_submit, submit_type) = queue_item
|
|
455
|
-
self._log.debug(
|
|
455
|
+
self._log.debug("[ORDER SUBMIT]: %s", order_submit)
|
|
456
456
|
self._task_manager.create_task(
|
|
457
457
|
self._run_submit(
|
|
458
458
|
submit_handlers[submit_type],
|
|
@@ -476,9 +476,12 @@ class ExecutionManagementSystem(ABC):
|
|
|
476
476
|
except Exception:
|
|
477
477
|
self._log.error(
|
|
478
478
|
"Order submit handler failed, "
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
479
|
+
"account_type=%s, submit_type=%s, order_submit=%s\n"
|
|
480
|
+
"Traceback: %s",
|
|
481
|
+
account_type,
|
|
482
|
+
submit_type,
|
|
483
|
+
order_submit,
|
|
484
|
+
traceback.format_exc(),
|
|
482
485
|
)
|
|
483
486
|
finally:
|
|
484
487
|
semaphore.release()
|
|
@@ -81,31 +81,31 @@ class OrderManagementSystem(ABC):
|
|
|
81
81
|
valid = self._cache._order_status_update(order) # INITIALIZED -> PENDING
|
|
82
82
|
match order.status:
|
|
83
83
|
case OrderStatus.PENDING:
|
|
84
|
-
self._log.debug(
|
|
84
|
+
self._log.debug("ORDER STATUS PENDING: %s", order)
|
|
85
85
|
self._msgbus.send(endpoint="pending", msg=order)
|
|
86
86
|
case OrderStatus.FAILED:
|
|
87
|
-
self._log.debug(
|
|
87
|
+
self._log.debug("ORDER STATUS FAILED: %s", order)
|
|
88
88
|
self._msgbus.send(endpoint="failed", msg=order)
|
|
89
89
|
case OrderStatus.ACCEPTED:
|
|
90
|
-
self._log.debug(
|
|
90
|
+
self._log.debug("ORDER STATUS ACCEPTED: %s", order)
|
|
91
91
|
self._msgbus.send(endpoint="accepted", msg=order)
|
|
92
92
|
case OrderStatus.PARTIALLY_FILLED:
|
|
93
|
-
self._log.debug(
|
|
93
|
+
self._log.debug("ORDER STATUS PARTIALLY FILLED: %s", order)
|
|
94
94
|
self._msgbus.send(endpoint="partially_filled", msg=order)
|
|
95
95
|
case OrderStatus.CANCELED:
|
|
96
|
-
self._log.debug(
|
|
96
|
+
self._log.debug("ORDER STATUS CANCELED: %s", order)
|
|
97
97
|
self._msgbus.send(endpoint="canceled", msg=order)
|
|
98
98
|
case OrderStatus.CANCELING:
|
|
99
|
-
self._log.debug(
|
|
99
|
+
self._log.debug("ORDER STATUS CANCELING: %s", order)
|
|
100
100
|
self._msgbus.send(endpoint="canceling", msg=order)
|
|
101
101
|
case OrderStatus.CANCEL_FAILED:
|
|
102
|
-
self._log.debug(
|
|
102
|
+
self._log.debug("ORDER STATUS CANCEL FAILED: %s", order)
|
|
103
103
|
self._msgbus.send(endpoint="cancel_failed", msg=order)
|
|
104
104
|
case OrderStatus.FILLED:
|
|
105
|
-
self._log.debug(
|
|
105
|
+
self._log.debug("ORDER STATUS FILLED: %s", order)
|
|
106
106
|
self._msgbus.send(endpoint="filled", msg=order)
|
|
107
107
|
case OrderStatus.EXPIRED:
|
|
108
|
-
self._log.debug(
|
|
108
|
+
self._log.debug("ORDER STATUS EXPIRED: %s", order)
|
|
109
109
|
self._msgbus.send(endpoint="expired", msg=order)
|
|
110
110
|
|
|
111
111
|
if valid and order.status == OrderStatus.CANCEL_FAILED:
|
|
@@ -139,7 +139,7 @@ class OrderManagementSystem(ABC):
|
|
|
139
139
|
except asyncio.CancelledError:
|
|
140
140
|
raise
|
|
141
141
|
except Exception as exc:
|
|
142
|
-
self._log.error(
|
|
142
|
+
self._log.error("Order query scan loop stopped unexpectedly: %s", exc)
|
|
143
143
|
raise
|
|
144
144
|
|
|
145
145
|
def _scan_timed_out_orders(self) -> None:
|
|
@@ -209,19 +209,25 @@ class OrderManagementSystem(ABC):
|
|
|
209
209
|
|
|
210
210
|
try:
|
|
211
211
|
self._log.debug(
|
|
212
|
-
|
|
212
|
+
"Querying order status: reason=%s symbol=%s oid=%s",
|
|
213
|
+
reason,
|
|
214
|
+
order.symbol,
|
|
215
|
+
oid,
|
|
213
216
|
)
|
|
214
217
|
queried_order = await self.query_order(oid=oid, symbol=order.symbol)
|
|
215
218
|
if queried_order is None:
|
|
216
219
|
self._log.warning(
|
|
217
|
-
|
|
218
|
-
|
|
220
|
+
"Order status query returned no order: reason=%s symbol=%s oid=%s",
|
|
221
|
+
reason,
|
|
222
|
+
order.symbol,
|
|
223
|
+
oid,
|
|
219
224
|
)
|
|
220
225
|
return
|
|
221
226
|
if queried_order.oid is None:
|
|
222
227
|
self._log.error(
|
|
223
|
-
|
|
224
|
-
|
|
228
|
+
"Order status query returned order without oid: symbol=%s oid=%s",
|
|
229
|
+
order.symbol,
|
|
230
|
+
oid,
|
|
225
231
|
)
|
|
226
232
|
return
|
|
227
233
|
self.order_status_update(queried_order)
|
|
@@ -229,8 +235,11 @@ class OrderManagementSystem(ABC):
|
|
|
229
235
|
raise
|
|
230
236
|
except Exception as exc:
|
|
231
237
|
self._log.error(
|
|
232
|
-
|
|
233
|
-
|
|
238
|
+
"Order status query failed: reason=%s symbol=%s oid=%s: %s",
|
|
239
|
+
reason,
|
|
240
|
+
order.symbol,
|
|
241
|
+
oid,
|
|
242
|
+
exc,
|
|
234
243
|
)
|
|
235
244
|
finally:
|
|
236
245
|
self._querying_oids.discard(oid)
|
|
@@ -181,7 +181,7 @@ class RetryManager(Generic[T]):
|
|
|
181
181
|
"""
|
|
182
182
|
Cancel the retry operation.
|
|
183
183
|
"""
|
|
184
|
-
self._log.debug(
|
|
184
|
+
self._log.debug("Canceling %r", self)
|
|
185
185
|
self.cancel_event.set()
|
|
186
186
|
|
|
187
187
|
def clear(self) -> None:
|
|
@@ -195,19 +195,25 @@ class RetryManager(Generic[T]):
|
|
|
195
195
|
self.message = None
|
|
196
196
|
|
|
197
197
|
def _cancel(self) -> None:
|
|
198
|
-
self._log.warning(
|
|
198
|
+
self._log.warning("Canceled retry for '%s'", self.name)
|
|
199
199
|
self.result = False
|
|
200
200
|
self.message = "Canceled retry"
|
|
201
201
|
|
|
202
202
|
def _log_retry(self, retry: int, retry_delay_ms: int) -> None:
|
|
203
203
|
self._log.warning(
|
|
204
|
-
|
|
205
|
-
|
|
204
|
+
"Retrying %s/%s for '%s' in %ss%s",
|
|
205
|
+
retry,
|
|
206
|
+
self.max_retries,
|
|
207
|
+
self.name,
|
|
208
|
+
retry_delay_ms / 1000,
|
|
209
|
+
self._details_str(),
|
|
206
210
|
)
|
|
207
211
|
|
|
208
212
|
def _log_error(self) -> None:
|
|
209
213
|
self._log.error(
|
|
210
|
-
|
|
214
|
+
"Failed on '%s'%s",
|
|
215
|
+
self.name,
|
|
216
|
+
self._details_str(),
|
|
211
217
|
)
|
|
212
218
|
|
|
213
219
|
def _details_str(self) -> str:
|
|
@@ -73,7 +73,7 @@ class SubscriptionManagementSystem:
|
|
|
73
73
|
return
|
|
74
74
|
exc = task.exception()
|
|
75
75
|
if exc:
|
|
76
|
-
self._log.error(
|
|
76
|
+
self._log.error("Queue put failed: %r", exc)
|
|
77
77
|
|
|
78
78
|
def _infer_account_type(self, symbol: str) -> AccountType:
|
|
79
79
|
"""Infer account type from symbol"""
|
|
@@ -143,7 +143,7 @@ class SubscriptionManagementSystem:
|
|
|
143
143
|
)
|
|
144
144
|
|
|
145
145
|
self._safe_put(self._subscribe_queue, subscription)
|
|
146
|
-
self._log.debug(
|
|
146
|
+
self._log.debug("Subscription queued: %s for %s", data_type, symbols)
|
|
147
147
|
|
|
148
148
|
def unsubscribe(
|
|
149
149
|
self,
|
|
@@ -172,7 +172,7 @@ class SubscriptionManagementSystem:
|
|
|
172
172
|
)
|
|
173
173
|
|
|
174
174
|
self._safe_put(self._unsubscribe_queue, unsubscription)
|
|
175
|
-
self._log.debug(
|
|
175
|
+
self._log.debug("Unsubscription queued: %s for %s", data_type, symbols)
|
|
176
176
|
|
|
177
177
|
def _subscribe_trade(
|
|
178
178
|
self, subscription: SubscriptionSubmit, account_type: AccountType
|
|
@@ -448,7 +448,7 @@ class SubscriptionManagementSystem:
|
|
|
448
448
|
|
|
449
449
|
while True:
|
|
450
450
|
subscription = await self._subscribe_queue.get()
|
|
451
|
-
self._log.debug(
|
|
451
|
+
self._log.debug("[SUBSCRIPTION]: %s", subscription)
|
|
452
452
|
|
|
453
453
|
grouped = self._group_symbols_by_account_type(subscription.symbols)
|
|
454
454
|
|
|
@@ -494,7 +494,7 @@ class SubscriptionManagementSystem:
|
|
|
494
494
|
|
|
495
495
|
while True:
|
|
496
496
|
unsubscription = await self._unsubscribe_queue.get()
|
|
497
|
-
self._log.debug(
|
|
497
|
+
self._log.debug("[UNSUBSCRIPTION]: %s", unsubscription)
|
|
498
498
|
|
|
499
499
|
grouped = self._group_symbols_by_account_type(unsubscription.symbols)
|
|
500
500
|
# Process each account type group
|
|
@@ -67,7 +67,7 @@ class Listener(WSListener):
|
|
|
67
67
|
if self._specific_ping_msg:
|
|
68
68
|
transport.send(WSMsgType.TEXT, self._specific_ping_msg)
|
|
69
69
|
self._log.debug(
|
|
70
|
-
|
|
70
|
+
"Sent user specific ping: `%s`.", self._specific_ping_msg.decode()
|
|
71
71
|
)
|
|
72
72
|
else:
|
|
73
73
|
transport.send_ping()
|
|
@@ -124,14 +124,17 @@ class Listener(WSListener):
|
|
|
124
124
|
case WSMsgType.CLOSE:
|
|
125
125
|
close_code = frame.get_close_code()
|
|
126
126
|
self._log.warning(
|
|
127
|
-
|
|
127
|
+
"Received close frame. Close code: %s", close_code
|
|
128
128
|
)
|
|
129
129
|
return
|
|
130
130
|
except Exception as e:
|
|
131
131
|
import traceback
|
|
132
132
|
|
|
133
133
|
self._log.error(
|
|
134
|
-
|
|
134
|
+
"Error processing message: %s\nTraceback: %s\nws_frame: %s",
|
|
135
|
+
e,
|
|
136
|
+
traceback.format_exc(),
|
|
137
|
+
self._decode_frame(frame),
|
|
135
138
|
)
|
|
136
139
|
|
|
137
140
|
|
|
@@ -204,7 +207,7 @@ class WSClient(ABC):
|
|
|
204
207
|
try:
|
|
205
208
|
self._connection_change_callback(client_id, connected)
|
|
206
209
|
except Exception as exc: # pragma: no cover - defensive logging
|
|
207
|
-
self._log.warning(
|
|
210
|
+
self._log.warning("Connection change callback error: %s", exc)
|
|
208
211
|
|
|
209
212
|
def _is_client_connected(self, client_id: int) -> bool:
|
|
210
213
|
return self._transports.get(client_id) is not None
|
|
@@ -289,7 +292,7 @@ class WSClient(ABC):
|
|
|
289
292
|
|
|
290
293
|
async def _connect(self, client_id: int):
|
|
291
294
|
self._log.debug(
|
|
292
|
-
|
|
295
|
+
"Connecting to Websocket at %s (client %s)...", self._url, client_id
|
|
293
296
|
)
|
|
294
297
|
WSListenerFactory = lambda: Listener( # noqa: E731
|
|
295
298
|
self._callback,
|
|
@@ -309,7 +312,9 @@ class WSClient(ABC):
|
|
|
309
312
|
self._transports[client_id] = transport
|
|
310
313
|
self._listeners[client_id] = listener
|
|
311
314
|
self._log.debug(
|
|
312
|
-
|
|
315
|
+
"Websocket connected successfully to %s (client %s).",
|
|
316
|
+
self._url,
|
|
317
|
+
client_id,
|
|
313
318
|
)
|
|
314
319
|
self._notify_connection_change(client_id, True)
|
|
315
320
|
|
|
@@ -350,20 +355,22 @@ class WSClient(ABC):
|
|
|
350
355
|
if transport is None:
|
|
351
356
|
break
|
|
352
357
|
await transport.wait_disconnected()
|
|
353
|
-
self._log.debug(
|
|
358
|
+
self._log.debug("Websocket disconnected (client %s).", client_id)
|
|
354
359
|
self._notify_connection_change(client_id, False)
|
|
355
360
|
except asyncio.CancelledError:
|
|
356
361
|
self._log.debug(
|
|
357
|
-
|
|
362
|
+
"Websocket connection loop cancelled (client %s).", client_id
|
|
358
363
|
)
|
|
359
364
|
break
|
|
360
365
|
except Exception as e:
|
|
361
|
-
self._log.error(
|
|
366
|
+
self._log.error("Connection error: %s", e)
|
|
362
367
|
# finally:
|
|
363
368
|
# self._clean_up_client(client_id)
|
|
364
369
|
|
|
365
370
|
self._log.warning(
|
|
366
|
-
|
|
371
|
+
"Websocket reconnecting in %s seconds (client %s)...",
|
|
372
|
+
self._reconnect_interval,
|
|
373
|
+
client_id,
|
|
367
374
|
)
|
|
368
375
|
await asyncio.sleep(self._reconnect_interval)
|
|
369
376
|
|
|
@@ -384,11 +391,11 @@ class WSClient(ABC):
|
|
|
384
391
|
if target_id is None:
|
|
385
392
|
target_id = self._primary_client_id()
|
|
386
393
|
if target_id is None:
|
|
387
|
-
self._log.warning(
|
|
394
|
+
self._log.warning("Websocket not connected. drop msg: %s", payload)
|
|
388
395
|
return
|
|
389
396
|
transport = self._transports.get(target_id)
|
|
390
397
|
if transport is None:
|
|
391
|
-
self._log.warning(
|
|
398
|
+
self._log.warning("Websocket not connected. drop msg: %s", payload)
|
|
392
399
|
return
|
|
393
400
|
transport.send(WSMsgType.TEXT, msgspec.json.encode(payload))
|
|
394
401
|
|
|
@@ -14,16 +14,21 @@ class LogConfig:
|
|
|
14
14
|
Maps directly to nexuslog.basicConfig() parameters.
|
|
15
15
|
|
|
16
16
|
Attributes:
|
|
17
|
-
filename: Optional file path for log output. If
|
|
17
|
+
filename: Optional file path for log output. If "auto", writes to
|
|
18
|
+
logs/{strategy_id}_{user_id}.log. If None, logs to stdout.
|
|
18
19
|
level: Minimum log level to record. Options: TRACE, DEBUG, INFO, WARNING, ERROR
|
|
19
20
|
unix_ts: If True, emit unix timestamps instead of formatted local time.
|
|
21
|
+
color: ANSI colorization of log output. "auto" colors only when writing
|
|
22
|
+
to a color-capable terminal, "off" disables it, "always" forces it
|
|
23
|
+
even for files and pipes.
|
|
20
24
|
"""
|
|
21
25
|
|
|
22
|
-
filename: str | None = None
|
|
26
|
+
filename: Literal["auto"] | str | None = None
|
|
23
27
|
level: LOG_LEVELS = "INFO"
|
|
24
28
|
name_levels: dict[str | None, LOG_LEVELS] | None = None
|
|
25
29
|
unix_ts: bool = False
|
|
26
30
|
batch_size: int = 1
|
|
31
|
+
color: Literal["auto", "off", "always"] = "auto"
|
|
27
32
|
|
|
28
33
|
def __post_init__(self):
|
|
29
34
|
if self.level not in ["TRACE", "DEBUG", "INFO", "WARNING", "ERROR"]:
|
|
@@ -38,6 +43,11 @@ class LogConfig:
|
|
|
38
43
|
f"Invalid level: {level} for logger name: {name}. Must be one of TRACE, DEBUG, INFO, WARNING, ERROR."
|
|
39
44
|
)
|
|
40
45
|
|
|
46
|
+
if self.color not in ["auto", "off", "always"]:
|
|
47
|
+
raise ValueError(
|
|
48
|
+
f"Invalid color: {self.color}. Must be one of auto, off, always."
|
|
49
|
+
)
|
|
50
|
+
|
|
41
51
|
|
|
42
52
|
@dataclass
|
|
43
53
|
class BasicConfig:
|
|
@@ -265,7 +275,7 @@ class Config:
|
|
|
265
275
|
)
|
|
266
276
|
zero_mq_signal_config: ZeroMQSignalConfig | None = None
|
|
267
277
|
db_path: str = ".keys/cache.db"
|
|
268
|
-
storage_backend: StorageType = StorageType.
|
|
278
|
+
storage_backend: StorageType = StorageType.MEMORY
|
|
269
279
|
cache_sync_interval: int = 60
|
|
270
280
|
cache_expired_time: int = 3600
|
|
271
281
|
log_config: LogConfig = field(default_factory=LogConfig)
|
|
@@ -277,7 +287,7 @@ class Config:
|
|
|
277
287
|
order_query_config: OrderQueryConfig = field(default_factory=OrderQueryConfig)
|
|
278
288
|
|
|
279
289
|
def __post_init__(self):
|
|
280
|
-
if self.log_config.filename
|
|
290
|
+
if self.log_config.filename == "auto":
|
|
281
291
|
import os
|
|
282
292
|
|
|
283
293
|
log_folder = "logs"
|