""" Cartea-Jaimungal HFT Model — Stochastic Control for High-Frequency Trading. Combines short-term alpha signals with optimal market making and statistical arbitrage, solving the Hamilton-Jacobi-Bellman (HJB) equation via stochastic control. Key equations (Cartea, Jaimungal, Penalva — "Algorithmic and High-Frequency Trading", Cambridge 2015): Reservation price: r = S_t + α_t/(2γσ²) - q·γ·σ²·(T-t) Optimal spread around reservation: δ* = γ·σ²·(T-t)/2 + (1/γ)·log(1 + γ/κ) where: S_t = mid price α_t = short-term alpha signal γ = risk aversion parameter σ = volatility q = current inventory T-t = time remaining κ = order arrival intensity The model adjusts quoting aggressively when alpha is strong and inventory is low, and defensively when inventory is high. Usage: from strategies.cartea_jaimungal import CarteaJaimungal cj = CarteaJaimungal(gamma=0.1, sigma=0.01, kappa=1.5) bid, ask, res_price = cj.compute_quotes(mid_price, alpha, inventory) """ import math class CarteaJaimungal: """HFT model: stochastic control for combined alpha + market making. Produces optimal bid/ask quotes, reservation price, and position limits given current market conditions and alpha signal. """ def __init__(self, gamma: float = 0.1, sigma: float = 0.01, kappa: float = 1.5, T: float = 10.0, max_inventory: float = 0.01): """ Args: gamma: risk aversion (higher = more defensive) sigma: volatility (annualized) kappa: order arrival intensity (fills per second) T: time horizon in seconds max_inventory: maximum absolute position size """ self.gamma = gamma self.sigma = sigma self.kappa = kappa self.T = T self.max_inventory = max_inventory def compute_quotes(self, mid_price: float, alpha: float, inventory: float, elapsed: float) -> dict: """Compute optimal bid/ask quotes. Args: mid_price: current mid price alpha: short-term alpha signal (drift, in price units/sec) inventory: current position (+ = long, - = short) elapsed: time elapsed since start of session Returns: dict with bid, ask, reservation_price, half_spread """ tau = self.T - elapsed if tau < 0.01: tau = 0.01 # Prevent singularity at expiry sig2 = self.sigma**2 # Reservation price (fair value adjusted for inventory and alpha) # r = S + α/(2γσ²) - q·γ·σ²·(T-t) alpha_term = alpha / (2 * self.gamma * sig2) if sig2 > 1e-10 else 0 inventory_penalty = inventory * self.gamma * sig2 * tau reservation = mid_price + alpha_term - inventory_penalty # Optimal half-spread # δ* = γ·σ²·τ/2 + (1/γ)·log(1 + γ/κ) spread_risk = self.gamma * sig2 * tau / 2.0 if self.kappa > 0 and self.gamma > 0: log_term = (1.0 / self.gamma) * math.log(1.0 + self.gamma / self.kappa) else: log_term = 0.001 half_spread = max(spread_risk + log_term, 0.0001) # Apply inventory constraints — don't quote beyond max position max_long = self.max_inventory max_short = -self.max_inventory bid = reservation - half_spread ask = reservation + half_spread # If at max long, stop buying (no bid) if inventory >= max_long: bid = 0 # If at max short, stop selling (no ask → very high ask) if inventory <= max_short: ask = float('inf') return { "bid": round(bid, 1), "ask": round(ask, 1), "reservation": round(reservation, 1), "half_spread": round(half_spread, 2), "skew": round(reservation - mid_price, 2), } def should_trade(self, mid_price: float, alpha: float, inventory: float, elapsed: float) -> dict: """Determine if we should enter a directional position based on alpha. Returns dict with side, size, and confidence. """ quotes = self.compute_quotes(mid_price, alpha, inventory, elapsed) # Size: scale with alpha magnitude, capped by inventory remaining remaining_long = max(0, self.max_inventory - inventory) remaining_short = max(0, self.max_inventory + inventory) alpha_strength = abs(alpha) threshold = self.gamma * self.sigma**2 * 0.1 # Minimum edge signal = None size = 0.0 confidence = 0.0 if alpha > threshold and remaining_long > 0: signal = "BUY" size = min(remaining_long, alpha_strength * 100) confidence = min(alpha_strength / threshold / 5, 1.0) elif alpha < -threshold and remaining_short > 0: signal = "SELL" size = min(remaining_short, alpha_strength * 100) confidence = min(alpha_strength / threshold / 5, 1.0) return { "signal": signal, "size": round(size, 6), "confidence": round(confidence, 4), "quotes": quotes, }