"""Quantitative toolbox CLI commands.

All quant commands accept data as JSON arrays via options.
For complex inputs, pass a JSON file: --data @prices.json
"""

from __future__ import annotations

import json
import os
from pathlib import Path

import click


def _parse_json(value: str):
    """Parse a JSON string or @filename reference."""
    if value.startswith("@"):
        raw_path = value[1:]
        resolved = Path(raw_path).resolve()
        cwd = Path.cwd().resolve()
        home_horizon = Path.home().resolve() / ".horizon"
        if not (str(resolved).startswith(str(cwd)) or str(resolved).startswith(str(home_horizon))):
            raise click.BadParameter(
                f"File path must be within the current directory or ~/.horizon/: {resolved}"
            )
        with open(str(resolved)) as f:
            return json.load(f)
    return json.loads(value)


@click.group()
def quant():
    """Quantitative analytics toolbox."""
    pass


# ---------------------------------------------------------------------------
# Statistics & signals
# ---------------------------------------------------------------------------


@quant.command()
@click.option("--p", "prob", required=True, type=float, help="Probability (0-1)")
@click.pass_context
def entropy(ctx, prob):
    """Compute Shannon entropy for a binary probability."""
    from horizon.tools import compute_shannon_entropy
    from horizon.cli._fmt import output

    output(compute_shannon_entropy(p=prob), json_mode=ctx.obj.get("json", False))


@quant.command("kl-divergence")
@click.option("--p", required=True, help="JSON array of P distribution")
@click.option("--q", required=True, help="JSON array of Q distribution")
@click.pass_context
def kl_divergence(ctx, p, q):
    """Compute KL divergence between two distributions."""
    from horizon.tools import compute_kl_divergence
    from horizon.cli._fmt import output

    output(compute_kl_divergence(p=_parse_json(p), q=_parse_json(q)), json_mode=ctx.obj.get("json", False))


@quant.command()
@click.option("--prices", required=True, help="JSON array of prices")
@click.pass_context
def hurst(ctx, prices):
    """Compute Hurst exponent (mean-reversion vs trend)."""
    from horizon.tools import compute_hurst_exponent
    from horizon.cli._fmt import output

    output(compute_hurst_exponent(prices=_parse_json(prices)), json_mode=ctx.obj.get("json", False))


@quant.command("variance-ratio")
@click.option("--returns", required=True, help="JSON array of returns")
@click.option("--period", default=2, type=int, help="Period for ratio")
@click.pass_context
def variance_ratio(ctx, returns, period):
    """Compute variance ratio test for random walk."""
    from horizon.tools import compute_variance_ratio
    from horizon.cli._fmt import output

    output(compute_variance_ratio(returns=_parse_json(returns), period=period), json_mode=ctx.obj.get("json", False))


@quant.command("cornish-fisher-var")
@click.option("--returns", required=True, help="JSON array of returns")
@click.option("--confidence", default=0.95, type=float, help="Confidence level")
@click.pass_context
def cornish_fisher_var(ctx, returns, confidence):
    """Compute Cornish-Fisher Value-at-Risk."""
    from horizon.tools import compute_cornish_fisher_var
    from horizon.cli._fmt import output

    output(compute_cornish_fisher_var(returns=_parse_json(returns), confidence=confidence), json_mode=ctx.obj.get("json", False))


@quant.command("greeks")
@click.option("--prob", required=True, type=float, help="Probability estimate")
@click.option("--price", required=True, type=float, help="Market price")
@click.option("--size", default=1.0, type=float, help="Position size")
@click.pass_context
def greeks(ctx, prob, price, size):
    """Compute prediction market Greeks (delta, gamma, theta, vega)."""
    from horizon.tools import compute_prediction_greeks
    from horizon.cli._fmt import output

    output(compute_prediction_greeks(prob=prob, price=price, size=size), json_mode=ctx.obj.get("json", False))


@quant.command("deflated-sharpe")
@click.option("--returns", required=True, help="JSON array of returns")
@click.option("--n-trials", default=1, type=int, help="Number of independent trials")
@click.pass_context
def deflated_sharpe(ctx, returns, n_trials):
    """Compute deflated Sharpe ratio (multiple testing correction)."""
    from horizon.tools import compute_deflated_sharpe
    from horizon.cli._fmt import output

    output(compute_deflated_sharpe(returns=_parse_json(returns), n_trials=n_trials), json_mode=ctx.obj.get("json", False))


@quant.command("signal-diagnostics")
@click.option("--predictions", required=True, help="JSON array of predictions")
@click.option("--outcomes", required=True, help="JSON array of outcomes")
@click.pass_context
def signal_diagnostics(ctx, predictions, outcomes):
    """Run signal diagnostics (IC, accuracy, calibration)."""
    from horizon.tools import run_signal_diagnostics
    from horizon.cli._fmt import output

    output(run_signal_diagnostics(predictions=_parse_json(predictions), outcomes=_parse_json(outcomes)), json_mode=ctx.obj.get("json", False))


@quant.command("market-efficiency")
@click.option("--prices", required=True, help="JSON array of prices")
@click.pass_context
def market_efficiency(ctx, prices):
    """Test market efficiency (runs test, autocorrelation)."""
    from horizon.tools import run_market_efficiency
    from horizon.cli._fmt import output

    output(run_market_efficiency(prices=_parse_json(prices)), json_mode=ctx.obj.get("json", False))


