KarlQuant commited on
Commit
54e270e
Β·
verified Β·
1 Parent(s): b2b6575

Update Quasar_axrvi_ranker.py

Browse files
Files changed (1) hide show
  1. Quasar_axrvi_ranker.py +54 -19
Quasar_axrvi_ranker.py CHANGED
@@ -176,7 +176,7 @@ class PortfolioRiskConfig:
176
  kelly_fraction: float = 0.5
177
  max_portfolio_risk: float = 0.02
178
  drawdown_halt_pct: float = 0.10
179
- halt_duration_secs: float = 180.0 # 3-minute automatic cooldown
180
  drawdown_reduce_pct: float = 0.05
181
  cvar_floor: float = -0.02
182
  min_notional: float = 1.0
@@ -1744,6 +1744,13 @@ class AXRVINet(nn.Module):
1744
  mean_quantiles = torch.stack(mc_quantiles, dim=0).mean(dim=0)
1745
  mean_regime = torch.stack(mc_regime, dim=0).mean(dim=0)
1746
 
 
 
 
 
 
 
 
1747
  return {
1748
  "significance_weight": mean_sig,
1749
  "epistemic_variance": epistemic_var,
@@ -1751,6 +1758,10 @@ class AXRVINet(nn.Module):
1751
  "log_var": mean_log_var.unsqueeze(-1),
1752
  "quantiles": mean_quantiles,
1753
  "regime_probs": mean_regime,
 
 
 
 
1754
  }
1755
 
1756
 
@@ -2728,12 +2739,28 @@ class PortfolioRiskManager:
2728
  if current_price <= 0:
2729
  return 0.0, "Invalid price"
2730
 
2731
- # ── Layer 3: circuit breaker β€” DISABLED for training data collection ──
2732
- dd = self._current_drawdown()
 
 
 
2733
  if dd >= self.cfg.drawdown_halt_pct:
2734
- logger.info(
2735
- f"[PortfolioRiskManager] ⚠️ Drawdown={dd:.1%} (circuit breaker disabled β€” training mode)"
2736
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
2737
 
2738
  # Drawdown reduce (halve sizes)
2739
  dd_adj = 0.5 if dd >= self.cfg.drawdown_reduce_pct else 1.0
@@ -3526,12 +3553,11 @@ class QuasarAXRVIBridge:
3526
  aleatoric_std_arr = np.full(len(active_ids), 0.1)
3527
 
3528
  # [S1] Extract value-head estimates (VΜ‚_t = Ê[R_{t+Ο„} | F_t] proxy)
3529
- value_t = out.get("log_var") # log_var shape: (1, N, 1) β€” use value instead
3530
- # Prefer the actual value head if available (non-MC path has it)
3531
  if "value" in out:
3532
  v_arr = out["value"].squeeze(0).squeeze(-1).detach().numpy()
3533
  else:
3534
- v_arr = significance_arr # fallback: use significance as proxy
3535
 
3536
  significance_map: Dict[str, float] = {}
3537
  epistemic_map: Dict[str, float] = {}
@@ -3635,13 +3661,13 @@ class QuasarAXRVIBridge:
3635
  logger.debug(f"[{asset_id}] SKIP β€” hub_confidence=0.00")
3636
  continue
3637
 
3638
- # Gate C β€” disabled for training data collection
3639
- # if sig_w < self.config.score_threshold:
3640
- # logger.info(
3641
- # f"[{asset_id}] SKIP β€” significance={sig_w:.3f} "
3642
- # f"< threshold={self.config.score_threshold:.3f}"
3643
- # )
3644
- # continue
3645
 
3646
  # Gate D + E β€” DynamicExecutionGate (includes martingale null-hypothesis [S7])
3647
  buf = self.asset_buffers.get(asset_id)
@@ -3661,8 +3687,8 @@ class QuasarAXRVIBridge:
3661
  martingale_deviation = mart_dev_raw, # [S7]
3662
  )
