Skip to content
Finance Lessons

Adversarial Machine Learning & Model Robustness in Trading

Adversarial Examples on Trading Models

FGSM and PGD — tiny crafted input perturbations that flip a model — reframed for market features. Why a few basis points of engineered noise can flip a position, why high-dimensional near-linear models are so fragile, and the L-p geometry of an attack on a trading classifier.

20 min Updated Jun 23, 2026

In 2014 a research group took a photo of a panda, added a layer of noise so faint a human eye sees no change whatsoever, and a state-of-the-art image classifier that had called the original “panda” with 58% confidence now called the doctored image a gibbon with 99% confidence. The noise was not random. It was engineered — each pixel nudged in the exact direction that pushed the model toward “gibbon.”

That image launched a field. The unsettling lesson: a model can be 97% accurate and 100% breakable, because high accuracy on natural inputs says nothing about behavior under inputs an adversary designs. And here is the part that should keep a quant up at night — markets are full of adversaries who can design your inputs. You don’t feed a trading model pixels. You feed it engineered features: momentum, realized volatility, bid–ask spread, order-flow imbalance, RSI. And every one of those features can be moved by someone willing to trade against you.

This lesson reframes the entire adversarial-examples toolkit — FGSM, PGD, the L-p geometry, the linearity explanation — for a trading model whose “pixels” are order-book features and whose “imperceptible noise” is a few basis points of strategically placed flow. Once you see your signal as a function an opponent can probe, robustness stops being a vision-research curiosity and becomes table stakes.

What is an adversarial example

Before you read — take a guess

A classifier is 98% accurate on real market data. An attacker adds a perturbation no human reviewer would flag as unusual and the model flips its call. What does the 98% accuracy tell you about this attack?

Analogy. A bank teller can verify a thousand real signatures flawlessly and still be fooled by one forger who studied exactly how the teller checks. The teller’s 99.9% accuracy on honest signatures is irrelevant to the forger, who isn’t sampling from the honest distribution — they’re searching for the one signature that slips through. An adversarial example is the forged signature, found by someone who knows the model’s weak spots.

Definition. Let a model produce a decision from an input feature vector xx via a score function and a loss L(x,y)L(x, y) measuring how wrong it is on the true label yy. An adversarial example is a perturbed input x=x+δx' = x + \delta where the perturbation δ\delta is small under some chosen norm and yet maximizes the loss (or flips the output). Formally, the attacker solves

maxδ  L(x+δ,y)subject toδpε.\max_{\delta} \; L(x + \delta, y) \quad \text{subject to} \quad \lVert \delta \rVert_p \le \varepsilon.

The constraint δpε\lVert \delta \rVert_p \le \varepsilon is the perturbation budget: δ\delta must be tiny under the LpL_p norm, capped by ε\varepsilon. “Tiny” is what makes it adversarial rather than just a different input — the change is supposed to be imperceptible (in vision) or cheap (in markets).

Reframe for markets. Replace pixels with engineered features. A trading model might score a feature vector

x=(momentum,  volatility,  spread,  order-flow imbalance,  RSI).x = (\text{momentum},\; \text{volatility},\; \text{spread},\; \text{order-flow imbalance},\; \text{RSI}).

A long signal fires when the score clears a boundary. An adversarial example here is a crafted nudge to those features — say momentum down 0.3 standard deviations, imbalance flipped sign, spread widened a touch — engineered so the model that wanted to go long now goes short. Crucially, you don’t perturb the features directly: you perturb the order book just enough that the engineered features computed from it move where you want them. The attack lives in the market; the damage lands in the feature vector.

Worked example. Suppose a long fires when the score is at least 0.60. Today’s honest feature vector scores 0.62 — a comfortable long. An attacker computes that shifting four features by a combined budget of 0.05 (under the relevant norm) drops the score to 0.55. That is below 0.60, so the model now flips to short. The position reverses on a perturbation a risk officer eyeballing the features might not even notice: each feature moved a sliver, none looks anomalous, yet the aggregate was aimed precisely at the boundary.

Warning:

Off-distribution is the whole point