@quant.command("stress-test")
@click.pass_context
def stress_test(ctx):
    """Run stress test on current portfolio."""
    from horizon.tools import run_stress_test
    from horizon.cli._fmt import output

    output(run_stress_test(), json_mode=ctx.obj.get("json", False))


@quant.command()
@click.pass_context
def tearsheet(ctx):
    """Generate a performance tearsheet."""
    from horizon.tools import generate_tearsheet_report
    from horizon.cli._fmt import output

    output(generate_tearsheet_report(), json_mode=ctx.obj.get("json", False))


@quant.command("bayesian-optimize")
@click.pass_context
def bayesian_optimize(ctx):
    """Run Bayesian parameter optimization."""
    from horizon.tools import run_bayesian_optimize
    from horizon.cli._fmt import output

    output(run_bayesian_optimize(), json_mode=ctx.obj.get("json", False))


@quant.command()
@click.option("--prices", required=True, help="JSON array of prices")
@click.option("--returns", required=True, help="JSON array of returns (or same length)")
@click.pass_context
def hawkes(ctx, prices, returns):
    """Compute Hawkes process intensity."""
    from horizon.tools import compute_hawkes_intensity
    from horizon.cli._fmt import output

    output(compute_hawkes_intensity(timestamps=_parse_json(prices), marks=_parse_json(returns)), json_mode=ctx.obj.get("json", False))


@quant.command("correlation-matrix")
@click.option("--returns-matrix", required=True, help="JSON 2D array of returns")
@click.pass_context
def correlation_matrix(ctx, returns_matrix):
    """Compute correlation matrix."""
    from horizon.tools import compute_correlation_matrix
    from horizon.cli._fmt import output

    output(compute_correlation_matrix(returns_matrix=_parse_json(returns_matrix)), json_mode=ctx.obj.get("json", False))


# ---------------------------------------------------------------------------
# Bars & labeling (AFML)
# ---------------------------------------------------------------------------


@quant.command("tick-bars")
@click.option("--prices", required=True, help="JSON array of tick prices")
@click.option("--threshold", required=True, type=int, help="Ticks per bar")
@click.pass_context
def tick_bars(ctx, prices, threshold):
    """Construct tick bars."""
    from horizon.tools import compute_tick_bars
    from horizon.cli._fmt import output

    output(compute_tick_bars(prices=_parse_json(prices), threshold=threshold), json_mode=ctx.obj.get("json", False))


@quant.command("volume-bars")
@click.option("--prices", required=True, help="JSON array of prices")
@click.option("--volumes", required=True, help="JSON array of volumes")
@click.option("--threshold", required=True, type=float, help="Volume per bar")
@click.pass_context
def volume_bars(ctx, prices, volumes, threshold):
    """Construct volume bars."""
    from horizon.tools import compute_volume_bars
    from horizon.cli._fmt import output

    output(compute_volume_bars(prices=_parse_json(prices), volumes=_parse_json(volumes), threshold=threshold), json_mode=ctx.obj.get("json", False))


@quant.command("dollar-bars")
@click.option("--prices", required=True, help="JSON array of prices")
@click.option("--volumes", required=True, help="JSON array of volumes")
@click.option("--threshold", required=True, type=float, help="Dollar volume per bar")
@click.pass_context
def dollar_bars(ctx, prices, volumes, threshold):
    """Construct dollar bars."""
    from horizon.tools import compute_dollar_bars
    from horizon.cli._fmt import output

    output(compute_dollar_bars(prices=_parse_json(prices), volumes=_parse_json(volumes), threshold=threshold), json_mode=ctx.obj.get("json", False))


@quant.command("cusum-filter")
@click.option("--prices", required=True, help="JSON array of prices")
@click.option("--threshold", required=True, type=float, help="CUSUM threshold")
@click.pass_context
def cusum_filter(ctx, prices, threshold):
    """Apply CUSUM filter to detect structural breaks."""
    from horizon.tools import compute_cusum_filter
    from horizon.cli._fmt import output

    output(compute_cusum_filter(prices=_parse_json(prices), threshold=threshold), json_mode=ctx.obj.get("json", False))


@quant.command("triple-barrier")
@click.option("--prices", required=True, help="JSON array of prices")
@click.option("--upper", required=True, type=float, help="Upper barrier")
@click.option("--lower", required=True, type=float, help="Lower barrier")
@click.option("--max-holding", required=True, type=int, help="Max holding period")
@click.pass_context
def triple_barrier(ctx, prices, upper, lower, max_holding):
    """Apply triple-barrier labeling."""
    from horizon.tools import compute_triple_barrier
    from horizon.cli._fmt import output

    output(compute_triple_barrier(prices=_parse_json(prices), upper_barrier=upper, lower_barrier=lower, max_holding=max_holding), json_mode=ctx.obj.get("json", False))


