Spaces:
Running
Running
Upload Quasar_axrvi_ranker.py
Browse files- Quasar_axrvi_ranker.py +50 -7
Quasar_axrvi_ranker.py
CHANGED
|
@@ -5036,6 +5036,10 @@ class QuasarAXRVIBridge:
|
|
| 5036 |
# β
FIX 1: Cache live broker profit so rotation closes use the real P&L.
|
| 5037 |
# trade.profit stays None only if Deriv never sent a profit field at all.
|
| 5038 |
trade.profit = float(raw_profit)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5039 |
|
| 5040 |
# ββ Terminal state check ββββββββββββββββββββββββββββββββββββββββββββββ
|
| 5041 |
is_terminal = (
|
|
@@ -5462,15 +5466,18 @@ class QuasarAXRVIBridge:
|
|
| 5462 |
G_t = sgn(direction) Β· log(S_t / S_entry) β fees (immediate liquidation value)
|
| 5463 |
C_t = model value estimate of future return (continuation value)
|
| 5464 |
|
| 5465 |
-
Exit
|
| 5466 |
-
|
|
|
|
|
|
|
|
|
|
| 5467 |
|
| 5468 |
Minimum holding guard: do not evaluate stopping until
|
| 5469 |
_trade_tick_counts[trade_id] >= shreve_config.min_holding_ticks.
|
| 5470 |
|
| 5471 |
REFILL TRIGGER (v7):
|
| 5472 |
-
After any position is closed, if open_trade_count drops below
|
| 5473 |
-
call rank_and_gate() to refill. This ensures the
|
| 5474 |
continuously without waiting for the next scheduled _rank_loop cycle.
|
| 5475 |
|
| 5476 |
CLOSING TIMEOUT FIX (v6.1):
|
|
@@ -5580,10 +5587,46 @@ class QuasarAXRVIBridge:
|
|
| 5580 |
f"+ buffer={sc.stopping_value_buffer:.5f}"
|
| 5581 |
)
|
| 5582 |
|
| 5583 |
-
# ββ
|
| 5584 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5585 |
should_stop = True
|
| 5586 |
-
stop_reason =
|
|
|
|
|
|
|
|
|
|
| 5587 |
|
| 5588 |
if should_stop:
|
| 5589 |
logger.info(f"[{trade.asset}] EXIT | {stop_reason}")
|
|
|
|
| 5036 |
# β
FIX 1: Cache live broker profit so rotation closes use the real P&L.
|
| 5037 |
# trade.profit stays None only if Deriv never sent a profit field at all.
|
| 5038 |
trade.profit = float(raw_profit)
|
| 5039 |
+
# β
FIX-MAXLOSS: cache on dedicated field so OptStop max-loss stop
|
| 5040 |
+
# can read the broker-authoritative live P&L without touching trade.profit
|
| 5041 |
+
# (which is also used for the terminal-event authoritative close).
|
| 5042 |
+
trade._last_poc_profit = float(raw_profit)
|
| 5043 |
|
| 5044 |
# ββ Terminal state check ββββββββββββββββββββββββββββββββββββββββββββββ
|
| 5045 |
is_terminal = (
|
|
|
|
| 5466 |
G_t = sgn(direction) Β· log(S_t / S_entry) β fees (immediate liquidation value)
|
| 5467 |
C_t = model value estimate of future return (continuation value)
|
| 5468 |
|
| 5469 |
+
Exit priority (evaluated in order, first match wins):
|
| 5470 |
+
1. OptStop β G_t β₯ C_t + stopping_value_buffer (profit target)
|
| 5471 |
+
2. MaxLoss β broker live P&L β€ β(sl_frac Γ stake) (stop-loss)
|
| 5472 |
+
3. HARD WALL β holding_duration > 120 s (absolute failsafe)
|
| 5473 |
+
4. SoftExpiry β holding_duration > expiry_time (default 60 s)
|
| 5474 |
|
| 5475 |
Minimum holding guard: do not evaluate stopping until
|
| 5476 |
_trade_tick_counts[trade_id] >= shreve_config.min_holding_ticks.
|
| 5477 |
|
| 5478 |
REFILL TRIGGER (v7):
|
| 5479 |
+
After any position is closed, if open_trade_count drops below 3, immediately
|
| 5480 |
+
call rank_and_gate() to refill. This ensures the 3-trade minimum is maintained
|
| 5481 |
continuously without waiting for the next scheduled _rank_loop cycle.
|
| 5482 |
|
| 5483 |
CLOSING TIMEOUT FIX (v6.1):
|
|
|
|
| 5587 |
f"+ buffer={sc.stopping_value_buffer:.5f}"
|
| 5588 |
)
|
| 5589 |
|
| 5590 |
+
# ββ Max-loss stop: exit when broker live P&L hits stop-loss ββ
|
| 5591 |
+
# Uses the broker-authoritative profit cached from poc stream.
|
| 5592 |
+
# This fires even when G_t < 0 (price moved against us), which
|
| 5593 |
+
# the pure OptStop rule misses because it only exits on G_t >= C_t.
|
| 5594 |
+
if not should_stop:
|
| 5595 |
+
sl_frac = ASSET_STOP_LOSS_FRAC.get(trade.asset, 0.50)
|
| 5596 |
+
stake = trade.buy_price if (trade.buy_price and trade.buy_price > 0) else self.trade_config.amount
|
| 5597 |
+
sl_threshold = -(sl_frac * stake)
|
| 5598 |
+
# Prefer broker-authoritative live P&L from poc stream
|
| 5599 |
+
live_loss = getattr(trade, "_last_poc_profit", None)
|
| 5600 |
+
if live_loss is None:
|
| 5601 |
+
# Fallback: approximate from spot price (less accurate)
|
| 5602 |
+
live_loss = trade.unrealized_pnl
|
| 5603 |
+
if live_loss is not None and live_loss <= sl_threshold:
|
| 5604 |
+
should_stop = True
|
| 5605 |
+
stop_reason = (
|
| 5606 |
+
f"MaxLoss: live_pnl={live_loss:+.4f} β€ "
|
| 5607 |
+
f"sl_threshold={sl_threshold:+.4f} "
|
| 5608 |
+
f"(SL={sl_frac:.0%} Γ stake={stake:.2f})"
|
| 5609 |
+
)
|
| 5610 |
+
|
| 5611 |
+
# ββ Hard absolute wall: 120 s regardless of everything ββββββββ
|
| 5612 |
+
# Safety net for cases where OptStop & MaxLoss both fail to fire
|
| 5613 |
+
# (e.g. G_t oscillating near zero, poc profit not yet received).
|
| 5614 |
+
# 120 s = 2Γ the intended 60 s expiry β generous but finite.
|
| 5615 |
+
MAX_HOLDING_SECONDS = 120
|
| 5616 |
+
if not should_stop and trade.holding_duration > MAX_HOLDING_SECONDS:
|
| 5617 |
+
should_stop = True
|
| 5618 |
+
stop_reason = (
|
| 5619 |
+
f"HARD WALL: held {trade.holding_duration:.0f}s "
|
| 5620 |
+
f"> {MAX_HOLDING_SECONDS}s absolute limit"
|
| 5621 |
+
)
|
| 5622 |
+
|
| 5623 |
+
# ββ Soft expiry: configured expiry_time (default 60 s) ββββββββ
|
| 5624 |
+
elif not should_stop and trade.holding_duration > self.trade_config.expiry_time:
|
| 5625 |
should_stop = True
|
| 5626 |
+
stop_reason = (
|
| 5627 |
+
f"SoftExpiry: held {trade.holding_duration:.0f}s "
|
| 5628 |
+
f"> expiry_time={self.trade_config.expiry_time}s"
|
| 5629 |
+
)
|
| 5630 |
|
| 5631 |
if should_stop:
|
| 5632 |
logger.info(f"[{trade.asset}] EXIT | {stop_reason}")
|