"""
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,
        }