@quant.command("frac-diff")
@click.option("--prices", required=True, help="JSON array of prices")
@click.option("--d", "diff_order", default=0.5, type=float, help="Differentiation order")
@click.pass_context
def frac_diff(ctx, prices, diff_order):
    """Apply fractional differentiation."""
    from horizon.tools import compute_frac_diff
    from horizon.cli._fmt import output

    output(compute_frac_diff(prices=_parse_json(prices), d=diff_order), json_mode=ctx.obj.get("json", False))


@quant.command("min-frac-diff")
@click.option("--prices", required=True, help="JSON array of prices")
@click.pass_context
def min_frac_diff(ctx, prices):
    """Find minimum fractional differentiation for stationarity."""
    from horizon.tools import compute_min_frac_diff
    from horizon.cli._fmt import output

    output(compute_min_frac_diff(prices=_parse_json(prices)), json_mode=ctx.obj.get("json", False))


@quant.command("hrp")
@click.option("--covariance", required=True, help="JSON 2D covariance matrix")
@click.pass_context
def hrp(ctx, covariance):
    """Compute Hierarchical Risk Parity weights."""
    from horizon.tools import compute_hrp_weights
    from horizon.cli._fmt import output

    output(compute_hrp_weights(covariance=_parse_json(covariance)), json_mode=ctx.obj.get("json", False))


@quant.command("denoise-cov")
@click.option("--covariance", required=True, help="JSON 2D covariance matrix")
@click.pass_context
def denoise_cov(ctx, covariance):
    """Denoise a covariance matrix (Marchenko-Pastur)."""
    from horizon.tools import compute_denoise_covariance
    from horizon.cli._fmt import output

    output(compute_denoise_covariance(covariance=_parse_json(covariance)), json_mode=ctx.obj.get("json", False))


# ---------------------------------------------------------------------------
# Advanced models
# ---------------------------------------------------------------------------


@quant.command("kalman")
@click.option("--observations", required=True, help="JSON array of observations")
@click.pass_context
def kalman(ctx, observations):
    """Run Kalman filter on observations."""
    from horizon.tools import kalman_filter_state_tool
    from horizon.cli._fmt import output

    output(kalman_filter_state_tool(observations=_parse_json(observations)), json_mode=ctx.obj.get("json", False))


@quant.command("particle-filter")
@click.option("--observations", required=True, help="JSON array of observations")
@click.option("--n-particles", default=1000, type=int, help="Number of particles")
@click.pass_context
def particle_filter(ctx, observations, n_particles):
    """Run particle filter on observations."""
    from horizon.tools import particle_filter_state_tool
    from horizon.cli._fmt import output

    output(particle_filter_state_tool(observations=_parse_json(observations), n_particles=n_particles), json_mode=ctx.obj.get("json", False))


@quant.command("bocpd")
@click.option("--data", required=True, help="JSON array of data points")
@click.pass_context
def bocpd(ctx, data):
    """Bayesian online change-point detection."""
    from horizon.tools import bocpd_detect_tool
    from horizon.cli._fmt import output

    output(bocpd_detect_tool(data=_parse_json(data)), json_mode=ctx.obj.get("json", False))


@quant.command("lead-lag")
@click.option("--returns-a", required=True, help="JSON array of returns for asset A")
@click.option("--returns-b", required=True, help="JSON array of returns for asset B")
@click.pass_context
def lead_lag(ctx, returns_a, returns_b):
    """Analyze lead-lag relationship between two assets."""
    from horizon.tools import lead_lag_analysis_tool
    from horizon.cli._fmt import output

    output(lead_lag_analysis_tool(returns_a=_parse_json(returns_a), returns_b=_parse_json(returns_b)), json_mode=ctx.obj.get("json", False))


@quant.command("granger")
@click.option("--x", required=True, help="JSON array of X series")
@click.option("--y", required=True, help="JSON array of Y series")
@click.option("--max-lag", default=5, type=int, help="Maximum lag to test")
@click.pass_context
def granger(ctx, x, y, max_lag):
    """Granger causality test."""
    from horizon.tools import granger_test_tool
    from horizon.cli._fmt import output

    output(granger_test_tool(x=_parse_json(x), y=_parse_json(y), max_lag=max_lag), json_mode=ctx.obj.get("json", False))


@quant.command("market-graph")
@click.option("--returns-matrix", required=True, help="JSON 2D array of returns")
@click.pass_context
def market_graph(ctx, returns_matrix):
    """Build and analyze market correlation graph."""
    from horizon.tools import market_graph_analysis_tool
    from horizon.cli._fmt import output

    output(market_graph_analysis_tool(returns_matrix=_parse_json(returns_matrix)), json_mode=ctx.obj.get("json", False))


@quant.command("gp-execution")
@click.option("--total-size", required=True, type=float, help="Total order size")
@click.option("--n-steps", default=10, type=int, help="Number of execution steps")
@click.option("--risk-aversion", default=0.1, type=float, help="Risk aversion parameter")
@click.pass_context
def gp_execution(ctx, total_size, n_steps, risk_aversion):
    """Gaussian process optimal execution trajectory."""
    from horizon.tools import gp_optimal_execution_tool
    from horizon.cli._fmt import output

    output(gp_optimal_execution_tool(total_size=total_size, n_steps=n_steps, risk_aversion=risk_aversion), json_mode=ctx.obj.get("json", False))


