"""Deep test of all remaining Python SDK modules."""
import sys
import time
import math

errors = []

def check(name, condition, detail=""):
    if not condition:
        msg = f"FAIL: {name}"
        if detail:
            msg += f" -- {detail}"
        errors.append(msg)
        print(msg)
    else:
        print(f"  OK: {name}")


import horizon

print("=" * 60)
print("TEST: horizon __init__.py full import")
print("=" * 60)

check("version", horizon.__version__ == "0.4.5")
check("run callable", callable(horizon.run))
check("backtest callable", callable(horizon.backtest))

for name in [
    "Engine", "Side", "OrderSide", "Market", "Quote", "RiskConfig", "Order",
    "Position", "Fill", "EngineStatus", "FeedSnapshot", "Event", "Outcome",
    "Context", "Risk", "BinanceWS", "RESTFeed", "PolymarketBook", "KalshiBook",
    "Polymarket", "Kalshi", "discover_markets", "discover_events", "top_markets",
    "kelly", "kelly_no", "kelly_size", "fractional_kelly", "multi_kelly",
    "edge", "combine_signals", "ema", "zscore", "decay_weight",
    "reservation_price", "optimal_spread", "competitive_spread", "mm_size",
    "estimate_volatility", "monte_carlo", "SimPosition", "SimulationResult",
    "Signal", "signal_combiner", "price_signal", "imbalance_signal",
    "spread_signal", "momentum_signal", "flow_signal", "market_maker",
    "kelly_sizer", "kelly_sizer_with_liquidity",
    "ArbResult", "arb_scanner", "arb_sweep",
    "regime_signal", "feed_guard", "FeedHealthReport", "FeedHealthStatus",
    "inventory_skewer", "adaptive_spread", "SpreadMetrics",
    "execution_tracker", "ExecQuality", "cross_hedger",
    "MarkovRegimeModel", "prices_to_returns", "markov_regime",
    "shannon_entropy", "kl_divergence", "mutual_information",
    "kyles_lambda", "amihud_ratio", "roll_spread",
    "cornish_fisher_var", "cornish_fisher_cvar", "prediction_greeks",
    "information_coefficient", "signal_half_life", "hurst_exponent",
    "variance_ratio", "deflated_sharpe",
    "VpinDetector", "CusumDetector", "OfiTracker",
    "toxic_flow", "microstructure", "change_detector",
    "strategy_significance", "signal_diagnostics", "market_efficiency",
    "StressScenario", "ScenarioResult", "StressTestResult", "stress_test",
    "CPCVResult", "cpcv", "probability_of_overfitting",
    "CalibrationResult", "calibration_curve", "log_loss", "edge_decay",
    "FeedMetrics", "BacktestResult", "Metrics", "compute_metrics",
    "MetricsCollector", "MetricsServer", "update_engine_metrics",
    "AlertManager", "AlertType", "Alert", "WebhookChannel", "LogChannel",
    "PriceAggregator", "AggregatedPrice",
    "CalibrationTracker",
    "Trade", "PolymarketPosition", "Holder", "WalletProfile", "MarketFlow",
    "get_market_trades", "get_wallet_trades", "get_wallet_positions",
    "get_wallet_value", "get_top_holders", "get_wallet_profile",
    "analyze_market_flow",
    "TWAP", "VWAP", "Iceberg", "ExecAlgo",
    "walk_forward", "WalkForwardResult", "WalkForwardWindow",
    "fetch_prices", "fetch_trades", "ohlc_to_ticks",
    "register_expiry", "register_market_expiries", "check_lifecycle",
    "time_to_expiry", "approaching_expiry",
    "simulate", "quotes",
]:
    has = hasattr(horizon, name)
    check(f"export: {name}", has)


print("\n" + "=" * 60)
print("TEST: hz.quotes() helper")
print("=" * 60)

qs = horizon.quotes(0.55, 0.04)
check("quotes returns list", isinstance(qs, list))
q = qs[0]
check("quotes bid", abs(q.bid - 0.53) < 0.001, f"got {q.bid}")
check("quotes ask", abs(q.ask - 0.57) < 0.001, f"got {q.ask}")
check("quotes size default 5", q.size == 5.0)

qs2 = horizon.quotes(0.50, 0.10, size=20.0)
check("quotes custom size", qs2[0].size == 20.0)


print("\n" + "=" * 60)
print("TEST: Rust math functions")
print("=" * 60)

