Skip to content
Finance Lessons

Deep Learning for Market Data

Attention & Transformers for Markets

Self-attention as 'who looks at whom' — scaled dot-product attention, why a direct one-hop link across time beats a decaying hidden state, multi-head attention and positional encoding, transformers on the limit-order book (the DeepLOB lineage), and the data-hunger that punishes them on small samples.

18 min Updated Jun 19, 2026

By now you’ve met two ways to make a network remember the past. The RNN/LSTM/GRU threads a single hidden state through time like a game of telephone — every step whispers to the next, and gradients fade out over the chain (lesson 2’s vanishing-gradient problem). The Temporal Convolutional Network instead stacks dilated, causal convolutions to widen a fixed receptive field (lesson 3) — fast and parallel, but the horizon is whatever you hard-wired into the dilation schedule.

Attention is the third answer, and it’s almost insultingly direct: don’t thread memory, and don’t widen a window — just let every time step look directly at every other time step and decide for itself who matters. No chain to decay through. No fixed window to outgrow. Step 200 can read step 1 in a single hop. That’s the whole trick, and the Transformer is what you get when you build an entire architecture out of nothing but that trick.

Let’s earn it.

Self-attention: who looks at whom

Before you read — take a guess

Before we define anything: in self-attention, what does each output position actually compute?

The analogy. Picture a trading desk meeting. You (the query) ask a question: “what’s relevant to where price goes next?” Every analyst in the room has a name tag (a key) describing what they know, and a briefing (a value) they’ll hand over. You glance at every name tag, score how well each matches your question, turn those scores into attention percentages, and walk out with a blend of briefings weighted by relevance. Self-attention is that meeting, run for every time step at once, with every step playing both analyst and asker.

Precise definition. Each input token (say, the feature vector at time tt) is linearly projected into three vectors: a query qtq_t, a key ktk_t, and a value vtv_t, via learned matrices WQ,WK,WVW_Q, W_K, W_V. Stack them into matrices Q,K,VQ, K, V. Scaled dot-product attention is:

Attention(Q,K,V)=softmax ⁣(QKdk)V\text{Attention}(Q,K,V)=\text{softmax}\!\left(\frac{QK^\top}{\sqrt{d_k}}\right)V

The matrix QKQK^\top holds every query-key dot product (the raw relevance scores). Dividing by dk\sqrt{d_k} — where dkd_k is the key dimension — rescales those scores. The softmax turns each row into a probability distribution that sums to 1, and multiplying by VV produces the weighted average of value vectors.

Why dk\sqrt{d_k}? If qq and kk are roughly independent unit-variance entries, their dot product qk=i=1dkqikiq\cdot k=\sum_{i=1}^{d_k} q_i k_i has variance about dkd_k — so it grows with dimension. Large scores push the softmax into saturation: one weight goes to ~1, the rest to ~0, and the gradient through softmax nearly vanishes. Dividing by dk\sqrt{d_k} pulls the variance back to roughly 1, keeping the softmax in its responsive, well-conditioned regime.

Worked example — a tiny 3-key softmax. One query attends to three keys with dk=4d_k = 4, so dk=2\sqrt{d_k}=2. Suppose the raw dot products are qk1=8q\cdot k_1 = 8, qk2=4q\cdot k_2 = 4, qk3=2q\cdot k_3 = 2.

Scale each by dk=2\sqrt{d_k}=2: scaled scores are 4, 2, 14,\ 2,\ 1.

Exponentiate: e4=54.60e^{4}=54.60, e2=7.389e^{2}=7.389, e1=2.718e^{1}=2.718. Sum =64.71=64.71.

Normalize: w1=54.60/64.71=0.844w_1 = 54.60/64.71 = 0.844, w2=7.389/64.71=0.114w_2 = 7.389/64.71 = 0.114, w3=2.718/64.71=0.042w_3 = 2.718/64.71 = 0.042 (sums to 1.000).

Now blend the value vectors. Say v1=[1,0]v_1=[1,0], v2=[0,1]v_2=[0,1], v3=[1,1]v_3=[1,1]. The output is

