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