Files
DOLPHIN/prod/D-VAE-RegimeMonitor-MC-F_imrovement.py

365 lines
14 KiB
Python
Raw Normal View History

"""
HD Disentangled Regime Monitor
A concrete architecture for:
1. Numba-optimized HD feature extraction.
2. Disentangled Temporal VAE for regime encoding.
3. FLINT-backed 512-bit Latent Optimizer for "Most Likely Future" projection.
"""
import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
from numba import jit, float64
import math
# --- High Precision Imports ---
# We wrap this in a try-except block to allow the module to load for viewing
# even if python-flint isn't installed, though it won't run without it.
try:
from flint import arb, acb_mat, ctx
FLINT_ENABLED = True
except ImportError:
FLINT_ENABLED = False
print("Warning: python-flint not found. High-precision optimizer will run in mock mode.")
# =============================================================================
# SECTION 1: NUMBA-OPTIMIZED FEATURE ENGINEERING
# =============================================================================
@jit(nopython=True, fastmath=True, cache=True)
def construct_hd_features(prices, volumes, window_size):
"""
Constructs High-Dimensional feature tensors from raw market data.
Optimized with Numba to handle high-throughput data streams.
Args:
prices (np.array): 1D array of price points.
volumes (np.array): 1D array of volume points.
window_size (int): Lookback window size.
Returns:
np.array: Feature tensor of shape (n_samples, window_size, n_features).
"""
n_samples = len(prices) - window_size
# Features: LogRet, Volatility, RelVolume, Acceleration
n_features = 4
features = np.empty((n_samples, window_size, n_features), dtype=np.float64)
for i in range(n_samples):
window_p = prices[i : i + window_size]
window_v = volumes[i : i + window_size]
# 1. Log Returns
log_p = np.log(window_p)
rets = np.diff(log_p)
features[i, 1:, 0] = rets
features[i, 0, 0] = 0.0
# 2. Rolling Volatility (Standard Deviation of returns over mini-windows)
# Simplified for numba: rolling std dev
std_val = np.std(rets)
features[i, :, 1] = std_val
# 3. Relative Volume
mean_vol = np.mean(window_v)
if mean_vol > 0:
features[i, :, 2] = window_v / mean_vol
else:
features[i, :, 2] = 0.0
# 4. Acceleration (Second derivative of price)
if window_size > 2:
acc = np.diff(log_p, n=2)
features[i, 2:, 3] = acc
features[i, :2, 3] = 0.0
return features
# =============================================================================
# SECTION 2: DISENTANGLED TEMPORAL VAE (PyTorch)
# =============================================================================
class BetaTCVAE(nn.Module):
"""
Temporal VAE with Total Correlation disentanglement.
Uses a Bidirectional GRU encoder and a Generative GRU decoder.
Forces latent space to separate "Knobs" (Trend, Vol, etc).
"""
def __init__(self, input_dim, hidden_dim, latent_dim, num_layers=1, beta=5.0):
super(BetaTCVAE, self).__init__()
self.latent_dim = latent_dim
self.beta = beta
# Encoder: Bidirectional GRU
self.encoder_rnn = nn.GRU(input_dim, hidden_dim, num_layers,
batch_first=True, bidirectional=True)
self.fc_mu = nn.Linear(hidden_dim * 2, latent_dim)
self.fc_var = nn.Linear(hidden_dim * 2, latent_dim)
# Decoder: Generative GRU
# Input to decoder is latent vector z repeated across time steps
self.decoder_rnn = nn.GRU(latent_dim + input_dim, hidden_dim, num_layers, batch_first=True)
self.decoder_out = nn.Linear(hidden_dim, input_dim)
def encode(self, x):
# x: (batch, seq, feat)
_, h = self.encoder_rnn(x)
# Concatenate forward and backward final hidden states
h_cat = torch.cat((h[0], h[1]), dim=1)
return self.fc_mu(h_cat), self.fc_var(h_cat)
def reparameterize(self, mu, logvar):
std = torch.exp(0.5 * logvar)
eps = torch.randn_like(std)
return mu + eps * std
def decode(self, z, seq_len):
# Repeat z for seq_len steps to seed the generator
z_rep = z.unsqueeze(1).repeat(1, seq_len, 1)
# Auto-regressive logic simplified: we feed z and noise/placeholder
# In a real system, this would be teacher-forcing or iterative sampling
placeholder = torch.zeros(z_rep.shape[0], seq_len, z_rep.shape[2]).to(z.device)
dec_input = torch.cat((z_rep, placeholder), dim=-1)
out, _ = self.decoder_rnn(dec_input)
return self.decoder_out(out)
def forward(self, x):
mu, logvar = self.encode(x)
z = self.reparameterize(mu, logvar)
recon_x = self.decode(z, x.shape[1])
return recon_x, mu, logvar, z
def loss_function(self, recon_x, x, mu, logvar, z):
# Reconstruction Loss
BCE = F.mse_loss(recon_x, x, reduction='sum')
# KL Divergence
# 0.5 * sum(1 + log(sigma^2) - mu^2 - sigma^2)
KLD = -0.5 * torch.sum(1 + logvar - mu.pow(2) - logvar.exp())
# Total Correlation (TC) Minimization (Simplified for Beta-VAE style)
# In a full TC-VAE, we would calculate minibatch TC. Here we use
# a high Beta to force independence statistically.
return BCE + self.beta * KLD
# =============================================================================
# SECTION 3: FLINT 512-BIT LATENT OPTIMIZER
# =============================================================================
class FlintLatentOptimizer:
"""
Navigates the latent space using FLINT 512-bit precision.
Solves for the 'Most Likely Future' by descending the potential energy landscape.
"""
def __init__(self, precision=512):
if not FLINT_ENABLED:
raise RuntimeError("FLINT library not installed.")
self.precision = precision
ctx.precision = precision
# In a real scenario, these dynamics W are learned.
# Here we initialize a random transition matrix for demonstration.
# We assume z_t+1 = W * tanh(z_t) + noise
self.W_dim = 10 # Example latent dim
self.W_np = np.eye(self.W_dim) * 0.9 + np.random.randn(self.W_dim, self.W_dim) * 0.1
# Convert W to FLINT matrix
self.W_flint = self._to_flint_matrix(self.W_np)
def _to_flint_matrix(self, arr):
rows, cols = arr.shape
mat = acb_mat(rows, cols)
for r in range(rows):
for c in range(cols):
# Convert float to string for exact arb initialization
mat[r, c] = arb(str(arr[r, c]))
return mat
def _to_flint_vec(self, arr):
vec = acb_mat(len(arr), 1)
for i in range(len(arr)):
vec[i, 0] = arb(str(arr[i]))
return vec
def solve_most_likely_future(self, z_current_numpy, steps=10):
"""
Projects the current latent state forward to find the stable attractor.
Uses 512-bit arithmetic to avoid "noise" hallucination.
"""
# 1. Convert numpy latent vector to FLINT arb vector
z_state = self._to_flint_vec(z_current_numpy)
# 2. Iterate Dynamics
# We look for a stable state (Fixed Point).
# z_{t+1} = W @ z_t
# We stop when ||z_{t+1} - z_t|| < epsilon (512-bit epsilon)
for _ in range(steps):
# Matrix Multiply in High Precision
z_next = self.W_flint * z_state
# Convergence Check (Simplified)
# In a real app, we compute delta norm here using arb functions
z_state = z_next
# 3. Return result as string (to preserve precision) and numpy array
# We extract the real part (midpoint of the ball)
result_np = np.zeros(self.W_dim)
for i in range(self.W_dim):
# arb to string to float (lossy, but for the sizing logic)
result_np[i] = float(str(z_state[i, 0].real()))
return result_np
# =============================================================================
# SECTION 4: MAIN MONITOR CLASS
# =============================================================================
class AdaptiveRegimeMonitor:
"""
The main controller integrating Feature Engineering, VAE, and High-Precision Optimization.
"""
def __init__(self, input_dim=4, hidden_dim=32, latent_dim=10, precision=512):
self.vae = BetaTCVAE(input_dim, hidden_dim, latent_dim)
self.latent_optimizer = None
if FLINT_ENABLED:
try:
self.latent_optimizer = FlintLatentOptimizer(precision=precision)
print(f"Initialized FLINT Optimizer at {precision}-bit precision.")
except Exception as e:
print(f"Failed to init FLINT: {e}. Using fallback.")
# Latent Mapping (Mock semantic labels for "Knobs")
self.regime_knobs = {
'trend_strength': 0, # Index in latent vector
'volatility_regime': 1,
'fragility': 2
}
def train_vae(self, data_loader, epochs=5):
"""Trains the VAE to learn the market 'physics'."""
optimizer = torch.optim.Adam(self.vae.parameters(), lr=1e-3)
self.vae.train()
print("Training VAE...")
for epoch in range(epochs):
total_loss = 0
for batch in data_loader:
x = batch[0] # assuming dataloader returns (x, y)
optimizer.zero_grad()
recon_x, mu, logvar, z = self.vae(x)
loss = self.vae.loss_function(recon_x, x, mu, logvar, z)
loss.backward()
optimizer.step()
total_loss += loss.item()
print(f"Epoch {epoch+1}, Loss: {total_loss:.4f}")
print("Training Complete.")
def compute_regime_score(self, raw_window, raw_volume):
"""
Full pipeline execution:
1. Features (Numba)
2. Latent State (VAE)
3. Future Projection (FLINT)
4. Sizing Output
"""
# 1. Features
# Note: Numba returns (N, Win, Feat). We take the last sample for live trading
# or simulate a batch. Here we expand dims to simulate batch=1
features = construct_hd_features(raw_window, raw_volume, len(raw_window))
last_feature_window = features[-1:] # Shape (1, Window, Feat)
# 2. Encode
self.vae.eval()
with torch.no_grad():
x_tensor = torch.tensor(last_feature_window, dtype=torch.float32)
_, mu, _, z = self.vae(x_tensor)
# Current Latent State (Numpy)
z_current = mu.squeeze().numpy()
# 3. Optimize Future (FLINT)
if self.latent_optimizer:
z_future = self.latent_optimizer.solve_most_likely_future(z_current)
else:
# Fallback if FLINT missing
z_future = z_current * 0.95 # Dummy decay
# 4. Disentangled Interpretation ("Knobs")
# We apply sigmoid to normalize the score between 0 and 1
trend_score = torch.sigmoid(torch.tensor(z_future[self.regime_knobs['trend_strength']]))
fragility_score = torch.sigmoid(torch.tensor(z_future[self.regime_knobs['fragility']]))
# Regime Quality Indicator
# High Quality = High Trend + Low Fragility
regime_quality = (trend_score * (1 - fragility_score)).item()
return regime_quality, z_future
# =============================================================================
# SECTION 5: BUILT-IN TESTS
# =============================================================================
def run_tests():
print("-" * 40)
print("RUNNING MODULE TESTS")
print("-" * 40)
# 1. Test Numba Features
print("\n[1/3] Testing Numba Feature Engineering...")
try:
dummy_prices = np.random.rand(1000) * 100 + 50
dummy_vols = np.random.rand(1000) * 1000
feats = construct_hd_features(dummy_prices, dummy_vols, 64)
assert feats.shape == (936, 64, 4) # 1000 - 64 = 936 samples
print(" SUCCESS: Feature shape correct.")
except Exception as e:
print(f" FAILED: {e}")
# 2. Test VAE Structure
print("\n[2/3] Testing VAE Architecture...")
try:
vae = BetaTCVAE(input_dim=4, hidden_dim=16, latent_dim=6)
dummy_input = torch.randn(10, 64, 4) # Batch 10, Seq 64, Feat 4
recon, mu, logvar, z = vae(dummy_input)
loss = vae.loss_function(recon, dummy_input, mu, logvar, z)
assert z.shape == (10, 6)
print(f" SUCCESS: VAE Forward pass OK, Latent Dim: 6.")
except Exception as e:
print(f" FAILED: {e}")
# 3. Test Full Pipeline (with Mock Data)
print("\n[3/3] Testing Full Pipeline (Mock Market Data)...")
try:
# Generate synthetic data
prices = np.cumsum(np.random.randn(200)) + 100
vols = np.abs(np.random.randn(200)) * 10
# Initialize Monitor
monitor = AdaptiveRegimeMonitor(input_dim=4, hidden_dim=16, latent_dim=10)
# We skip training for the test and just run inference
# Run compute
score, future_z = monitor.compute_regime_score(prices, vols)
print(f" Regime Quality Score: {score:.5f}")
print(f" Future Latent Vector (First 3): {future_z[:3]}")
assert 0.0 <= score <= 1.0
print(" SUCCESS: Full pipeline integration OK.")
except Exception as e:
print(f" FAILED: {e}")
print("\n" + "="*40)
print("TESTS COMPLETE")
print("="*40)
if __name__ == "__main__":
run_tests()