Spaces:
Running
Running
Upload 2 files
Browse files- hub_dashboard_service.py +18 -10
- ranker_logging.py +10 -5
hub_dashboard_service.py
CHANGED
|
@@ -88,16 +88,22 @@ class TradeLogParser:
|
|
| 88 |
r'(?:TRADE CLOSED|no-cid fallback|FORCE-CLOSED.*?timeout).*?(?:pnl|profit)=([+-]?[\d.]+)'
|
| 89 |
)
|
| 90 |
|
| 91 |
-
# FIX v2.
|
| 92 |
-
#
|
| 93 |
-
#
|
| 94 |
-
#
|
| 95 |
-
#
|
| 96 |
-
#
|
| 97 |
-
#
|
| 98 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 99 |
TRADE_CLOSE_RE_WITH_EXIT = re.compile(
|
| 100 |
-
r'TRADE CLOSED \| ID=(\S+) \| pnl=([+-]?[\d.]+)
|
|
|
|
| 101 |
)
|
| 102 |
|
| 103 |
# Fallback for Line 1: pnl + return%, no exit_price
|
|
@@ -259,7 +265,9 @@ class TradeLogParser:
|
|
| 259 |
if m2e:
|
| 260 |
trade_id = m2e.group(1)
|
| 261 |
pnl = float(m2e.group(2))
|
| 262 |
-
|
|
|
|
|
|
|
| 263 |
logger.debug(
|
| 264 |
f"[TradeLogParser] Matched CLOSE+EXIT: {trade_id} "
|
| 265 |
f"pnl={pnl} exit_price={_exit_price}"
|
|
|
|
| 88 |
r'(?:TRADE CLOSED|no-cid fallback|FORCE-CLOSED.*?timeout).*?(?:pnl|profit)=([+-]?[\d.]+)'
|
| 89 |
)
|
| 90 |
|
| 91 |
+
# FIX v2.3: Dual-format regex for trade close lines that carry exit_price.
|
| 92 |
+
# Root cause (v2.2 bug): ranker_logging.trade_close() wrote exit_price ONLY
|
| 93 |
+
# into the trailing JSON metadata blob, e.g.:
|
| 94 |
+
# TRADE CLOSED | ID=... | pnl=... | return=...% | {"exit_price": 4364.21}
|
| 95 |
+
# but this regex looked for it as a pipe-delimited text field:
|
| 96 |
+
# TRADE CLOSED | ID=... | pnl=... | exit_price=... <- never present
|
| 97 |
+
# so TRADE_CLOSE_RE_WITH_EXIT never matched and exit_price was always None.
|
| 98 |
+
#
|
| 99 |
+
# Fix has TWO parts:
|
| 100 |
+
# 1. ranker_logging.py now writes exit_price into the message text too.
|
| 101 |
+
# 2. This regex matches BOTH formats so old log files still parse correctly:
|
| 102 |
+
# Group 3 - pipe-delimited text field (new format, post-fix)
|
| 103 |
+
# Group 4 - JSON metadata field (old format, pre-fix)
|
| 104 |
TRADE_CLOSE_RE_WITH_EXIT = re.compile(
|
| 105 |
+
r'TRADE CLOSED \| ID=(\S+) \| pnl=([+-]?[\d.]+)'
|
| 106 |
+
r'.*?(?:\| exit_price=([\d.]+)|"exit_price":\s*([\d.]+))'
|
| 107 |
)
|
| 108 |
|
| 109 |
# Fallback for Line 1: pnl + return%, no exit_price
|
|
|
|
| 265 |
if m2e:
|
| 266 |
trade_id = m2e.group(1)
|
| 267 |
pnl = float(m2e.group(2))
|
| 268 |
+
# Group 3 = pipe-delimited "| exit_price=..." (new format, post-fix)
|
| 269 |
+
# Group 4 = JSON metadata '"exit_price": ...' (old format, pre-fix)
|
| 270 |
+
_exit_price = float(m2e.group(3) or m2e.group(4))
|
| 271 |
logger.debug(
|
| 272 |
f"[TradeLogParser] Matched CLOSE+EXIT: {trade_id} "
|
| 273 |
f"pnl={pnl} exit_price={_exit_price}"
|
ranker_logging.py
CHANGED
|
@@ -244,14 +244,19 @@ class RankerLogger:
|
|
| 244 |
asset=asset, metadata=metadata)
|
| 245 |
|
| 246 |
def trade_close(self, trade_id: str, asset: str, pnl: float, return_pct: float, exit_price: Optional[float] = None):
|
| 247 |
-
"""Log trade closing. ✅ FIX v2.
|
| 248 |
metadata = {"trade_id": trade_id, "pnl": pnl, "return_pct": return_pct}
|
| 249 |
-
#
|
| 250 |
if exit_price is not None:
|
| 251 |
metadata["exit_price"] = exit_price
|
| 252 |
-
|
| 253 |
-
|
| 254 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 255 |
|
| 256 |
def ranking_update(self, rankings: List[Dict], top_asset: str, top_score: float):
|
| 257 |
"""Log ranking cycle results."""
|
|
|
|
| 244 |
asset=asset, metadata=metadata)
|
| 245 |
|
| 246 |
def trade_close(self, trade_id: str, asset: str, pnl: float, return_pct: float, exit_price: Optional[float] = None):
|
| 247 |
+
"""Log trade closing. ✅ FIX v2.2: exit_price written into message text AND metadata."""
|
| 248 |
metadata = {"trade_id": trade_id, "pnl": pnl, "return_pct": return_pct}
|
| 249 |
+
# Include exit_price in metadata for JSON export
|
| 250 |
if exit_price is not None:
|
| 251 |
metadata["exit_price"] = exit_price
|
| 252 |
+
# ✅ FIX v2.2: Also embed exit_price in the pipe-delimited message so the
|
| 253 |
+
# dashboard regex (TRADE_CLOSE_RE_WITH_EXIT) can capture it directly.
|
| 254 |
+
# Previously exit_price lived only in the trailing JSON metadata, which the
|
| 255 |
+
# regex never reached — causing the EXIT column to always display "—".
|
| 256 |
+
msg = f"TRADE CLOSED | ID={trade_id} | pnl={pnl:+.4f} | return={return_pct:+.2%}"
|
| 257 |
+
if exit_price is not None:
|
| 258 |
+
msg += f" | exit_price={exit_price}"
|
| 259 |
+
self._log(LogLevel.INFO, EventCategory.TRADE, msg, asset=asset, metadata=metadata)
|
| 260 |
|
| 261 |
def ranking_update(self, rankings: List[Dict], top_asset: str, top_score: float):
|
| 262 |
"""Log ranking cycle results."""
|