k = horizon.kelly(0.6, 0.5)
check("kelly(0.6, 0.5) positive", k > 0, f"got {k}")
check("kelly(0.6, 0.5) value", abs(k - 0.2) < 0.01, f"got {k}")
k_no = horizon.kelly_no(0.6, 0.5)
check("kelly_no returns float", isinstance(k_no, float))
fk = horizon.fractional_kelly(0.6, 0.5, 0.25)
check("fractional_kelly", abs(fk - k * 0.25) < 0.01)
ks = horizon.kelly_size(0.6, 0.5, 1000.0, 0.25, 100.0)
check("kelly_size positive", ks > 0)
check("kelly_size <= max_size", ks <= 100.0)
mk = horizon.multi_kelly([0.6, 0.7], [0.5, 0.4], 100.0)
check("multi_kelly returns list", isinstance(mk, list))
check("multi_kelly length 2", len(mk) == 2)
lak = horizon.liquidity_adjusted_kelly(0.6, 0.5, 1000.0, 0.25, 50.0, 100.0)
check("liquidity_adjusted_kelly", lak > 0 and lak <= 100.0)
e = horizon.edge(0.6, 0.5)
check("edge(0.6, 0.5) == 0.1", abs(e - 0.1) < 0.001)

cs = horizon.combine_signals([(0.6, 1.0), (0.8, 1.0)], "weighted_avg")
check("combine_signals", abs(cs - 0.7) < 0.001)
ema_val = horizon.ema([1.0, 2.0, 3.0, 4.0, 5.0], 3)
check("ema reasonable", 3.0 < ema_val < 5.0)
z = horizon.zscore(100.0, 2.5, 1.29)  # zscore(value, mean, std)
check("zscore large for outlier", z > 1.0, f"got {z}")
dw = horizon.decay_weight(60.0, 300.0)
check("decay_weight in (0,1)", 0 < dw < 1)

rp = horizon.reservation_price(0.5, 10.0, 0.5, 0.02, 1.0)
check("reservation_price float", isinstance(rp, float))
os_val = horizon.optimal_spread(0.02, 10.0, 0.5, 1.5, 1.0)
check("optimal_spread positive", os_val > 0)
cs_val = horizon.competitive_spread(0.04, 0.03, 0.0, 0.5)
check("competitive_spread positive", cs_val > 0)
bid_sz, ask_sz = horizon.mm_size(5.0, 10.0, 100.0, 0.3, 0.5)
check("mm_size bid > 0", bid_sz > 0)
check("mm_size ask > 0", ask_sz > 0)
vol = horizon.estimate_volatility([0.50, 0.51, 0.49, 0.52, 0.48, 0.53])
check("estimate_volatility > 0", vol > 0)

sp = horizon.SimPosition(market_id="m1", side="yes", size=10.0, entry_price=0.5, current_price=0.55)
sim = horizon.monte_carlo([sp], 1000, None, 42)
check("monte_carlo mean_pnl float", isinstance(sim.mean_pnl, float))
check("monte_carlo win_prob in [0,1]", 0 <= sim.win_probability <= 1)

se = horizon.shannon_entropy(0.5)
check("shannon_entropy(0.5) ~ 1.0", abs(se - 1.0) < 0.01)
kld = horizon.kl_divergence([0.3, 0.4, 0.3], [0.25, 0.5, 0.25])
check("kl_divergence >= 0", kld >= 0, f"got {kld}")

returns_data = [0.01, -0.02, 0.03, -0.01, 0.02, -0.03, 0.01]
volumes_data = [100.0, 150.0, 120.0, 80.0, 200.0, 90.0, 110.0]
signed_vols = [v * (1.0 if r >= 0 else -1.0) for r, v in zip(returns_data, volumes_data)]
kl_val = horizon.kyles_lambda(returns_data, signed_vols)
check("kyles_lambda float", isinstance(kl_val, float))
ar = horizon.amihud_ratio(returns_data, volumes_data)
check("amihud_ratio float", isinstance(ar, float))
rs = horizon.roll_spread(returns_data)
check("roll_spread float", isinstance(rs, float))

returns_for_cf = [0.01 * (i % 3 - 1) for i in range(100)]
cf_var = horizon.cornish_fisher_var(returns_for_cf, 0.05)
check("cornish_fisher_var float", isinstance(cf_var, float))
cf_cvar = horizon.cornish_fisher_cvar(returns_for_cf, 0.05)
check("cornish_fisher_cvar float", isinstance(cf_cvar, float))

