Docs
Getting started
MultiEdge Signal Relay is auditable signal-distribution infrastructure — not execution. The fastest way in is the Python SDK (multiedge-relay, Apache 2.0); everything it does is plain HTTP you can also call directly — see the API reference.
1 · Install
$ uv add multiedge-relay 2 · Publish a signal
client_signal_id is your idempotency key: retries return the original ack (200, duplicate: true) instead of publishing twice. The ack carries the gapless per-strategy sequence.
from multiedge_relay import Signal, SignalPublisher
relay = SignalPublisher(api_key="mesk_...")
ack = relay.publish(Signal(strategy_id="str_dm_rotation",
client_signal_id="2026-08-14-close", # safe to retry
payload=rebalance)) # 201, or 200 duplicate 3 · Subscribe with catch-up
The SDK keeps a durable cursor. On start it gap-fills over REST from that cursor, dedupes the overlap with the live stream by sequence, and only then goes live — every signal, in order, no matter how long you were offline. Delivery is at-least-once end to end: after a crash the uncommitted tail is redelivered, which is exactly what the state store in the next step is for.
from multiedge_relay import SignalSubscriber
def handle(signal, meta):
store(signal) # live AND caught-up signals, in sequence order
sub = SignalSubscriber(api_key="mesk_...",
strategy_id="str_dm_rotation",
on_signal=handle)
sub.run() # resumes from the persisted cursor, gap-fills, then goes live 4 · Process exactly once
SqliteStateStore records which signal_ids your handler has completed in one local SQLite file, and commits that marker atomically with your handler's success — so crash redelivery, reconnect overlap, webhook retries, and operator replays never run it twice. State you write through the store's transaction is exactly-once; external side effects keep only a microscopic redelivery window. The file prunes and vacuums itself, staying tiny.
from multiedge_relay import SignalSubscriber, SqliteStateStore
store = SqliteStateStore() # ~/.multiedge/state.db — stdlib sqlite3, no extra deps
sub = SignalSubscriber(api_key="mesk_...",
strategy_id="str_dm_rotation",
on_signal=store.exactly_once(handle), # once per signal_id
cursor_store=store) # cursor + dedup, one file
sub.run() # crash, restart, replay — your handler never runs twice for one signal 5 · Verify webhook signatures
Every webhook delivery is signed HMAC-SHA256 over "{unix_ts}." + raw body bytes with your per-endpoint secret. Verify over the raw received bytes — never re-serialize. The relay retries non-2xx deliveries up the ladder, so dedupe on signal_id — the same state store from step 4 does it in one line.
from multiedge_relay import verify_signature
@app.post("/webhooks/multiedge")
async def receive(request: Request):
body = await request.body() # RAW bytes — never re-parse before verifying
signal = verify_signature(body, dict(request.headers), WEBHOOK_SECRET)
# HMAC-SHA256 over "{ts}." + body; constant-time compare; 5-min freshness
with store.process(signal) as fresh: # same SqliteStateStore dedups retries
if fresh:
handle(signal)
return {"ok": True} # ACK duplicates too, so the retry ladder stops The full walkthrough — producer and consumer, line by line
Complete portfolios, HOLD for unchanged tickers, heartbeats, corrections with the :r2 convention, failure handling, and a safe apply loop — every sample validated against the relay's own schema.
From signal to broker — live execution at IBKR
The relay hands you the target book; your code owns execution. Build your own execution layer at Interactive Brokers with ib_async — exactly-once processing, idempotent rebalance-to-target, paper-first, hard guardrails — in about eighty lines of Python.
Sealed mode — end-to-end encryption
Per-strategy E2E encryption with post-quantum hybrid cryptography: the relay stores and forwards ciphertext it cannot read. Threat model, wire format, and full setup.
API reference
The full REST surface — publish, catch-up, deliveries, replay, endpoints, entitlements — as interactive OpenAPI 3.1.