3663
  if not execute:
3664
- logger.info(f"[{asset_id}] Gate D/E warn (training mode): {reason}")
3665
- # continue ← disabled for training data collection
3666
 
3667
  logger.info(
3668
  f"[{asset_id}] EXECUTE {snap.dominant_signal} | "
@@ -3672,7 +3698,12 @@ class QuasarAXRVIBridge:
3672
  await self.process_axrvi_signal(
3673
  asset = asset_id,
3674
  action = snap.dominant_signal,
3675
- value_estimate = value_map.get(asset_id, 0.001),
 
 
 
 
 
3676
  realized_vol = realized_vol,
3677
  significance = sig_w,
3678
  cvar_05 = cvar_map.get(asset_id, 0.0),
@@ -4533,6 +4564,8 @@ async def run_live_trading_system(
4533
  hub_ws_url: str = "ws://localhost:7860/ws/subscribe",
4534
  enable_logging: bool = True,
4535
  shreve_config: Optional[ShreveConfig] = None,
 
 
4536
  ) -> None:
4537
  config = AssetRankerConfig(
4538
  asset_symbols = asset_symbols or list(ASSET_REGISTRY.keys()),
@@ -4546,6 +4579,8 @@ async def run_live_trading_system(
4546
  reward_strategy = reward_strategy,
4547
  hub_ws_url = hub_ws_url,
4548
  enable_logging = enable_logging,
 
 
4549
  )
4550
  await bridge.run()
4551
 
 
176
  kelly_fraction: float = 0.5
177
  max_portfolio_risk: float = 0.02
178
  drawdown_halt_pct: float = 0.10
179
+ halt_duration_secs: float = 5.0 # 3-minute automatic cooldown
180
  drawdown_reduce_pct: float = 0.05
181
  cvar_floor: float = -0.02
182
  min_notional: float = 1.0
 
1744
  mean_quantiles = torch.stack(mc_quantiles, dim=0).mean(dim=0)
1745
  mean_regime = torch.stack(mc_regime, dim=0).mean(dim=0)
1746
 
1747
+ # Recompute value and cvar_05 from mean quantiles so downstream
1748
+ # Kelly sizing and optimal-stopping always have a proper value estimate.
1749
+ mid = mean_quantiles.shape[-1] // 2
1750
+ n_tail = max(1, mean_quantiles.shape[-1] // 5)
1751
+ mean_value = mean_quantiles[..., mid].unsqueeze(-1) # (1, N, 1)
1752
+ mean_cvar05 = mean_quantiles[..., :n_tail].mean(dim=-1) # (1, N)
1753
+
1754
  return {
1755
  "significance_weight": mean_sig,
1756
  "epistemic_variance": epistemic_var,
 
1758
  "log_var": mean_log_var.unsqueeze(-1),
1759
  "quantiles": mean_quantiles,
1760
  "regime_probs": mean_regime,
1761
+ # Added: required by rank_and_gate for Kelly sizing [S1/S6] and
1762
+ # optimal-stopping continuation value C_t [S8].
1763
+ "value": mean_value,
1764
+ "cvar_05": mean_cvar05,
1765
  }
1766
 
1767
 
 
2739
  if current_price <= 0:
2740
  return 0.0, "Invalid price"
2741
 
2742
+ # ── Layer 3: circuit breaker ──────────────────────────────────────────
2743
+ dd = self._current_drawdown()
2744
+ now = time.time()
2745
+
2746
+ # Auto-expiring halt: set _halt_until when drawdown first breaches threshold.
2747
  if dd >= self.cfg.drawdown_halt_pct:
2748
+ if self._halt_until is None:
2749
+ self._halt_until = now + self.cfg.halt_duration_secs
2750
+ logger.warning(
2751
+ f"[PortfolioRiskManager] πŸ›‘ Circuit breaker TRIGGERED "
2752
+ f"drawdown={dd:.1%} β€” halting for {self.cfg.halt_duration_secs:.0f}s"
2753
+ )
2754
+ if now < self._halt_until:
2755
+ remaining = self._halt_until - now
2756
+ return 0.0, (
2757
+ f"Drawdown halt active: dd={dd:.1%} β‰₯ halt={self.cfg.drawdown_halt_pct:.1%}. "
2758
+ f"Auto-resumes in {remaining:.0f}s"
2759
+ )
2760
+ else:
2761
+ # Halt expired β€” reset so the breaker can re-trigger next time
2762
+ self._halt_until = None
2763
+ logger.info("[PortfolioRiskManager] βœ… Circuit breaker auto-resumed")
2764
 
2765
  # Drawdown reduce (halve sizes)
2766
  dd_adj = 0.5 if dd >= self.cfg.drawdown_reduce_pct else 1.0
 
3553
  aleatoric_std_arr = np.full(len(active_ids), 0.1)
3554
 
3555
  # [S1] Extract value-head estimates (VΜ‚_t = Ê[R_{t+Ο„} | F_t] proxy)
3556
+ # "value" is now always present β€” MC path computes it from mean_quantiles.
 
3557
  if "value" in out:
3558
  v_arr = out["value"].squeeze(0).squeeze(-1).detach().numpy()
3559
  else:
3560
+ v_arr = significance_arr # last-resort fallback
3561
 
3562
  significance_map: Dict[str, float] = {}
3563
  epistemic_map: Dict[str, float] = {}
 
3661
  logger.debug(f"[{asset_id}] SKIP β€” hub_confidence=0.00")
3662
  continue
3663
 
3664
+ # Gate C β€” significance threshold
3665
+ if sig_w < self.config.score_threshold:
3666
+ logger.info(
3667
+ f"[{asset_id}] SKIP Gate C β€” significance={sig_w:.3f} "
3668
+ f"< threshold={self.config.score_threshold:.3f}"
3669
+ )
3670
+ continue
3671
 
3672
  # Gate D + E β€” DynamicExecutionGate (includes martingale null-hypothesis [S7])
3673
  buf = self.asset_buffers.get(asset_id)
 
3687
  martingale_deviation = mart_dev_raw, # [S7]
3688
  )
3689
  if not execute:
3690
+ logger.info(f"[{asset_id}] Gate D/E BLOCKED: {reason}")
3691
+ continue
3692
 
3693
  logger.info(
3694
  f"[{asset_id}] EXECUTE {snap.dominant_signal} | "
 
3698
  await self.process_axrvi_signal(
3699
  asset = asset_id,
3700
  action = snap.dominant_signal,
3701
+ # Floor at 1e-4: an untrained distributional head can produce
3702
+ # negative median quantiles; passing a negative value_estimate
3703
+ # into Kelly sizing immediately triggers "No positive edge" and
3704
+ # vetoes every trade. The 1e-4 floor lets the system collect
3705
+ # experience while the model is still learning. [S1/Kelly]
3706
+ value_estimate = max(value_map.get(asset_id, 0.001), 1e-4),
3707
  realized_vol = realized_vol,
3708
  significance = sig_w,
3709
  cvar_05 = cvar_map.get(asset_id, 0.0),
 
4564
  hub_ws_url: str = "ws://localhost:7860/ws/subscribe",
4565
  enable_logging: bool = True,
4566
  shreve_config: Optional[ShreveConfig] = None,
4567
+ checkpoint_dir: str = "./Ranker",
4568
+ resume: bool = True,
4569
  ) -> None:
4570
  config = AssetRankerConfig(
4571
  asset_symbols = asset_symbols or list(ASSET_REGISTRY.keys()),
 
4579
  reward_strategy = reward_strategy,
4580
  hub_ws_url = hub_ws_url,
4581
  enable_logging = enable_logging,
4582
+ checkpoint_dir = checkpoint_dir,
4583
+ resume = resume,
4584
  )
4585
  await bridge.run()
4586