Week 6 · Surveillance

Security Analytics — Building a SIEM

Your market emits a firehose of events; somewhere in it is one participant behaving badly — this week you build the pipeline that finds them, and you pay for every false alarm yourself.

3 hours streaming ingestion detection rules online anomaly detection alert latency HW5
What is it?

A real-time pipeline whose output is a human decision

A SIEM — security information and event management — is a streaming system that turns raw logs into a small number of things a person should look at. Structurally it is the same loop as week 3's trading system: events arrive, something decides, something acts. What changes is the actuator. The output of this pipeline is an analyst's attention, and that is the scarcest resource in the system.

Every SIEM, whether it costs nothing or millions, is these five stages. Each has its own cost, its own queue, and its own way of failing.

ingest

Read the source without losing it. Tail a file, consume a socket, replay a tape. Cheap per event, and the place where backpressure has to be decided.

parse

Text → structure. Regexes and JSON loads. Usually the most expensive stage, and the one that silently drops what it cannot match.

normalise

Structure → one schema. A common event model, so a rule can be written once and run over an order tape and an auth log alike.

detect

Rules and models over windows of normalised events. Where your accuracy-for-latency trade lives, exactly as in week 4.

alert

Emit, deduplicate, group, route. Rare and expensive per unit, and the only stage whose consumer is a person.

The arena already produces the tape: every session is recorded to sessions/session_*.jsonl and can be replayed deterministically with scripts/replay_session.py. So the same pipeline you build for an sshd auth log runs over your own market's order flow — which is the point of the normalise stage.

The pipeline, with its queues showing

widget 1 · simulator
stage keeping up utilisation above 80% bottleneck — queue growing, events dropping

Normalised events & alerts

Budget

Each stage is a single-server queue: service rate is 1 / cost, utilisation is ρ = λ / μ, and waiting time blows up as ρ → 1 — not at ρ = 1. Micro-batching amortises fixed per-pass overhead, so it raises throughput, and it adds a batch-fill wait, so it raises latency. That is the whole streaming-systems trade in one slider.
How does it work?

Rules first, and then the operating point they imply

A detection rule is a predicate over a window of normalised events. Three of them catch most market-manipulation patterns, and all three are simple arithmetic — the difficulty is never writing the rule, it is choosing the threshold.

Spoofing / layering

Post size you never intend to trade, to move the price, then cancel. The signature is a high cancel-to-fill ratio combined with size resting away from the touch. Honest market makers also cancel constantly — which is exactly why this rule generates false positives.

Wash trading / self-match

Trade with yourself to manufacture volume. The signature is a fill whose buyer and seller resolve to the same beneficial owner. Cheap to detect, and unambiguous — the rare rule that can run at near-perfect precision.

Quote stuffing

Flood the venue with messages to degrade everyone else's view. The signature is a message rate far above peers with no corresponding fills. Note the arena already meters this: exchange/limits.py answers RATE_LIMITED over quota.

Build the rules, then own the confusion matrix

widget 2 · rule builder

Confusion matrix

ParticipantMsgsCancels/fillMsg rateSelf-matchesRules firedOutcome
Eight participants over one 600-second session, seeded and labelled. One is a scripted spoofer, one stuffs quotes, one washes trades — and one is an honest market maker whose cancel-to-fill ratio is naturally enormous, because that is what quoting is. Find a threshold that catches the first three without accusing the fourth; if you cannot, say so in the incident report rather than shipping the false positive.

When you cannot write the rule

Rules need a pattern you already know. For everything else you need a detector that learns “normal” online and flags departures — a rolling z-score, an EWMA residual, a half-space tree. All of them expose the same single dial, and turning it is the entire engineering decision: lower the threshold and you detect sooner and cry wolf more often.

Detect the drift — and price the false alarms

widget 3 · detector

Detection latency vs false alarms

Detector log

600 ticks of one participant's message rate, anomaly injected at tick 350. The z-score uses a trailing window and never sees the future; EWMA weights recent history exponentially with an equivalent memory. “Consecutive breaches” is the cheapest false-alarm filter there is — and it buys its precision with latency, which is the same trade the threshold makes, from the other direction.
Why does it matter in trading?

Because an alert nobody reads is not a detection

A venue that cannot show a regulator how it surveils its own market has a business problem, not an engineering one. But the failure mode in practice is never “we had no detector” — it is “the detector fired, into a queue of nine thousand other alerts”. Precision is not a nicety. It is the constraint that decides whether your system functions at all.

