"""Horizon SDK MCP Server.

Exposes the Horizon trading engine as MCP tools for Claude Code,
Cursor, Claude Desktop, and other MCP-compatible clients.

Run: python -m horizon.mcp

Security:
- HORIZON_API_KEY: authenticates the engine against the Horizon backend.
- HORIZON_MCP_TOKEN: required for HTTP transport. When set, incoming HTTP
  requests must include ``Authorization: Bearer <token>``. The ``__main__``
  entry point refuses to start HTTP without this variable. stdio transport
  (the default) is inherently local and does not require the token.
"""

from __future__ import annotations

import os
import logging

from mcp.server.fastmcp import FastMCP

from horizon import tools
from horizon import provider_tools

logger = logging.getLogger(__name__)

_horizon_key = os.environ.get("HORIZON_API_KEY")
if _horizon_key:
    logger.info("MCP server: HORIZON_API_KEY set, engine will authenticate")
else:
    logger.info("MCP server: no HORIZON_API_KEY set, running in unauthenticated mode")

_mcp_token = os.environ.get("HORIZON_MCP_TOKEN")
if _mcp_token:
    logger.info("MCP server: HORIZON_MCP_TOKEN set, HTTP auth enabled")

mcp = FastMCP(
    "horizon-sdk",
    instructions=(
        "Horizon SDK - prediction market trading engine. "
        "Use these tools to check positions, submit orders, manage risk, "
        "discover markets, analyze wallets, start live data feeds (Binance, "
        "Chainlink, Alpaca, Polymarket, Kalshi, ESPN, NWS, and custom REST), "
        "check feed health, run Monte Carlo simulations, execute arbitrage, "
        "compute Kelly-optimal sizing, run quantitative analytics, stress tests, "
        "signal diagnostics, stock hedging analysis, manage account credentials "
        "on Polymarket and Kalshi, and deploy strategies to the Horizon Cloud "
        "(create, validate, deploy, monitor, stop). "
        "Fund tools (fund_*) manage a multi-strategy fund: deploy/stop/pause/scale "
        "strategies, track NAV and P&L, run stress tests, monitor correlations, "
        "track execution quality, and query persistent strategy memory. "
        "Cloud tools (cloud_*) use the HORIZON_API_KEY env var for authentication. "
        "Marketplace tools (marketplace_*) let users browse, publish, subscribe to, "
        "and purchase strategies from the Horizon marketplace. "
        "Never pass API keys, private keys, or passwords as tool parameters - "
        "always use environment variables instead. "
        "Some tools require a Pro or Ultra plan. Tools marked [PRO] require a Pro "
        "or Ultra subscription. Tools marked [ULTRA] require an Ultra subscription. "
        "If a user tries a gated tool without the required plan, it returns an error "
        "with an upgrade link. Check the user's plan with cloud_account before "
        "calling gated tools."
    ),
)


# ---------------------------------------------------------------------------
# Tools
# ---------------------------------------------------------------------------

@mcp.tool()
def engine_status() -> dict:
    """Get trading engine status: PnL, open orders, positions, kill switch state, uptime."""
    return tools.engine_status()


@mcp.tool()
def list_positions() -> list[dict]:
    """List all open positions with size, entry price, PnL, and exchange."""
    return tools.list_positions()


@mcp.tool()
def list_open_orders(market_id: str | None = None) -> list[dict]:
    """List open orders. Optionally filter by market_id."""
    return tools.list_open_orders(market_id)


@mcp.tool()
def list_recent_fills(limit: int = 20) -> list[dict]:
    """List recent trade fills. Default last 20."""
    return tools.list_recent_fills(limit)


@mcp.tool()
def submit_order(
    market_id: str, side: str, price: float, size: float, market_side: str = "yes",
) -> dict:
    """Submit a limit order. side: 'buy' or 'sell'. market_side: 'yes' or 'no'. price: 0-1 (probability). Returns order ID."""
    return tools.submit_order(market_id, side, price, size, market_side)


@mcp.tool()
def cancel_order(order_id: str) -> dict:
    """Cancel a single order by its ID."""
    return tools.cancel_order(order_id)


@mcp.tool()
def cancel_all_orders() -> dict:
    """Cancel ALL open orders across all exchanges. Use with caution."""
    return tools.cancel_all_orders()


@mcp.tool()
def cancel_market_orders(market_id: str) -> dict:
    """Cancel all orders for a specific market."""
    return tools.cancel_market_orders(market_id)


@mcp.tool()
def activate_kill_switch(reason: str) -> dict:
    """EMERGENCY STOP. Activates the kill switch and cancels all orders. Use only in emergencies."""
    return tools.activate_kill_switch(reason)


@mcp.tool()
def deactivate_kill_switch() -> dict:
    """Deactivate the kill switch to resume normal trading."""
    return tools.deactivate_kill_switch()


@mcp.tool()
def get_feed_snapshot(name: str) -> dict:
    """Get the latest price/bid/ask snapshot for a named feed."""
    return tools.get_feed_snapshot(name)


@mcp.tool()
def list_all_feeds() -> list[dict]:
    """List all active feed snapshots with current prices."""
    return tools.list_all_feeds()


@mcp.tool()
def start_feed(
    name: str,
    feed_type: str,
    config_json: str | None = None,
    symbol: str | None = None,
    url: str | None = None,
    interval: float = 5.0,
) -> dict:
    """Start a live data feed. feed_type: 'binance_ws', 'polymarket_book', 'kalshi_book', 'rest', 'predictit', 'manifold', 'espn', 'nws', 'rest_json_path', 'chainlink', 'alpaca'. For chainlink: config_json='{"contract_address":"0x...","rpc_url":"https://..."}'. For alpaca: config_json='{"symbol":"AAPL","feed":"iex"}'. For binance_ws: symbol='btcusdt'."""
    return tools.start_feed(name, feed_type, config_json, symbol, url, interval)


@mcp.tool()
def discover_markets(
    exchange: str = "polymarket",
    query: str = "",
    limit: int = 10,
    market_type: str = "all",
    category: str = "",
) -> list[dict]:
    """Search for prediction markets on Polymarket or Kalshi. market_type: 'all' (binary+multi), 'binary', or 'multi'. category: tag filter (e.g., 'crypto', 'politics', 'sports')."""
    return tools.discover(exchange, query, limit, market_type, category)


@mcp.tool()
def kelly_sizing(
    prob: float,
    price: float,
    bankroll: float = 1000.0,
    fraction: float = 0.25,
    max_size: float = 100.0,
) -> dict:
    """Compute Kelly-optimal position size. prob: estimated probability (0-1). price: market price (0-1)."""
    return tools.kelly_sizing(prob, price, bankroll, fraction, max_size)


@mcp.tool()
def check_parity(market_id: str, feed_name: str | None = None) -> dict:
    """Check YES/NO price parity for a market. Optionally specify feed_name to use a specific feed."""
    return tools.check_parity(market_id, feed_name)


@mcp.tool()
def get_feed_metrics(name: str) -> dict:
    """Get connection metrics for a feed: update count, errors, reconnects, connection status."""
    return tools.feed_metrics(name)


@mcp.tool()
def get_all_feed_metrics() -> list[dict]:
    """Get connection metrics for all active feeds."""
    return tools.all_feed_metrics()


@mcp.tool()
def add_stop_loss(
    market_id: str, side: str, order_side: str, size: float, trigger: float
) -> dict:
    """Add a stop-loss order. side: 'yes'/'no'. order_side: 'buy'/'sell'. Triggers at price."""
    return tools.add_stop_loss(market_id, side, order_side, size, trigger)


@mcp.tool()
def add_take_profit(
    market_id: str, side: str, order_side: str, size: float, trigger: float
) -> dict:
    """Add a take-profit order. side: 'yes'/'no'. order_side: 'buy'/'sell'. Triggers at price."""
    return tools.add_take_profit(market_id, side, order_side, size, trigger)


@mcp.tool()
def list_contingent_orders() -> list[dict]:
    """List all pending stop-loss and take-profit orders."""
    return tools.list_contingent_orders()


# ---------------------------------------------------------------------------
# Feed Health
# ---------------------------------------------------------------------------

@mcp.tool()
def check_feed_health(stale_threshold: float = 30.0) -> dict:
    """Check staleness and health of all feeds. Reports which feeds are stale, age in seconds, and whether all feeds are down."""
    return tools.check_feed_health(stale_threshold)


# ---------------------------------------------------------------------------
# Discovery
# ---------------------------------------------------------------------------

@mcp.tool()
def discover_events(query: str = "", limit: int = 10, min_volume: float = 0.0) -> list[dict]:
    """Discover multi-outcome events on Polymarket. Returns events with all outcomes and prices."""
    return tools.discover_event(query, limit, min_volume)


@mcp.tool()
def get_market_detail(slug_or_id: str, exchange: str = "polymarket") -> dict:
    """Get full detail for a single market: metadata, prices, price history, orderbook, trades, holders."""
    return tools.market_detail(slug_or_id, exchange)


@mcp.tool()
def top_markets(exchange: str = "polymarket", limit: int = 10, category: str = "") -> list[dict]:
    """Get the highest-volume active markets. Exchange: 'polymarket' or 'kalshi'."""
    return tools.get_top_markets(exchange, limit, category)


# ---------------------------------------------------------------------------
# Wallet Analytics (Polymarket public - no auth needed)
# ---------------------------------------------------------------------------

@mcp.tool()
def get_wallet_trades(address: str, limit: int = 50, condition_id: str | None = None) -> list[dict]:
    """Fetch trade history for a Polymarket wallet address (0x...). No auth required."""
    return tools.wallet_trades(address, limit, condition_id)


@mcp.tool()
def get_market_trades(condition_id: str, limit: int = 50, side: str | None = None, min_size: float = 0.0) -> list[dict]:
    """Fetch recent trades for a Polymarket market by condition ID. Filter by side ('BUY'/'SELL') and min USDC size."""
    return tools.market_trades(condition_id, limit, side, min_size)


@mcp.tool()
def get_wallet_positions(address: str, limit: int = 50, sort_by: str = "CURRENT") -> list[dict]:
    """Fetch open positions for a Polymarket wallet. Sort: TOKENS, CURRENT, INITIAL, CASHPNL, PERCENTPNL, PRICE, AVGPRICE."""
    return tools.wallet_positions(address, limit, sort_by)


@mcp.tool()
def get_wallet_value(address: str) -> dict:
    """Get total USD value of all open positions in a Polymarket wallet."""
    return tools.wallet_value(address)


@mcp.tool()
def get_wallet_profile(address: str) -> dict:
    """Get public profile for a Polymarket wallet: pseudonym, name, bio, X handle."""
    return tools.wallet_profile(address)


@mcp.tool()
def get_top_holders(condition_id: str, limit: int = 20) -> list[dict]:
    """Get top token holders for a Polymarket market. Returns wallet, amount, and optional pseudonym."""
    return tools.market_top_holders(condition_id, limit)


@mcp.tool()
def analyze_market_flow(condition_id: str, trade_limit: int = 500, top_n: int = 10) -> dict:
    """Analyze trade flow for a market: buy/sell volume, net flow, unique wallets, top buyers/sellers."""
    return tools.market_flow(condition_id, trade_limit, top_n)


