Spaces:
Running
Running
Update ranker_logging.py
Browse files- ranker_logging.py +65 -1
ranker_logging.py
CHANGED
|
@@ -238,7 +238,7 @@ class RankerLogger:
|
|
| 238 |
|
| 239 |
def trade_open(self, trade_id: str, asset: str, direction: str, price: float, qty: float):
|
| 240 |
"""Log trade opening."""
|
| 241 |
-
metadata = {"trade_id": trade_id, "price": price, "qty": qty}
|
| 242 |
self._log(LogLevel.INFO, EventCategory.TRADE,
|
| 243 |
f"TRADE OPENED | ID={trade_id} | Dir={direction} | Entry={price:.4f} | Qty={qty:.6f}",
|
| 244 |
asset=asset, metadata=metadata)
|
|
@@ -332,6 +332,70 @@ class RankerLogger:
|
|
| 332 |
"logs": entries
|
| 333 |
}, f, indent=2)
|
| 334 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 335 |
|
| 336 |
class RankerLogBridge:
|
| 337 |
"""
|
|
|
|
| 238 |
|
| 239 |
def trade_open(self, trade_id: str, asset: str, direction: str, price: float, qty: float):
|
| 240 |
"""Log trade opening."""
|
| 241 |
+
metadata = {"trade_id": trade_id, "price": price, "qty": qty, "direction": direction}
|
| 242 |
self._log(LogLevel.INFO, EventCategory.TRADE,
|
| 243 |
f"TRADE OPENED | ID={trade_id} | Dir={direction} | Entry={price:.4f} | Qty={qty:.6f}",
|
| 244 |
asset=asset, metadata=metadata)
|
|
|
|
| 332 |
"logs": entries
|
| 333 |
}, f, indent=2)
|
| 334 |
|
| 335 |
+
@staticmethod
|
| 336 |
+
def make_trade_ws_hook(ws_send_fn):
|
| 337 |
+
"""
|
| 338 |
+
Factory for the on_event callback that forwards TRADE log entries to the
|
| 339 |
+
Executo Hub via WebSocket.
|
| 340 |
+
|
| 341 |
+
Usage in your executor space ranker:
|
| 342 |
+
|
| 343 |
+
import asyncio, json
|
| 344 |
+
from ranker_logging import RankerLogger
|
| 345 |
+
|
| 346 |
+
async def connect_to_hub():
|
| 347 |
+
# ws = your websockets/websocket-client connection to the hub
|
| 348 |
+
ranker_logger = RankerLogger(
|
| 349 |
+
name="QuasarAXRVI_V75",
|
| 350 |
+
on_event=RankerLogger.make_trade_ws_hook(
|
| 351 |
+
lambda payload: asyncio.create_task(ws.send(json.dumps(payload)))
|
| 352 |
+
)
|
| 353 |
+
)
|
| 354 |
+
|
| 355 |
+
Args:
|
| 356 |
+
ws_send_fn: callable(dict) → sends a dict as JSON to the hub WS publisher.
|
| 357 |
+
Can be a coroutine wrapper or sync function.
|
| 358 |
+
"""
|
| 359 |
+
def _hook(entry: dict) -> None:
|
| 360 |
+
if entry.get("category") != "TRADE":
|
| 361 |
+
return
|
| 362 |
+
msg = entry.get("message", "")
|
| 363 |
+
meta = entry.get("metadata") or {}
|
| 364 |
+
ts = entry.get("timestamp", "")
|
| 365 |
+
|
| 366 |
+
if "TRADE OPENED" in msg:
|
| 367 |
+
payload = {
|
| 368 |
+
"type": "trade_opened",
|
| 369 |
+
"data": {
|
| 370 |
+
"trade_id": meta.get("trade_id"),
|
| 371 |
+
"asset": entry.get("asset"),
|
| 372 |
+
"direction": meta.get("direction", "?"),
|
| 373 |
+
"entry": meta.get("price"),
|
| 374 |
+
"qty": meta.get("qty", 0.0),
|
| 375 |
+
"opened_at": ts,
|
| 376 |
+
},
|
| 377 |
+
}
|
| 378 |
+
elif "TRADE CLOSED" in msg:
|
| 379 |
+
payload = {
|
| 380 |
+
"type": "trade_closed",
|
| 381 |
+
"data": {
|
| 382 |
+
"trade_id": meta.get("trade_id"),
|
| 383 |
+
"asset": entry.get("asset"),
|
| 384 |
+
"pnl": meta.get("pnl", 0.0),
|
| 385 |
+
"exit_price": meta.get("exit_price"),
|
| 386 |
+
"closed_at": ts,
|
| 387 |
+
},
|
| 388 |
+
}
|
| 389 |
+
else:
|
| 390 |
+
return
|
| 391 |
+
|
| 392 |
+
try:
|
| 393 |
+
ws_send_fn(payload)
|
| 394 |
+
except Exception:
|
| 395 |
+
pass # never let a WS error crash the ranker
|
| 396 |
+
|
| 397 |
+
return _hook
|
| 398 |
+
|
| 399 |
|
| 400 |
class RankerLogBridge:
|
| 401 |
"""
|