pg = horizon.prediction_greeks(0.5, 10.0, True, t_hours=24.0, vol=0.2)
check("prediction_greeks", isinstance(pg, horizon.PredictionGreeks))
check("prediction_greeks.delta", isinstance(pg.delta, float))

ic = horizon.information_coefficient([0.5, 0.6, 0.7, 0.8], [0.0, 1.0, 1.0, 1.0])
check("information_coefficient float", isinstance(ic, float))

long_prices = [0.5 + 0.01 * math.sin(i * 0.1) for i in range(100)]
h = horizon.hurst_exponent(long_prices)
check("hurst_exponent in [0,1]", 0.0 <= h <= 1.0)

vr = horizon.variance_ratio(returns_for_cf, 2)
check("variance_ratio float", isinstance(vr, float))

ds = horizon.deflated_sharpe(1.5, 252, 10, 0.0, 3.0)
check("deflated_sharpe in [0,1]", 0 <= ds <= 1)

bt = horizon.bonferroni_threshold(0.05, 10)
check("bonferroni_threshold", abs(bt - 0.005) < 0.001)

bh = horizon.benjamini_hochberg([0.01, 0.03, 0.04, 0.5], 0.05)
check("benjamini_hochberg list", isinstance(bh, list))

vpin = horizon.VpinDetector(100.0, 50)
check("VpinDetector update float", isinstance(vpin.update(0.5, 10.0, True), float))
cusum = horizon.CusumDetector(5.0, 0.0)
check("CusumDetector update bool", isinstance(cusum.update(1.0), bool))
ofi_obj = horizon.OfiTracker()
check("OfiTracker update float", isinstance(ofi_obj.update(10.0, 5.0), float))

cal = horizon.calibration_curve([0.3, 0.5, 0.7, 0.9], [0.0, 0.0, 1.0, 1.0], 4)
check("calibration_curve has ece", isinstance(cal.ece, float))
ll = horizon.log_loss([0.3, 0.5, 0.7, 0.9], [0.0, 0.0, 1.0, 1.0])
check("log_loss positive", ll > 0)
ed = horizon.edge_decay(
    entry_prices=[0.4, 0.5, 0.6, 0.3, 0.7],
    outcomes=[1.0, 1.0, 0.0, 1.0, 0.0],
    entry_ts=[100.0, 200.0, 300.0, 400.0, 500.0],
    resolution_ts=[200.0, 300.0, 400.0, 500.0, 600.0],
    n_buckets=3,
)
check("edge_decay", isinstance(ed, horizon.EdgeDecayResult))

mrm = horizon.MarkovRegimeModel(n_states=2)
check("MarkovRegimeModel not trained", not mrm.is_trained())
returns_for_hmm = [0.01 * math.sin(i * 0.2) + 0.001 * (i % 5) for i in range(200)]
mrm.fit(returns_for_hmm, max_iters=50, tol=1e-4)
check("MarkovRegimeModel trained", mrm.is_trained())
probs = mrm.filter_step(0.01)
check("filter_step probs sum ~1", abs(sum(probs) - 1.0) < 0.01)

r2 = horizon.prices_to_returns([100.0, 101.0, 99.0, 102.0])
check("prices_to_returns length", len(r2) == 3)


print("\n" + "=" * 60)
print("TEST: analytics.py")
print("=" * 60)

from horizon.analytics import Metrics, compute_metrics
m = Metrics()
check("Metrics default", m.total_return == 0.0 and m.sharpe_ratio == 0.0)
equity = [(float(i), 1000.0 + i * 10.0) for i in range(100)]
metrics = compute_metrics(equity, [], 1000.0)
check("compute_metrics total_return > 0", metrics.total_return > 0)
check("compute_metrics sharpe > 0", metrics.sharpe_ratio > 0)
equity_dd = [(0.0, 1000.0), (1.0, 1100.0), (2.0, 900.0), (3.0, 1050.0)]
metrics_dd = compute_metrics(equity_dd, [], 1000.0)
check("compute_metrics dd detected", metrics_dd.max_drawdown > 0)


print("\n" + "=" * 60)
print("TEST: signals.py")
print("=" * 60)

from horizon.signals import Signal, signal_combiner, price_signal, imbalance_signal, spread_signal, momentum_signal, flow_signal
from horizon.context import Context, FeedData
from horizon._horizon import Market