# ---------------------------------------------------------------------------
# Simulation
# ---------------------------------------------------------------------------

@mcp.tool()
def simulate_portfolio(scenarios: int = 10000, seed: int | None = None) -> dict:
    """[PRO] Run Monte Carlo simulation on current engine positions. Returns VaR, CVaR, win probability, mean/median PnL."""
    return tools.simulate_portfolio(scenarios, seed)


# ---------------------------------------------------------------------------
# Arbitrage
# ---------------------------------------------------------------------------

@mcp.tool()
def execute_arbitrage(
    market_id: str,
    buy_exchange: str,
    sell_exchange: str,
    buy_price: float,
    sell_price: float,
    size: float,
) -> dict:
    """Execute atomic cross-exchange arbitrage. Buys on one exchange, sells on another. Auto-cancels buy if sell fails."""
    return tools.execute_arb(market_id, buy_exchange, sell_exchange, buy_price, sell_price, size)


# ---------------------------------------------------------------------------
# Quantitative Analytics
# ---------------------------------------------------------------------------

@mcp.tool()
def shannon_entropy(p: float) -> dict:
    """Compute binary Shannon entropy for a probability (0-1). Maximum at p=0.5."""
    return tools.compute_shannon_entropy(p)


@mcp.tool()
def kl_divergence(p: list[float], q: list[float]) -> dict:
    """Compute KL divergence between two probability distributions. Measures information loss."""
    return tools.compute_kl_divergence(p, q)


@mcp.tool()
def hurst_exponent(prices: list[float]) -> dict:
    """Compute Hurst exponent for a price series. >0.5 trending, <0.5 mean-reverting, ~0.5 random walk."""
    return tools.compute_hurst_exponent(prices)


@mcp.tool()
def variance_ratio_test(returns: list[float], period: int = 2) -> dict:
    """Variance ratio test for market efficiency. 1.0 = random walk."""
    return tools.compute_variance_ratio(returns, period)


@mcp.tool()
def cornish_fisher_var(returns: list[float], confidence: float = 0.95) -> dict:
    """Cornish-Fisher VaR and CVaR (skew/kurtosis-adjusted). More accurate than Gaussian VaR for prediction markets."""
    return tools.compute_cornish_fisher_var(returns, confidence)


@mcp.tool()
def prediction_greeks(
    price: float, size: float, is_yes: bool = True, t_hours: float = 24.0, vol: float = 0.2,
) -> dict:
    """Compute prediction Greeks (delta, gamma, theta, vega) for a binary market position."""
    return tools.compute_prediction_greeks(price, size, is_yes, t_hours, vol)


@mcp.tool()
def deflated_sharpe(
    sharpe: float, n_obs: int, n_trials: int, skew: float = 0.0, kurt: float = 3.0,
) -> dict:
    """Deflated Sharpe Ratio test: is the observed Sharpe statistically significant given multiple strategy trials?"""
    return tools.compute_deflated_sharpe(sharpe, n_obs, n_trials, skew, kurt)


@mcp.tool()
def signal_diagnostics(predictions: list[float], outcomes: list[float]) -> dict:
    """[PRO] Signal quality diagnostics: information coefficient (Spearman), half-life, Hurst exponent."""
    return tools.run_signal_diagnostics(predictions, outcomes)


@mcp.tool()
def market_efficiency_test(prices: list[float]) -> dict:
    """[PRO] Analyze market efficiency: variance ratio, Hurst exponent, and efficiency flag."""
    return tools.run_market_efficiency(prices)


@mcp.tool()
def stress_test(n_simulations: int = 10000, seed: int | None = None) -> dict:
    """[PRO] Run stress test on current positions under adverse scenarios (correlation spike, all-resolve-no,liquidity shock, tail risk)."""
    return tools.run_stress_test(n_simulations, seed)


# ---------------------------------------------------------------------------
# Tearsheet
# ---------------------------------------------------------------------------

@mcp.tool()
def generate_tearsheet(
    equity_curve: list[float] | None = None,
    timestamps: list[float] | None = None,
    trades: list[dict] | None = None,
    initial_capital: float = 1000.0,
) -> dict:
    """[PRO] Generate a tearsheet: monthly returns, drawdown analysis, trade stats, tail ratio."""
    return tools.generate_tearsheet_report(equity_curve, timestamps, trades, initial_capital)


# ---------------------------------------------------------------------------
# Bayesian Optimization
# ---------------------------------------------------------------------------

@mcp.tool()
def bayesian_optimize(
    param_space: dict[str, list[float]],
    n_iterations: int = 20,
    n_initial: int = 5,
    seed: int | None = None,
) -> dict:
    """[PRO] Run Bayesian optimization with GP surrogate to find optimal strategy parameters. param_space: {name: [min, max]}."""
    return tools.run_bayesian_optimize(param_space, n_iterations, n_initial, seed)


# ---------------------------------------------------------------------------
# Portfolio Management
# ---------------------------------------------------------------------------

@mcp.tool()
def portfolio_metrics() -> dict:
    """Get portfolio metrics: total value, PnL, exposure, diversification, concentration."""
    return tools.get_portfolio_metrics()


@mcp.tool()
def portfolio_weights(method: str = "equal") -> dict:
    """Compute optimal portfolio weights. method: 'equal', 'kelly', 'risk_parity', 'min_variance'."""
    return tools.get_portfolio_weights(method)


# ---------------------------------------------------------------------------
# Hot-Reload Parameters
# ---------------------------------------------------------------------------

@mcp.tool()
def update_params(params: dict[str, float]) -> dict:
    """Hot-reload: update runtime parameters on the trading engine. Changes take effect next cycle."""
    return tools.update_runtime_params(params)


@mcp.tool()
def get_params() -> dict:
    """Get all current runtime parameters from the trading engine."""
    return tools.get_runtime_params()


# ---------------------------------------------------------------------------
# Hawkes Process
# ---------------------------------------------------------------------------

@mcp.tool()
def hawkes_intensity(
    event_times: list[float],
    mu: float = 0.1,
    alpha: float = 0.5,
    beta: float = 1.0,
) -> dict:
    """Compute Hawkes self-exciting process intensity from event timestamps. Useful for trade arrival modeling."""
    return tools.compute_hawkes_intensity(event_times, mu, alpha, beta)


# ---------------------------------------------------------------------------
# Ledoit-Wolf Correlation
# ---------------------------------------------------------------------------

@mcp.tool()
def correlation_matrix(
    returns: list[list[float]],
) -> dict:
    """Compute Ledoit-Wolf shrinkage covariance matrix from return observations. Each row is [asset1_return, asset2_return, ...]."""
    return tools.compute_correlation_matrix(returns)


# ---------------------------------------------------------------------------
# Resources
# ---------------------------------------------------------------------------

# ---------------------------------------------------------------------------
# AFML: Information-Driven Bars
# ---------------------------------------------------------------------------

@mcp.tool()
def tick_bars(
    timestamps: list[float], prices: list[float], volumes: list[float], bar_size: int,
) -> list[dict]:
    """Generate tick bars (fixed ticks per bar). AFML Ch. 2. Returns OHLCV bars."""
    return tools.compute_tick_bars(timestamps, prices, volumes, bar_size)


@mcp.tool()
def volume_bars(
    timestamps: list[float], prices: list[float], volumes: list[float], bar_size: float,
) -> list[dict]:
    """Generate volume bars (fixed total volume per bar). AFML Ch. 2."""
    return tools.compute_volume_bars(timestamps, prices, volumes, bar_size)


@mcp.tool()
def dollar_bars(
    timestamps: list[float], prices: list[float], volumes: list[float], bar_size: float,
) -> list[dict]:
    """Generate dollar bars (fixed dollar value per bar). AFML Ch. 2."""
    return tools.compute_dollar_bars(timestamps, prices, volumes, bar_size)


# ---------------------------------------------------------------------------
# AFML: Triple Barrier Labeling
# ---------------------------------------------------------------------------

@mcp.tool()
def cusum_filter(prices: list[float], threshold: float) -> list[int]:
    """CUSUM structural break filter. Returns indices where cumulative deviation exceeds threshold."""
    return tools.compute_cusum_filter(prices, threshold)


@mcp.tool()
def triple_barrier_labels(
    prices: list[float],
    timestamps: list[float],
    events: list[int],
    pt_sl: list[float] | None = None,
    min_ret: float = 0.0,
    max_holding: int = 100,
    vol_span: int = 20,
) -> list[dict]:
    """Apply triple barrier labeling (profit-taking, stop-loss, vertical barrier). AFML Ch. 3."""
    return tools.compute_triple_barrier(prices, timestamps, events, pt_sl, min_ret, max_holding, vol_span)


# ---------------------------------------------------------------------------
# AFML: Fractional Differentiation
# ---------------------------------------------------------------------------

@mcp.tool()
def frac_diff(
    series: list[float], d: float, threshold: float = 1e-5,
) -> list[float]:
    """Apply fractional differentiation (FFD). Makes series stationary while preserving memory. AFML Ch. 5."""
    return tools.compute_frac_diff(series, d, threshold)


@mcp.tool()
def min_frac_diff(
    series: list[float], p_threshold: float = 0.05, max_d: float = 1.0, n_steps: int = 10,
) -> dict:
    """Find minimum d for stationarity via grid search + ADF test. AFML Ch. 5."""
    return tools.compute_min_frac_diff(series, p_threshold, max_d, n_steps)


# ---------------------------------------------------------------------------
# AFML: HRP & Denoising
# ---------------------------------------------------------------------------

@mcp.tool()
def hrp_weights(covariance: list[list[float]]) -> dict:
    """Compute Hierarchical Risk Parity portfolio weights from covariance matrix. Lopez de Prado."""
    return tools.compute_hrp_weights(covariance)


@mcp.tool()
def denoise_covariance(covariance: list[list[float]], n_observations: int) -> dict:
    """Denoise covariance matrix using Marcenko-Pastur random matrix theory. Separates signal from noise eigenvalues."""
    return tools.compute_denoise_covariance(covariance, n_observations)


# ---------------------------------------------------------------------------
# Wallet Profiler
# ---------------------------------------------------------------------------

@mcp.tool()
def profile_wallet(address: str, trade_limit: int = 500) -> dict:
    """[PRO] Deep behavioral profile for a Polymarket wallet. Analyzes temporal patterns, market selection, order flow, edge detection, and generates concrete profit strategies (copy, fade, front-run, etc.)."""
    return tools.wallet_profiler(address, trade_limit)


# ---------------------------------------------------------------------------
# Stealth Execution (Ultra)
# ---------------------------------------------------------------------------

@mcp.tool()
def stealth_estimate_impact(market_id: str, size: float, feed_name: str | None = None) -> dict:
    """[ULTRA] Estimate market impact before execution. Returns slippage (bps), Kyle's lambda, recommended strategy (market/twap/iceberg/sniper), and estimated duration. Requires Ultra plan."""
    return tools.stealth_estimate_impact(market_id, size, feed_name)


