Reading the Tape
SMA, Bollinger bands, RSI, volume & volatility — from intuition to executable BAZ.HFT rulesOn the typography. Like its sibling modules, this page keeps its Tufte CSS conventions — a narrow measure, numbered sidenotes, a wide right margin for glosses — reflowed onto the RAGBAZ token sheet. Every chart below is live: it is drawn in your browser from a synthetic price tape you control. Terms carry a margin gloss and link to the glossary. Markets print a stream of numbers — price, volume, time — and every trading idea ever written is a way of reading that stream. This module builds the five classic instruments for reading it (moving averages, Bollinger bands, RSI, volume, volatility), shows the two opposite temperaments a strategy can have (riding the waves and trading the noise), and then writes both temperaments as real, compilable BAZ.HFT rules — the same files the BAZ trading stack executes.
A moving average is a memory. A Bollinger band is a tolerance. RSI is a speedometer. Volume is a show of hands. Volatility is the weather. Strategies are just sentences built from these five words.
1 · The two moods of a market
Regime — a stretch of time in which the market keeps one statistical personality: trending (returns correlate positively) or mean-reverting (they correlate negatively). Most strategy failures are regime mismatches, not bad indicators. Zoom into any price chart and you will find two personalities taking turns. Sometimes price goes somewhere: buyers (or sellers) stay in charge for hours and each step tends to continue the last one — a trend. The rest of the time price goes nowhere: it oscillates around a fair value, overshooting and snapping back — chop, or noise. The tragedy of trading is that the best strategy for one mood is close to the worst for the other. A trend-follower gets whipsawed to death in chop; a mean-reverter steps in front of every breakout in a trend.
All the charts in this module share one synthetic tape. Choose its mood here — every widget below redraws from the same series, so you can watch each indicator succeed and fail as the regime changes.
2 · Moving averages: price with a memory
SMA. The simple moving average over the last N closes: μN(t) = (1/N) Σi=0..N−1 close(t−i). Bigger N → smoother and slower. BAZ.HFT spells it μ(state.close), computed by Palantir over a ring buffer of the last N bars.
The rawest question about a price is “is it higher or lower than it used to be?” A simple moving average answers it by averaging the last N closes: today against the smoothed memory of recent days. Keep two memories — a short one that reacts fast and a long one that barely moves — and their relationship becomes a trend detector: when the fast average climbs above the slow one, recent prices are beating older prices, which is the definition of “going up”.
Golden / death cross. Trader slang for the fast SMA crossing above (golden) or below (death) the slow SMA. The names come from daily charts (50/200); the mechanics are identical at 15-minute scale with 9/21. The moments the two averages cross are the classic entry signals. Try dragging the windows: short windows produce many early, noisy crosses; long windows produce few, late, reliable ones. There is no setting that is both early and reliable — that trade-off is the entire game.
3 · Riding the waves: trend following
A wave-rider assumes the current move will continue and wants to be aboard for the middle of it — missing the start, leaving before the end, but capturing the body. The temperament: enter rarely, hold long, take a wide stop, because the one big move pays for the several small failures. Here is the idea as a complete, compilable BAZ.HFT strategy — this exact file lives in the dialect’s example set:
#pragma dialect glither.hft ; fold first-match
#pragma version 2.0-beta
/// # BAZ.CX: Riding the Waves
/// Trend following momentum on BTC/USDT.
/// SMA cross confirmation + RSI momentum filter.
/// Wide stops at -3%; every signal timestamped to backlog.
state wave_fast =
bars | window = 9
| timeframe = 15m
| tracks = [ close ]
state wave_slow =
bars | window = 21
| timeframe = 15m
| tracks = [ close ]
state wave_rsi =
bars | window = 14
| timeframe = 15m
| tracks = [ close ]
rule golden_cross_long =
tick | μ(wave_fast.close) > μ(wave_slow.close)
| rsi(wave_rsi.close) > 60.0
=> transact market_buy
on fill into position(target = μ(wave_slow.close))
after 4h market_close_all (reason = trend_exit)
then_log backlog
rule death_cross_short =
tick | μ(wave_fast.close) < μ(wave_slow.close)
| rsi(wave_rsi.close) < 40.0
=> transact market_sell
on fill into position(target = μ(wave_slow.close))
after 4h market_close_all (reason = trend_exit)
then_log backlog
rule wave_take_profit =
position | position.unrealized_pnl >= 0.08
=> market_close_all (reason = momentum_target_hit)
then_log backlog
rule wave_stop_loss =
position | position.unrealized_pnl <= -0.03
=> market_close_all (reason = trend_reversal_stop)
then_log backlog
state blocks declare ring-buffered bar windows; rule blocks are guard | conditions => action pipelines; fold first-match means the first rule whose conditions hold wins the tick.
Reading a rule. tick | A | B => transact … reads: on every tick, if A and B hold, transact. on fill into position(…) arms the position with a target; after 4h market_close_all is a time fence; then_log backlog writes the signed evidence record.
Four rules, one temperament. The two entry rules demand agreement: the SMA cross says “the trend flipped” and the RSI filter (>60 for longs, <40 for shorts) says “with conviction” — filtering out the limp crosses that happen inside chop. The exits encode wave-rider psychology exactly: take profit at +8%, stop out at −3%, and if neither happens the 4-hour fence closes the ride. Watch it work — and watch what the same file does to a choppy tape:
4 · Bollinger bands: a corridor of normal
Bands. Over the last N closes take the mean μ and standard deviation σ = √((1/N)Σ(close−μ)²). The bands are μ ± kσ, classically N=20, k=2. In BAZ.HFT: bbands_lower(s.close, dev = 2.5), bbands_mid, bbands_upper.
Wrap the moving average in a measure of how much price usually wobbles and you get Bollinger bands: a center line (the 20-bar mean) with an envelope k standard deviations either side. For a layman: a rubber corridor around the recent average. Price inside the corridor is business as usual; price poking outside it is statistically stretched — roughly a 2-in-100 event per bar at k=2 if returns were well-behaved (they aren’t, which is why practitioners widen to 2.5 for entries).
The corridor breathes: it tightens when the market is calm and balloons when it panics. That width is itself a signal — a squeeze (very narrow bands) often precedes a violent move, because quiet markets attract the orders that end them.
5 · RSI: the speedometer
RSI (Wilder, 1978). Split the last N bar-to-bar changes into gains and losses, smooth each (Wilder’s smoothing: avg ← (avg·(N−1) + new)/N), form RS = avgGain/avgLoss, then RSI = 100 − 100/(1+RS). All gains → 100; all losses → 0; a balanced market idles near 50. The relative strength index compresses recent price action into a single dial from 0 to 100: how much of the recent movement was up versus down. Above 70 the market has been climbing almost without breathing — “overbought”; below 30 it has been falling the same way — “oversold”. Two opposite readings of the dial correspond exactly to our two temperaments: the wave-rider treats a high RSI as confirmation (the move has momentum, join it), the noise-trader treats an extreme RSI as exhaustion (the move has overrun, fade it). Same number, opposite bets — the regime decides who is right.
6 · Volume: the show of hands
Volume confirmation. In BAZ.HFT the noise strategy demands volume(s.volume) > μ(s.volume) — the current bar’s volume above its own 20-bar average — before it will fade an extreme. An extreme on thin volume is a rumour; on heavy volume it is a vote.
Price says what happened; volume says how many participants made it happen. A price poking out of the Bollinger corridor on trickle volume is a few small orders wandering in an empty room — it means little and reverts easily. The same poke on a surge of volume is a crowd event: real money disagreed about value. Nearly every robust signal on this page becomes more honest when you require volume to second the motion; that is exactly the volume(…) > μ(…) clause in the strategies.
7 · Volatility: the weather report
Realized volatility. Take log returns r(t) = ln(close(t)/close(t−1)), their rolling standard deviation over N bars, and scale by √(bars per year) to annualize. Volatility clusters: calm follows calm, storm follows storm (Mandelbrot, 1963) — which is why yesterday’s weather is a usable forecast. Volatility is the size of the market’s typical step, regardless of direction — the weather in which every other signal operates. It sets the two numbers no strategy can ignore: how wide the stop must be (a stop inside the market’s ordinary wobble is a donation) and how large the position may be (risk a fixed fraction, so double the weather means half the size). The wave strategy’s −3% stop and the noise strategy’s −0.8% stop are not style choices — they are the same rule quoted in two different climates: 15-minute swells versus 1-minute ripples.
8 · Trading the noise: mean reversion
The opposite temperament. In chop, every excursion is a mistake that the market corrects; a noise-trader’s job is to sell the overshoot and buy the undershoot — fade the move — and to be gone in seconds. Where the wave-rider wanted one big win to pay for many small losses, the noise-trader collects many small wins and must therefore forbid the one big loss: tight stops, short timeouts, and a demand that volume confirm each extreme. Again as a real dialect example, verbatim:
#pragma dialect glither.hft ; fold first-match
#pragma version 2.0-beta
/// # BAZ.CX: Trading from Noise
/// Mean reversion scalping on BTC/USDT.
/// Entries at extreme RSI + Bollinger Band touch with volume confirmation.
/// Tight stops at -0.8% P&L; every signal timestamped to backlog.
state noise_rsi =
bars | window = 14
| timeframe = 1m
| tracks = [ close ]
state noise_bb =
bars | window = 20
| timeframe = 1m
| tracks = [ close, volume ]
rule rsi_oversold_reversal =
tick | rsi(noise_rsi.close) < 25.0
| tick.close < bbands_lower(noise_bb.close, dev = 2.5)
| volume(noise_bb.volume) > μ(noise_bb.volume)
=> transact market_buy
on fill into position(target = bbands_mid(noise_bb.close))
after 30s market_close_all (reason = mean_reversion_timeout)
then_log backlog
rule rsi_overbought_reversal =
tick | rsi(noise_rsi.close) > 75.0
| tick.close > bbands_upper(noise_bb.close, dev = 2.5)
| volume(noise_bb.volume) > μ(noise_bb.volume)
=> transact market_sell
on fill into position(target = bbands_mid(noise_bb.close))
after 30s market_close_all (reason = mean_reversion_timeout)
then_log backlog
rule noise_stop_loss =
position | position.unrealized_pnl <= -0.008
=> market_close_all (reason = noise_vol_stop)
then_log backlog
rule noise_take_profit =
position | position.unrealized_pnl >= 0.015
=> market_close_all (reason = mean_reversion_target_hit)
then_log backlog
The asymmetry. Wave: stop −3%, target +8%, few trades — pays on the ratio. Noise: stop −0.8%, target +1.5%, many trades — pays on the hit-rate. Neither survives in the other’s regime; both log every decision to the backlog so you can prove which regime you were actually in. Run it on the shared tape. On choppy, entries appear at the band-edges and resolve to the midline; flip the tape to trending and watch the same rules bleed — each “overshoot” it fades keeps going, and only the tight stop keeps the damage survivable. That contrast, not any single indicator, is the lesson of this module.
9 · Composing your own: a squeeze breakout
The five instruments compose. Here is a third temperament written from the same vocabulary — a breakout rule that waits for the corridor to stretch, requires the crowd to show up, and asks the dial for momentum. It fires far less often than either example above, which is precisely its virtue:
#pragma dialect glither.hft ; fold first-match
#pragma version 2.0-beta
/// # BAZ.CX: Squeeze Breakout
/// Volume-confirmed Bollinger breakout with momentum agreement.
state box =
bars | window = 20
| timeframe = 5m
| tracks = [ close, volume ]
state box_rsi =
bars | window = 14
| timeframe = 5m
| tracks = [ close ]
rule squeeze_breakout_long =
tick | tick.close > bbands_upper(box.close, dev = 2.0)
| volume(box.volume) > μ(box.volume)
| rsi(box_rsi.close) > 55.0
=> transact market_buy
on fill into position(target = μ(box.close))
after 2h market_close_all (reason = breakout_expiry)
then_log backlog
rule breakout_stop =
position | position.unrealized_pnl <= -0.015
=> market_close_all (reason = failed_breakout)
then_log backlog
tick.close > bbands_upper) here becomes the reason to join it — the difference is one RSI comparison and a regime opinion. Vocabulary is cheap; the temperament is the strategy.10 · From this page to production: CX, Palantir, Luna
Everything you just manipulated by hand has a permanent seat in the BAZ trade-signal stack. The division of labour:
exchange feeds the numbers the decisions the humans
┌────────────────┐ ticks & ┌──────────────────┐ WS ┌──────────────────┐ ┌──────────────────┐
│ BAZ.CX │───candles──▶│ BAZ.Palantir │──────▶│ BAZ.Luna │ │ backlog │
│ market-data │ (:8000) │ validates rules │stream │ CodeMirror 6 │ │ Ed25519-signed │
│ subscriptions │ │ ring buffers │ │ notebook, │ │ monetary audit │
│ per symbol │ │ SMA/BB/RSI/vol │ │ candlestick │ │ receipts, one │
└────────────────┘ │ enrichment │ │ charts, backlog │ │ per transact │
└──────────────────┘ │ viewer │ └──────────────────┘
▲ └──────────────────┘ ▲
│ compiled WASM strategy │
┌──────────────────┐ │
│ BAZ.HFT │─────────────────────────────────────────┘
│ glither.hft │ every fill emits a signed receipt
│ compiler │
└──────────────────┘
state blocks in a strategy are provisioning instructions for Palantir’s ring buffers; the indicator calls are requests against its enrichment engine; the then_log backlog clauses land as signed receipts that Luna renders.- BAZ.CX is the market-data layer: it holds the exchange subscriptions and streams raw ticks and candles for each subscribed symbol (Palantir reconnects to it with exponential backoff — the feed, not the strategy, is the fragile part).
- BAZ.Palantir is the Rust orchestration server in the middle. It validates and stores glither.hft rules, derives which symbols they need and registers those with CX, keeps each
statewindow in a bounded ring buffer, computes exactly the indicators from this page (μ, bands, RSI, volume averages) over those buffers, and streams the enriched events onward over WebSocket. - BAZ.Luna is where humans watch: a browser notebook (CodeMirror 6) for editing strategies like the three above, candlestick charts for the enriched stream — the professional rendering of the toy canvases on this page — and a backlog viewer for the receipts.
- BAZ.HFT itself is the compiler dialect: it turns the
.glithfiles into WASM components and gives everytransacta cryptographic afterlife — an Ed25519-signed monetary audit receipt in the backlog, so a strategy’s claimed behaviour and its actual behaviour can be reconciled after the fact.
← ragbaz product
The stack this module teaches against is the BAZ Trade Signal Stack — glither.hft compiler with signed receipts, Palantir orchestration, Luna frontend. open prospectus →
11 · Glossary
- tick
- One market update — the newest trade or quote. Strategies here are evaluated on every tick.
- bar / candle
- All the ticks of one time slice summarized as open, high, low, close (and volume). A “15m bar” compresses fifteen minutes.
- timeframe
- The bar size a state window is built from — the strategies above use 1m, 5m and 15m.
- SMA / μ
- Simple moving average: the mean of the last N closes. The market’s short-term memory.
- golden / death cross
- Fast SMA crossing above / below the slow SMA — the classic trend-flip signal.
- Bollinger bands
- A corridor of “normal”: the 20-bar mean ± k standard deviations. Touches mark statistically stretched prices.
- squeeze
- Unusually narrow bands — a calm that often precedes a violent move.
- σ (standard deviation)
- The typical size of the wobble around the average; the yardstick the bands are built from.
- RSI
- Relative strength index, 0–100: how one-sided recent movement has been. >70 overbought, <30 oversold.
- volume
- How much was actually traded in a bar — the number of hands behind the price move.
- volatility
- The size of the market’s typical step, direction-blind. Sets stop widths and position sizes.
- regime
- The market’s current statistical personality: trending or mean-reverting (chop).
- trend following
- “Riding the waves” — betting the current move continues. Few trades, wide stops, big targets.
- mean reversion
- “Trading the noise” — betting the overshoot snaps back. Many trades, tight stops, small targets.
- fade
- To trade against the most recent move — selling the spike, buying the dip.
- whipsaw
- Being repeatedly stopped out as chop crosses your signals back and forth. The trend-follower’s tax in the wrong regime.
- long / short
- A position that profits when price rises / falls.
market_buyopens longs,market_sellshorts. - stop loss / take profit
- Exit rules on the position’s unrealized P&L: the pre-committed loss you accept, the win you bank.
- P&L
- Profit and loss.
position.unrealized_pnlis the open position’s current fraction gained or lost. - slippage
- The gap between the price a signal saw and the price the fill got. The toy model on this page pretends it is zero; production must not.
- glither.hft
- The BAZ.HFT strategy dialect:
statewindows + guardedrulepipelines, compiled to WASM.fold first-match= first matching rule wins the tick. - ring buffer
- A fixed-size window that forgets the oldest entry as each new one arrives — how Palantir stores every
stateblock. - backlog
- The append-only record of every signal and fill, each entry Ed25519-signed — the strategy’s audit trail, browsable in Luna.
- Ed25519 receipt
- A digital signature attached to each transaction record, proving origin and integrity of the monetary audit trail.