0.844[1,0]+0.114[0,1]+0.042[1,1]=[0.886, 0.156].0.844\,[1,0] + 0.114\,[0,1] + 0.042\,[1,1] = [0.886,\ 0.156].

Notice the output leans heavily on v1v_1 (the best-matching key) but isn’t a hard pick — it’s a soft, differentiable blend. Now compare what happens without scaling: the unscaled scores 8,4,28,4,2 give e8=2981e^8=2981, e4=54.6e^4=54.6, e2=7.39e^2=7.39, weights 0.980,0.018,0.0020.980, 0.018, 0.002 — the softmax has nearly collapsed onto key 1, throwing away most of the relevance structure and flattening the gradient. That’s exactly the saturation dk\sqrt{d_k} is there to prevent.

For markets we add a causal mask: an output at time tt may only attend to keys at times t\le t (you can’t peek at the future). In the score matrix that zeroes out the upper triangle before the softmax, so future positions get weight 0.

Self-attention: who looks at whom
10027732467246423642363236323632363123456789123456789Key — input step jQuery — output step i
Effective keys attended (avg)2.4

A causal T×T self-attention matrix. Each row is one output (query) step; columns are the input (key) steps it attends to; the upper triangle is masked (no peeking at the future) and every row's weights softmax-sum to 1. Flip between a 'Recent (momentum)' pattern that hugs the diagonal, an 'Event-driven' pattern that spikes on one past bar, and a 'Diffuse' pattern that spreads attention; then drag the softmax-temperature slider and watch the effective number of keys attended expand or collapse.

Warning:

Softmax weights are not feature importances

A high attention weight means “this position was useful for blending the value vectors here”, not “this input causes the target.” Attention maps are seductive — they look like an explanation — but they’re computed from learned projections, are not unique (you can permute heads and rescale), and famously do not equal causal attribution. Treat a pretty heatmap as a debugging hint, never as evidence that bar 37 drives the next return.

When to use it

Reach for self-attention when relevance is content-dependent and irregular — when which past bar matters depends on what happened, not on a fixed lag. An earnings surprise, a regime break, or a specific level being retested are things attention can route to directly. If the dependency is a smooth, fixed-lag decay (yesterday matters most, then the day before, monotonically), a cheap exponential filter or a small AR term may match it with a fraction of the parameters — don’t summon a Transformer to relearn a moving average.

Fill in the scaling rationale.

Pick the right option for each blank, then check.

We divide the raw scores by √(d_k) so the softmax does not , which would flatten the gradient and discard relevance structure.

Why attention beats recurrence for long range

Before you read — take a guess

Two positions are 200 steps apart. How many computational hops does the signal travel between them in self-attention versus in a vanilla RNN?

The analogy. The RNN is a message passed hand-to-hand down a line of 200 people — by the end it’s “purple monkey dishwasher,” and each handoff multiplies in another Jacobian factor, so the gradient either vanishes or explodes (exactly the failure from lesson 2). Attention is a group chat: position 200 @-mentions position 1 directly, and the message arrives intact in one hop.

Precise statement. Define maximum path length as the longest distance, in computational steps, that information must travel between any two input positions for them to interact. For a recurrent net, two positions ii and jj interact only through the chain of hidden states between them, so the path length is O(ij)O(|i-j|), up to O(T)O(T). For self-attention, every position attends to every other in a single layer, so the path length is O(1)O(1) — independent of TT. Short paths mean gradients don’t have to survive a long product of Jacobians, so long-range learning doesn’t decay.

The bill arrives as compute and memory. The score matrix QKQK^\top is T×TT\times T, so self-attention costs O(T2d)O(T^2 \cdot d) time and O(T2)O(T^2) memory. Double the sequence length and the attention cost quadruples.