@mcp.tool()
def stealth_execute(
    market_id: str, side: str, order_side: str, size: float, price: float, strategy: str = "auto",
) -> dict:
    """[ULTRA] Execute an order using stealth algorithms. strategy: 'auto', 'twap', 'iceberg', 'sniper'. Returns execution plan with routes and child orders. Requires Ultra plan."""
    return tools.stealth_execute_order(market_id, side, order_side, size, price, strategy)


# ---------------------------------------------------------------------------
# Sentinel Risk Monitoring (Ultra)
# ---------------------------------------------------------------------------

@mcp.tool()
def sentinel_report() -> dict:
    """[ULTRA] Portfolio risk report: regime detection, VaR/CVaR, drawdown levels, correlation alerts, hedge suggestions, risk budget. Requires Ultra plan."""
    return tools.get_sentinel_report()


# ---------------------------------------------------------------------------
# Oracle Forecasting (Ultra)
# ---------------------------------------------------------------------------

@mcp.tool()
def oracle_forecast(
    market_id: str, market_title: str = "", market_price: float = 0.0,
    exchange: str = "polymarket",
) -> dict:
    """[ULTRA] Forecast market probability using 6-signal ensemble (smart money, microstructure, momentum, volume, concentration, temporal). exchange: 'polymarket' or 'kalshi' (Kalshi uses 3/6 signals). Returns ensemble_prob, edge_bps, signal breakdown. Requires Ultra plan."""
    return tools.oracle_forecast_market(market_id, market_title, market_price, exchange=exchange)


@mcp.tool()
def oracle_edges(
    min_edge_bps: float = 200.0, max_markets: int = 30,
    exchange: str = "polymarket",
) -> list[dict]:
    """[ULTRA] Scan top markets for edge opportunities where ensemble forecast diverges from market price. exchange: 'polymarket' or 'kalshi'. Requires Ultra plan."""
    return tools.oracle_scan_edges(min_edge_bps, max_markets, exchange=exchange)


# ---------------------------------------------------------------------------
# Whale Galaxy (Ultra)
# ---------------------------------------------------------------------------

@mcp.tool()
def whale_galaxy_scan(top_n: int = 20, max_markets: int = 50) -> dict:
    """[ULTRA] Scan Polymarket for whale wallets: rank by score, detect coordinated clusters, generate alerts. Requires Ultra plan."""
    return tools.whale_galaxy_scan(top_n, max_markets)


# ---------------------------------------------------------------------------
# LLM Signal Engine
# ---------------------------------------------------------------------------

@mcp.tool()
def llm_forecast(
    market_id: str, market_title: str = "", market_price: float = 0.5,
    provider: str = "anthropic", model: str = "",
) -> dict:
    """Forecast market probability via LLM + news analysis. Returns llm_prob, edge_bps, reasoning, confidence. model: any litellm-compatible model string (e.g., 'openrouter/meta-llama/llama-3-70b')."""
    return tools.llm_forecast_market(market_id, market_title, market_price, provider, model)


@mcp.tool()
def llm_scan(
    min_edge_bps: float = 200.0, max_markets: int = 20, provider: str = "anthropic",
    model: str = "", exchange: str = "polymarket",
) -> list[dict]:
    """Scan top markets for LLM-detected edges. Returns markets where |edge| exceeds threshold. exchange: 'polymarket' or 'kalshi'. model: any litellm-compatible model string."""
    return tools.llm_scan_markets(min_edge_bps, max_markets, provider, model, exchange=exchange)


# ---------------------------------------------------------------------------
# Resolution Analysis
# ---------------------------------------------------------------------------

@mcp.tool()
def analyze_resolution(
    market_id: str, market_title: str = "", provider: str = "anthropic",
    model: str = "", exchange: str = "polymarket",
) -> dict:
    """Analyze resolution conditions, ambiguity risks, and edge cases for a market. exchange: 'polymarket' or 'kalshi'. Returns ambiguity_score, conditions, risk_level. model: any litellm-compatible model string."""
    return tools.analyze_resolution_tool(market_id, market_title, provider, model, exchange=exchange)


# ---------------------------------------------------------------------------
# Resolution Sniping
# ---------------------------------------------------------------------------

@mcp.tool()
def sniper_scan(
    provider: str = "anthropic", max_markets: int = 10, model: str = "",
    exchange: str = "polymarket",
) -> list[dict]:
    """Scan for markets about to resolve. exchange: 'polymarket' or 'kalshi'. Returns resolution signals with confidence and evidence. model: any litellm-compatible model string."""
    return tools.sniper_scan_tool(provider, max_markets, model, exchange=exchange)


# ---------------------------------------------------------------------------
# Wallet Tracker (Ultra)
# ---------------------------------------------------------------------------

@mcp.tool()
def track_wallet(wallet: str, market_id: str = "", limit: int = 500) -> dict | list[dict]:
    """[ULTRA] Compute directional signals for a Polymarket wallet. If market_id is given, returns signal for that market; otherwise returns signals for all markets the wallet holds positions in, sorted by strength. Requires Ultra plan."""
    return tools.track_wallet_tool(wallet, market_id, limit)


@mcp.tool()
def wallet_consensus(market_id: str, min_quality: float = 0.1) -> dict:
    """[ULTRA] Compute multi-wallet consensus signal for a market. Discovers top holders, scores each, and aggregates into a weighted directional signal. min_quality: minimum |composite_score| to include a wallet. Requires Ultra plan."""
    return tools.wallet_consensus_tool(market_id, min_quality)


@mcp.tool()
def scan_wallet_signals(wallet: str, limit: int = 500) -> list[dict]:
    """[ULTRA] Scan all positions held by a wallet and return directional signals sorted by strength. Equivalent to track_wallet without a market_id. Requires Ultra plan."""
    return tools.scan_wallet_signals_tool(wallet, limit)


# ---------------------------------------------------------------------------
# Microstructure Invariance
# ---------------------------------------------------------------------------

@mcp.tool()
def invariance_analysis(
    dollar_volume: float, volatility: float, n_trades: int, avg_trade_size: float,
) -> dict:
    """Compute microstructure invariance: trading activity, invariant bet size, market impact, and efficiency score. Based on Kyle-Obizhaeva invariance."""
    return tools.invariance_analysis_tool(dollar_volume, volatility, n_trades, avg_trade_size)


# ---------------------------------------------------------------------------
# Signal Selection (Elastic Net)
# ---------------------------------------------------------------------------

@mcp.tool()
def signal_selection(
    x: list[list[float]], y: list[float], alpha: float = 1.0, l1_ratio: float = 0.5,
) -> dict:
    """Fit elastic net regression for signal selection. Identifies which features are predictive (nonzero coefficients). alpha: regularization strength. l1_ratio: 1.0=LASSO, 0.0=Ridge."""
    return tools.signal_selection_tool(x, y, alpha, l1_ratio)


# ---------------------------------------------------------------------------
# ACD Duration Model
# ---------------------------------------------------------------------------

@mcp.tool()
def duration_analysis(
    durations: list[float], omega: float = 0.1, alpha: float = 0.1, beta: float = 0.8,
) -> dict:
    """Analyze trade inter-arrival durations using an ACD (Autoregressive Conditional Duration) model. Detects unusual timing patterns."""
    return tools.duration_analysis_tool(durations, omega, alpha, beta)


# ---------------------------------------------------------------------------
# Volatility Signature Plot
# ---------------------------------------------------------------------------

@mcp.tool()
def volatility_signature(
    prices: list[float], max_freq: int | None = None, n_points: int = 20,
) -> dict:
    """Compute volatility signature plot: realized volatility at different sampling frequencies. Identifies optimal sampling frequency and microstructure noise."""
    return tools.volatility_signature_tool(prices, max_freq, n_points)


# ---------------------------------------------------------------------------
# Kalman Filter
# ---------------------------------------------------------------------------

@mcp.tool()
def kalman_filter_state(
    observations: list[float], state_dim: int = 1, obs_dim: int = 1,
) -> dict:
    """Run Kalman filter on scalar observations. Returns filtered state estimate. Useful for tracking latent market state."""
    return tools.kalman_filter_state_tool(observations, state_dim, obs_dim)


# ---------------------------------------------------------------------------
# Particle Filter
# ---------------------------------------------------------------------------

@mcp.tool()
def particle_filter_state(
    observations: list[float], n_particles: int = 1000, process_noise: float = 0.1, measurement_noise: float = 0.5,
) -> dict:
    """Run particle filter (Sequential Monte Carlo) on observations. Handles nonlinear/non-Gaussian state estimation."""
    return tools.particle_filter_state_tool(observations, n_particles, process_noise, measurement_noise)


# ---------------------------------------------------------------------------
# BOCPD Change Point Detection
# ---------------------------------------------------------------------------

@mcp.tool()
def bocpd_detect(
    observations: list[float], hazard_rate: float = 250.0, threshold: float = 0.5,
) -> dict:
    """Bayesian Online Change Point Detection. Identifies structural breaks in a time series (regime changes, volatility shifts)."""
    return tools.bocpd_detect_tool(observations, hazard_rate, threshold)


# ---------------------------------------------------------------------------
# Lead-Lag Analysis
# ---------------------------------------------------------------------------

@mcp.tool()
def lead_lag_analysis(
    series_a: list[float], series_b: list[float], max_lag: int = 10,
) -> dict:
    """Cross-correlation analysis to detect lead-lag relationships between two series. Returns peak lag and correlation at each lag."""
    return tools.lead_lag_analysis_tool(series_a, series_b, max_lag)


# ---------------------------------------------------------------------------
# Granger Causality
# ---------------------------------------------------------------------------

@mcp.tool()
def granger_test(
    x: list[float], y: list[float], max_lag: int = 5,
) -> dict:
    """Granger causality test: does x Granger-cause y? Returns F-statistic, p-value, and significance at 5% level."""
    return tools.granger_test_tool(x, y, max_lag)


# ---------------------------------------------------------------------------
# Market Graph Analysis
# ---------------------------------------------------------------------------

@mcp.tool()
def market_graph_analysis(
    corr_matrix: list[list[float]], names: list[str], threshold: float = 0.5,
) -> dict:
    """Build correlation graph from a matrix. Returns nodes with centrality metrics, edges, communities, MST, and density."""
    return tools.market_graph_analysis_tool(corr_matrix, names, threshold)


# ---------------------------------------------------------------------------
# Garleanu-Pedersen Optimal Execution
# ---------------------------------------------------------------------------

@mcp.tool()
def gp_optimal_execution(
    current_pos: float, target_pos: float, alpha_decay: float, temp_impact: float,
    perm_impact: float, risk_aversion: float, n_steps: int, dt: float,
) -> dict:
    """Compute Garleanu-Pedersen optimal execution trajectory. Balances alpha decay against market impact."""
    return tools.gp_optimal_execution_tool(
        current_pos, target_pos, alpha_decay, temp_impact, perm_impact, risk_aversion, n_steps, dt,
    )


# ---------------------------------------------------------------------------
# Almgren-Chriss Optimal Liquidation
# ---------------------------------------------------------------------------

@mcp.tool()
def ac_optimal_liquidation(
    total_shares: float, n_steps: int, volatility: float, perm_impact: float,
    temp_impact: float, risk_aversion: float,
) -> dict:
    """Compute Almgren-Chriss optimal liquidation schedule. Minimizes execution cost subject to risk constraint."""
    return tools.ac_optimal_liquidation_tool(
        total_shares, n_steps, volatility, perm_impact, temp_impact, risk_aversion,
    )


