Skip to main content
Ultra Feature. Requires an Ultra subscription. Get started at api.mathematicalcompany.com

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

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
)
FieldTypeDefaultDescription
methodstrrequired"parity", "cross_exchange", "event", "spread", "stat", "mm", "latency"
weightfloat1.0Scoring weight multiplier
max_capitalfloat500.0Max capital for this method
enabledboolTrueWhether to include in scans
kwargsdict{}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,
)
ParameterTypeDefaultDescription
methodslist[ArbMethodConfig]requiredMethods to run
total_capitalfloat1000.0Total capital budget
max_concurrentint3Max simultaneous positions
rebalance_intervalfloat60.0Seconds between rebalancing
auto_executeboolFalseAuto-execute top opportunities
cooldownfloat5.0Seconds between executions
Stores list[CompositeArbResult] in ctx.params["last_composite_arb"].

CompositeArbResult

FieldTypeDescription
methodstrWhich arb method found the opportunity
scorefloatComposite score
net_edgefloatExpected edge
capital_neededfloatCapital required
detailsdictMethod-specific details
timestampfloatDetection 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.