ctx = Context(feeds={"btc": FeedData(price=0.55, bid=0.53, ask=0.57)}, market=Market(id="m1", name="M1", slug="m1"), params={})
ps = price_signal("btc", weight=0.5)
check("price_signal Signal", isinstance(ps, Signal))
check("price_signal value", abs(ps.fn(ctx) - 0.55) < 0.001)
check("imbalance_signal", isinstance(imbalance_signal("btc").fn(ctx), float))
check("spread_signal in [0,1]", 0 <= spread_signal("btc").fn(ctx) <= 1)
check("momentum_signal float", isinstance(momentum_signal("btc").fn(ctx), float))
check("flow_signal float", isinstance(flow_signal("btc").fn(ctx), float))
combiner = signal_combiner([ps, imbalance_signal("btc")], method="weighted_avg")
check("signal_combiner in [0,1]", 0 <= combiner(ctx) <= 1)


print("\n" + "=" * 60)
print("TEST: mm.py market_maker")
print("=" * 60)

from horizon.mm import market_maker
mm_fn = market_maker(base_spread=0.04, gamma=0.5, size=5.0)
quotes = mm_fn(ctx)
check("market_maker returns list", isinstance(quotes, list))
if quotes:
    q = quotes[0]
    check("mm quote bid < ask", q.bid < q.ask)
    check("mm quote in range", 0.01 <= q.bid <= 0.99 and 0.01 <= q.ask <= 0.99)


print("\n" + "=" * 60)
print("TEST: kelly_sizer.py")
print("=" * 60)

from horizon.kelly_sizer import kelly_sizer, kelly_sizer_with_liquidity
ks_fn = kelly_sizer(fraction=0.25, bankroll=1000.0)
check("kelly_sizer positive for edge", ks_fn(ctx, 0.65) > 0)
ctx_bad = Context(feeds={"btc": FeedData(price=1.0)}, market=Market(id="m1", name="M1", slug="m1"), params={})
check("kelly_sizer 0 for price=1", ks_fn(ctx_bad, 0.65) == 0.0)
check("kelly_sizer_with_liquidity positive", kelly_sizer_with_liquidity()(ctx, 0.65) > 0)


print("\n" + "=" * 60)
print("TEST: inventory_skew.py")
print("=" * 60)

from horizon.inventory_skew import inventory_skewer
from horizon._horizon import Quote
skewer = inventory_skewer(max_skew=0.03, max_position=100.0)
quotes_in = [Quote(bid=0.45, ask=0.55, size=5.0)]
quotes_out = skewer(ctx, quotes_in)
check("skewer returns list of Quotes", isinstance(quotes_out, list) and len(quotes_out) > 0)


print("\n" + "=" * 60)
print("TEST: adaptive_spread.py")
print("=" * 60)

from horizon.adaptive_spread import adaptive_spread, SpreadMetrics
adapter = adaptive_spread()
result = adapter(ctx, quotes_in)
check("adaptive_spread returns list", isinstance(result, list))
check("SpreadMetrics injected", isinstance(ctx.params.get("spread_metrics"), SpreadMetrics))


print("\n" + "=" * 60)
print("TEST: execution_tracker.py")
print("=" * 60)

from horizon.execution_tracker import execution_tracker, ExecQuality
tracker_fn = execution_tracker(window=50)
ctx_track = Context(feeds={"btc": FeedData(price=0.55, bid=0.53, ask=0.57)}, market=Market(id="m1", name="M1", slug="m1"), params={"_recent_fills": []})
tracker_fn(ctx_track)
check("exec_quality injected", isinstance(ctx_track.params.get("exec_quality"), ExecQuality))


print("\n" + "=" * 60)
print("TEST: feed_guard.py")
print("=" * 60)

from horizon.feed_guard import feed_guard, FeedHealthReport
guard = feed_guard(stale_threshold=30.0, kill_threshold=60.0)
ctx_guard = Context(feeds={"btc": FeedData(price=0.55, timestamp=time.time())}, market=Market(id="m1", name="M1", slug="m1"), params={})
guard(ctx_guard)
fh = ctx_guard.params["feed_health"]
check("FeedHealthReport not stale", not fh.all_stale)

ctx_stale = Context(feeds={"btc": FeedData(price=0.55, timestamp=time.time() - 100)}, market=Market(id="m1", name="M1", slug="m1"), params={})
guard_stale = feed_guard(stale_threshold=30.0, kill_threshold=0.0)
guard_stale(ctx_stale)
check("Stale feed detected", ctx_stale.params["feed_health"].all_stale)


print("\n" + "=" * 60)
print("TEST: hedger.py, regime.py, markov.py, quant.py")
print("=" * 60)