# ---------------------------------------------------------------------------
# Queue Fill Probability
# ---------------------------------------------------------------------------

@mcp.tool()
def queue_fill_probability(
    queue_size: float, position: float, arrival_rate: float, cancel_rate: float, time_horizon: float,
) -> dict:
    """Estimate probability of being filled given queue position. Models order arrival and cancellation dynamics."""
    return tools.queue_fill_probability_tool(queue_size, position, arrival_rate, cancel_rate, time_horizon)


# ---------------------------------------------------------------------------
# Copula Fitting
# ---------------------------------------------------------------------------

@mcp.tool()
def fit_copula(
    u: list[float], v: list[float], family: str = "gaussian",
) -> dict:
    """Fit a copula to bivariate data (uniform marginals). family: 'gaussian', 'student_t', 'clayton', 'gumbel', 'frank'."""
    return tools.fit_copula_tool(u, v, family)


@mcp.tool()
def best_copula(
    u: list[float], v: list[float],
) -> dict:
    """Find the best-fitting copula family for bivariate data by AIC. Tests Gaussian, Student-t, Clayton, Gumbel, Frank."""
    return tools.best_copula_tool(u, v)


# ---------------------------------------------------------------------------
# Vine Copulas
# ---------------------------------------------------------------------------

@mcp.tool()
def vine_copula_fit(
    data: list[list[float]], vine_type: str = "cvine",
) -> dict:
    """[ULTRA] Fit a vine copula for high-dimensional dependence modeling. vine_type: 'cvine' (star) or 'dvine' (path). Requires Ultra plan."""
    return tools.vine_copula_fit_tool(data, vine_type)


@mcp.tool()
def vine_tail_risk(
    data: list[list[float]], vine_type: str = "cvine", threshold: float = 0.05, n_sim: int = 10000,
) -> dict:
    """[ULTRA] Estimate joint tail risk using vine copula simulation. Returns probability of simultaneous extreme events. Requires Ultra plan."""
    return tools.vine_tail_risk_tool(data, vine_type, threshold, n_sim)


# ---------------------------------------------------------------------------
# Cross-Impact Matrix
# ---------------------------------------------------------------------------

@mcp.tool()
def cross_impact_matrix(
    returns_matrix: list[list[float]], volume_matrix: list[list[float]], names: list[str],
) -> dict:
    """[ULTRA] Estimate cross-impact matrix: how order flow in one market impacts returns in others. Requires Ultra plan."""
    return tools.cross_impact_matrix_tool(returns_matrix, volume_matrix, names)


# ---------------------------------------------------------------------------
# Entropy Pooling
# ---------------------------------------------------------------------------

@mcp.tool()
def entropy_pooling(
    prior_probs: list[float], scenarios: list[list[float]], views: list[dict],
) -> dict:
    """[ULTRA] Meucci entropy pooling: update scenario probabilities with views while minimizing KL divergence. views: list of {coefficients, bound, is_equality}. Requires Ultra plan."""
    return tools.entropy_pooling_tool(prior_probs, scenarios, views)


# ---------------------------------------------------------------------------
# Robust Portfolio
# ---------------------------------------------------------------------------

@mcp.tool()
def robust_portfolio(
    expected_returns: list[float], covariance: list[list[float]], return_uncertainty: list[float],
    uncertainty_budget: float = 1.0,
) -> dict:
    """[ULTRA] Robust portfolio optimization under return uncertainty. Optimizes worst-case return within uncertainty set. Requires Ultra plan."""
    return tools.robust_portfolio_tool(expected_returns, covariance, return_uncertainty, uncertainty_budget)


# ---------------------------------------------------------------------------
# Optimal Exit (Longstaff-Schwartz)
# ---------------------------------------------------------------------------

@mcp.tool()
def optimal_exit(
    s0: float, mu: float, sigma: float, dt: float, n_steps: int, n_paths: int, strike: float,
) -> dict:
    """Compute optimal exit policy via Longstaff-Schwartz. Generates GBM paths, then solves for optimal stopping boundary."""
    return tools.optimal_exit_tool(s0, mu, sigma, dt, n_steps, n_paths, strike)


# ---------------------------------------------------------------------------
# Binary Option Pricing
# ---------------------------------------------------------------------------

@mcp.tool()
def binary_option_price(
    model: str, strike: float, time_to_expiry: float, s0: float = 1.0, r: float = 0.0,
) -> dict:
    """Price a binary option using characteristic function models. model: 'heston' (stochastic vol), 'merton' (jump diffusion), 'vg' (variance gamma)."""
    return tools.binary_option_price_tool(model, strike, time_to_expiry, s0, r)


# ---------------------------------------------------------------------------
# HJB Optimal Market Making
# ---------------------------------------------------------------------------

@mcp.tool()
def hjb_optimal_quotes(
    max_inventory: int = 10, n_time_steps: int = 100, gamma: float = 0.1, sigma: float = 0.3,
    kappa: float = 1.5, alpha: float = 0.01, terminal_time: float = 1.0,
    current_inventory: int = 0, time_fraction: float = 0.0,
) -> dict:
    """[ULTRA] Solve HJB equation for optimal bid/ask quotes (Avellaneda-Stoikov market making). Returns optimal spread given inventory and time. Requires Ultra plan."""
    return tools.hjb_optimal_quotes_tool(
        max_inventory, n_time_steps, gamma, sigma, kappa, alpha, terminal_time,
        current_inventory, time_fraction,
    )


# ---------------------------------------------------------------------------
# Resources
# ---------------------------------------------------------------------------

@mcp.resource("horizon://status")
def resource_status() -> str:
    """Current engine status as JSON context."""
    import json
    return json.dumps(tools.engine_status(), indent=2)


@mcp.resource("horizon://positions")
def resource_positions() -> str:
    """Current positions as JSON context."""
    import json
    return json.dumps(tools.list_positions(), indent=2)


# ---------------------------------------------------------------------------
# Stock Hedging Tools
# ---------------------------------------------------------------------------

@mcp.tool()
def hedge_analysis(
    spot_prices: list[float],
    hedge_prices: list[float],
    window: int = 60,
    position_size: float = 1.0,
) -> dict:
    """Compute hedge ratio, effectiveness, basis risk, and sensitivities between a spot and hedge instrument."""
    return tools.hedge_analysis(spot_prices, hedge_prices, window, position_size)


@mcp.tool()
def hedge_scenario_analysis(
    spot_position: float,
    spot_price: float,
    hedge_position: float,
    hedge_price: float,
    spot_moves: list[float] | None = None,
    correlation: float = 0.8,
    hedge_vol: float = 0.2,
    spot_vol: float = 0.2,
) -> list[dict] | dict:
    """Run scenario analysis: P&L under various spot price moves for a hedged position."""
    return tools.hedge_scenario_analysis(
        spot_position, spot_price, hedge_position, hedge_price,
        spot_moves, correlation, hedge_vol, spot_vol,
    )


@mcp.tool()
def multi_ticker_hedge(
    spot_returns: list[float],
    hedge_returns_matrix: list[list[float]],
    tickers: list[str],
) -> dict:
    """Find optimal multi-ticker hedge weights using OLS with Cholesky decomposition."""
    return tools.multi_ticker_hedge_analysis(spot_returns, hedge_returns_matrix, tickers)


@mcp.tool()
def hedge_cost_summary() -> dict:
    """Get hedge trading cost summary from the running engine."""
    return tools.hedge_cost_summary()


# ---------------------------------------------------------------------------
# Logit-Space Pricing
# ---------------------------------------------------------------------------

@mcp.tool()
def logit_greeks(price: float, size: float, is_yes: bool = True) -> dict:
    """Compute logit-space Greeks (delta, gamma, theta, vega) for a binary option position. Correct near 0/1 boundaries."""
    return tools.logit_greeks_tool(price, size, is_yes)


@mcp.tool()
def logit_reservation_price(
    mid: float, inventory: float, gamma: float, belief_vol: float, t: float = 1.0,
) -> dict:
    """Compute logit-space reservation price. Works correctly near probability boundaries (0 and 1)."""
    return tools.logit_reservation_price_tool(mid, inventory, gamma, belief_vol, t)


@mcp.tool()
def logit_optimal_spread(
    belief_vol: float, inventory: float, gamma: float, kappa: float, t: float = 1.0,
) -> dict:
    """Compute logit-space optimal bid-ask spread for market making."""
    return tools.logit_optimal_spread_tool(belief_vol, inventory, gamma, kappa, t)


@mcp.tool()
def logit_pnl_decompose(
    entry_price: float, exit_price: float, size: float, is_yes: bool = True,
) -> dict:
    """Decompose P&L into logit-space components: delta, gamma, theta, and residual contributions."""
    return tools.logit_pnl_decompose_tool(entry_price, exit_price, size, is_yes)


@mcp.tool()
def toxicity_adjusted_spread(
    base_spread: float, vpin: float, ofi: float,
    vpin_weight: float = 0.5, ofi_weight: float = 0.5,
) -> dict:
    """Widen spread based on order flow toxicity (VPIN and OFI). Higher toxicity = wider spread."""
    return tools.toxicity_adjusted_spread_tool(base_spread, vpin, ofi, vpin_weight, ofi_weight)


# ---------------------------------------------------------------------------
# Bootstrap Methods
# ---------------------------------------------------------------------------

@mcp.tool()
def bootstrap_sharpe(returns: list[float], n_bootstrap: int = 1000) -> dict:
    """Bootstrap confidence interval for Sharpe ratio. Returns point estimate + 95% CI."""
    return tools.bootstrap_sharpe_tool(returns, n_bootstrap)


@mcp.tool()
def bootstrap_mean(data: list[float], n_bootstrap: int = 1000) -> dict:
    """Bootstrap confidence interval for mean of a dataset."""
    return tools.bootstrap_mean_tool(data, n_bootstrap)


@mcp.tool()
def bootstrap_var(returns: list[float], n_bootstrap: int = 1000, confidence: float = 0.95) -> dict:
    """Bootstrap confidence interval for Value at Risk."""
    return tools.bootstrap_var_tool(returns, n_bootstrap, confidence)


@mcp.tool()
def bootstrap_edge(probs: list[float], prices: list[float], n_bootstrap: int = 1000) -> dict:
    """Bootstrap confidence interval for trading edge (expected profit per trade)."""
    return tools.bootstrap_edge_tool(probs, prices, n_bootstrap)


@mcp.tool()
def bootstrap_hypothesis_test(
    sample_a: list[float], sample_b: list[float], n_bootstrap: int = 1000,
) -> dict:
    """Bootstrap hypothesis test: are two samples significantly different? Returns p-value."""
    return tools.bootstrap_hypothesis_tool(sample_a, sample_b, n_bootstrap)


# ---------------------------------------------------------------------------
# Breeden-Litzenberger / Risk-Neutral Density
# ---------------------------------------------------------------------------

@mcp.tool()
def risk_neutral_density(prices: list[float], strikes: list[float]) -> dict:
    """Extract risk-neutral probability density from option/prediction market prices (Breeden-Litzenberger). Returns density curve, mean, and variance."""
    return tools.risk_neutral_density_tool(prices, strikes)