@quant.command("ac-liquidation")
@click.option("--total-size", required=True, type=float, help="Total position to liquidate")
@click.option("--n-steps", default=10, type=int, help="Number of time steps")
@click.option("--risk-aversion", default=0.1, type=float, help="Risk aversion")
@click.pass_context
def ac_liquidation(ctx, total_size, n_steps, risk_aversion):
    """Almgren-Chriss optimal liquidation schedule."""
    from horizon.tools import ac_optimal_liquidation_tool
    from horizon.cli._fmt import output

    output(ac_optimal_liquidation_tool(total_size=total_size, n_steps=n_steps, risk_aversion=risk_aversion), json_mode=ctx.obj.get("json", False))


@quant.command("queue-fill")
@click.option("--queue-position", required=True, type=int, help="Position in queue")
@click.option("--queue-size", required=True, type=int, help="Total queue size")
@click.pass_context
def queue_fill(ctx, queue_position, queue_size):
    """Estimate queue fill probability."""
    from horizon.tools import queue_fill_probability_tool
    from horizon.cli._fmt import output

    output(queue_fill_probability_tool(queue_position=queue_position, queue_size=queue_size), json_mode=ctx.obj.get("json", False))


@quant.command("copula")
@click.option("--x", required=True, help="JSON array of X values")
@click.option("--y", required=True, help="JSON array of Y values")
@click.option("--family", default="gaussian", help="Copula family")
@click.pass_context
def copula(ctx, x, y, family):
    """Fit a copula to bivariate data."""
    from horizon.tools import fit_copula_tool
    from horizon.cli._fmt import output

    output(fit_copula_tool(x=_parse_json(x), y=_parse_json(y), family=family), json_mode=ctx.obj.get("json", False))


@quant.command("best-copula")
@click.option("--x", required=True, help="JSON array of X values")
@click.option("--y", required=True, help="JSON array of Y values")
@click.pass_context
def best_copula(ctx, x, y):
    """Find best-fitting copula family."""
    from horizon.tools import best_copula_tool
    from horizon.cli._fmt import output

    output(best_copula_tool(x=_parse_json(x), y=_parse_json(y)), json_mode=ctx.obj.get("json", False))


@quant.command("vine-copula")
@click.option("--data", required=True, help="JSON 2D array of data")
@click.pass_context
def vine_copula(ctx, data):
    """Fit a vine copula to multivariate data."""
    from horizon.tools import vine_copula_fit_tool
    from horizon.cli._fmt import output

    output(vine_copula_fit_tool(data=_parse_json(data)), json_mode=ctx.obj.get("json", False))


@quant.command("vine-tail-risk")
@click.option("--data", required=True, help="JSON 2D array of data")
@click.pass_context
def vine_tail_risk(ctx, data):
    """Compute tail risk from vine copula."""
    from horizon.tools import vine_tail_risk_tool
    from horizon.cli._fmt import output

    output(vine_tail_risk_tool(data=_parse_json(data)), json_mode=ctx.obj.get("json", False))


@quant.command("cross-impact")
@click.option("--returns-matrix", required=True, help="JSON 2D array of returns")
@click.option("--volumes-matrix", required=True, help="JSON 2D array of volumes")
@click.pass_context
def cross_impact(ctx, returns_matrix, volumes_matrix):
    """Estimate cross-impact matrix."""
    from horizon.tools import cross_impact_tool
    from horizon.cli._fmt import output

    output(cross_impact_tool(returns_matrix=_parse_json(returns_matrix), volumes_matrix=_parse_json(volumes_matrix)), json_mode=ctx.obj.get("json", False))


@quant.command("entropy-pooling")
@click.option("--prior-probs", required=True, help="JSON array of prior probabilities")
@click.option("--views", required=True, help="JSON 2D array of view constraints")
@click.option("--view-values", required=True, help="JSON array of view target values")
@click.pass_context
def entropy_pooling(ctx, prior_probs, views, view_values):
    """Entropy pooling (Black-Litterman generalization)."""
    from horizon.tools import entropy_pool_tool
    from horizon.cli._fmt import output

    output(entropy_pool_tool(prior_probs=_parse_json(prior_probs), views=_parse_json(views), view_values=_parse_json(view_values)), json_mode=ctx.obj.get("json", False))


@quant.command("robust-portfolio")
@click.option("--expected-returns", required=True, help="JSON array of expected returns")
@click.option("--cov-matrix", required=True, help="JSON 2D covariance matrix")
@click.option("--uncertainty", default=0.1, type=float, help="Uncertainty parameter")
@click.pass_context
def robust_portfolio(ctx, expected_returns, cov_matrix, uncertainty):
    """Robust portfolio optimization."""
    from horizon.tools import robust_optimize_tool
    from horizon.cli._fmt import output

    output(robust_optimize_tool(expected_returns=_parse_json(expected_returns), cov_matrix=_parse_json(cov_matrix), uncertainty=uncertainty), json_mode=ctx.obj.get("json", False))


