Webhook-Rezepte
Alarme zeigen Ihnen, was der Markt tut. Mit Webhooks und Telegram handeln Sie danach, mit Ihren eigenen Tools und Ihren eigenen Konten. Wir handeln nie für Sie, verwahren nie Gelder und fragen nie nach API-Schlüsseln mit Handelsrechten.
Bevor Sie beginnen
- Fügen Sie auf der Seite Alarme einen Custom-JSON-Webhook hinzu. VIP Quant und die Testphase enthalten ihn.
- Kopieren Sie das Signatur-Secret. Es wird einmal angezeigt.
- Drücken Sie Test senden und prüfen Sie, ob Ihr Endpunkt ihn empfängt.
- Richten Sie eine Regel auf das Ziel. Der Standardalarm geht ebenfalls dorthin, solange er aktiv ist.
Antworten Sie innerhalb von 8 Sekunden mit einem beliebigen 2xx. Fehlgeschlagene Sendungen werden mit derselben id wiederholt, deduplizieren Sie also darüber. Jedes Snippet unten wurde gegen eine echte signierte Zustellung getestet. Sie sind für alles verantwortlich, was Sie auf Basis dieser Ereignisse automatisieren.
Ereignisse
opportunity.alert: Eine Route passte zu einer Regel oder zum Standardalarm.rule.idistdefaultbeim Standardalarm.listing.alert: Ein Markt wurde gerade gelistet, mit einem Ereignis pro Listing.opportunity.closed: Eine gemeldete Route hat geschlossen, falls die Regel das verlangt.position.alert: Bei einem getrackten Trade ist etwas passiert.notice.alert: Eine Börse hat ein Delisting, eine Aussetzung von Ein- oder Auszahlungen, eine Wartung oder ein Listing beim Standardalarm angekündigt.trackedbesagt, dass es einen getrackten Trade betrifft.watch.alert: Eine Wallet, die Sie beobachten, hat einen Transfer empfangen oder gesendet, beim Standardalarm.event.txist null, wenn der Transaktions-Hash nicht bekannt ist.alert.test: von Test senden, mit"test": trueund einem fiktiven Coin namens TEST.
{
"id": "evt_7c1e...",
"type": "listing.alert",
"test": false,
"sentAt": "2026-11-02T10:00:00.000Z",
"rule": { "id": "...", "name": "New listings" },
"listing": { "coin": "ABC", "venue": "Binance", "kind": "spot", "symbol": "ABCUSDT", "listedMs": 1793000000000 },
"window": { "sizeUsd": 1000, "openMs": 4000, "avgWindowMs": null, "avgPeakNetBps": null, "windowSamples": 0 },
"opportunity": { "id": "ABC|Binance:spot|OKX:spot", "coin": "ABC", "...": "..." }
}Alle Felder stehen in der Webhook-Doku.
Signatur prüfen
Jede Anfrage enthält X-ArbitrageX-Timestamp und X-ArbitrageX-Signature, das ist sha256= plus der Hex-HMAC-SHA256 von timestamp.rawBody. Prüfen Sie sie auf den rohen Bytes, vor dem Parsen, denn geparstes und neu kodiertes JSON passt nicht. Lehnen Sie Timestamps ab, die mehr als 5 Minuten von jetzt abweichen, um Replays zu verhindern.
Node.js
import { createHmac, timingSafeEqual } from "node:crypto";
export function verify(rawBody, headers, secret) {
const ts = headers["x-arbitragex-timestamp"];
if (!(Math.abs(Date.now() / 1000 - Number(ts)) <= 300)) return false;
const expected =
"sha256=" + createHmac("sha256", secret).update(`${ts}.${rawBody}`).digest("hex");
const given = Buffer.from(String(headers["x-arbitragex-signature"]));
const wanted = Buffer.from(expected);
return given.length === wanted.length && timingSafeEqual(given, wanted);
}Python
import hashlib
import hmac
import time
def verify(raw_body: bytes, headers: dict, secret: str) -> bool:
ts = headers.get("x-arbitragex-timestamp", "")
try:
if abs(time.time() - int(ts)) > 300:
return False
except ValueError:
return False
signed = ts.encode() + b"." + raw_body
expected = "sha256=" + hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected.encode(), headers.get("x-arbitragex-signature", "").encode())Go
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"math"
"net/http"
"strconv"
"time"
)
func Verify(rawBody []byte, headers http.Header, secret string) bool {
ts := headers.Get("X-ArbitrageX-Timestamp")
unix, err := strconv.ParseInt(ts, 10, 64)
if err != nil || math.Abs(float64(time.Now().Unix()-unix)) > 300 {
return false
}
mac := hmac.New(sha256.New, []byte(secret))
mac.Write([]byte(ts + "."))
mac.Write(rawBody)
expected := "sha256=" + hex.EncodeToString(mac.Sum(nil))
return hmac.Equal([]byte(expected), []byte(headers.Get("X-ArbitrageX-Signature")))
}Ausführungstools
Jedes Beispiel unten ist der Body, den ein Tool erwartet, geschrieben gegen unseren Standard-Payload: Jeder Wert in spitzen Klammern ist das Feld von uns, das dort hingehört. Bauen Sie ihn in einem Zapier- oder Make-Schritt nach einem Catch-Webhook oder in Ihrem eigenen Relay, etwa dem Empfänger unter Eigenes Skript ausführen, und senden Sie ihn an die Webhook-URL des Tools. Werte in Großbuchstaben sind Ihre eigenen. Nur opportunity.alert und listing.alert sollten ein Tool erreichen: Filtern Sie test und jeden anderen Typ heraus, damit eine Schließ-Meldung keinen Trade eröffnen kann. Wir handeln nie: Was ein Tool mit einer Nachricht macht, liegt bei Ihnen und dem Tool.
Jedes Tool ändert seine Felder mit der Zeit. Prüfen Sie ein Beispiel gegen die aktuelle Doku des Tools, bevor Sie sich darauf verlassen.
3Commas signal bot
Für den Custom-Webhook eines 3Commas-Signal-Bots, mit Secret und Bot-UUID aus den Nachrichteneinstellungen des Bots. Ein Signal-Bot handelt auf einer Börse, beschränken Sie die Regel also mit ihrem Börsenfilter auf Routen, die dort kaufen.
{
"secret": "YOUR_3COMMAS_SECRET",
"bot_uuid": "YOUR_BOT_UUID",
"max_lag": "300",
"timestamp": "<sentAt>",
"trigger_price": "<opportunity.buy.price>",
"tv_exchange": "<opportunity.buy.venue in capitals, e.g. BINANCE>",
"tv_instrument": "<opportunity.coin>USDT",
"action": "enter_long"
}Hummingbot
Hummingbot nimmt Befehle über seine eigenen Schnittstellen statt über einen Webhook an, daher geht dies an eine kleine Skript-Strategie von Ihnen oder an eine Brücke zu seiner MQTT-Schnittstelle. Verwenden Sie den Namen Ihres eigenen Connectors.
{
"connector": "<opportunity.buy.venue in lower case, e.g. binance>",
"trading_pair": "<opportunity.coin>-USDT",
"side": "BUY",
"price": <opportunity.buy.price>,
"amount_usd": <window.sizeUsd>,
"hedge_connector": "<opportunity.sell.venue in lower case>",
"hedge_price": <opportunity.sell.price>,
"net_bps": <opportunity.netBps>,
"event_id": "<id>"
}Cornix signal
Klartext im Signal-Layout von Cornix, für die Webhook-Adresse, die Cornix einem TradingView-Bot gibt. Setzen Sie Ihren eigenen Stop und prüfen Sie das Layout gegen die aktuelle Anleitung von Cornix.
#<opportunity.coin>/USDT Exchanges: <opportunity.buy.venue> Signal Type: Regular (Long) Entry Targets: 1) <opportunity.buy.price> Take-Profit Targets: 1) <opportunity.sell.price> Stop Targets: 1) YOUR_STOP_PRICE
TradingView-style JSON
Die Feldnamen, die ein TradingView-Alarm sendet, für Tools, die TradingView-Alarme empfangen.
{
"ticker": "<opportunity.coin>USDT",
"exchange": "<opportunity.buy.venue in capitals>",
"action": "buy",
"price": <opportunity.buy.price>,
"time": "<sentAt>",
"message": "<opportunity.coin> <opportunity.buy.venue> to <opportunity.sell.venue>, net <opportunity.netBps> bps"
}Flat JSON
Jeder Wert auf oberster Ebene, für eine Tabelle, n8n oder eine Datenbank.
{
"id": "<id>",
"coin": "<opportunity.coin>",
"net_bps": <opportunity.netBps>,
"net_usd_1k": <opportunity.netUsd.1000>,
"max_size_usd": <opportunity.maxSizeUsd>,
"buy_exchange": "<opportunity.buy.venue>",
"buy_price": <opportunity.buy.price>,
"buy_url": "<opportunity.buy.url>",
"sell_exchange": "<opportunity.sell.venue>",
"sell_price": <opportunity.sell.price>,
"sell_url": "<opportunity.sell.url>"
}Weitere Kanäle
Jeder Kanal unten wird auf der Seite Alarme eingerichtet, hat eine Schaltfläche Test senden und liest sich wie die Telegram-Nachricht, in der Sprache des Kontos. Regeln, Abklingzeiten, Ruhezeiten, das Rate-Limit und die automatische Pause funktionieren auf allen gleich.
- Slack (ab Pro Desk): Fügen Sie in Slack die App Incoming Webhooks zu einem Kanal hinzu und fügen Sie die
hooks.slack.com-URL ein, die sie Ihnen gibt. - ntfy (ab Starter): kostenlose Benachrichtigungen auf Handy und Desktop. Fügen Sie eine Topic-URL wie
https://ntfy.sh/your-topicoder eine auf Ihrem eigenen Server ein und abonnieren Sie dasselbe Topic in der ntfy-App. Wer den Namen eines öffentlichen Topics kennt, kann es lesen, wählen Sie also einen langen, zufälligen Namen oder fügen Sie für ein geschütztes Topic ein Zugriffstoken hinzu. Jeder Alarm hat Schaltflächen für beide Börsen. - E-Mail-Zusammenfassung (ab Starter): eine Mail an die Adresse Ihres Kontos mit jedem Alarm seit der letzten, jede volle Stunde oder jeden Tag um 08:00 UTC. Gibt es nichts Neues, wird nichts gesendet.
WhatsApp bietet keinen kostenlosen Weg, über den ein Dienst Ihnen schreiben kann, daher gibt es das nicht. Für alles andere ist ein eigener Webhook einfaches JSON über https.
An Slack senden
Ein Cloudflare Worker, ganz ohne eigenen Server. Legen Sie ARBITRAGEX_SECRET und SLACK_WEBHOOK_URL als Secrets an und verwenden Sie dann die URL des Workers als Ziel. Derselbe Code läuft überall mit Web Crypto.
async function verify(rawBody, headers, secret) {
const ts = headers.get("x-arbitragex-timestamp");
if (!(Math.abs(Date.now() / 1000 - Number(ts)) <= 300)) return false;
const encoder = new TextEncoder();
const key = await crypto.subtle.importKey(
"raw", encoder.encode(secret), { name: "HMAC", hash: "SHA-256" }, false, ["verify"],
);
const given = (headers.get("x-arbitragex-signature") ?? "").replace("sha256=", "");
if (!/^[0-9a-f]{64}$/.test(given)) return false;
const bytes = Uint8Array.from(given.match(/../g), (pair) => parseInt(pair, 16));
return crypto.subtle.verify("HMAC", key, bytes, encoder.encode(`${ts}.${rawBody}`));
}
export default {
async fetch(request, env) {
const rawBody = await request.text();
if (!(await verify(rawBody, request.headers, env.ARBITRAGEX_SECRET))) {
return new Response("bad signature", { status: 401 });
}
const event = JSON.parse(rawBody);
if (event.type === "opportunity.alert" || event.type === "listing.alert") {
const { coin, buy, sell, netBps } = event.opportunity;
await fetch(env.SLACK_WEBHOOK_URL, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
text: `${coin}: ${buy.venue} to ${sell.venue}, net ${netBps.toFixed(1)} bps`,
}),
});
}
return new Response(null, { status: 204 });
},
};Tabelle führen
Hängt pro Alarm eine Zeile an eine CSV an, die Sie in jeder Tabellenkalkulation öffnen können, nur mit der Python-Standardbibliothek. Stellen Sie es hinter https: Wir senden nur an öffentliche https-Adressen.
import csv
import json
from http.server import BaseHTTPRequestHandler, HTTPServer
import hashlib
import hmac
import time
def verify(raw_body: bytes, headers: dict, secret: str) -> bool:
ts = headers.get("x-arbitragex-timestamp", "")
try:
if abs(time.time() - int(ts)) > 300:
return False
except ValueError:
return False
signed = ts.encode() + b"." + raw_body
expected = "sha256=" + hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected.encode(), headers.get("x-arbitragex-signature", "").encode())
SECRET = "your signing secret"
seen = set()
class Receiver(BaseHTTPRequestHandler):
def do_POST(self):
raw_body = self.rfile.read(int(self.headers["Content-Length"]))
headers = {key.lower(): value for key, value in self.headers.items()}
if not verify(raw_body, headers, SECRET):
self.send_response(401)
self.end_headers()
return
self.send_response(204)
self.end_headers()
event = json.loads(raw_body)
if event["test"] or event["id"] in seen or "opportunity" not in event:
return
seen.add(event["id"])
row = event["opportunity"]
with open("alerts.csv", "a", newline="") as file:
csv.writer(file).writerow(
[event["sentAt"], event["type"], row["coin"], row["buy"]["venue"], row["sell"]["venue"], row["netBps"]]
)
if __name__ == "__main__":
HTTPServer(("127.0.0.1", 8000), Receiver).serve_forever()Eigenes Skript ausführen
Ein Empfänger ohne Abhängigkeiten, der sofort antwortet und jeden Alarm an eine Funktion von Ihnen übergibt. Was diese Funktion tut, schreiben und verantworten Sie selbst. Alarme sind Schätzungen aus Marktdaten, und eine Route kann schließen, bevor etwas ankommt, das Sie starten.
import { createServer } from "node:http";
import { createHmac, timingSafeEqual } from "node:crypto";
function verify(rawBody, headers, secret) {
const ts = headers["x-arbitragex-timestamp"];
if (!(Math.abs(Date.now() / 1000 - Number(ts)) <= 300)) return false;
const expected =
"sha256=" + createHmac("sha256", secret).update(`${ts}.${rawBody}`).digest("hex");
const given = Buffer.from(String(headers["x-arbitragex-signature"]));
const wanted = Buffer.from(expected);
return given.length === wanted.length && timingSafeEqual(given, wanted);
}
const seen = new Set();
function onAlert(event) {
// Your code goes here. You decide what happens next, and we never see it.
console.log(event.type, event.opportunity.coin, event.opportunity.netBps);
}
export const server = createServer((req, res) => {
const parts = [];
req.on("data", (part) => parts.push(part));
req.on("end", () => {
const rawBody = Buffer.concat(parts).toString("utf8");
if (!verify(rawBody, req.headers, process.env.ARBITRAGEX_SECRET)) {
return res.writeHead(401).end();
}
const event = JSON.parse(rawBody);
// Answer first: anything slower than 8 seconds counts as a failed delivery.
res.writeHead(204).end();
if (event.test || seen.has(event.id)) return;
seen.add(event.id);
if (event.type === "opportunity.alert" || event.type === "listing.alert") onAlert(event);
});
});Zapier und Make
Ein eigener Webhook ist ein einfacher JSON-POST, daher funktioniert eine Catch-Webhook-URL von beiden als Ziel. Fügen Sie einen Filterschritt auf type und test hinzu und ordnen Sie dann Felder wie opportunity.coin und opportunity.netBps zu. Diese Schritte prüfen unsere Signatur nicht für Sie, behandeln Sie die Catch-URL also wie ein Passwort und starten Sie dort nichts, was Sie nicht auch einen Fremden starten lassen würden.
Telegram
Wenn Sie Alarme nur auf dem Handy wollen, überspringen Sie all das. Drücken Sie auf der Seite Alarme Telegram verbinden und dann Start im sich öffnenden Chat. Siehe Alarm-Doku.