@mcp.tool()
def implied_probability_between(
    prices: list[float], strikes: list[float], lower: float, upper: float,
) -> dict:
    """Compute the market-implied probability that the outcome falls between lower and upper bounds."""
    return tools.implied_probability_tool(prices, strikes, lower, upper)


@mcp.tool()
def cross_asset_edge(
    prices_a: list[float], strikes_a: list[float],
    prices_b: list[float], strikes_b: list[float],
) -> dict:
    """Find pricing edge between two correlated assets by comparing their risk-neutral densities."""
    return tools.cross_asset_edge_tool(prices_a, strikes_a, prices_b, strikes_b)


# ---------------------------------------------------------------------------
# Yield-Adjusted Kelly / Treasury
# ---------------------------------------------------------------------------

@mcp.tool()
def yield_adjusted_kelly(
    prob: float, price: float, rfr: float, duration_days: float, fraction: float = 0.5,
) -> dict:
    """Kelly sizing adjusted for risk-free rate. Accounts for opportunity cost of capital locked in positions. Also returns breakeven edge threshold."""
    return tools.yield_adjusted_kelly_tool(prob, price, rfr, duration_days, fraction)


# ---------------------------------------------------------------------------
# Synthetic Index
# ---------------------------------------------------------------------------

@mcp.tool()
def compute_index_level(
    prices: list[float], weights: list[float], base: float = 100.0,
) -> dict:
    """Compute synthetic index level from component market prices and weights."""
    return tools.compute_index_level_tool(prices, weights, base)


@mcp.tool()
def index_tracking_error(
    index_levels: list[float], benchmark_levels: list[float],
) -> dict:
    """Compute tracking error between a synthetic index and a benchmark."""
    return tools.index_tracking_error_tool(index_levels, benchmark_levels)


# ---------------------------------------------------------------------------
# Correlation Regime Detection
# ---------------------------------------------------------------------------

@mcp.tool()
def correlation_regime(
    returns_a: list[float], returns_b: list[float], window: int = 60,
) -> dict:
    """Detect correlation regime shifts between two return series. Flags when correlation structure breaks down."""
    return tools.correlation_regime_tool(returns_a, returns_b, window)


@mcp.tool()
def contagion_score(returns_matrix: list[list[float]], window: int = 60) -> dict:
    """Compute contagion risk score for a portfolio of assets. Higher score = more systemic risk."""
    return tools.contagion_score_tool(returns_matrix, window)


# ---------------------------------------------------------------------------
# Portable Alpha / Market Neutral
# ---------------------------------------------------------------------------

@mcp.tool()
def beta_decompose(strategy_returns: list[float], benchmark_returns: list[float]) -> dict:
    """Decompose strategy returns into alpha (skill) and beta (market exposure) components."""
    return tools.beta_decompose_tool(strategy_returns, benchmark_returns)


# ---------------------------------------------------------------------------
# Options Overlay
# ---------------------------------------------------------------------------

@mcp.tool()
def build_options_chain(
    mid_price: float, n_strikes: int = 5, vol: float = 0.3, t_days: float = 30.0,
) -> list[dict] | dict:
    """Build synthetic options chain for a prediction market price. Returns strikes with call/put prices and Greeks."""
    return tools.build_options_chain_tool(mid_price, n_strikes, vol, t_days)


@mcp.tool()
def compute_vol_surface(
    prices: list[float], strikes: list[float], expirations: list[float],
) -> list[dict] | dict:
    """Compute implied volatility surface from market prices across strikes and expirations."""
    return tools.vol_surface_tool(prices, strikes, expirations)


# ---------------------------------------------------------------------------
# Liquidity Aggregation
# ---------------------------------------------------------------------------

@mcp.tool()
def best_venue(bids: list[float], asks: list[float], venues: list[str]) -> dict:
    """Find the best venue for execution across multiple exchanges. Returns best bid/ask and which venue offers them."""
    return tools.best_venue_tool(bids, asks, venues)


# ---------------------------------------------------------------------------
# Adversarial Simulation
# ---------------------------------------------------------------------------

@mcp.tool()
def vulnerability_score(returns: list[float]) -> dict:
    """Compute vulnerability score for a strategy's return series. Higher = more fragile to adversarial conditions."""
    return tools.vulnerability_score_tool(returns)


# ---------------------------------------------------------------------------
# Characteristic Function Pricing
# ---------------------------------------------------------------------------

@mcp.tool()
def binary_price_heston(
    price: float, strike: float, t: float, vol: float,
    kappa: float = 2.0, theta: float = 0.04, xi: float = 0.3, rho: float = -0.7,
) -> dict:
    """Price a binary option using the Heston stochastic volatility model. More accurate than Black-Scholes for vol smile."""
    return tools.binary_price_heston_tool(price, strike, t, vol, kappa, theta, xi, rho)


@mcp.tool()
def implied_vol_from_binary(binary_price: float, spot: float, strike: float, t: float) -> dict:
    """Extract implied volatility from a binary option's market price."""
    return tools.implied_vol_tool(binary_price, spot, strike, t)


# ---------------------------------------------------------------------------
# Evolution Optimizer
# ---------------------------------------------------------------------------

@mcp.tool()
def evolution_optimize(
    param_names: list[str], param_mins: list[float], param_maxs: list[float],
    population_size: int = 50, generations: int = 100,
) -> dict:
    """Initialize evolutionary optimization for strategy parameters. Defines search space and population."""
    return tools.evolution_optimize_tool(param_names, param_mins, param_maxs, population_size, generations)


# ---------------------------------------------------------------------------
# Two-Scale Realized Volatility
# ---------------------------------------------------------------------------

@mcp.tool()
def two_scale_realized_vol(prices: list[float]) -> dict:
    """Compute two-scale realized volatility (robust to microstructure noise). Also returns noise variance estimate."""
    return tools.two_scale_vol_tool(prices)


# ---------------------------------------------------------------------------
# Cross-Impact Matrix
# ---------------------------------------------------------------------------

@mcp.tool()
def cross_impact(
    returns_matrix: list[list[float]], volumes_matrix: list[list[float]],
) -> dict:
    """Estimate cross-impact matrix: how trading one asset moves prices of others. Essential for multi-asset execution."""
    return tools.cross_impact_tool(returns_matrix, volumes_matrix)


# ---------------------------------------------------------------------------
# Trigger System
# ---------------------------------------------------------------------------

@mcp.tool()
def evaluate_trigger(condition: str, threshold: float, current_value: float) -> dict:
    """Evaluate a trigger condition. Returns whether the trigger fired. Conditions: 'above', 'below', 'crosses'."""
    return tools.evaluate_trigger_tool(condition, threshold, current_value)


# ---------------------------------------------------------------------------
# Multi-Leg Execution
# ---------------------------------------------------------------------------

@mcp.tool()
def validate_multi_leg(
    market_ids: list[str], sides: list[str], prices: list[float], sizes: list[float],
) -> dict:
    """Validate a multi-leg order for consistency and aggregate risk. Checks all legs before execution."""
    return tools.validate_legs_tool(market_ids, sides, prices, sizes)


# ---------------------------------------------------------------------------
# Account & Credentials
# ---------------------------------------------------------------------------

@mcp.tool()
def horizon_setup(email: str, name: str = "") -> dict:
    """Create a Horizon account and generate an SDK key. Tries login first, then signup. Set HORIZON_PASSWORD env var before calling (do NOT pass password as argument). Saves encrypted key to ~/.horizon/credentials.json."""
    return tools.horizon_setup(email, "", name)


@mcp.tool()
def horizon_login(email: str) -> dict:
    """Login to existing Horizon account and generate a new SDK key. Set HORIZON_PASSWORD env var before calling (do NOT pass password as argument). Saves encrypted key to ~/.horizon/credentials.json."""
    return tools.horizon_login(email, "")


@mcp.tool()
def horizon_key_status() -> dict:
    """Check if Horizon SDK credentials are configured. Reports whether a key is set via HORIZON_API_KEY env var or saved in ~/.horizon/credentials.json."""
    return tools.horizon_key_status()


# ---------------------------------------------------------------------------
# Cloud Deploy / Monitor
#
# All cloud tools authenticate via HORIZON_API_KEY env var (hz_sdk_ key).
# Never pass API keys, private keys, or passwords as tool parameters.
# ---------------------------------------------------------------------------

def _get_cloud():
    """Create a HorizonCloud client using HORIZON_API_KEY env var."""
    from horizon.cloud import HorizonCloud
    return HorizonCloud()


@mcp.tool()
def cloud_list_strategies() -> dict:
    """List all strategies deployed on the Horizon Cloud. Returns a list with each strategy's id, name, and status. Use this to find strategy IDs for other cloud operations."""
    try:
        return {"strategies": _get_cloud().list_strategies()}
    except Exception as e:
        return {"error": str(e)}


@mcp.tool()
def cloud_get_strategy(strategy_id: str) -> dict:
    """Get detailed info for a single strategy: name, status, class_name, code validation state, creation date. Requires the strategy UUID from cloud_list_strategies."""
    try:
        return _get_cloud().get_strategy(strategy_id)
    except Exception as e:
        return {"error": str(e)}


@mcp.tool()
def cloud_create_strategy(name: str, code: str, description: str = "") -> dict:
    """Create a new strategy on the Horizon Cloud from Python code.

    The code must:
    - Import horizon/hz and use hz.run() or hz.quotes()
    - Have at least one function with a 'ctx' parameter (pipeline function)
    - NOT import forbidden modules (os, subprocess, socket, requests, etc.)
    - NOT use forbidden builtins (eval, exec, open, __import__, etc.)

    Returns the strategy record with id, name, class_name, and status.
    On validation failure returns error with validation_errors list."""
    try:
        return _get_cloud().create_strategy(name=name, code=code, description=description)
    except Exception as e:
        return {"error": str(e)}


@mcp.tool()
def cloud_validate(strategy_id: str) -> dict:
    """Validate a strategy's code in two phases: (1) static analysis for forbidden patterns, (2) worker sandbox for AST parsing and import verification. Returns {valid: bool, phase: str, errors: [...]}. Must pass before deploying."""
    try:
        return _get_cloud().validate(strategy_id)
    except Exception as e:
        return {"error": str(e)}


@mcp.tool()
def cloud_list_credentials() -> dict:
    """List saved exchange credentials (metadata only: id, label, exchange). Private keys are never returned. Use this to find credential IDs for cloud_deploy."""
    try:
        return {"credentials": _get_cloud().list_credentials()}
    except Exception as e:
        return {"error": str(e)}


@mcp.tool()
def cloud_save_credentials(label: str = "Default", exchange: str = "polymarket") -> dict:
    """Save exchange credentials encrypted at rest (AES-256-GCM). The private key is read from POLYMARKET_PRIVATE_KEY env var (do NOT pass it as argument). Returns credential id and label. exchange must be 'polymarket' or 'kalshi'."""
    try:
        private_key = os.environ.get("POLYMARKET_PRIVATE_KEY", "")
        if exchange == "kalshi":
            private_key = os.environ.get("KALSHI_API_KEY", private_key)
        if not private_key:
            return {"error": f"Set {'POLYMARKET_PRIVATE_KEY' if exchange == 'polymarket' else 'KALSHI_API_KEY'} env var before calling this tool."}
        return _get_cloud().save_credentials(private_key=private_key, label=label, exchange=exchange)
    except Exception as e:
        return {"error": str(e)}


