---
title: "Composite Scanner"
description: "Meta-scanner that scores all arbitrage methods and routes capital to the best opportunities."
icon: "layer-group"
---

<Warning>
  **Ultra Feature.** Requires an Ultra subscription. [Get started at api.mathematicalcompany.com](https://api.mathematicalcompany.com)
</Warning>

# Composite Arbitrage Scanner

A meta-scanner that runs all configured arbitrage methods, scores opportunities, and routes capital to the best ones. The ultimate arbitrage tool for running a full arb desk.

## How It Works

1. Configure which arb methods to run with weights and capital limits
2. Each cycle, the composite scanner checks `ctx.params` for results from individual scanners
3. Score each opportunity: `score = (net_edge / cost) * confidence * weight * (1 - utilization)`
4. Rank and execute the top-N (limited by `max_concurrent` and `total_capital`)

## ArbMethodConfig

```python
method = hz.ArbMethodConfig(
    method="parity",          # Method name
    weight=1.0,               # Scoring weight
    max_capital=500.0,        # Capital limit for this method
    enabled=True,             # Enable/disable
    kwargs={"size": 50.0},    # Extra parameters
)
```

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `method` | `str` | required | `"parity"`, `"cross_exchange"`, `"event"`, `"spread"`, `"stat"`, `"mm"`, `"latency"` |
| `weight` | `float` | `1.0` | Scoring weight multiplier |
| `max_capital` | `float` | `500.0` | Max capital for this method |
| `enabled` | `bool` | `True` | Whether to include in scans |
| `kwargs` | `dict` | `{}` | Extra parameters |

## Pipeline: composite_arb

```python
scanner = hz.composite_arb(
    methods=[
        hz.ArbMethodConfig(method="parity", weight=2.0),
        hz.ArbMethodConfig(method="cross_exchange", weight=1.5),
        hz.ArbMethodConfig(method="event", weight=1.0),
        hz.ArbMethodConfig(method="spread", weight=0.8),
        hz.ArbMethodConfig(method="stat", weight=1.0),
        hz.ArbMethodConfig(method="latency", weight=2.0),
    ],
    total_capital=1000.0,
    max_concurrent=3,
    rebalance_interval=60.0,
    auto_execute=False,
    cooldown=5.0,
)
```

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `methods` | `list[ArbMethodConfig]` | required | Methods to run |
| `total_capital` | `float` | `1000.0` | Total capital budget |
| `max_concurrent` | `int` | `3` | Max simultaneous positions |
| `rebalance_interval` | `float` | `60.0` | Seconds between rebalancing |
| `auto_execute` | `bool` | `False` | Auto-execute top opportunities |
| `cooldown` | `float` | `5.0` | Seconds between executions |

Stores `list[CompositeArbResult]` in `ctx.params["last_composite_arb"]`.

## CompositeArbResult

| Field | Type | Description |
|-------|------|-------------|
| `method` | `str` | Which arb method found the opportunity |
| `score` | `float` | Composite score |
| `net_edge` | `float` | Expected edge |
| `capital_needed` | `float` | Capital required |
| `details` | `dict` | Method-specific details |
| `timestamp` | `float` | Detection timestamp |

## Example: Full Arb Desk

```python
import horizon as hz

# Individual scanners feed results into ctx.params
parity = hz.parity_arb_scanner(
    "election-winner",
    exchange="polymarket",
    feed_name="polymarket",
    auto_execute=False,
)
cross = hz.arb_scanner(
    "election-winner",
    exchanges=["polymarket", "kalshi"],
    feed_map={"polymarket": "polymarket", "kalshi": "kalshi"},
)
spread = hz.spread_convergence(
    "gop-senate", "gop-house",
    "gop_senate_feed", "gop_house_feed",
)

# Composite scanner reads their results and decides what to execute
composite = hz.composite_arb(
    methods=[
        hz.ArbMethodConfig(method="parity", weight=2.0),
        hz.ArbMethodConfig(method="cross_exchange", weight=1.5),
        hz.ArbMethodConfig(method="spread", weight=0.8),
    ],
    total_capital=5000.0,
    max_concurrent=2,
    rebalance_interval=30.0,
    auto_execute=True,
)

hz.run(
    name="arb_desk",
    exchanges=[
        hz.Polymarket(private_key="0x..."),
        hz.Kalshi(api_key="..."),
    ],
    markets=["election-winner", "gop-senate", "gop-house"],
    feeds={
        "polymarket": hz.PolymarketBook("election-winner"),
        "kalshi": hz.KalshiBook("KXELECTION-25"),
        "gop_senate_feed": hz.PolymarketBook("gop-senate"),
        "gop_house_feed": hz.PolymarketBook("gop-house"),
    },
    pipeline=[parity, cross, spread, composite],
    interval=0.5,
)
```

<Note>
  The composite scanner reads results from individual scanners via `ctx.params`. Run individual scanners earlier in the pipeline so their results are available when the composite scanner runs.
</Note>
