What is the DMI?
The Directional Movement Index was developed in 1978 by J. Welles Wilder as part of his Directional Movement System — the same system the ADX comes from. While the ADX only tells you how strong the market is trending, the two DMI lines answer the other half of the question: in which direction?
- +DI (Positive Directional Indicator) — strength of the upward move
- −DI (Negative Directional Indicator) — strength of the downward move
The simple reading: +DI above −DI → bulls are in control. −DI above +DI → bears. The gap between the two lines is the raw material from which the ADX is calculated in the first place.
Calculation
Step 1 — Directional Movement per bar: where was there more movement today, up or down? Only one side wins per bar.
up_move = high_t − high_{t−1}
down_move = low_{t−1} − low_t
+DM = up_move if up_move > down_move and up_move > 0 else 0
−DM = down_move if down_move > up_move and down_move > 0 else 0
Step 2 — smooth everything with Wilder's EMA (α = 1/period) and normalise it to the True Range (TR) so the lines are comparable as a percentage of total volatility:
+DI = 100 · EMA(+DM) / EMA(TR)
−DI = 100 · EMA(−DM) / EMA(TR)
The ADX is then derived from this:
DX = 100 · |+DI − −DI| / (+DI + −DI)
ADX = EMA(DX)
Because the ADX takes the absolute value of the difference, the information about which side is ahead is lost — this is exactly the information +DI and −DI give back.
Interpretation
| Constellation | Meaning |
|---|---|
| +DI > −DI | upward pressure dominates |
| −DI > +DI | downward pressure dominates |
| +DI crosses −DI from below | classic long signal (DI crossover) |
| −DI crosses +DI from below | classic short signal |
| +DI and −DI close together | tug-of-war → no trend (ADX falls) |
| both lines far apart | clear trend (ADX rises) |
How Botty uses the DMI
indicators/compute.py calculates +DI and −DI on every tick (as columns plus_di / minus_di) — they are the intermediate result of the ADX calculation and are persisted as well.
The main use is the regime filter (indicators/regime.py): the ADX decides whether there is a trend at all (≥ 25 with hysteresis), and once it says yes, the DMI picks the direction:
if adx >= 25:
regime = TREND_UP if plus_di >= minus_di else TREND_DOWN
This produces the three clean regime blocks trend_up / trend_down / range that the /regime chart colours — bar-for-bar identical to what the live strategies see. Without the DMI, only "trend yes/no" would be known, not "up or down".
What it delivers
✅ Gives the directionless ADX the missing direction information — only the two together form a complete trend picture. ✅ Volatility-normalised (via the TR), hence comparable across different market phases. ✅ Free: it falls out as an intermediate step of the ADX calculation anyway.
❌ DI crossovers alone are extremely whipsaw-prone in sideways markets — which is why Botty uses them only in combination with the ADX strength filter, never in isolation. ❌ Lagging (smoothed), reacts with a delay in fast reversals. ❌ Says nothing about the trend's duration — only about the current direction.