@quant.command("optimal-exit")
@click.option("--prices", required=True, help="JSON array of prices")
@click.option("--pnl", required=True, type=float, help="Current position PnL")
@click.option("--vol", required=True, type=float, help="Volatility estimate")
@click.option("--t-remaining", required=True, type=float, help="Time remaining")
@click.pass_context
def optimal_exit(ctx, prices, pnl, vol, t_remaining):
    """Optimal stopping / exit analysis."""
    from horizon.tools import optimal_exit_tool
    from horizon.cli._fmt import output

    output(optimal_exit_tool(prices=_parse_json(prices), position_pnl=pnl, vol=vol, t_remaining=t_remaining), json_mode=ctx.obj.get("json", False))


@quant.command("binary-price")
@click.option("--prob", required=True, type=float, help="Probability")
@click.option("--vol", default=0.3, type=float, help="Volatility")
@click.option("--t", default=1.0, type=float, help="Time to expiry")
@click.pass_context
def binary_price(ctx, prob, vol, t):
    """Price a binary option."""
    from horizon.tools import binary_option_price_tool
    from horizon.cli._fmt import output

    output(binary_option_price_tool(prob=prob, vol=vol, t=t), json_mode=ctx.obj.get("json", False))


@quant.command("binary-heston")
@click.option("--spot", required=True, type=float, help="Spot price")
@click.option("--strike", required=True, type=float, help="Strike price")
@click.option("--t", required=True, type=float, help="Time to expiry")
@click.option("--v0", default=0.04, type=float, help="Initial variance")
@click.pass_context
def binary_heston(ctx, spot, strike, t, v0):
    """Price a binary option with Heston stochastic vol."""
    from horizon.tools import binary_price_heston_tool
    from horizon.cli._fmt import output

    output(binary_price_heston_tool(spot=spot, strike=strike, t=t, v0=v0), json_mode=ctx.obj.get("json", False))


@quant.command("implied-vol")
@click.option("--binary-price", "price", required=True, type=float, help="Observed binary price")
@click.option("--spot", required=True, type=float, help="Spot price")
@click.option("--strike", required=True, type=float, help="Strike price")
@click.option("--t", required=True, type=float, help="Time to expiry")
@click.pass_context
def implied_vol(ctx, price, spot, strike, t):
    """Extract implied volatility from a binary option price."""
    from horizon.tools import implied_vol_tool
    from horizon.cli._fmt import output

    output(implied_vol_tool(binary_price=price, spot=spot, strike=strike, t=t), json_mode=ctx.obj.get("json", False))


@quant.command("hjb-quotes")
@click.pass_context
def hjb_quotes(ctx):
    """Compute HJB optimal market-making quotes."""
    from horizon.tools import hjb_optimal_quotes_tool
    from horizon.cli._fmt import output

    output(hjb_optimal_quotes_tool(), json_mode=ctx.obj.get("json", False))


@quant.command("logit-greeks")
@click.option("--price", required=True, type=float, help="Market price")
@click.option("--size", required=True, type=float, help="Position size")
@click.option("--is-yes", is_flag=True, default=True, help="YES side position")
@click.pass_context
def logit_greeks(ctx, price, size, is_yes):
    """Compute logit model Greeks."""
    from horizon.tools import logit_greeks_tool
    from horizon.cli._fmt import output

    output(logit_greeks_tool(price=price, size=size, is_yes=is_yes), json_mode=ctx.obj.get("json", False))


@quant.command("logit-reservation")
@click.option("--price", required=True, type=float, help="Market price")
@click.option("--inventory", required=True, type=float, help="Current inventory")
@click.pass_context
def logit_reservation(ctx, price, inventory):
    """Compute logit reservation price."""
    from horizon.tools import logit_reservation_price_tool
    from horizon.cli._fmt import output

    output(logit_reservation_price_tool(price=price, inventory=inventory), json_mode=ctx.obj.get("json", False))


@quant.command("logit-spread")
@click.option("--price", required=True, type=float, help="Market price")
@click.option("--inventory", required=True, type=float, help="Current inventory")
@click.pass_context
def logit_spread(ctx, price, inventory):
    """Compute logit optimal spread."""
    from horizon.tools import logit_optimal_spread_tool
    from horizon.cli._fmt import output

    output(logit_optimal_spread_tool(price=price, inventory=inventory), json_mode=ctx.obj.get("json", False))


@quant.command("logit-pnl")
@click.option("--price", required=True, type=float, help="Market price")
@click.option("--entry-price", required=True, type=float, help="Entry price")
@click.option("--size", required=True, type=float, help="Position size")
@click.pass_context
def logit_pnl(ctx, price, entry_price, size):
    """Decompose PnL using logit model."""
    from horizon.tools import logit_pnl_decompose_tool
    from horizon.cli._fmt import output

    output(logit_pnl_decompose_tool(price=price, entry_price=entry_price, size=size), json_mode=ctx.obj.get("json", False))


@quant.command("toxicity-spread")
@click.option("--price", required=True, type=float, help="Market price")
@click.option("--inventory", required=True, type=float, help="Current inventory")
@click.pass_context
def toxicity_spread(ctx, price, inventory):
    """Compute toxicity-adjusted optimal spread."""
    from horizon.tools import toxicity_adjusted_spread_tool
    from horizon.cli._fmt import output

    output(toxicity_adjusted_spread_tool(price=price, inventory=inventory), json_mode=ctx.obj.get("json", False))


