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 _:
- Price direction (optional):
P_UP,P_DNorP_FLvs. the previous trade. - Relative volume band (always):
V0…Vn. Trade size divided by the rolling average (window you choose) is bucketed by your band edges — n edges give n+1 bands. - Aggressor side (optional):
A_B(buyer) orA_S(seller).
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)
- Fields: price change % (with lookback window), relative volume (with rolling window), trade size, trade value (price×qty), price level, aggressor side.
- Operators:
>= > <= < == !=(aggressor: ==/!= buy/sell). - No match? By default nothing is emitted — patterns then describe sequences of events (“PUP then BIGBUY”), regardless of the trades in between. Optionally emit a default token (e.g. OTHER) to make every trade count.
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,} # repetitionMatching 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.