Worked example — counting the cost. Take T=500T = 500 bars and model width d=64d = 64.

  • RNN sequential path between the first and last bar: 500\approx 500 hops; the operation count to process the sequence is O(Td2)=500×642=2,048,000O(T \cdot d^2) = 500 \times 64^2 = 2{,}048{,}000, but it’s sequential — step t+1t+1 waits for step tt.
  • Self-attention path between any two bars: 11 hop; the dominant cost is O(T2d)=5002×64=16,000,000O(T^2 \cdot d) = 500^2 \times 64 = 16{,}000{,}000 — about 8× the RNN’s arithmetic here, but fully parallel across positions (one big matmul, no waiting).

So attention trades more (parallelizable) arithmetic for a vastly shorter gradient path and no sequential bottleneck. On modern hardware the parallelism usually wins on wall-clock, right up until TT gets large enough that the T2T^2 memory term bites.

Unrolling a recurrent model — and watching the past fade
x1h1x2h2x3h3x4h4x5h5x6h6x7h7x8h8Input xₜHidden state hₜŷ
Step-1 info surviving at h_T3.5%

The contrast made visual: the recurrent unroll threads one hidden state down the chain, and the memory-retention bar decays the further back you ask it to remember — that decay IS the vanishing gradient. Self-attention would replace this entire chain with a single direct edge from step i to step j. One hop, no decay.

PropertyRNN / LSTMTCNTransformer
Max path length between positionsO(T)O(T)O(logT)O(\log T) (dilated)O(1)O(1)
Parallelism over timeNone (sequential)FullFull
Compute per layerO(Td2)O(T\,d^2)O(Tkd)O(T\,k\,d)O(T2d)O(T^2 d)
Memory horizonSoft, decays (gating helps)Fixed by dilation scheduleWhole sequence, content-addressed
Main failure modeVanishing/exploding gradientOutgrows fixed receptive fieldO(T2)O(T^2) cost + data hunger
Warning:

O(1) path length is not O(1) compute

“Constant path length” sounds like a free lunch — it isn’t. The short gradient path is bought with quadratic compute and memory in sequence length. On long financial sequences (think tick data over a full session) the T2T^2 memory term is the real constraint, and it’s why production setups reach for windowing, chunking, or efficient-attention variants. Don’t confuse the learning advantage (short paths) with a cost advantage (there isn’t one).

When to use it

Prefer attention over recurrence when the dependency is genuinely long-range and content-routed, and your sequences are short enough that T2T^2 is affordable. If TT is small (a handful of lags) and the signal is local, an LSTM or a TCN is cheaper and harder to overfit. If TT is enormous and the dependency is mostly local with rare long jumps, a TCN’s logarithmic-depth receptive field is often the sweet spot.

Pick a term, then click its definition.

Multi-head attention & positional encoding

Before you read — take a guess

Self-attention computes a weighted average over positions. What does that imply if you shuffle the order of the input bars before adding any position information?

The analogy for heads. One analyst can only hold one thesis at a time. A desk hires several — a momentum specialist, an events specialist, a liquidity specialist — and each reads the same tape through a different lens, then you pool their notes. Multi-head attention runs hh attention computations in parallel, each with its own lower-dimensional WQ,WK,WVW_Q, W_K, W_V projections, and concatenates the results through an output projection. One head can learn to track recent drift, another to spike on a specific past event, another to watch a level.

Precise definition. With model dimension dmodeld_{\text{model}} split across hh heads, each head works in dimension dk=dmodel/hd_k = d_{\text{model}}/h:

headi=Attention(QWiQ,KWiK,VWiV),MultiHead=Concat(head1,,headh)WO.\text{head}_i = \text{Attention}(QW_i^Q, KW_i^K, VW_i^V), \qquad \text{MultiHead} = \text{Concat}(\text{head}_1,\dots,\text{head}_h)\,W^O.

Splitting the budget keeps total cost roughly the same as single-head while letting heads specialize.

Positional encoding. Because attention is permutation-invariant, we add a position-dependent vector ptp_t to each token’s embedding xtx_t so identical features at different times are distinguishable. Sinusoidal encodings use

PE(t,2i)=sin ⁣(t100002i/d),PE(t,2i+1)=cos ⁣(t100002i/d),PE_{(t,2i)} = \sin\!\left(\frac{t}{10000^{2i/d}}\right), \qquad PE_{(t,2i+1)} = \cos\!\left(\frac{t}{10000^{2i/d}}\right),