@quant.command("bootstrap-sharpe")
@click.option("--returns", required=True, help="JSON array of returns")
@click.option("--n", default=1000, type=int, help="Bootstrap samples")
@click.pass_context
def bootstrap_sharpe(ctx, returns, n):
    """Bootstrap confidence interval for Sharpe ratio."""
    from horizon.tools import bootstrap_sharpe_tool
    from horizon.cli._fmt import output

    output(bootstrap_sharpe_tool(returns=_parse_json(returns), n_bootstrap=n), json_mode=ctx.obj.get("json", False))


@quant.command("bootstrap-mean")
@click.option("--data", required=True, help="JSON array of data")
@click.option("--n", default=1000, type=int, help="Bootstrap samples")
@click.pass_context
def bootstrap_mean(ctx, data, n):
    """Bootstrap confidence interval for mean."""
    from horizon.tools import bootstrap_mean_tool
    from horizon.cli._fmt import output

    output(bootstrap_mean_tool(data=_parse_json(data), n_bootstrap=n), json_mode=ctx.obj.get("json", False))


@quant.command("bootstrap-var")
@click.option("--returns", required=True, help="JSON array of returns")
@click.option("--n", default=1000, type=int, help="Bootstrap samples")
@click.option("--confidence", default=0.95, type=float, help="Confidence level")
@click.pass_context
def bootstrap_var(ctx, returns, n, confidence):
    """Bootstrap confidence interval for VaR."""
    from horizon.tools import bootstrap_var_tool
    from horizon.cli._fmt import output

    output(bootstrap_var_tool(returns=_parse_json(returns), n_bootstrap=n, confidence=confidence), json_mode=ctx.obj.get("json", False))


@quant.command("bootstrap-edge")
@click.option("--probs", required=True, help="JSON array of probabilities")
@click.option("--prices", required=True, help="JSON array of prices")
@click.option("--n", default=1000, type=int, help="Bootstrap samples")
@click.pass_context
def bootstrap_edge(ctx, probs, prices, n):
    """Bootstrap confidence interval for trading edge."""
    from horizon.tools import bootstrap_edge_tool
    from horizon.cli._fmt import output

    output(bootstrap_edge_tool(probs=_parse_json(probs), prices=_parse_json(prices), n_bootstrap=n), json_mode=ctx.obj.get("json", False))


@quant.command("bootstrap-hypothesis")
@click.option("--sample-a", required=True, help="JSON array of sample A")
@click.option("--sample-b", required=True, help="JSON array of sample B")
@click.option("--n", default=1000, type=int, help="Bootstrap samples")
@click.pass_context
def bootstrap_hypothesis(ctx, sample_a, sample_b, n):
    """Bootstrap hypothesis test (A vs B)."""
    from horizon.tools import bootstrap_hypothesis_tool
    from horizon.cli._fmt import output

    output(bootstrap_hypothesis_tool(sample_a=_parse_json(sample_a), sample_b=_parse_json(sample_b), n_bootstrap=n), json_mode=ctx.obj.get("json", False))


@quant.command("risk-neutral-density")
@click.option("--prices", required=True, help="JSON array of option prices")
@click.option("--strikes", required=True, help="JSON array of strikes")
@click.pass_context
def risk_neutral_density(ctx, prices, strikes):
    """Extract risk-neutral density (Breeden-Litzenberger)."""
    from horizon.tools import risk_neutral_density_tool
    from horizon.cli._fmt import output

    output(risk_neutral_density_tool(prices=_parse_json(prices), strikes=_parse_json(strikes)), json_mode=ctx.obj.get("json", False))


@quant.command("implied-probability")
@click.option("--prices", required=True, help="JSON array of option prices")
@click.option("--strikes", required=True, help="JSON array of strikes")
@click.option("--lower", required=True, type=float, help="Lower bound")
@click.option("--upper", required=True, type=float, help="Upper bound")
@click.pass_context
def implied_probability(ctx, prices, strikes, lower, upper):
    """Extract implied probability for a range."""
    from horizon.tools import implied_probability_tool
    from horizon.cli._fmt import output

    output(implied_probability_tool(prices=_parse_json(prices), strikes=_parse_json(strikes), lower=lower, upper=upper), json_mode=ctx.obj.get("json", False))


@quant.command("cross-asset-edge")
@click.option("--returns-a", required=True, help="JSON array of returns for asset A")
@click.option("--returns-b", required=True, help="JSON array of returns for asset B")
@click.pass_context
def cross_asset_edge(ctx, returns_a, returns_b):
    """Compute cross-asset edge signal."""
    from horizon.tools import cross_asset_edge_tool
    from horizon.cli._fmt import output

    output(cross_asset_edge_tool(returns_a=_parse_json(returns_a), returns_b=_parse_json(returns_b)), json_mode=ctx.obj.get("json", False))


@quant.command("yield-kelly")
@click.option("--prob", required=True, type=float, help="Win probability")
@click.option("--price", required=True, type=float, help="Market price")
@click.option("--yield-rate", default=0.05, type=float, help="Risk-free yield rate")
@click.pass_context
def yield_kelly(ctx, prob, price, yield_rate):
    """Yield-adjusted Kelly sizing."""
    from horizon.tools import yield_adjusted_kelly_tool
    from horizon.cli._fmt import output

    output(yield_adjusted_kelly_tool(prob=prob, price=price, yield_rate=yield_rate), json_mode=ctx.obj.get("json", False))