The reason backtests never warn you about this is that backtests replay natural history — inputs the market actually produced. Adversarial inputs are, by construction, the ones history didn’t produce because no one was attacking you yet. A spotless out-of-sample Sharpe measures behavior on the honest distribution and is silent about the adversarial one. Robustness is a separate axis you must test on purpose.

When to use it

Reach for the adversarial-example lens whenever some other agent both observes your behavior and benefits from flipping it — a counterparty fading your flow, a predatory HFT hunting your stops, a rival reverse-engineering your signal. If your model’s inputs are computed from public market data that anyone can move by trading, assume the inputs are attackable and ask how cheaply your decision can be flipped.

Pin down the definition.

Pick the right option for each blank, then check.

An adversarial example is a perturbation that is yet is engineered to .

The linearity explanation — why high-dimensional models are fragile

Before you read — take a guess

A quant argues: 'My model is basically a linear score on the features, so it's simple and therefore safe from these exotic adversarial attacks.' How should you react?

Analogy. Imagine a vote where 100 judges each get a tiny say. Bribe each judge with a single dollar — trivial, almost imperceptible per judge — and you’ve spent $100 of influence in total. In a close vote, $100 of coordinated nudges flips the outcome even though no individual judge was bought for a meaningful sum. High dimensionality is having many judges: lots of features, each cheap to nudge, summing to a decisive shove. That’s the linear model’s curse.

Definition. For a linear score s(x)=wx=iwixis(x) = w \cdot x = \sum_i w_i x_i, perturbing the input by δ\delta changes the score by exactly

s(x+δ)s(x)=wδ=iwiδi.s(x + \delta) - s(x) = w \cdot \delta = \sum_i w_i\, \delta_i.

Under an LL_\infty budget — meaning every feature may move by at most ε\varepsilon, i.e. the largest single-feature change is capped (this is the “max-norm” constraint) — the worst-case score shift is achieved by pushing each feature the full ε\varepsilon in the sign of its weight:

maxδε  wδ=εiwi=εw1.\max_{\lVert \delta \rVert_\infty \le \varepsilon} \; w \cdot \delta = \varepsilon \sum_i \lvert w_i \rvert = \varepsilon\, \lVert w \rVert_1.

Stare at that result. The worst-case damage scales with w1\lVert w \rVert_1, the sum of the absolute weights — which grows with the number of features dd. Double the dimension and (for similar typical weights) you roughly double the attacker’s leverage, at the same per-feature budget ε\varepsilon. High dimensionality, the thing that makes modern models powerful, is also what makes them fragile.

Worked example. Take a model with d=100d = 100 features, an average absolute weight of wi=0.1\lvert w_i \rvert = 0.1, and an LL_\infty budget of ε=0.01\varepsilon = 0.01 (each feature may move by one part in a hundred of its scaled range). The worst-case score shift is

εw1=0.01×(100×0.1)=0.01×10=0.1.\varepsilon \, \lVert w \rVert_1 = 0.01 \times (100 \times 0.1) = 0.01 \times 10 = 0.1.

A score shift of 0.1 from a per-feature nudge of just 0.01 — a 10x amplification, courtesy of dimension. If the decision boundary sits within 0.1 of the current score (as it routinely does near a signal threshold), that’s enough to flip the trade. Now scale to d=1000d = 1000 features at the same per-feature budget and the worst-case shift is 0.01×(1000×0.1)=1.00.01 \times (1000 \times 0.1) = 1.0 — ten times larger, for free.

Features ddPer-feature budget ε\varepsilonAvg wi\lvert w_i \rvertWorst-case score shift εw1\varepsilon \lVert w \rVert_1
100.010.10.01
1000.010.10.10
1,0000.010.11.00
10,0000.010.110.0
Warning:

'My model is basically linear, so it's safe' is exactly backwards

This is the single most dangerous intuition in the whole topic. Nonlinearity isn’t what makes models vulnerable — local linearity is. A near-linear high-dimensional model hands the attacker a clean, additive budget: nudge many features a little, sum the pushes, cross the boundary. Worse, because the gradient direction is stable and roughly shared across similar models, an attack crafted on one near-linear model transfers to others. Linearity is why adversarial examples are cheap and portable.

