Vortex Indicator (VI)
I. What is the Vortex Indicator
The Vortex Indicator (VI) was first published in 2010 by Swiss engineer Etienne Botes and market analyst Douglas Siepman in the Technical Analysis of Stocks & Commodities magazine.
The Vortex Indicator belongs to the Trend Direction class of indicators. It identifies the direction and turning points of a trend by comparing Positive Vortex Movement (+VM) and Negative Vortex Movement (-VM). The default period is .
The indicator draws its inspiration from Austrian naturalist Viktor Schauberger (1885—1958) and his research on vortex movements in water. Schauberger observed that water flow contains both positive and negative vortex forces. Botes and Siepman applied this natural phenomenon as an analogy to financial market price movements: the forces of rising and falling prices behave like two intertwined vortex streams.
The core idea of the Vortex Indicator is: in a strong uptrend, the distance between the current high and the previous low (positive vortex force) will consistently exceed the distance between the current low and the previous high (negative vortex force), and vice versa.
II. Mathematical Principles and Calculation
2.1 Vortex Movement (VM)
Positive Vortex Movement (+VM):
Negative Vortex Movement (-VM):
Where is the current high, is the current low, and and are the previous bar’s high and low respectively.
2.2 True Range (TR)
2.3 Vortex Indicators (+VI / -VI)
Sum +VM, -VM, and TR over periods respectively:
Note that the Vortex Indicator uses rolling sums rather than moving averages. The values of +VI and -VI typically fluctuate between 0.7 and 1.3. Using TR as the denominator provides normalization, making the indicator comparable across instruments with different volatility levels.
2.4 Calculation Steps Summary
- Calculate +VM = |current high - previous low| for each bar
- Calculate -VM = |current low - previous high| for each bar
- Calculate the True Range for each bar
- Sum +VM, -VM, and TR over period respectively
- +VI = Sum(+VM) / Sum(TR)
- -VI = Sum(-VM) / Sum(TR)
III. Python Implementation
import numpy as np
import pandas as pd
def vortex(high: pd.Series, low: pd.Series, close: pd.Series,
period: int = 14) -> pd.DataFrame:
"""
Calculate the Vortex Indicator.
Parameters:
high : Series of high prices
low : Series of low prices
close : Series of closing prices
period : Rolling sum period, default 14
Returns:
DataFrame containing plus_vi, minus_vi columns
"""
# Vortex Movement
plus_vm = (high - low.shift(1)).abs()
minus_vm = (low - high.shift(1)).abs()
# True Range
prev_close = close.shift(1)
tr1 = high - low
tr2 = (high - prev_close).abs()
tr3 = (low - prev_close).abs()
tr = pd.concat([tr1, tr2, tr3], axis=1).max(axis=1)
# Rolling sums
sum_plus_vm = plus_vm.rolling(window=period).sum()
sum_minus_vm = minus_vm.rolling(window=period).sum()
sum_tr = tr.rolling(window=period).sum()
# Vortex Indicators
plus_vi = sum_plus_vm / sum_tr
minus_vi = sum_minus_vm / sum_tr
return pd.DataFrame({
'plus_vi': plus_vi,
'minus_vi': minus_vi
})
# ============ Usage Example ============
if __name__ == '__main__':
np.random.seed(42)
n_days = 120
# Generate simulated OHLCV data with alternating trends
trend = np.concatenate([
np.linspace(0, 8, 40), # Uptrend
np.linspace(8, 3, 40), # Downtrend
np.linspace(3, 10, 40) # Uptrend
])
noise = np.cumsum(np.random.randn(n_days) * 0.2)
base_price = 50 + trend + noise
df = pd.DataFrame({
'open': base_price + np.random.randn(n_days) * 0.2,
'high': base_price + np.abs(np.random.randn(n_days) * 0.5),
'low': base_price - np.abs(np.random.randn(n_days) * 0.5),
'close': base_price,
'volume': np.random.randint(1000, 10000, n_days)
})
# Calculate Vortex Indicator
vi = vortex(df['high'], df['low'], df['close'], period=14)
df = pd.concat([df, vi], axis=1)
print("=== Vortex Indicator Results (Last 10 Rows) ===")
print(df[['close', 'plus_vi', 'minus_vi']].tail(10).round(4).to_string())
# Crossover signal detection
df['signal'] = np.nan
valid = df.dropna(subset=['plus_vi', 'minus_vi'])
for i in range(1, len(valid)):
idx = valid.index[i]
prev_idx = valid.index[i - 1]
if (valid.loc[idx, 'plus_vi'] > valid.loc[idx, 'minus_vi'] and
valid.loc[prev_idx, 'plus_vi'] <= valid.loc[prev_idx, 'minus_vi']):
df.loc[idx, 'signal'] = 1 # Bullish crossover
elif (valid.loc[idx, 'minus_vi'] > valid.loc[idx, 'plus_vi'] and
valid.loc[prev_idx, 'minus_vi'] <= valid.loc[prev_idx, 'plus_vi']):
df.loc[idx, 'signal'] = -1 # Bearish crossover
signals = df.dropna(subset=['signal'])
print(f"\n=== Crossover Signals (Total: {len(signals)}) ===")
for _, row in signals.iterrows():
direction = "Bullish (+VI crosses above -VI)" if row['signal'] == 1 else "Bearish (-VI crosses above +VI)"
print(f" Price={row['close']:.2f} +VI={row['plus_vi']:.4f} -VI={row['minus_vi']:.4f} {direction}")
IV. Problems the Indicator Solves
4.1 Trend Direction Identification
The Vortex Indicator directly reflects trend direction through the relative position of +VI and -VI:
- +VI > -VI: uptrend dominates, positive vortex force is stronger
- -VI > +VI: downtrend dominates, negative vortex force is stronger
- +VI approx -VI: market direction is unclear, possibly in consolidation
4.2 Trend Reversal Signals
Crossovers of +VI and -VI provide early signals of trend reversals:
- +VI crosses above -VI: bullish signal, a potential shift from downtrend to uptrend
- -VI crosses above +VI: bearish signal, a potential shift from uptrend to downtrend
4.3 Trend Strength Assessment
The wider the gap between +VI and -VI, the more defined the trend:
| Condition | Interpretation |
|---|---|
| +VI and -VI gap is large (> 0.2) | Strong and well-defined trend |
| +VI and -VI gap is small (< 0.1) | Weak trend or trend transition in progress |
| Two lines intertwined | Range-bound market, no clear trend |
In sideways, range-bound markets, +VI and -VI will cross frequently, generating numerous false signals. It is recommended to combine with ADX or other trend strength indicators for filtering: only execute Vortex crossover signals when ADX > 20.
4.4 Typical Trading Strategies
- Basic Crossover Strategy: go long when +VI crosses above -VI; go short when -VI crosses above +VI
- Threshold Filter Strategy: only confirm a bullish signal when +VI exceeds a threshold (e.g., 1.10)
- Multi-Indicator Confirmation Strategy: enter when Vortex crossover + moving average direction agree + ADX > 25
- Stop-Loss Strategy: use the swing low/high before the crossover point as the stop-loss level
V. Advantages, Disadvantages, and Use Cases
Advantages
| Advantage | Description |
|---|---|
| Simple and intuitive calculation | Only requires summation and division; no complex recursive smoothing |
| Clear signals | +VI and -VI crossovers provide unambiguous trend change signals |
| Captures gaps | Normalized through True Range, correctly reflects the impact of price gaps |
| Good symmetry | The calculation structure of +VI and -VI is perfectly symmetric, treating both bullish and bearish directions equally |
Disadvantages
| Disadvantage | Description |
|---|---|
| Many false signals in range-bound markets | Frequent crossovers during sideways action, low signal reliability |
| Does not quantify trend strength | Unlike ADX, it does not provide a 0—100 strength reading |
| Period sensitivity | Too short a period increases noise; too long reduces responsiveness |
| Lacks built-in filtering mechanism | Requires additional indicators for confirmation |
Use Cases
- Best suited for: capturing trend direction and reversals in clearly trending markets
- Well suited for: serving as a directional confirmation component in multi-indicator systems
- Not suited for: low-volatility, range-bound markets
Comparison with Similar Indicators
| Indicator | Signal Method | Characteristics |
|---|---|---|
| Vortex Indicator | +VI/-VI crossover | Simple calculation; inspired by natural vortex movement |
| ADX/DMI | +DI/-DI crossover + ADX strength | More comprehensive (direction + strength); but more lagging |
| Aroon | Aroon Up/Down crossover | Based on position of highs/lows; faster response |
| MACD | Signal line crossover | Based on moving average difference; more momentum-focused |
Parameter Tuning Recommendations: The default 14-period setting is suitable for daily chart analysis. Botes and Siepman suggest adjusting for different time frames: use 21—34 periods for weekly charts, and 7—14 periods for hourly charts. If you encounter too many false signals, consider increasing the period for smoother output. You can also add a simple “signal confirmation” rule — wait 1—2 bars after the crossover to confirm the direction before entering.