giving each position a unique multi-frequency fingerprint; learned positional embeddings are a trained lookup table instead. Either way, the model now knows when each token sat.

Worked mini-example — why order matters for returns. Consider two day-return sequences built from the same multiset of moves {+5%,5%}\{+5\%, -5\%\}:

  • Sequence A: +5%+5\% then 5%-5\%. Cumulative wealth: 1.05×0.95=0.99751.05 \times 0.95 = 0.9975 (down 0.25%).
  • Sequence B: 5%-5\% then +5%+5\%. Cumulative wealth: 0.95×1.05=0.99750.95 \times 1.05 = 0.9975 (also down 0.25%).

Compounding happens to commute here, so the terminal wealth is identical — but the path is not, and path is what risk lives on. Sequence A drew down to 1.05 then fell; Sequence B drew down to 0.95 first. A momentum signal, a trailing stop, a margin call, or a volatility estimate all read these two as different worlds. A model that can’t tell A from B (no positional info) literally cannot represent “the drop came first.” Order is not decoration; it’s signal.

Sort each statement under the mechanism it describes.

Place each item in the right group.

  • Splits d_model into h lower-dimensional projections, then concatenates
  • Injects 'when' so permuting inputs no longer leaves outputs unchanged
  • Lets one sub-computation track momentum while another spikes on a past event
  • Can be sinusoidal multi-frequency or a learned lookup table
Warning:

Heads do not come pre-labeled 'momentum' and 'events'

It’s tempting to narrate “head 3 is the earnings head.” In practice head roles are emergent, entangled, and often redundant — pruning studies routinely delete a chunk of heads with negligible loss. Don’t hard-code an interpretation of a head into your research story, and don’t trust that a head you eyeballed as “the event head” will play that role on out-of-sample data or after a retrain.

When to use it

Use multiple heads whenever you suspect several distinct relational patterns coexist (trend, mean-reversion, event-response) — it’s nearly free given a fixed dmodeld_{\text{model}}. Always include positional encoding for time series; the only time you’d drop it is for genuinely orderless set inputs (e.g. attending across the cross-section of assets at a single timestamp, where there is no intrinsic order to encode).

If terminal wealth is identical for both orderings, why does a model still need positional information?

Answer. Because almost everything we actually care about in finance is path-dependent, even when the endpoint isn’t. Drawdown, trailing stops, margin calls, realized volatility, and any momentum or autocorrelation signal all read the two orderings as different states of the world. Self-attention without positional encoding sees both sequences as the same set of returns and is structurally incapable of distinguishing “the crash came first” from “the rally came first.” Positional encoding restores that distinction.

Transformers on the limit order book

Before you read — take a guess

Lesson 1 argued deep learning earns its keep on high signal-to-noise subproblems. Which task best fits a Transformer's appetite?

The analogy. Forecasting next year’s return from a few yearly observations is like predicting a coin’s lifetime average from three flips — mostly noise. Nowcasting the next mid-price tick from the live order book is more like reading a crowded auction room in real time: the bids and offers are right there, stacked and quantified, and the next move is mechanically related to that pressure. That’s the high-SNR, data-rich regime lesson 1 said DL was built for.

The limit order book (LOB) as input. At each instant the book lists the best NN price levels on each side with their resting sizes. A standard DeepLOB-style input is the top N=10N=10 levels: for each level, the bid price, bid size, ask price, ask size — 10×4=4010 \times 4 = 40 features per snapshot — stacked over a window of, say, the last 100 snapshots. This is a structured, normalized, high-frequency tensor, not a handful of noisy daily bars.

The labels. Define the mid-price mt=(ptask+ptbid)/2m_t = (p^{\text{ask}}_t + p^{\text{bid}}_t)/2. To denoise the tick-level wiggle, compare smoothed means before and after: let mm_- be the average mid over the previous kk ticks and m+m_+ the average over the next kk. The label is the sign of the smoothed change against a threshold α\alpha:

t={up(m+m)/m>+αflatm+m/mαdown(m+m)/m<α\ell_t = \begin{cases} \text{up} & (m_+ - m_-)/m_- > +\alpha \\ \text{flat} & |m_+ - m_-|/m_- \le \alpha \\ \text{down} & (m_+ - m_-)/m_- < -\alpha \end{cases}

A three-class up/flat/down classification over a horizon of a few ticks.

Worked example — labeling one event. Best bid =100.00= 100.00, best ask =100.02= 100.02, so mt=100.01m_t = 100.01. Over the previous k=5k=5 ticks the mid averaged m=100.00m_- = 100.00; over the next 5 it averages m+=100.05m_+ = 100.05. The smoothed return is (100.05100.00)/100.00=0.0005=5(100.05 - 100.00)/100.00 = 0.0005 = 5 basis points. With a threshold α=2\alpha = 2 bp, 5>25 > 2, so this snapshot is labeled up. Had m+=100.01m_+ = 100.01 (a 11 bp move), 1<21 < 2, it would be flat — the threshold absorbs micro-noise so the model isn’t trained to chase rounding.

The DeepLOB lineage. The influential recipe (Zhang, Zohren & Roberts, 2019) is a stack: convolutions over the price/volume levels first compress the 40-feature snapshot into local microstructure features (treating adjacent levels like adjacent pixels), an inception module captures multi-scale patterns, and then a temporal model — originally an LSTM, in later variants an attention/Transformer block — integrates over the time window before a softmax over up/flat/down. Attention’s role: route the prediction to the moments in the recent book that actually mattered (a big sweep, a sudden imbalance) rather than smearing across the window.

LOB nowcastingDaily-return forecasting
Signal-to-noiseHigh (next tick ≈ current pressure)Brutally low
Sample countMillions of snapshots/dayHundreds of obs/year
HorizonA few ticks / millisecondsDays to years
LabelMechanical, smoothed mid moveDominated by unforecastable noise
DL verdict (lesson 1)Plays to DL’s strengthsPlays to DL’s weaknesses
Warning:

LOB success does not generalize up to daily returns

DeepLOB-style results are real — but they live in a specific regime: ultra-short horizon, enormous sample counts, high SNR. People routinely (and wrongly) cite them as proof that “Transformers beat classical models in finance,” then bolt the same architecture onto daily or weekly returns and faceplant. Nowcasting microstructure and forecasting macro returns are different sports. Worse, intraday LOB results that ignore latency, queue position, and transaction costs can be untradeable even when the classification accuracy is genuine.

When to use it

Deploy a Transformer-on-LOB when you have genuine high-frequency book data, a short horizon, and millions of labeled snapshots — and when your backtest models latency, fees, and fill realism honestly. Do not transplant the architecture to low-frequency return forecasting and expect the magic to follow; there the data is too scarce and too noisy for the model’s capacity, which is exactly the next section’s problem.

Fill in the regime where LOB Transformers earn their keep.

Pick the right option for each blank, then check.

The DeepLOB task works because mid-price nowcasting is a problem, the opposite of forecasting daily returns from a few hundred yearly observations.

The data-hunger problem

Before you read — take a guess

Among RNNs, TCNs, and Transformers, why does the Transformer tend to overfit fastest on small return datasets?

The analogy. A convolution arrives with an opinion (“nearby things relate”), and recurrence arrives with another (“time is a chain”) — these inductive biases are guardrails that constrain what the model can fit. A Transformer arrives with almost no opinion: any position can relate to any position, learned freely. Maximum flexibility is a superpower when data is abundant and a curse when it’s scarce. On a few hundred noisy yearly returns, a bias-free, parameter-heavy model is a sponge that soaks up noise and calls it signal.

Precise statement. Generalization error decomposes loosely as bias plus variance. Strong inductive bias lowers variance at the cost of some bias; the Transformer’s weak bias and large parameter count PP maximize variance, and variance is governed by the ratio of PP to the effective number of independent samples NN. Financial return series have a tiny effective NN (overlapping, autocorrelated, regime-shifting), so P/NP/N is enormous and the model overfits before it generalizes.

Worked example — the parameter-to-sample ratio. Suppose a modest Transformer block has P200,000P \approx 200{,}000 parameters and you train on 20 years of daily equity returns: 20×2525,04020 \times 252 \approx 5{,}040 observations — and because returns are serially correlated and regimes overlap, the effective independent sample count might be closer to a few hundred. Take Neff250N_{\text{eff}} \approx 250. Then P/Neff200,000/250=800P/N_{\text{eff}} \approx 200{,}000/250 = 800 parameters per effective sample. A healthy supervised setup wants that ratio well below 1. At 800-to-1 the model can fit the training set perfectly and learn essentially nothing that survives out of sample.

Remedies.

RemedyWhat it doesCost / caveat
Heavy regularization (dropout, weight decay, early stopping)Shrinks effective capacityTuning each setting is itself a trial (see below)
Pretraining / transferLearns structure from a large related corpus, fine-tunes on the small targetPretraining data must be relevant; regime mismatch hurts
Simpler modelLower PP, stronger bias (LSTM, TCN, or just a linear model)May underfit genuine long-range structure
Pool the cross-sectionTrain one model across many assets to raise effective NNAssumes shared dynamics; leaks if not aligned in time
Warning:

Every architecture you try inflates your trial count

Here’s the trap that sinks careers: you try a 4-head model, then 8 heads, then add a layer, then swap sinusoidal for learned encodings, then tune dropout — and quietly run dozens of configurations against the same data. Each one is a trial, and the more trials you run, the higher the best-looking Sharpe you’ll find by pure luck. This is exactly the multiple-testing problem from Machine Learning for Alpha: you must deflate your Sharpe by the number of trials. A Transformer’s huge design space makes it a multiple-testing minefield — the architecture search itself manufactures false discoveries. Pre-register your design and count every variant.

When to use it

Use a Transformer for return prediction only when you can honestly raise the effective sample count — pooling a broad cross-section, leaning on relevant pretraining, or working in a high-frequency regime with millions of snapshots. Otherwise, the smaller-bias-stronger model (or even a regularized linear model) usually wins out of sample, and you’ve spent far fewer trials getting there. The sober bottom line — which lesson 6 will hammer — is that architecture rarely rescues a low-SNR, data-starved problem; matching model capacity to genuine information content is the whole game.

Each lever below either raises effective sample size or shrinks effective capacity. Sort them.

Place each item in the right group.

  • Pool training across a broad cross-section of assets
  • Swap the Transformer for a smaller LSTM or TCN
  • Apply dropout, weight decay, and early stopping
  • Pretrain on a large related corpus, then fine-tune

Recap

Big picture

Attention & Transformers for markets

  • Attention & Transformers
    • Self-attention
      • Query / key / value blend
      • softmax(QKᵀ/√dₖ)V
      • √dₖ prevents softmax saturation
      • Causal mask: no future peeking
    • Why it beats recurrence
      • Path length O(1) vs O(T)
      • No vanishing gradient (lesson 2)
      • Cost: O(T²) compute & memory
    • Multi-head + position
      • Heads = parallel relational views
      • Permutation-invariant → need positional encoding
      • Sinusoidal or learned
    • LOB transformers
      • Top-N bid/ask price & size
      • Label: smoothed mid up/flat/down
      • DeepLOB: conv → inception → temporal
      • High-SNR nowcasting (lesson 1)
    • Data hunger
      • Most params, least inductive bias
      • P/N huge on returns → overfit
      • Each variant inflates trial count
      • Pretrain / pool / regularize / simplify
Self-attention is a direct one-hop link across time — powerful where the dependency is long-range and content-routed, dangerous where data is scarce.

Attention & Transformers — check yourself

Question 1 of 50 correct

A query attends to two keys with d_k = 4. The raw dot products are 6 and 2. After scaling by √dₖ, what are the scaled scores fed to the softmax?

Check your answer to continue.

Mark lesson as complete