@mcp.tool()
def cloud_deploy(strategy_id: str, credential_id: str, mode: str = "paper", markets: str = "") -> dict:
    """Deploy a validated strategy to the Horizon Cloud.

    Args:
        strategy_id: UUID from cloud_create_strategy or cloud_list_strategies
        credential_id: UUID from cloud_save_credentials or cloud_list_credentials
        mode: 'paper' (simulated, default) or 'live' (real orders, requires Pro/Ultra plan)
        markets: optional comma-separated market slugs to override hz.run(markets=[...])

    Returns deployment record with id and status (pending/starting/running).
    The strategy must be validated first (cloud_validate)."""
    try:
        cloud = _get_cloud()
        market_list = [m.strip() for m in markets.split(",") if m.strip()] if markets else None
        return cloud.deploy(strategy_id, credential_id=credential_id, mode=mode, markets=market_list)
    except Exception as e:
        return {"error": str(e)}


@mcp.tool()
def cloud_stop(strategy_id: str) -> dict:
    """Stop all active deployments for a strategy. Returns {success: bool, worker_reachable: bool}."""
    try:
        return _get_cloud().stop(strategy_id)
    except Exception as e:
        return {"error": str(e)}


@mcp.tool()
def cloud_status(strategy_id: str) -> dict:
    """Get the current deployment status for a strategy. Returns the latest active deployment, or the most recent one if none active. Status can be: pending, queued, starting, running, stopped, error."""
    try:
        return _get_cloud().status(strategy_id)
    except Exception as e:
        return {"error": str(e)}


@mcp.tool()
def cloud_metrics(strategy_id: str, limit: int = 50) -> dict:
    """Get performance metrics for the active deployment: total PnL, win rate, Sharpe, max drawdown, and history. limit controls history length (default 50)."""
    try:
        return _get_cloud().metrics(strategy_id, limit=limit)
    except Exception as e:
        return {"error": str(e)}


@mcp.tool()
def cloud_logs(strategy_id: str, limit: int = 100, level: str = "") -> dict:
    """Get deployment logs for a strategy, oldest first. Optional level filter: 'info', 'error', or 'warning'. Default limit is 100 entries (max 500)."""
    try:
        return {"logs": _get_cloud().logs(strategy_id, limit=limit, level=level or None)}
    except Exception as e:
        return {"error": str(e)}


@mcp.tool()
def cloud_account() -> dict:
    """Get Horizon Cloud account info: current plan (free/pro/ultra), plan limits, current usage counts."""
    try:
        return _get_cloud().account()
    except Exception as e:
        return {"error": str(e)}


# ---------------------------------------------------------------------------
# Marketplace tools
# ---------------------------------------------------------------------------


def _get_marketplace():
    from horizon.marketplace import Marketplace
    return Marketplace()


@mcp.tool()
def marketplace_browse(query: str = "", mode: str = "", exchange: str = "", sort_by: str = "sharpe", limit: int = 20) -> dict:
    """Browse the Horizon strategy marketplace. Search by query, filter by mode ('signal' or 'package'), exchange, and sort by 'sharpe', 'pnl', 'subscribers', 'price', or 'newest'. Returns public listings with performance metrics."""
    try:
        mp = _get_marketplace()
        listings = mp.browse(
            query=query,
            mode=mode or None,
            exchange=exchange or None,
            sort_by=sort_by,
            limit=limit,
        )
        return {"listings": [
            {
                "id": l.id, "name": l.name, "mode": l.mode, "price": l.price,
                "author": l.author_name, "sharpe": l.sharpe_ratio,
                "total_pnl": l.total_pnl, "win_rate": l.win_rate,
                "subscribers": l.total_subscribers, "exchange": l.exchange,
                "tags": l.tags, "status": l.status, "description": l.description[:200],
            }
            for l in listings
        ]}
    except Exception as e:
        return {"error": str(e)}


@mcp.tool()
def marketplace_get_listing(listing_id: str) -> dict:
    """Get full details for a marketplace listing including performance metrics, author info, and pricing."""
    try:
        mp = _get_marketplace()
        l = mp.get_listing(listing_id)
        return {
            "id": l.id, "name": l.name, "mode": l.mode, "price": l.price,
            "currency": l.currency, "author": l.author_name,
            "description": l.description, "tags": l.tags,
            "exchange": l.exchange, "markets": l.markets,
            "max_sales": l.max_sales, "status": l.status,
            "sharpe": l.sharpe_ratio, "total_pnl": l.total_pnl,
            "max_drawdown": l.max_drawdown, "win_rate": l.win_rate,
            "subscribers": l.total_subscribers, "avg_monthly_return": l.avg_monthly_return,
            "live_since": l.live_since, "created_at": l.created_at,
        }
    except Exception as e:
        return {"error": str(e)}


@mcp.tool()
def marketplace_leaderboard(timeframe: str = "30d", limit: int = 20) -> dict:
    """Get top-performing marketplace strategies. Timeframe: '7d', '30d', '90d', or 'all'."""
    try:
        mp = _get_marketplace()
        listings = mp.leaderboard(timeframe=timeframe, limit=limit)
        return {"leaderboard": [
            {
                "id": l.id, "name": l.name, "author": l.author_name,
                "sharpe": l.sharpe_ratio, "total_pnl": l.total_pnl,
                "win_rate": l.win_rate, "subscribers": l.total_subscribers,
                "price": l.price, "mode": l.mode,
            }
            for l in listings
        ]}
    except Exception as e:
        return {"error": str(e)}


@mcp.tool()
def marketplace_publish(strategy_id: str, mode: str = "signal", price: float = 0.0, description: str = "", tags: str = "", exchange: str = "", max_sales: int = 0) -> dict:
    """Publish a strategy to the marketplace. mode: 'signal' (copy trading, subscribers get signals without seeing code) or 'package' (direct sale, buyer gets encrypted code). Revenue: 90% to you, 10% platform. tags: comma-separated. max_sales: 0 = unlimited, 1 = exclusive. Strategy must exist on Horizon Cloud first."""
    try:
        mp = _get_marketplace()
        tag_list = [t.strip() for t in tags.split(",") if t.strip()] if tags else None
        listing = mp.publish(
            strategy_id,
            mode=mode,
            price=price,
            description=description,
            tags=tag_list,
            exchange=exchange or "",
            max_sales=max_sales if max_sales > 0 else None,
        )
        return {
            "id": listing.id, "name": listing.name, "mode": listing.mode,
            "price": listing.price, "status": listing.status,
        }
    except Exception as e:
        return {"error": str(e)}


@mcp.tool()
def marketplace_my_listings() -> dict:
    """List all your published marketplace listings with status and earnings info."""
    try:
        mp = _get_marketplace()
        listings = mp.my_listings()
        return {"listings": [
            {
                "id": l.id, "name": l.name, "mode": l.mode,
                "price": l.price, "status": l.status,
                "subscribers": l.total_subscribers,
                "total_pnl": l.total_pnl, "sharpe": l.sharpe_ratio,
            }
            for l in listings
        ]}
    except Exception as e:
        return {"error": str(e)}


@mcp.tool()
def marketplace_listing_earnings(listing_id: str) -> dict:
    """Get earnings breakdown for a listing you published. Shows total revenue, your 90% share, platform fee, and subscriber/purchase counts."""
    try:
        mp = _get_marketplace()
        e = mp.listing_earnings(listing_id)
        return {
            "listing_id": e.listing_id,
            "total_revenue": e.total_revenue,
            "author_share": e.author_share,
            "platform_fee": e.platform_fee,
            "total_subscribers": e.total_subscribers,
            "total_purchases": e.total_purchases,
            "currency": e.currency,
        }
    except Exception as e:
        return {"error": str(e)}


@mcp.tool()
def marketplace_subscribe(listing_id: str, size_scale: float = 1.0, max_position: float = 1000.0) -> dict:
    """Subscribe to a signal-mode marketplace listing. You'll receive trading signals without seeing the strategy code. size_scale: multiplier for signal sizes (0.5 = half). max_position: USD limit per market. Use marketplace_follow to start executing signals."""
    try:
        mp = _get_marketplace()
        sub = mp.subscribe(listing_id, size_scale=size_scale, max_position=max_position)
        return {
            "id": sub.id, "listing_name": sub.listing_name,
            "status": sub.status, "size_scale": sub.size_scale,
            "max_position": sub.max_position,
        }
    except Exception as e:
        return {"error": str(e)}


@mcp.tool()
def marketplace_unsubscribe(subscription_id: str) -> dict:
    """Cancel a marketplace subscription. Stops signals immediately."""
    try:
        mp = _get_marketplace()
        return mp.unsubscribe(subscription_id)
    except Exception as e:
        return {"error": str(e)}


@mcp.tool()
def marketplace_my_subscriptions() -> dict:
    """List all your marketplace subscriptions (active and past)."""
    try:
        mp = _get_marketplace()
        subs = mp.my_subscriptions()
        return {"subscriptions": [
            {
                "id": s.id, "listing_name": s.listing_name,
                "status": s.status, "size_scale": s.size_scale,
                "total_signals": s.total_signals, "total_pnl": s.total_pnl,
            }
            for s in subs
        ]}
    except Exception as e:
        return {"error": str(e)}


@mcp.tool()
def marketplace_signals(subscription_id: str, limit: int = 50) -> dict:
    """Get recent signals for a subscription. Returns order signals (market, side, price, size) that the strategy has emitted."""
    try:
        mp = _get_marketplace()
        signals = mp.signals(subscription_id, limit=limit)
        return {"signals": [
            {
                "id": s.id, "market_id": s.market_id, "side": s.side,
                "order_side": s.order_side, "price": s.price,
                "size": s.size, "timestamp": s.timestamp,
            }
            for s in signals
        ]}
    except Exception as e:
        return {"error": str(e)}


@mcp.tool()
def marketplace_purchase(listing_id: str) -> dict:
    """Purchase a package-mode marketplace listing. Returns a checkout_url for Stripe payment. After payment, use marketplace_activate with the license_key to decrypt the strategy. Revenue: 90% to author, 10% platform."""
    try:
        mp = _get_marketplace()
        p = mp.purchase(listing_id)
        return {
            "id": p.id, "listing_name": p.listing_name,
            "status": p.status, "checkout_url": p.checkout_url,
            "license_key": p.license_key,
        }
    except Exception as e:
        return {"error": str(e)}


@mcp.tool()
def marketplace_my_purchases() -> dict:
    """List all your marketplace purchases."""
    try:
        mp = _get_marketplace()
        purchases = mp.my_purchases()
        return {"purchases": [
            {
                "id": p.id, "listing_name": p.listing_name,
                "status": p.status, "purchased_at": p.purchased_at,
                "expires_at": p.expires_at,
            }
            for p in purchases
        ]}
    except Exception as e:
        return {"error": str(e)}