@quant.command("index-level")
@click.option("--prices", required=True, help="JSON array of prices")
@click.option("--weights", required=True, help="JSON array of weights")
@click.option("--base", default=100.0, type=float, help="Base index level")
@click.pass_context
def index_level(ctx, prices, weights, base):
    """Compute synthetic index level."""
    from horizon.tools import compute_index_level_tool
    from horizon.cli._fmt import output

    output(compute_index_level_tool(prices=_parse_json(prices), weights=_parse_json(weights), base=base), json_mode=ctx.obj.get("json", False))


@quant.command("tracking-error")
@click.option("--index-levels", required=True, help="JSON array of index levels")
@click.option("--benchmark-levels", required=True, help="JSON array of benchmark levels")
@click.pass_context
def tracking_error(ctx, index_levels, benchmark_levels):
    """Compute index tracking error."""
    from horizon.tools import index_tracking_error_tool
    from horizon.cli._fmt import output

    output(index_tracking_error_tool(index_levels=_parse_json(index_levels), benchmark_levels=_parse_json(benchmark_levels)), json_mode=ctx.obj.get("json", False))


@quant.command("correlation-regime")
@click.option("--returns-a", required=True, help="JSON array of returns A")
@click.option("--returns-b", required=True, help="JSON array of returns B")
@click.option("--window", default=60, type=int, help="Rolling window size")
@click.pass_context
def correlation_regime(ctx, returns_a, returns_b, window):
    """Detect correlation regime shifts."""
    from horizon.tools import correlation_regime_tool
    from horizon.cli._fmt import output

    output(correlation_regime_tool(returns_a=_parse_json(returns_a), returns_b=_parse_json(returns_b), window=window), json_mode=ctx.obj.get("json", False))


@quant.command("contagion")
@click.option("--returns-matrix", required=True, help="JSON 2D array of returns")
@click.option("--window", default=60, type=int, help="Rolling window size")
@click.pass_context
def contagion(ctx, returns_matrix, window):
    """Compute contagion score across assets."""
    from horizon.tools import contagion_score_tool
    from horizon.cli._fmt import output

    output(contagion_score_tool(returns_matrix=_parse_json(returns_matrix), window=window), json_mode=ctx.obj.get("json", False))


@quant.command("beta-decompose")
@click.option("--strategy-returns", required=True, help="JSON array of strategy returns")
@click.option("--benchmark-returns", required=True, help="JSON array of benchmark returns")
@click.pass_context
def beta_decompose(ctx, strategy_returns, benchmark_returns):
    """Decompose returns into alpha and beta components."""
    from horizon.tools import beta_decompose_tool
    from horizon.cli._fmt import output

    output(beta_decompose_tool(strategy_returns=_parse_json(strategy_returns), benchmark_returns=_parse_json(benchmark_returns)), json_mode=ctx.obj.get("json", False))


@quant.command("options-chain")
@click.option("--mid-price", required=True, type=float, help="Mid price")
@click.option("--n-strikes", default=5, type=int, help="Number of strikes")
@click.option("--vol", default=0.3, type=float, help="Volatility")
@click.option("--t-days", default=30.0, type=float, help="Days to expiry")
@click.pass_context
def options_chain(ctx, mid_price, n_strikes, vol, t_days):
    """Build a synthetic options chain."""
    from horizon.tools import build_options_chain_tool
    from horizon.cli._fmt import output

    output(build_options_chain_tool(mid_price=mid_price, n_strikes=n_strikes, vol=vol, t_days=t_days), json_mode=ctx.obj.get("json", False))


@quant.command("vol-surface")
@click.option("--prices", required=True, help="JSON array of prices")
@click.option("--strikes", required=True, help="JSON array of strikes")
@click.option("--expirations", required=True, help="JSON array of expirations")
@click.pass_context
def vol_surface(ctx, prices, strikes, expirations):
    """Compute volatility surface."""
    from horizon.tools import vol_surface_tool
    from horizon.cli._fmt import output

    output(vol_surface_tool(prices=_parse_json(prices), strikes=_parse_json(strikes), expirations=_parse_json(expirations)), json_mode=ctx.obj.get("json", False))


@quant.command("best-venue")
@click.option("--bids", required=True, help="JSON array of bid prices")
@click.option("--asks", required=True, help="JSON array of ask prices")
@click.option("--venues", required=True, help="JSON array of venue names")
@click.pass_context
def best_venue(ctx, bids, asks, venues):
    """Find best execution venue."""
    from horizon.tools import best_venue_tool
    from horizon.cli._fmt import output

    output(best_venue_tool(bids=_parse_json(bids), asks=_parse_json(asks), venues=_parse_json(venues)), json_mode=ctx.obj.get("json", False))


@quant.command("vulnerability")
@click.option("--returns", required=True, help="JSON array of returns")
@click.pass_context
def vulnerability(ctx, returns):
    """Compute portfolio vulnerability score."""
    from horizon.tools import vulnerability_score_tool
    from horizon.cli._fmt import output

    output(vulnerability_score_tool(returns=_parse_json(returns)), json_mode=ctx.obj.get("json", False))