When to use it

Use the linearity argument to triage your exposure: the more features your model consumes and the more linear its decision surface near the threshold, the bigger the attacker’s w1\lVert w \rVert_1 leverage. It also tells you a partial defense — shrinking w1\lVert w \rVert_1 (sparser, more regularized, fewer-feature models) directly caps the worst-case LL_\infty damage. Reach for it any time you’re tempted to add “just a few more features” to a signal sitting near a boundary.

At a fixed per-feature budget epsilon, does moving from 100 features to 400 features make a near-linear model MORE or LESS vulnerable, and roughly by how much?

Hint. The worst-case score shift is epsilon times the sum of absolute weights, which scales with the feature count.

More vulnerable — by roughly 4x. The worst-case score shift is εw1=εiwi\varepsilon \lVert w \rVert_1 = \varepsilon \sum_i \lvert w_i \rvert. Holding the typical absolute weight fixed, quadrupling the feature count from 100 to 400 roughly quadruples w1\lVert w \rVert_1, so the attacker’s worst-case leverage at the same per-feature budget grows about fourfold. Adding features expands expressiveness and the attack surface in the same stroke — dimensionality is the adversary’s friend.

FGSM — the Fast Gradient Sign Method

Before you read — take a guess

The Fast Gradient Sign Method takes a single step to build an attack. Which direction does each feature get pushed?

Analogy. You’re at the bottom of a valley (low loss = correct decision) and want to climb out fastest with a fixed-length step in each compass direction. FGSM looks at the slope under your feet, and in every direction takes the full allowed step uphill. It doesn’t bother with how steep each direction is — just which way is up — because the LL_\infty budget rewards moving every coordinate the full amount. It’s the greedy, one-shot scramble out of the valley.

Definition. The Fast Gradient Sign Method (FGSM) constructs the perturbation in a single gradient step:

δ=εsign(xL(x,y)),x=x+δ.\delta = \varepsilon \cdot \operatorname{sign}\big(\nabla_x L(x, y)\big), \qquad x' = x + \delta.

Here xL\nabla_x L is the gradient of the loss with respect to the input (not the weights — we’re attacking the input, holding the model fixed), and sign()\operatorname{sign}(\cdot) maps each component to +1+1, 00, or 1-1. Multiplying by ε\varepsilon spends the full per-feature budget on each coordinate. Because it uses the sign rather than the raw magnitude, the result lands exactly on the corner of the LL_\infty ball — the worst-case linear point we derived above. One forward pass, one backward pass, done. That’s the “Fast” in FGSM.

Worked example (3-feature toy). A linear model scores s(x)=wxs(x) = w \cdot x with weights w=(0.9,0.7,0.6)w = (0.9,\, -0.7,\, 0.6) and goes long when s0s \ge 0. Today x=(0.5,0.4,0.3)x = (0.5,\, 0.4,\, 0.3), so

s(x)=0.9(0.5)+(0.7)(0.4)+0.6(0.3)=0.450.28+0.18=0.35>0    LONG.s(x) = 0.9(0.5) + (-0.7)(0.4) + 0.6(0.3) = 0.45 - 0.28 + 0.18 = 0.35 \gt 0 \;\Rightarrow\; \text{LONG}.

The attacker wants to flip to short, i.e. push the score down. For a linear score, the gradient of the score w.r.t. the input is just the weight vector, xs=w=(0.9,0.7,0.6)\nabla_x s = w = (0.9, -0.7, 0.6). To decrease the score maximally under an LL_\infty budget of ε=0.1\varepsilon = 0.1, push each feature against its weight’s sign:

δ=εsign(w)=0.1(+1,1,+1)=(0.1,+0.1,0.1).\delta = -\varepsilon \cdot \operatorname{sign}(w) = -0.1 \cdot (\,+1,\, -1,\, +1\,) = (-0.1,\, +0.1,\, -0.1).

The perturbed input is x=(0.4,0.5,0.2)x' = (0.4,\, 0.5,\, 0.2), and the new score is