@mcp.tool()
def marketplace_connect_payments() -> dict:
    """Start Stripe Connect onboarding to receive marketplace payments. Returns a URL to complete KYC and bank account setup. Required before publishing paid listings."""
    try:
        mp = _get_marketplace()
        return mp.connect_payments()
    except Exception as e:
        return {"error": str(e)}


@mcp.tool()
def marketplace_payment_status() -> dict:
    """Check your Stripe Connect status: whether payments are connected and payouts enabled."""
    try:
        mp = _get_marketplace()
        return mp.payment_status()
    except Exception as e:
        return {"error": str(e)}


@mcp.tool()
def marketplace_verify_performance(listing_id: str) -> dict:
    """Request Verified Performance badge for your listing. Requires signal-mode listing deployed for 14+ days with 10+ trades. Badge is computed from real cloud deployment metrics."""
    try:
        mp = _get_marketplace()
        vp = mp.verify_performance(listing_id)
        return {
            "verified": vp.verified, "verified_at": vp.verified_at,
            "sharpe": vp.sharpe, "total_pnl": vp.total_pnl,
            "win_rate": vp.win_rate, "max_drawdown": vp.max_drawdown,
            "total_trades": vp.total_trades,
            "measurement_period_days": vp.measurement_period_days,
        }
    except Exception as e:
        return {"error": str(e)}


@mcp.tool()
def marketplace_listing_badges(listing_id: str) -> dict:
    """Get verification badge details for a marketplace listing, including both Verified Performance (strategy-level) and Verified Trader (author-level) badges."""
    try:
        mp = _get_marketplace()
        return mp.listing_badges(listing_id)
    except Exception as e:
        return {"error": str(e)}


@mcp.tool()
def marketplace_verify_trader(exchange: str, wallet_address: str = "", credential_id: str = "") -> dict:
    """Start Verified Trader verification. For polymarket: provide wallet_address (on-chain verification). For kalshi: provide credential_id (API verification). Verification is async, use marketplace_verification_status to check progress."""
    try:
        mp = _get_marketplace()
        vr = mp.verify_trader(
            exchange,
            wallet_address=wallet_address or None,
            credential_id=credential_id or None,
        )
        return {
            "id": vr.id, "type": vr.type, "exchange": vr.exchange,
            "status": vr.status, "created_at": vr.created_at,
        }
    except Exception as e:
        return {"error": str(e)}


@mcp.tool()
def marketplace_verification_status() -> dict:
    """Check status of your verification requests for Verified Trader and Verified Performance badges."""
    try:
        mp = _get_marketplace()
        requests = mp.verification_status()
        return {"requests": [
            {
                "id": r.id, "type": r.type, "exchange": r.exchange,
                "status": r.status, "error_message": r.error_message,
                "created_at": r.created_at, "completed_at": r.completed_at,
            }
            for r in requests
        ]}
    except Exception as e:
        return {"error": str(e)}


# ---------------------------------------------------------------------------
# Fund management tools (requires HORIZON_FUND_MODE=1)
# ---------------------------------------------------------------------------

@mcp.tool()
def fund_status() -> dict:
    """[FUND] Get full fund status: NAV, drawdown, strategy count, kill switch, universe size. Requires HORIZON_FUND_MODE=1."""
    return tools.fund_status()


@mcp.tool()
def fund_report() -> dict:
    """Generate a full fund report with NAV, strategies, allocations."""
    return tools.fund_report()


@mcp.tool()
def fund_deploy_strategy(name: str, markets: list[str], mode: str = "paper") -> dict:
    """Deploy a strategy to the fund. mode: 'paper' or 'live'. Strategies start in paper mode by default."""
    return tools.fund_deploy_strategy(name, markets, mode)


@mcp.tool()
def fund_stop_strategy(name: str) -> dict:
    """Stop and remove a strategy from the fund."""
    return tools.fund_stop_strategy(name)


@mcp.tool()
def fund_pause_strategy(name: str) -> dict:
    """Pause a running strategy (keeps state, stops trading)."""
    return tools.fund_pause_strategy(name)


@mcp.tool()
def fund_resume_strategy(name: str) -> dict:
    """Resume a paused strategy."""
    return tools.fund_resume_strategy(name)


@mcp.tool()
def fund_list_strategies() -> dict:
    """List all strategies in the fund with state, capital allocation, and markets."""
    return tools.fund_list_strategies()


@mcp.tool()
def fund_scale_strategy(name: str, new_capital: float) -> dict:
    """Scale capital allocation for a strategy."""
    return tools.fund_scale_strategy(name, new_capital)


@mcp.tool()
def fund_nav_history(limit: int = 50) -> dict:
    """Get recent NAV history (time series of fund value, drawdown, HWM)."""
    return tools.fund_nav_history(limit)


@mcp.tool()
def fund_risk_dashboard() -> dict:
    """Consolidated risk dashboard: VaR budget, correlations, execution quality, kill switch."""
    return tools.fund_risk_dashboard()


@mcp.tool()
def fund_stress_test() -> dict:
    """Run fund-level stress scenarios (10%/20% drawdown, correlation spike, liquidity crisis, black swan)."""
    return tools.fund_stress_test()


@mcp.tool()
def fund_pnl_attribution() -> dict:
    """Get P&L attribution per strategy: realized, unrealized, and daily PnL breakdown."""
    return tools.fund_pnl_attribution()


@mcp.tool()
def fund_memory_query(category: str | None = None, key_contains: str | None = None, limit: int = 20) -> dict:
    """Query fund strategy memory. Categories: strategy_outcome, market_pattern, regime_mapping, edge_decay, failure_reason."""
    return tools.fund_memory_query(category, key_contains, limit)


@mcp.tool()
def fund_memory_record(category: str, key: str, value: str, confidence: float = 0.5) -> dict:
    """Record a learning/observation to fund memory for future reference."""
    return tools.fund_memory_record(category, key, value, confidence)


@mcp.tool()
def fund_correlation_matrix() -> dict:
    """Get cross-strategy correlation matrix to detect hidden concentration risk."""
    return tools.fund_correlation_matrix()


@mcp.tool()
def fund_execution_report() -> dict:
    """Execution quality report: fill rates, slippage, adverse selection, market impact per strategy."""
    return tools.fund_execution_report()


# ---------------------------------------------------------------------------
# Third-Party Providers: Unusual Whales
# ---------------------------------------------------------------------------

@mcp.tool()
def uw_options_flow(
    ticker: str | None = None,
    min_premium: int | None = None,
    is_sweep: bool | None = None,
    is_call: bool | None = None,
    is_put: bool | None = None,
    is_otm: bool | None = None,
    min_dte: int | None = None,
    max_dte: int | None = None,
    limit: int = 50,
) -> list[dict] | dict:
    """Get unusual options flow alerts from Unusual Whales. Filter by ticker, premium, sweeps, calls/puts, OTM, DTE."""
    return provider_tools.uw_options_flow(
        ticker=ticker, min_premium=min_premium, is_sweep=is_sweep,
        is_call=is_call, is_put=is_put, is_otm=is_otm,
        min_dte=min_dte, max_dte=max_dte, limit=limit,
    )


@mcp.tool()
def uw_stock_flow(ticker: str, side: str | None = None, min_premium: int | None = None) -> list[dict] | dict:
    """Get recent options flow for a specific ticker from Unusual Whales."""
    return provider_tools.uw_stock_flow(ticker, side=side, min_premium=min_premium)


@mcp.tool()
def uw_dark_pool(ticker: str | None = None, date: str | None = None, min_size: int | None = None, limit: int = 50) -> list[dict] | dict:
    """Get dark pool trades from Unusual Whales. Pass ticker for specific stock, omit for market-wide."""
    if ticker:
        return provider_tools.uw_dark_pool(ticker, date=date, min_size=min_size, limit=limit)
    return provider_tools.uw_dark_pool_recent()


@mcp.tool()
def uw_congress_trades(ticker: str | None = None, date: str | None = None, limit: int = 50) -> list[dict] | dict:
    """Get recent Congress member stock trades from Unusual Whales. Optionally filter by ticker and date (YYYY-MM-DD)."""
    return provider_tools.uw_congress_trades(limit=limit, ticker=ticker, date=date)


@mcp.tool()
def uw_congress_trader(name: str) -> list[dict] | dict:
    """Get trades for a specific Congress member."""
    return provider_tools.uw_congress_trader(name)


@mcp.tool()
def uw_insider_trades(
    ticker: str | None = None,
    min_value: int | None = None,
    is_officer: bool | None = None,
    is_director: bool | None = None,
    is_ten_percent_owner: bool | None = None,
    start_date: str | None = None,
    end_date: str | None = None,
    limit: int = 50,
) -> list[dict] | dict:
    """Get insider transactions (Form 4 filings) from Unusual Whales. Filter by ticker, value, role, date range."""
    return provider_tools.uw_insider_trades(
        ticker=ticker, min_value=min_value, is_officer=is_officer,
        is_director=is_director, is_ten_percent_owner=is_ten_percent_owner,
        start_date=start_date, end_date=end_date, limit=limit,
    )


@mcp.tool()
def uw_market_tide(date: str | None = None, otm_only: bool | None = None) -> list[dict] | dict:
    """Get Market Tide sentiment (net premium flow) from Unusual Whales."""
    return provider_tools.uw_market_tide(date=date, otm_only=otm_only)


@mcp.tool()
def uw_spike() -> dict:
    """Get SPIKE volatility indicator from Unusual Whales."""
    return provider_tools.uw_spike()


@mcp.tool()
def uw_economic_calendar() -> list[dict] | dict:
    """Get upcoming economic events calendar from Unusual Whales."""
    return provider_tools.uw_economic_calendar()


@mcp.tool()
def uw_fda_calendar() -> list[dict] | dict:
    """Get FDA calendar events from Unusual Whales."""
    return provider_tools.uw_fda_calendar()


@mcp.tool()
def uw_stock_info(ticker: str) -> dict:
    """Get stock information (price, market cap, sector) from Unusual Whales."""
    return provider_tools.uw_stock_info(ticker)


@mcp.tool()
def uw_stock_state(ticker: str) -> dict:
    """Get current stock state from Unusual Whales."""
    return provider_tools.uw_stock_state(ticker)


@mcp.tool()
def uw_greek_exposure(ticker: str) -> dict:
    """Get aggregate Greek exposure (GEX, DEX) from Unusual Whales."""
    return provider_tools.uw_greek_exposure(ticker)


@mcp.tool()
def uw_greek_exposure_by_strike(ticker: str) -> list[dict] | dict:
    """Get Greek exposure broken down by strike from Unusual Whales."""
    return provider_tools.uw_greek_exposure_by_strike(ticker)


@mcp.tool()
def uw_iv_rank(ticker: str) -> dict:
    """Get IV rank percentile from Unusual Whales."""
    return provider_tools.uw_iv_rank(ticker)


@mcp.tool()
def uw_vol_term_structure(ticker: str) -> list[dict] | dict:
    """Get IV term structure across expirations from Unusual Whales."""
    return provider_tools.uw_vol_term_structure(ticker)


@mcp.tool()
def uw_max_pain(ticker: str) -> dict:
    """Get max pain levels from Unusual Whales."""
    return provider_tools.uw_max_pain(ticker)