@quant.command("evolution-optimize")
@click.pass_context
def evolution_optimize(ctx):
    """Run evolutionary parameter optimization."""
    from horizon.tools import evolution_optimize_tool
    from horizon.cli._fmt import output

    output(evolution_optimize_tool(), json_mode=ctx.obj.get("json", False))


@quant.command("invariance")
@click.option("--prices", required=True, help="JSON array of prices")
@click.option("--volumes", required=True, help="JSON array of volumes")
@click.option("--durations", required=True, help="JSON array of durations")
@click.pass_context
def invariance(ctx, prices, volumes, durations):
    """Market microstructure invariance analysis."""
    from horizon.tools import invariance_analysis_tool
    from horizon.cli._fmt import output

    output(invariance_analysis_tool(prices=_parse_json(prices), volumes=_parse_json(volumes), durations=_parse_json(durations)), json_mode=ctx.obj.get("json", False))


@quant.command("signal-selection")
@click.option("--signals", required=True, help="JSON 2D array of signal values")
@click.option("--returns", required=True, help="JSON array of returns")
@click.pass_context
def signal_selection(ctx, signals, returns):
    """Select best signals from a set."""
    from horizon.tools import signal_selection_tool
    from horizon.cli._fmt import output

    output(signal_selection_tool(signals=_parse_json(signals), returns=_parse_json(returns)), json_mode=ctx.obj.get("json", False))


@quant.command("duration-analysis")
@click.option("--durations", required=True, help="JSON array of trade durations")
@click.pass_context
def duration_analysis(ctx, durations):
    """Analyze inter-trade duration patterns."""
    from horizon.tools import duration_analysis_tool
    from horizon.cli._fmt import output

    output(duration_analysis_tool(durations=_parse_json(durations)), json_mode=ctx.obj.get("json", False))


@quant.command("vol-signature")
@click.option("--prices", required=True, help="JSON array of prices")
@click.option("--max-lag", default=50, type=int, help="Maximum lag")
@click.pass_context
def vol_signature(ctx, prices, max_lag):
    """Compute volatility signature plot."""
    from horizon.tools import volatility_signature_tool
    from horizon.cli._fmt import output

    output(volatility_signature_tool(prices=_parse_json(prices), max_lag=max_lag), json_mode=ctx.obj.get("json", False))


@quant.command("two-scale-vol")
@click.option("--prices", required=True, help="JSON array of prices")
@click.pass_context
def two_scale_vol(ctx, prices):
    """Compute two-scale realized volatility."""
    from horizon.tools import two_scale_vol_tool
    from horizon.cli._fmt import output

    output(two_scale_vol_tool(prices=_parse_json(prices)), json_mode=ctx.obj.get("json", False))


@quant.command("acd")
@click.option("--durations", required=True, help="JSON array of durations")
@click.pass_context
def acd(ctx, durations):
    """Fit ACD (Autoregressive Conditional Duration) model."""
    from horizon.tools import fit_acd_tool
    from horizon.cli._fmt import output

    output(fit_acd_tool(durations=_parse_json(durations)), json_mode=ctx.obj.get("json", False))


@quant.command("elastic-net")
@click.option("--x", required=True, help="JSON 2D array of features")
@click.option("--y", required=True, help="JSON array of targets")
@click.option("--alpha", default=1.0, type=float, help="Regularization strength")
@click.option("--l1-ratio", default=0.5, type=float, help="L1/L2 ratio")
@click.pass_context
def elastic_net(ctx, x, y, alpha, l1_ratio):
    """Fit elastic net regression."""
    from horizon.tools import elastic_net_fit_tool
    from horizon.cli._fmt import output

    output(elastic_net_fit_tool(x=_parse_json(x), y=_parse_json(y), alpha=alpha, l1_ratio=l1_ratio), json_mode=ctx.obj.get("json", False))


@quant.command("trigger")
@click.option("--condition", required=True, help="Trigger condition")
@click.option("--threshold", required=True, type=float, help="Trigger threshold")
@click.option("--current-value", required=True, type=float, help="Current value")
@click.pass_context
def trigger(ctx, condition, threshold, current_value):
    """Evaluate a trigger condition."""
    from horizon.tools import evaluate_trigger_tool
    from horizon.cli._fmt import output

    output(evaluate_trigger_tool(condition=condition, threshold=threshold, current_value=current_value), json_mode=ctx.obj.get("json", False))


@quant.command("validate-legs")
@click.option("--market-ids", required=True, help="JSON array of market IDs")
@click.option("--sides", required=True, help="JSON array of sides")
@click.option("--prices", required=True, help="JSON array of prices")
@click.option("--sizes", required=True, help="JSON array of sizes")
@click.pass_context
def validate_legs(ctx, market_ids, sides, prices, sizes):
    """Validate a multi-leg order."""
    from horizon.tools import validate_legs_tool
    from horizon.cli._fmt import output

    output(validate_legs_tool(market_ids=_parse_json(market_ids), sides=_parse_json(sides), prices=_parse_json(prices), sizes=_parse_json(sizes)), json_mode=ctx.obj.get("json", False))