from horizon.hedger import cross_hedger
check("cross_hedger returns list", isinstance(cross_hedger()(ctx, quotes_in), list))

from horizon.regime import regime_signal
rs_sig = regime_signal("btc")
check("regime_signal float", isinstance(rs_sig.fn(ctx), float))

from horizon.markov import markov_regime
mrm_fn = markov_regime(model=mrm, feed="btc", param_name="regime")
# First call sets _prev_price, second call computes log return and injects regime
ctx_mrm = Context(feeds={"btc": FeedData(price=0.55, bid=0.53, ask=0.57)}, market=Market(id="m1", name="M1", slug="m1"), params={})
mrm_fn(ctx_mrm)  # First call: sets _prev_price, no regime yet
ctx_mrm2 = Context(feeds={"btc": FeedData(price=0.56, bid=0.54, ask=0.58)}, market=Market(id="m1", name="M1", slug="m1"), params={})
mrm_fn(ctx_mrm2)  # Second call: computes log return, injects regime
check("markov_regime injects regime", "regime" in ctx_mrm2.params, f"params keys: {list(ctx_mrm2.params.keys())}")

from horizon.quant import toxic_flow, microstructure, change_detector, strategy_significance, signal_diagnostics, market_efficiency
check("toxic_flow dict", isinstance(toxic_flow("btc")(ctx), dict))
check("microstructure dict", isinstance(microstructure("btc")(ctx), dict))
check("change_detector dict", isinstance(change_detector()(ctx), dict))
ss_r = strategy_significance({"equity_curve": list(range(100, 120))}, n_trials=10)
check("strategy_significance dict", isinstance(ss_r, dict) and "is_significant" in ss_r)
check("signal_diagnostics dict", isinstance(signal_diagnostics([0.5, 0.6, 0.7], [0.0, 1.0, 1.0]), dict))
check("market_efficiency dict", isinstance(market_efficiency([0.5, 0.51, 0.49, 0.52] * 25), dict))


print("\n" + "=" * 60)
print("TEST: stress.py")
print("=" * 60)

from horizon.stress import stress_test
positions = [
    horizon.SimPosition(market_id="m1", side="yes", size=10.0, entry_price=0.5, current_price=0.6),
    horizon.SimPosition(market_id="m2", side="no", size=5.0, entry_price=0.3, current_price=0.4),
]
st = stress_test(positions, n_simulations=1000, seed=42)
check("stress_test 4 scenarios", len(st.scenarios) == 4)
check("stress_test worst_scenario", st.worst_scenario != "")
check("stress_test summary dict", isinstance(st.summary(), dict))


print("\n" + "=" * 60)
print("TEST: cpcv.py")
print("=" * 60)

from horizon.cpcv import cpcv, probability_of_overfitting, CPCVResult
def factory(params):
    mult = params.get("mult", 1.0)
    def strat(data):
        if not data or len(data) < 2: return 0.0
        mean = sum(data) / len(data)
        std = (sum((x - mean)**2 for x in data) / len(data)) ** 0.5
        return (mean / std) * mult if std > 0 else 0.0
    return strat

cpcv_r = cpcv([0.01 * math.sin(i * 0.1) + 0.005 for i in range(200)], factory, [{"mult": 0.5}, {"mult": 1.0}, {"mult": 2.0}], n_groups=4)
check("cpcv oos_sharpes populated", len(cpcv_r.oos_sharpes) > 0)
check("cpcv pbo in [0,1]", 0 <= cpcv_r.pbo <= 1)


print("\n" + "=" * 60)
print("TEST: calibration.py")
print("=" * 60)

from horizon.calibration import CalibrationTracker
ct = CalibrationTracker(":memory:")
ct.log_prediction("mkt1", 0.8)
ct.log_prediction("mkt2", 0.3)
ct.resolve_market("mkt1", True)
ct.resolve_market("mkt2", False)
check("brier_score >= 0", ct.brier_score() >= 0)
check("log_loss >= 0", ct.log_loss() >= 0)
report = ct.report()
check("report.n_predictions == 2", report.n_predictions == 2)
check("report.n_resolved == 2", report.n_resolved == 2)
ct.close()


print("\n" + "=" * 60)
print("TEST: alerts.py")
print("=" * 60)

from horizon.alerts import AlertManager, AlertType, LogChannel
mgr = AlertManager(channels=[LogChannel()], max_alerts_per_window=5)
check("alert sent", mgr.alert(AlertType.FILL, "Test") is True)
check("recent_alerts has 1", len(mgr.recent_alerts) == 1)
for _ in range(10):
    mgr.alert(AlertType.CUSTOM, "spam")