@mcp.tool()
def uw_option_chains(ticker: str) -> list[dict] | dict:
    """Get full option chains from Unusual Whales."""
    return provider_tools.uw_option_chains(ticker)


@mcp.tool()
def uw_short_interest(ticker: str) -> dict:
    """Get short interest and float data from Unusual Whales."""
    return provider_tools.uw_short_interest(ticker)


@mcp.tool()
def uw_short_volume(ticker: str) -> list[dict] | dict:
    """Get short volume and ratio history from Unusual Whales."""
    return provider_tools.uw_short_volume(ticker)


@mcp.tool()
def uw_ftd(ticker: str) -> list[dict] | dict:
    """Get failures to deliver from Unusual Whales."""
    return provider_tools.uw_ftd(ticker)


@mcp.tool()
def uw_screener_stocks() -> list[dict] | dict:
    """Screen stocks across multiple criteria from Unusual Whales."""
    return provider_tools.uw_screener_stocks()


@mcp.tool()
def uw_screener_options(limit: int = 50, min_premium: int | None = None, is_otm: bool | None = None, min_volume_oi_ratio: float | None = None) -> list[dict] | dict:
    """Screen hottest option chains from Unusual Whales."""
    return provider_tools.uw_screener_options(limit=limit, min_premium=min_premium, is_otm=is_otm, min_volume_oi_ratio=min_volume_oi_ratio)


@mcp.tool()
def uw_earnings(ticker: str) -> list[dict] | dict:
    """Get earnings history from Unusual Whales."""
    return provider_tools.uw_earnings(ticker)


@mcp.tool()
def uw_earnings_schedule(time: str = "premarket") -> list[dict] | dict:
    """Get earnings schedule from Unusual Whales. time: 'premarket' or 'afterhours'."""
    return provider_tools.uw_earnings_schedule(time)


@mcp.tool()
def uw_etf_holdings(ticker: str) -> list[dict] | dict:
    """Get ETF holdings breakdown from Unusual Whales."""
    return provider_tools.uw_etf_holdings(ticker)


@mcp.tool()
def uw_etf_flow(ticker: str) -> list[dict] | dict:
    """Get ETF inflow/outflow data from Unusual Whales."""
    return provider_tools.uw_etf_flow(ticker)


@mcp.tool()
def uw_seasonality(ticker: str) -> list[dict] | dict:
    """Get monthly seasonality for a ticker from Unusual Whales."""
    return provider_tools.uw_seasonality(ticker)


@mcp.tool()
def uw_news() -> list[dict] | dict:
    """Get financial news headlines from Unusual Whales."""
    return provider_tools.uw_news()


@mcp.tool()
def uw_prediction_whales() -> list[dict] | dict:
    """Get prediction market whales from Unusual Whales."""
    return provider_tools.uw_prediction_whales()


@mcp.tool()
def uw_prediction_unusual() -> list[dict] | dict:
    """Get unusual prediction market activity from Unusual Whales."""
    return provider_tools.uw_prediction_unusual()


@mcp.tool()
def uw_crypto_whales() -> list[dict] | dict:
    """Get recent crypto whale transactions from Unusual Whales."""
    return provider_tools.uw_crypto_whales()


@mcp.tool()
def uw_institutions() -> list[dict] | dict:
    """List all tracked institutions from Unusual Whales."""
    return provider_tools.uw_institutions()


@mcp.tool()
def uw_institution_holdings(name: str) -> list[dict] | dict:
    """Get holdings for a specific institution from Unusual Whales."""
    return provider_tools.uw_institution_holdings(name)


@mcp.tool()
def uw_institution_ownership(ticker: str) -> list[dict] | dict:
    """Get institutional ownership of a ticker from Unusual Whales."""
    return provider_tools.uw_institution_ownership(ticker)


@mcp.tool()
def uw_greek_exposure_by_expiry(ticker: str) -> list[dict] | dict:
    """Get Greek exposure broken down by expiry from Unusual Whales."""
    return provider_tools.uw_greek_exposure_by_expiry(ticker)


# ---------------------------------------------------------------------------
# Third-Party Providers: Massive (Polygon.io)
# ---------------------------------------------------------------------------

@mcp.tool()
def massive_aggregates(
    ticker: str,
    from_date: str,
    to_date: str,
    multiplier: int = 1,
    timespan: str = "day",
    limit: int = 5000,
) -> list[dict] | dict:
    """Get OHLCV bars from Massive (Polygon). from_date/to_date: YYYY-MM-DD. timespan: second/minute/hour/day/week/month."""
    return provider_tools.massive_aggregates(ticker, multiplier, timespan, from_date, to_date, limit=limit)


@mcp.tool()
def massive_snapshot(ticker: str) -> dict:
    """Get real-time snapshot (price, day OHLCV, last trade/quote) from Massive."""
    return provider_tools.massive_snapshot(ticker)


@mcp.tool()
def massive_gainers_losers(direction: str = "gainers") -> list[dict] | dict:
    """Get top 20 gainers or losers from Massive. direction: 'gainers' or 'losers'."""
    return provider_tools.massive_gainers_losers(direction)


@mcp.tool()
def massive_last_trade(ticker: str) -> dict:
    """Get most recent trade for a ticker from Massive."""
    return provider_tools.massive_last_trade(ticker)


@mcp.tool()
def massive_last_quote(ticker: str) -> dict:
    """Get most recent NBBO quote for a ticker from Massive."""
    return provider_tools.massive_last_quote(ticker)


@mcp.tool()
def massive_ticker_details(ticker: str) -> dict:
    """Get ticker details (name, market cap, description, sector) from Massive."""
    return provider_tools.massive_ticker_details(ticker)


@mcp.tool()
def massive_search_tickers(query: str = "", market: str | None = None, limit: int = 20) -> list[dict] | dict:
    """Search/list tickers on Massive. market: stocks/crypto/fx/otc."""
    return provider_tools.massive_search_tickers(query, market=market, limit=limit)


@mcp.tool()
def massive_market_status() -> dict:
    """Get current market status (open/closed/early hours) from Massive."""
    return provider_tools.massive_market_status()


@mcp.tool()
def massive_options_chain(
    underlying: str,
    contract_type: str | None = None,
    expiration_date: str | None = None,
    expiration_date_gte: str | None = None,
    strike_price_gte: float | None = None,
    strike_price_lte: float | None = None,
    limit: int = 250,
) -> list[dict] | dict:
    """Get options chain with Greeks from Massive. Includes delta, gamma, theta, vega, IV. Use expiration_date for exact date, expiration_date_gte for 'on or after'."""
    return provider_tools.massive_options_chain(
        underlying, contract_type=contract_type, expiration_date=expiration_date,
        expiration_date_gte=expiration_date_gte,
        strike_price_gte=strike_price_gte, strike_price_lte=strike_price_lte, limit=limit,
    )


@mcp.tool()
def massive_sma(ticker: str, timespan: str = "day", window: int = 50, limit: int = 50) -> list[dict] | dict:
    """Get Simple Moving Average from Massive."""
    return provider_tools.massive_sma(ticker, timespan=timespan, window=window, limit=limit)


@mcp.tool()
def massive_ema(ticker: str, timespan: str = "day", window: int = 50, limit: int = 50) -> list[dict] | dict:
    """Get Exponential Moving Average from Massive."""
    return provider_tools.massive_ema(ticker, timespan=timespan, window=window, limit=limit)


@mcp.tool()
def massive_rsi(ticker: str, timespan: str = "day", window: int = 14, limit: int = 50) -> list[dict] | dict:
    """Get Relative Strength Index from Massive."""
    return provider_tools.massive_rsi(ticker, timespan=timespan, window=window, limit=limit)


@mcp.tool()
def massive_macd(ticker: str, timespan: str = "day", limit: int = 50) -> list[dict] | dict:
    """Get MACD indicator from Massive."""
    return provider_tools.massive_macd(ticker, timespan=timespan, limit=limit)


@mcp.tool()
def massive_news(ticker: str | None = None, limit: int = 20) -> list[dict] | dict:
    """Get market news from Massive, optionally filtered by ticker."""
    return provider_tools.massive_news(ticker=ticker, limit=limit)


@mcp.tool()
def massive_dividends(ticker: str, limit: int = 20) -> list[dict] | dict:
    """Get dividend history from Massive."""
    return provider_tools.massive_dividends(ticker, limit=limit)


@mcp.tool()
def massive_crypto_snapshot(ticker: str) -> dict:
    """Get crypto snapshot from Massive (e.g. X:BTCUSD)."""
    return provider_tools.massive_crypto_snapshot(ticker)


@mcp.tool()
def massive_forex_conversion(from_currency: str, to_currency: str) -> dict:
    """Get real-time forex conversion rate from Massive."""
    return provider_tools.massive_forex_conversion(from_currency, to_currency)


@mcp.tool()
def massive_previous_close(ticker: str) -> list[dict] | dict:
    """Get previous day's OHLCV from Massive."""
    return provider_tools.massive_previous_close(ticker)


@mcp.tool()
def massive_splits(ticker: str, limit: int = 20) -> list[dict] | dict:
    """Get stock split history from Massive."""
    return provider_tools.massive_splits(ticker, limit=limit)


@mcp.tool()
def massive_grouped_daily(date: str, adjusted: bool = True) -> list[dict] | dict:
    """Get OHLCV for the entire market on a date from Massive. date: YYYY-MM-DD."""
    return provider_tools.massive_grouped_daily(date, adjusted=adjusted)


@mcp.tool()
def massive_market_holidays() -> list[dict] | dict:
    """Get upcoming market holidays from Massive."""
    return provider_tools.massive_market_holidays()


@mcp.tool()
def massive_options_contracts(
    underlying_ticker: str | None = None,
    contract_type: str | None = None,
    expiration_date: str | None = None,
    strike_price: float | None = None,
    limit: int = 100,
) -> list[dict] | dict:
    """List options contracts with filters from Massive."""
    return provider_tools.massive_options_contracts(
        underlying_ticker=underlying_ticker, contract_type=contract_type,
        expiration_date=expiration_date, strike_price=strike_price, limit=limit,
    )


@mcp.tool()
def massive_crypto_gainers_losers(direction: str = "gainers") -> list[dict] | dict:
    """Get top crypto gainers or losers from Massive. direction: 'gainers' or 'losers'."""
    return provider_tools.massive_crypto_gainers_losers(direction)


@mcp.tool()
def massive_forex_snapshot(ticker: str) -> dict:
    """Get forex pair snapshot from Massive (e.g. C:EURUSD)."""
    return provider_tools.massive_forex_snapshot(ticker)


# ---------------------------------------------------------------------------
# Entry point
# ---------------------------------------------------------------------------

def main(transport: str = "stdio") -> None:
    """Run the MCP server.

    For HTTP transport, ``HORIZON_MCP_TOKEN`` must be set. The
    ``__main__`` entry point enforces this before calling run().
    """
    if transport != "stdio" and not _mcp_token:
        raise RuntimeError(
            "HORIZON_MCP_TOKEN must be set for non-stdio transports. "
            "Set it with: export HORIZON_MCP_TOKEN='your-secret-token'"
        )
    mcp.run(transport=transport)