Run the arithmetic once and it changes how you set thresholds forever. A million events a day, an alert on one in a thousand, and you have a thousand alerts. At ten minutes of triage each that is 167 analyst-hours a day against maybe twelve you actually have. The backlog is not a staffing gap; it is a design output, and you chose it when you picked the threshold.

Alert-fatigue calculator

widget 4 · calculator

Demand vs capacity

Coverage vs precision

Six productive hours per analyst per day, $75 an hour loaded. Coverage is capacity divided by demand, capped at 100%; the queue is worked in arrival order, so the share of true positives you actually see equals your coverage. Notice what the precision slider does to “cost per true positive” — that number, not recall, is what buys you a second analyst.
Common mistakes

Five ways a SIEM becomes decoration

All five ship. All five pass a demo. Each one turns a working detector into a dashboard nobody trusts.

Tuning for recall and calling it done

You lower the threshold until the known-bad tape is caught 100% of the time, ship it, and drown. Recall is trivially purchasable — alert on everything and you have perfect recall — so a recall number with no precision beside it carries no information. The honest artefact is an operating point: this threshold, this precision, this recall, this many alerts per hour, and a sentence saying why that is the right corner of the curve for this venue.

The fix. Report precision, recall and alert volume together, always. Then choose the point by its cost: widget 4 converts a precision figure into analyst-hours, and analyst-hours is the currency the decision is actually made in.

Detecting on event time, then joining on wall-clock time

Your window is “the last 60 seconds”, but which 60? Events carry a venue timestamp, arrive out of order, and your consumer stamps its own receipt time. Mix the two and a burst that happened inside one second gets spread across three windows, or worse, a replay of yesterday's tape lands in today's window and fires every rule at once.

The fix. Pick one clock per window, name it in the schema, and handle late arrivals explicitly — a bounded lateness allowance and a watermark, or drop and count. Deterministic replay (scripts/replay_session.py) is the test: the same tape must produce the same alerts, every time.

The parse stage that drops what it cannot match

A regex fails on a line format you did not anticipate, the exception is swallowed, and the event vanishes. Your detectors then run over a silently filtered stream, and the filter correlates with exactly the unusual behaviour you were trying to catch — the attacker's malformed input is the input your parser rejects.

The fix. Every stage counts what it dropped and why, and those counters are themselves monitored. A parse-failure rate that jumps is a security signal, not a housekeeping detail. Route unparsed lines to a dead-letter stream and look at it.

An unbounded queue in front of a slow detector

Detection costs 2 ms and events arrive every 0.5 ms, so the queue grows for ever. You drop nothing, which feels responsible, and your alert latency grows linearly with uptime: by hour three you are alerting on this morning. This is week 4's queue-up failure wearing a security hat, and it is worse here, because a late alert about an intrusion is not a degraded alert — it is a useless one.

The fix. Bound every queue and decide what happens when it fills: drop-oldest, sample, or shed to a cheaper detector. Then put alert latency on the dashboard next to alert count, and make an SLO out of it — p99 end-to-end, not the mean.

Alerting on the individual event instead of the pattern

One cancelled order is not spoofing. One failed SSH login is not a brute-force attempt. Rules written per event produce a stream of individually meaningless alerts, and the analyst becomes the aggregation layer — badly, at three in the morning. The manipulation lives in the ratio and the rate, which are properties of a window, not of a record.

The fix. Make the window a first-class part of the rule, and alert on the entity, not the event: one alert per participant per window, with the evidence attached and a count. Then deduplicate and group before routing — an incident, not a thousand notifications.

Check yourself

Three questions

Instant feedback, nothing recorded. Pick the best answer.

Question 1
Your cancel-to-fill rule flags a participant with a ratio of 340×. Investigation shows it is an honest market maker. What does this tell you?
Question 2
Events arrive every 0.5 ms. Your detector takes 2 ms per event and sits behind an unbounded in-memory queue. What is the failure you should predict?
Question 3
800 alerts a day, 9 minutes of triage each, two analysts at six productive hours. What fraction of the true positives does the team actually see, and what is the lever?
Take it further

This week's work

Stream your own arena session tape through a parser, three rule detectors and one online anomaly model. Then defend an operating point and write the incident report on a seeded manipulation tape — what fired, what it missed, and what you would have told the venue to do.