Documentation

Everything you need to define patterns and consume signed match webhooks.

1. Alphabets — trades become tokens

An alphabet turns every live trade into exactly one token, assembled from up to three components joined by _:

Example with default config: P_UP_V3_A_B = price up, volume band 3, buyer-aggressed. The UI always shows the exact token set your config produces.

1b. Custom alphabets — your own symbols, your own margins

Instead of the classic scheme you can define each symbol yourself: pick a data field from the live stream, an operator and your own margin. Rules are evaluated top-down per trade; the first rule whose conditions all hold (AND) emits its symbol.

PUP     = price change ≥ 10%  (vs the price 500 trades back)
BIGBUY  = relative volume ≥ 2× (20-trade window)  AND  aggressor == buy
DUMP    = price change ≤ -5%  (vs 200 trades back)

2. Pattern language

Regex-like, over whole tokens (tokens are atomic — no sub-token matching):

P_UP_V3_A_B P_DN_V3_A_S      # sequence (space = then)
A | B                        # alternation
(A B)+                       # grouping + one-or-more
A*  A?  A{3}  A{2,5}  A{2,}  # repetition

Matching is streaming: the pattern fires on every accept, then resets (non-overlapping, leftmost-longest). Patterns are validated at save time by the same compiler the engine runs — what saves is what runs.

3. Webhook delivery

Every match POSTs JSON to your endpoint:

POST <your url>
X-Ictora-Signature: sha256=<hex hmac>
X-Ictora-Timestamp: <unix ms>

{
  "event": "match",
  "tenant": "…",
  "pattern": "(P_UP_V3_A_B)+",
  "instrument": "BTCUSDT",
  "price": 62761.93,
  "qty": 0.00024,
  "trade_id": 4010304129,
  "ts_event_ms": 1783603287787,   // exchange event time (the tick)
  "detected_ms": 1783603287896,   // our detection time — we report both, always
  "latency_ms": 109               // tick → detection (detected_ms − ts_event_ms)
}

4. Verifying the signature

The signature is HMAC-SHA256 of the raw request body with your channel secret. Node.js:

const crypto = require("crypto");

function verify(rawBody, signatureHeader, secret) {
  const expected = "sha256=" +
    crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
  return crypto.timingSafeEqual(
    Buffer.from(signatureHeader), Buffer.from(expected));
}

Python:

import hashlib, hmac

def verify(raw_body: bytes, signature_header: str, secret: str) -> bool:
    expected = "sha256=" + hmac.new(
        secret.encode(), raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(signature_header, expected)

Reject anything with a missing/invalid signature, and use X-Ictora-Timestamp to drop replays older than a few minutes.

5. Latency, honestly

detected_ms − ts_event_ms is exchange-to-detection latency: network transit plus microseconds of matching. Delivery to your endpoint adds your own network distance. We never blend the two numbers.