s(x)=0.9(0.4)+(0.7)(0.5)+0.6(0.2)=0.360.35+0.12=0.13.s(x') = 0.9(0.4) + (-0.7)(0.5) + 0.6(0.2) = 0.36 - 0.35 + 0.12 = 0.13.

Still positive — barely. Bump the budget to ε=0.2\varepsilon = 0.2: δ=(0.2,+0.2,0.2)\delta = (-0.2, +0.2, -0.2), x=(0.3,0.6,0.1)x' = (0.3, 0.6, 0.1), and

s(x)=0.9(0.3)+(0.7)(0.6)+0.6(0.1)=0.270.42+0.06=0.09<0    SHORT.s(x') = 0.9(0.3) + (-0.7)(0.6) + 0.6(0.1) = 0.27 - 0.42 + 0.06 = -0.09 \lt 0 \;\Rightarrow\; \text{SHORT}.

A per-feature nudge of 0.2 — within the noise of a single feature’s normal range — drags the score from +0.35+0.35 to 0.09-0.09 and flips the position. Note that the total drop is exactly εw1=0.2×(0.9+0.7+0.6)=0.2×2.2=0.44\varepsilon\,\lVert w \rVert_1 = 0.2 \times (0.9 + 0.7 + 0.6) = 0.2 \times 2.2 = 0.44, matching 0.35(0.09)=0.440.35 - (-0.09) = 0.44. The linearity formula predicted it on the nose.

Adversarial examples: a tiny nudge that flips the trade
MomentumVolatilitySpreadVolumeRSIScore vs. decision boundary
OriginalPerturbed
Model score0.19Perturbation size (L∞)0.150Decision:SHORTflipped by the attack

Crank up epsilon: FGSM nudges each feature by epsilon times the sign of its weight — exactly down the model's own sensitivity — and drags the score across the boundary, flipping LONG to SHORT while the bars barely move. Switch to random noise of the identical L-infinity size and the decision usually survives. The danger isn't the size of the change; it's that the attacker aims it down the gradient.

Warning:

'FGSM is a tiny change, so it's harmless'

The whole trap is that small does not mean safe. The perturbation is small per feature and small under the LL_\infty norm, yet because it’s perfectly aligned with the model’s sensitivity it produces a decisive change in the decision. A risk check that flags only “unusual-looking” inputs will wave the attack straight through — every feature still sits in a plausible range. Magnitude is not the right alarm; alignment with the gradient is.

When to use it

FGSM is the cheap first probe — one gradient step — for screening whether a model is adversarially fragile at all. If a single FGSM step flips your decision at a tiny ε\varepsilon, you have a serious problem and don’t need a fancier attack to prove it. Use it as a fast red-team smoke test and as the per-step engine inside the stronger iterative attack below.

Sort each statement under whether it describes FGSM accurately or is a misconception about it.

Place each item in the right group.

  • Uses one gradient step (one forward + one backward pass)
  • Lands on a corner of the L-infinity ball
  • Perturbs each feature by the full budget in the sign of the gradient
  • It needs many iterations to construct a single example
  • It scales each feature by the raw gradient magnitude
  • Because the change is small per feature, it can't flip the decision

PGD — Projected Gradient Descent, the strongest first-order attack

Before you read — take a guess

Projected Gradient Descent strengthens FGSM. What is the role of the 'projection' step in each iteration?

Analogy. FGSM is one giant leap toward higher loss and hopes the ground was straight. But the loss surface bends — what looked uphill at the start may curve away. PGD is the patient mountaineer: take a small step uphill, look again, take another small step, re-aim, repeat — and whenever a step would wander outside the fenced-off region (the budget), step back to the fence. Many small re-aimed steps follow the terrain’s curves far better than one blind leap, so PGD reliably reaches higher loss (a more confident misclassification) than FGSM at the same budget.

Definition. Projected Gradient Descent (PGD) is the iterated, projected generalization of FGSM. Starting from x0=xx_0 = x (often with a small random kick), it repeats for t=0,1,2,t = 0, 1, 2, \dots:

xt+1=ProjB(x,ε) ⁣(xt+αsign(xL(xt,y))).x_{t+1} = \operatorname{Proj}_{\mathcal{B}(x,\,\varepsilon)}\!\Big( x_t + \alpha \cdot \operatorname{sign}\big(\nabla_x L(x_t, y)\big) \Big).

Here α\alpha is a small step size (smaller than ε\varepsilon), each step is an FGSM-style signed-gradient move, and ProjB(x,ε)\operatorname{Proj}_{\mathcal{B}(x,\varepsilon)} projects the result back into the LL_\infty ball B(x,ε)\mathcal{B}(x, \varepsilon) of radius ε\varepsilon around the original input — for the max-norm ball this projection is just clipping each coordinate to stay within ε\varepsilon of its original value. Because it re-computes the gradient at each new point, PGD adapts to curvature that a single FGSM step ignores. It is widely regarded as the strongest first-order attack — the benchmark adversary that defenses are expected to survive.

Worked example (intuition for the gain). Take the 3-feature model above near the boundary. A single FGSM step at ε=0.15\varepsilon = 0.15 might land the score at, say, +0.02+0.02just failing to flip. PGD with the same total budget ε=0.15\varepsilon = 0.15 but step size α=0.05\alpha = 0.05 over 5 iterations re-aims after each small move: if the local gradient shifts (because a feature hit its clip and another now matters more), PGD redirects the remaining budget toward the features that still move the score, and lands at, say, 0.04-0.04 — a successful flip the one-shot leap missed. Same budget, smarter spend, stronger attack. (For a purely linear model FGSM is already optimal; PGD’s edge shows the moment the decision surface bends, which any nonlinear model or any model with feature clipping/interactions does.)

PropertyFGSMPGD
Steps1many (e.g. 10–100)
Per-step rulefull budget in sign of gradientsmall step α\alpha in sign of gradient
Budget enforcementone shot, lands on ball cornerproject / clip back into ball every step
Follows curvature?no (one blind leap)yes (re-aims each step)
Strengthweak baselinestrongest first-order attack
Compute costcheapest (1 gradient)higher (one gradient per step)
Best used forfast fragility smoke testrigorous robustness evaluation
Tip:

Evaluate against PGD, screen with FGSM

A model can look robust to FGSM and still crumble under PGD — surviving a single blind leap says little about surviving a patient, re-aiming climber. So the honest robustness claim is always stated against a strong attack: “robust to PGD at budget ε\varepsilon with NN steps.” Use FGSM to cheaply find obvious holes; use PGD (multiple restarts, many steps) to certify you didn’t just defeat the weakling.

When to use it

Use PGD whenever you actually want to measure robustness rather than just smoke-test it — it’s the gold-standard adversary for stress-evaluating a trading model’s decision surface, and (in the next lessons) the inner loop of adversarial training. If a defense only ever reports FGSM numbers, treat the robustness claim as unproven: weak-attack success is the classic way robustness gets overstated.

Capture the PGD iteration in words.

Pick the right option for each blank, then check.

PGD repeatedly takes a , then after each step, which lets it .

Adversarial examples in markets — the realistic threat

Before you read — take a guess

In the vision setting the attacker edits pixels directly. In a live market, how does an adversary actually move YOUR model's input features?

Analogy. The vision attacker has Photoshop and edits the image. The market attacker has a trading account and edits the market — and your features are computed from that market, so editing the market edits your features at one remove. Quoting and cancelling a wall of orders (spoofing) tilts the order-flow imbalance your model reads; a burst of wash-like prints (painting the tape) bends the short-horizon momentum it reads. They never touch your code. They move the world your code measures.

Definition — the budget becomes dollars. In vision, ε\varepsilon is a pixel-intensity cap and the constraint δpε\lVert \delta \rVert_p \le \varepsilon keeps the image looking unchanged. In markets the perturbation δ\delta is a change in your features induced by trading, and the budget is no longer “imperceptible” — it’s how much capital and market impact the attacker is willing to spend to bend those features. The optimization becomes:

maxδ  (harm to you from flipping the signal)s.t.cost(δ)budget in dollars.\max_{\delta} \; (\text{harm to you from flipping the signal}) \quad \text{s.t.} \quad \text{cost}(\delta) \le \text{budget in dollars}.

The LpL_p ball is replaced by a cost ball shaped by market-impact and execution realities — moving spread is cheap, moving a deep, liquid imbalance is expensive — but the geometry is the same: a small, well-aimed push down your gradient flips your call.

Worked example. A predatory desk knows (or guesses) you run a momentum signal that goes long when 1-minute return momentum clears a threshold. Your stop sits just below. They estimate that pressing $2 million of aggressive sell flow over 90 seconds will push short-horizon momentum down by enough to (a) flip your model from long to flat and (b) trip your stop. If tripping your stop lets them buy back $3 million of inventory from you at a better price — a $2.5M gain against a $2M cost — the attack is positive expected value for them. Your ε\varepsilon — the engineered move in your momentum feature — was bought for $2M, and it cleared your boundary. That is an adversarial example with a P&L statement attached.

Transferability — the black-box menace. Attackers usually don’t have your exact model. They don’t need it. Because near-linear models share gradient directions (the linearity explanation again), an adversarial example crafted on a surrogate model — a plausible momentum/imbalance signal the attacker builds themselves — often transfers and flips your model too. So a competitor can red-team you without ever seeing your weights: attack their own reasonable guess of your signal, and the same flow tends to flip the real thing.

Warning:

Your stop-loss is an adversarial objective

The most uncomfortable reframing: a stop-loss is a known, exploitable decision boundary. Anyone who can infer roughly where your stops cluster has a target, and the cost to push price into that cluster is their ε\varepsilon. “Stop hunting” is not folklore — it’s an adversarial attack on a model (your risk rule) whose boundary the attacker reverse-engineered. The defense is to make the boundary uncertain and unprofitable to hit, not to pretend no one is looking.

When to use it

Think in dollars-as-ε\varepsilon whenever you assess a live signal’s exposure: estimate how much capital it would take an adversary to move each input feature across your boundary, and ask whether the payoff from flipping your signal (triggering your stop, fading your flow, front-running your fills) exceeds that cost. If flipping you is cheap relative to what it earns them, you are a target — not because your backtest is bad, but because your boundary is predictable and profitable to push. Robustify the signals whose boundaries sit cheaply within an adversary’s reach.

Pick a term, then click its definition.

Recap

You walked in thinking a 98%-accurate model was a safe model and walked out knowing that accuracy and robustness are nearly independent axes. The arc: an adversarial example is a small-under-a-norm, loss-maximizing input change; the linearity explanation says high-dimensional near-linear models are fragile because a tiny per-feature budget sums to a large score shift (εw1\varepsilon \lVert w \rVert_1, growing with dimension); FGSM builds such an example in one signed-gradient step; PGD iterates and projects to find a stronger one and is the benchmark adversary; and in markets the whole apparatus reappears with ε\varepsilon measured in dollars — an opponent who trades to bend your features, whose attacks transfer from a surrogate, and whose favorite boundary is your stop-loss.

Big picture

Adversarial examples on trading models

  • Adversarial examples
    • What it is
      • Small under an L-p norm (budget epsilon)
      • Maximizes loss / flips output
      • Off-distribution: backtests are silent
      • Markets: perturb the order book, move the features
    • Linearity explanation
      • Score shift = w · delta
      • Worst case = epsilon × sum of |w_i|
      • Grows with feature count d
      • Why attacks transfer
    • FGSM
      • One signed-gradient step
      • Full budget per feature
      • Lands on L-infinity ball corner
      • Fast fragility smoke test
    • PGD
      • Iterate small steps + project to ball
      • Re-aims with curvature
      • Strongest first-order attack
      • The benchmark to evaluate against
    • In markets
      • Epsilon becomes a dollar / impact budget
      • Spoof imbalance, paint the tape
      • Transferability → black-box attacks
      • Stop-hunting = attack on your boundary
Build the map: what an attack is, why near-linear models are fragile, the two core attacks, and how it all lands in a live market.

Mixed check: can your model survive a fight?

Question 1 of 50 correct

A linear trading score uses 100 features with average absolute weight 0.1. An attacker has an L-infinity budget of 0.01 per feature. What is the worst-case score shift, and why does it matter?

Check your answer to continue.

Mark lesson as complete