Quant Memo
Advanced

Directed Acyclic Graphs for Causality

A way to draw what causes what before you run a single regression, so you can tell in advance which variables to control for and which ones will wreck your estimate if you touch them.

Prerequisites: Correlation vs Causation, Omitted Variable Bias

You have three variables sitting in a spreadsheet — say, trading volume, a momentum signal, and next-day returns — and they are all correlated with each other. Before you regress returns on the signal, you need an answer to a question no p-value can give you: if you control for volume, does that make your estimate of the signal's effect more accurate, or does it destroy the very thing you're trying to measure? Regression itself is silent on this. It will happily hand you a coefficient whichever variables you throw in, and it will not warn you that adding the wrong one turned a real effect into noise, or turned no effect into a fake one. You need to reason about causal structure before you touch the data, and a directed acyclic graph is the tool for doing that reasoning on paper.

An analogy: the wiring diagram, not the blueprint

Think of a building's electrical wiring diagram. It doesn't show you what the building looks like — it shows you which switch controls which light, and which circuits feed off which breaker. If you want to know whether flipping switch A will affect light B, you don't need to inspect the wires with a multimeter; you trace the diagram. Does a path run from A to B? Does that path pass through anything that's already switched off?

A causal graph is the same kind of object for a set of variables instead of wires. An arrow from one variable to another means "this one pushes on that one." Once you've drawn the diagram — and you draw it from domain knowledge, not from the data — you can trace paths on paper to work out which correlations are real signal, which are confounding, and which ones you'd create by accident just by controlling for the wrong thing.

Nodes, edges, and three shapes worth knowing

A directed acyclic graph, or DAG, is built from nodes (the variables) and edges (arrows between them) with two rules: every edge has a direction, and you can never follow the arrows in a loop back to where you started — that's what "acyclic" means. No variable causes itself, even indirectly.

Almost every DAG you'll draw in finance is built out of three small shapes repeating.

The chain: XZYX \to Z \to Y. Volume affects liquidity, liquidity affects the bid-ask spread. XX influences YY, but only through ZZ. If you control for ZZ here, you block the very channel you wanted to study — this is a mistake, not a fix.

The fork: XZYX \leftarrow Z \to Y. A single cause, ZZ, drives two effects. Market-wide volatility (ZZ) drives both a stock's option-implied volatility (XX) and its realized volatility (YY). XX and YY will be correlated even though neither causes the other — ZZ is a confounder, and here you must control for ZZ or you'll mistake a shared cause for a direct relationship.

The collider: XZYX \to Z \leftarrow Y. Two independent causes both point into the same node. Say a stock's earnings quality (XX) and its recent price momentum (YY) both independently influence whether an analyst issues a "buy" rating (ZZ). XX and YY are unrelated in the wild. But if you only look at stocks with a buy rating — i.e., you condition on ZZ — you manufacture a correlation between XX and YY that isn't there in reality. This is the trap almost nobody expects.

P(X,YZ)P(XZ)P(YZ)even when XY.P(X, Y \mid Z) \neq P(X \mid Z)\, P(Y \mid Z) \quad \text{even when } X \perp Y .

In plain English: conditioning on a collider can make two genuinely independent variables look statistically dependent, even though nothing links them causally.

Chain X Z Y

Fork (confounder) Z X Y

Collider X Y Z

Chain: control for Z and you block the effect. Fork: control for Z and you remove a spurious link. Collider: control for Z and you create one out of nothing.

Worked example 1: manufacturing correlation with a collider

Suppose earnings quality XX and momentum YY are each drawn independently, and an analyst assigns "buy" (Z=1Z=1) whenever their sum crosses a threshold. Here is a small synthetic population of 8 stocks, with XX and YY each either 0 (poor) or 1 (strong), assigned independently at random.

StockX (earnings)Y (momentum)X+YZ = buy if X+Y ≥ 1
10000
20111
31011
41121
50000
60111
71011
81121