time.sleep(0.1)
check("throttle works", mgr.alert(AlertType.CUSTOM, "throttled") is False)
mgr.clear_history()
check("clear_history", len(mgr.recent_alerts) == 0)


print("\n" + "=" * 60)
print("TEST: metrics.py")
print("=" * 60)

from horizon.metrics import MetricsCollector
mc = MetricsCollector()
mc.counter("test_counter").inc(5)
check("counter value 5", mc.counter("test_counter").value == 5.0)
mc.gauge("test_gauge").set(42.0)
check("gauge value 42", mc.gauge("test_gauge").value == 42.0)
mc.histogram("test_hist").observe(0.1)
rendered = mc.render()
check("render has counter", "test_counter" in rendered)
check("render has gauge", "test_gauge" in rendered)


print("\n" + "=" * 60)
print("TEST: lifecycle.py")
print("=" * 60)

from horizon.lifecycle import register_expiry, time_to_expiry, approaching_expiry
engine_lc = horizon.Engine(db_path=None)
engine_lc.set_daily_baseline(0.0)
check("register_expiry ISO", register_expiry(engine_lc, "mkt1", "2030-01-01T00:00:00Z") is True)
check("time_to_expiry > 0", time_to_expiry(engine_lc, "mkt1") > 0)
check("approaching_expiry False (far)", approaching_expiry(engine_lc, "mkt1") is False)
register_expiry(engine_lc, "mkt2", time.time() + 60)
check("approaching_expiry True (near)", approaching_expiry(engine_lc, "mkt2", lead_secs=300.0) is True)
check("register_expiry None", register_expiry(engine_lc, "mkt3", None) is False)


print("\n" + "=" * 60)
print("TEST: simulate.py")
print("=" * 60)

from horizon.simulate import simulate
sim_r = simulate(positions=[horizon.SimPosition(market_id="m1", side="yes", size=10.0, entry_price=0.5, current_price=0.6)], scenarios=5000, seed=42)
check("simulate mean_pnl float", isinstance(sim_r.mean_pnl, float))
check("simulate empty works", simulate(positions=[], scenarios=100) is not None)


print("\n" + "=" * 60)
print("TEST: backtest.py")
print("=" * 60)

from horizon.backtest import backtest, Tick
data = [{"timestamp": float(i), "price": 0.5 + 0.001 * math.sin(i * 0.1), "bid": 0.49 + 0.001 * math.sin(i * 0.1), "ask": 0.51 + 0.001 * math.sin(i * 0.1)} for i in range(50)]
def simple_quoter(ctx):
    fd = next(iter(ctx.feeds.values()), None)
    if fd and fd.price > 0:
        return [horizon.Quote(bid=fd.price - 0.02, ask=fd.price + 0.02, size=1.0)]
    return []
bt_r = backtest(name="test", data=data, pipeline=[simple_quoter], initial_capital=1000.0)
check("backtest equity non-empty", len(bt_r.equity_curve) > 0)
check("backtest metrics", bt_r.metrics is not None)
check("backtest summary", len(bt_r.summary()) > 50)


print("\n" + "=" * 60)
print("TEST: result.py, data.py, exchanges.py")
print("=" * 60)

br = horizon.BacktestResult(equity_curve=[(0.0, 1000.0), (1.0, 1050.0), (2.0, 980.0), (3.0, 1100.0)], initial_capital=1000.0)
check("BacktestResult total_return", br.metrics.total_return == 100.0)
check("pnl_by_market dict", isinstance(br.pnl_by_market(), dict))

from horizon.data import _parse_ts
check("_parse_ts float", _parse_ts(12345.0) == 12345.0)
check("_parse_ts ISO", _parse_ts("2024-01-01T00:00:00Z") > 0)

from horizon.exchanges import Polymarket, Kalshi
poly = Polymarket(private_key="0xtest", clob_url="https://test.com")
check("Polymarket private_key", poly.private_key == "0xtest")
kalshi_ex = Kalshi(email="test@test.com", password="pass123")
check("Kalshi email", kalshi_ex.email == "test@test.com")


print()
print("=" * 60)
print("SUMMARY")
print("=" * 60)
if errors:
    print(f"\n{len(errors)} ERRORS:")
    for e in errors:
        print(f"  {e}")
    sys.exit(1)
else:
    print(f"\nAll module tests PASSED")
