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: . Volume affects liquidity, liquidity affects the bid-ask spread. influences , but only through . If you control for here, you block the very channel you wanted to study — this is a mistake, not a fix.
The fork: . A single cause, , drives two effects. Market-wide volatility () drives both a stock's option-implied volatility () and its realized volatility (). and will be correlated even though neither causes the other — is a confounder, and here you must control for or you'll mistake a shared cause for a direct relationship.
The collider: . Two independent causes both point into the same node. Say a stock's earnings quality () and its recent price momentum () both independently influence whether an analyst issues a "buy" rating (). and are unrelated in the wild. But if you only look at stocks with a buy rating — i.e., you condition on — you manufacture a correlation between and that isn't there in reality. This is the trap almost nobody expects.
In plain English: conditioning on a collider can make two genuinely independent variables look statistically dependent, even though nothing links them causally.
Worked example 1: manufacturing correlation with a collider
Suppose earnings quality and momentum are each drawn independently, and an analyst assigns "buy" () whenever their sum crosses a threshold. Here is a small synthetic population of 8 stocks, with and each either 0 (poor) or 1 (strong), assigned independently at random.
| Stock | X (earnings) | Y (momentum) | X+Y | Z = buy if X+Y ≥ 1 |
|---|---|---|---|---|
| 1 | 0 | 0 | 0 | 0 |
| 2 | 0 | 1 | 1 | 1 |
| 3 | 1 | 0 | 1 | 1 |
| 4 | 1 | 1 | 2 | 1 |
| 5 | 0 | 0 | 0 | 0 |
| 6 | 0 | 1 | 1 | 1 |
| 7 | 1 | 0 | 1 | 1 |
| 8 | 1 | 1 | 2 | 1 |
Across all 8 stocks, and 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, — stocks 2,3,4,6,7,8. Among these six, look at the relationship between and : whenever (stocks 2, 6), is always 1. Whenever (stocks 3,4,7,8), is 0 half the time and 1 half the time — but overall, low never coincides with low 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, and 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 did this, not any real economic link.
Worked example 2: a confounded momentum signal
You suspect a momentum signal (, past 3-month return) predicts next-month return (), and you want to know whether to control for trading volume () 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 () that pushes both trading activity and recent price trend up at the same time, and also has its own direct effect on future returns through capital flows. That's a fork: , , , plus the effect you actually care about, .
Because (the re-rating event) is unobserved but drives both and , leaving it out biases your estimate of the momentum effect — this is exactly omitted variable bias restated as a graph. Volume sits downstream of , not on the causal path from to itself, so controlling for partially proxies for the missing and can reduce (though not eliminate) the bias, whereas controlling for a pure mediator sitting between and 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.
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