Across all 8 stocks, XX and YY are perfectly independent by construction: each combination of (0,0), (0,1), (1,0), (1,1) appears exactly twice. Now restrict to the buy-rated stocks only, Z=1Z=1 — stocks 2,3,4,6,7,8. Among these six, look at the relationship between XX and YY: whenever X=0X=0 (stocks 2, 6), YY is always 1. Whenever X=1X=1 (stocks 3,4,7,8), YY is 0 half the time and 1 half the time — but overall, low XX never coincides with low YY in this subsample, because a stock with poor earnings and poor momentum never gets a buy rating in the first place, so it's excluded. Within the buy-rated group, XX and YY now appear negatively associated — a stock analyst screening on "buy" has manufactured a correlation between two things that were never related. Conditioning on the collider ZZ did this, not any real economic link.

Worked example 2: a confounded momentum signal

You suspect a momentum signal (XX, past 3-month return) predicts next-month return (YY), and you want to know whether to control for trading volume (VV) in your regression. First draw the DAG: does volume cause momentum, does momentum cause volume, or is there a common driver? In practice, both momentum and volume tend to spike together after a common trigger — say, a sector-wide re-rating event (ZZ) that pushes both trading activity and recent price trend up at the same time, and ZZ also has its own direct effect on future returns through capital flows. That's a fork: ZXZ \to X, ZVZ \to V, ZYZ \to Y, plus the effect you actually care about, XYX \to Y.

Because ZZ (the re-rating event) is unobserved but drives both XX and YY, leaving it out biases your estimate of the momentum effect — this is exactly omitted variable bias restated as a graph. Volume VV sits downstream of ZZ, not on the causal path from XX to YY itself, so controlling for VV partially proxies for the missing ZZ and can reduce (though not eliminate) the bias, whereas controlling for a pure mediator sitting between XX and YY would destroy the effect you're measuring. The DAG is what tells you, before running any regression, which of the two moves you're making.

Z re-rating event (unobserved) X momentum V volume Y next return effect of interest
Z is a fork feeding both X and Y — leaving it out biases the X→Y estimate. V sits downstream of Z, not on the X→Y path, so it is a candidate control, not a mediator to avoid.

What this means in practice

A backdoor path is any path connecting your treatment and outcome that doesn't run through the causal arrow you care about — it flows backward out of the treatment node, along confounding arrows. Blocking a backdoor path means controlling for a variable that sits on it, at a fork or a chain, so the spurious flow of association is cut off. D-separation, stated without the notation, is just: two variables are independent, given some conditioning set, if every backdoor path between them is blocked and no collider on any path has been inadvertently opened. In quant work, this shows up constantly: control-variable choice in a factor regression, deciding whether to condition on a mediator like sector membership, or figuring out why adding a "liquidity" control flipped the sign of a signal's coefficient. Get the graph wrong and no amount of data will save the estimate — more data just makes a wrong answer more precise.

Draw the causal graph before you choose control variables. Controlling for a confounder (a fork) removes bias; controlling for a mediator (a chain) removes the very effect you're studying; controlling for a collider creates bias that wasn't there. Same statistical action, three different outcomes, and only the DAG tells you which one you're doing.

The most common mistake is "control for everything you have data on" — a kitchen-sink regression. This is safe only if every control is a pure confounder. In practice, data sets are full of colliders and mediators disguised as innocent-looking controls (a rating, a flag, an index-membership indicator — all downstream of multiple causes). Throwing them into a regression "just to be safe" can bias an unbiased estimate or hide a real effect entirely. The DAG forces you to justify each control causally, not just statistically.

Related concepts

Practice in interviews

Further reading

  • Pearl, Causality: Models, Reasoning, and Inference, ch. 1-3
  • Cunningham, Causal Inference: The Mixtape, ch. 3
ShareTwitterLinkedIn