Momentum:CTI(相关趋势指标)技术指标详解
一、CTI的定义
CTI(Correlation Trend Indicator,相关趋势指标) 是由著名技术分析师、数字信号处理专家John Ehlers于2020年在《股票与商品期货技术分析》(Stocks & Commodities)杂志上正式发表的一种新型动量震荡指标。
核心思想
CTI通过测量价格序列与理想趋势线 之间的统计相关性来判断市场的趋势强度。其中,"理想趋势线"被定义为一条具有恒定向上斜率的直线。CTI的本质是斯皮尔曼相关系数(Spearman Correlation Coefficient)在技术分析中的应用------它量化了当前价格走势与理想上升趋势之间的拟合程度。
与传统的趋势跟踪指标(如ADX、MACD)不同,CTI输出的数值严格限定在-1到+1之间,具有明确的统计意义。
CTI的核心特征
| 特征 | 说明 |
|---|---|
| 数值范围 | -1 到 +1(与相关系数相同) |
| 理论基础 | 斯皮尔曼等级相关系数 |
| 零轴分界 | +1表示完美上升趋势,-1表示完美下降趋势,0表示无趋势/随机 |
| 开发者 | John Ehlers(2020年) |
| 默认周期 | 12或20 |
CTI的设计背景
Ehlers开发CTI的初衷是创建一个既客观又直观的趋势测量工具。传统的趋势指标要么依赖于主观的参数选择,要么缺乏统一的度量标准。CTI通过引入严格的统计学方法,将复杂的趋势分析简化为一个在-1到+1之间波动的单值,使得不同品种、不同时间周期的趋势强度具有了可比性。
CTI与其他趋势指标的对比
| 对比维度 | CTI | ADX | RSI |
|---|---|---|---|
| 数值范围 | -1 到 +1(对称) | 0 到 100 | 0 到 100 |
| 趋势方向 | 同时提供方向(正负) | 不提供方向(仅强度) | 不直接提供 |
| 理论基础 | 统计相关性 | 价格波动扩张 | 涨跌幅比率 |
| 趋势识别 | 严格统计意义 | 经验阈值判断 | 超买超卖判断 |
二、CTI的计算方法
1. 核心公式
CTI的核心计算公式为:
CTI=SpearmanCorrelation(价格,理想趋势线) \mathrm{CTI} = \mathrm{SpearmanCorrelation}(价格,理想趋势线) CTI=SpearmanCorrelation(价格,理想趋势线)
其中,"理想趋势线"是一条从1到 nnn 的递增序列(1,2,3,⋯ ,n1,2,3,\cdots,n1,2,3,⋯,n)。
2. 斯皮尔曼相关系数分解
设周期为 nnn 的价格序列为 P1,P2,...,PnP_1,P_2,...,P_nP1,P2,...,Pn(PnP_nPn 为最新价格),理想趋势线序列为 T=1,2,3,⋯ ,nT = 1,2,3,\cdots,nT=1,2,3,⋯,n。
步骤一:计算价格序列的秩(Rank)
对于价格序列 PiP_iPi,计算其在 nnn 个价格中的排名 Rank(Pi)\mathrm{Rank}(P_i)Rank(Pi)(升序排列,最低价为1,最高价为 nnn)。如有价格相等,取平均秩次。
步骤二:计算秩差值
对于每个位置 iii,计算价格秩与趋势线秩的差值:
di=Rank(Pi)−i d_i = \mathrm{Rank}(P_i) - i di=Rank(Pi)−i
步骤三:计算斯皮尔曼相关系数
CTI=1−6∑i=1ndi2n(n2−1) \mathrm{CTI} = 1 - \frac{6\sum_{i=1}^n d_i^2}{n(n^2-1)} CTI=1−n(n2−1)6∑i=1ndi2
3. pandas_ta的简化实现
根据pandas_ta官方文档,CTI 的实现是线性回归相关系数的包装函数:
CTI=corr(价格,趋势线) \mathrm{CTI} = \mathrm{corr}(价格, 趋势线) CTI=corr(价格,趋势线)
其中 corrcorr 为皮尔逊相关系数。这一简化在数学上与斯皮尔曼相关系数有细微差异,但在大多数应用中结果相近。
皮尔逊相关系数公式:
CTI=∑i=1n(Pi−Pˉ)(Ti−Tˉ)∑i=1n(Pi−Pˉ)2∑i=1n(Ti−Tˉ)2 \mathrm{CTI} = \frac{\sum_{i=1}^n(P_i-\bar{P})(T_i - \bar{T})}{\sqrt{\sum_{i=1}^n(P_i-\bar{P})^2} \sqrt{\sum_{i=1}^n (T_i - \bar{T})^2}} CTI=∑i=1n(Pi−Pˉ)2 ∑i=1n(Ti−Tˉ)2 ∑i=1n(Pi−Pˉ)(Ti−Tˉ)
其中:
- Pˉ\bar{P}Pˉ:价格序列的均值
- Tˉ\bar{T}Tˉ:趋势线序列的均值
4. 参数说明
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
length |
int | 12 | CTI计算周期(John Ehlers通常建议12或20) |
offset |
int | 0 | 结果偏移周期数 |
5. 计算示例
假设某股票最近5个交易日(length=5)的收盘价如下:
| 位置(iii) | 趋势线(TiT_iTi) | 收盘价(PiP_iPi) | Rank(Pi)\mathrm{Rank}(P_i)Rank(Pi) | di=Rank(Pi)−id_i = \mathrm{Rank}(P_i)-idi=Rank(Pi)−i | di2d_i^2di2 |
|---|---|---|---|---|---|
| 1 | 1 | 100 | 1 | 0 | 0 |
| 2 | 2 | 102 | 2 | 0 | 0 |
| 3 | 3 | 105 | 4 | 1 | 1 |
| 4 | 4 | 103 | 3 | -1 | 1 |
| 5 | 5 | 108 | 5 | 0 | 0 |
计算过程:
- 价格排序:100(1), 102(2), 103(3), 105(4), 108(5)
- n=5n=5n=5,∑di2=0+0+1+1+0=2\sum d_i^2=0+0+1+1+0=2∑di2=0+0+1+1+0=2
- CTI=1−6×25×(25−1)=1−12120=1−0.1=0.9\mathrm{CTI} = 1− \frac{6 \times 2}{5 \times (25−1)} = 1 − \frac{12}{120} = 1 − 0.1 = 0.9CTI=1−5×(25−1)6×2=1−12012=1−0.1=0.9
该CTI值为0.9,表示价格序列与理想趋势线的相关性极高,存在强上升趋势。
6. 斯皮尔曼 vs 皮尔逊的区别
| 对比维度 | 斯皮尔曼相关系数 | 皮尔逊相关系数 |
|---|---|---|
| 测量对象 | 等级序的相关性 | 线性相关程度 |
| 对异常值敏感度 | 低(基于秩次) | 高(基于原始值) |
| 对数据分布要求 | 无(非参数) | 需近似正态分布 |
| pandas_ta实现 | --- | 采用皮尔逊法 |
三、CTI的使用方法
1. 趋势强度判断------最核心用法
CTI的正负值和绝对值大小反映了趋势的方向和强度:
| CTI区间 | 趋势含义 | 操作倾向 |
|---|---|---|
| CTI > 0.5 | 强上升趋势 | 价格与理想趋势高度相关,多头强势持仓 |
| 0.2 < CTI ≤ 0.5 | 中等上升趋势 | 上升趋势存在但不够强劲,顺势持仓 |
| 0 < CTI ≤ 0.2 | 弱上升趋势 | 上升趋势初期或末期,谨慎做多 |
| -0.2 ≤ CTI < 0 | 弱下降趋势 | 下降趋势初期或末期,谨慎做空 |
| -0.5 ≤ CTI < -0.2 | 中等下降趋势 | 下降趋势存在,顺势持仓空头 |
| CTI < -0.5 | 强下降趋势 | 价格与理想趋势呈强负相关,空头强势 |
CTI ≈ 0:价格序列呈随机漫步状态,无明显趋势,建议观望。
2. 零轴穿越
CTI围绕零轴波动,零轴是多空力量的分界线:
| 信号类型 | 触发条件 | 含义 | 操作建议 |
|---|---|---|---|
| 看涨穿越 | CTI从下方向上穿越零轴 | 市场从无趋势转向正相关,上升趋势可能启动 | 考虑建立多头仓位 |
| 看跌穿越 | CTI从上方向下穿越零轴 | 市场从正相关转向无趋势/负相关,上升趋势可能结束 | 考虑平仓或建立空头 |
3. 趋势衰竭识别
CTI值处于高位(>0.5)时,若开始从高点回落,可能意味着:
| CTI状态 | 含义 | 操作建议 |
|---|---|---|
| CTI从>0.5的高位回落 | 趋势强度正在减弱 | 警惕趋势反转,考虑减仓 |
| CTI连续多周期下降 | 趋势动能持续衰减 | 趋势可能即将结束,做好离场准备 |
4. 底背离与顶背离
与价格走势的背离是CTI的重要辅助信号:
| 背离类型 | 价格表现 | CTI表现 | 信号含义 |
|---|---|---|---|
| 顶背离 | 价格创出新高 | CTI未能创出新高(形成更低高点) | 上涨趋势强度减弱,警惕见顶 |
| 底背离 | 价格创出新低 | CTI未能创出新低(形成更高低点) | 下跌趋势强度减弱,关注反弹 |
5. 与其他指标的配合
| 配合指标 | 作用 | 具体用法 |
|---|---|---|
| 移动平均线 | 确认价格位置 | CTI > 0.5 + 价格在MA20上方 → 强趋势确认 |
| ADX | 验证趋势强度 | CTI与ADX同时显示强趋势时信号更可靠 |
| 成交量 | 验证动能 | CTI上升伴随成交量放大 → 趋势确认 |
| 支撑/阻力位 | 确认关键位置 | CTI信号出现在关键价位时可靠性更高 |
6. 注意事项与局限性
使用CTI前需了解以下要点:
| 局限性 | 说明 |
|---|---|
| 计算窗口敏感 | CTI仅关注固定窗口内的相关性,可能忽略更长时间尺度的趋势结构 |
| 滞后性质 | 基于历史数据的相关系数计算,本质上有滞后性 |
| 震荡市假信号 | 在横盘整理市场中,CTI可能在零轴附近随机波动,产生假穿越信号 |
| 参数需要优化 | 默认12周期可能不适合所有品种和时间框架,需要根据实际情况调整 |
| 不能单独使用 | 建议结合其他趋势确认指标使用 |
四、使用pandas_ta计算CTI(附示例代码)
1. pandas_ta中的CTI函数
pandas_ta库内置了CTI指标的完整实现,函数位于momentum模块中。
2. 函数完整参数
python
pandas_ta.momentum.cti(close, length=None, offset=None, **kwargs)
参数详解:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
close |
pd.Series | 必需 | 收盘价序列 |
length |
int | 12 | CTI计算周期(John Ehlers建议12或20) |
offset |
int | 0 | 结果偏移周期数 |
fillna |
(可选) | --- | 填充NaN值的方式 |
返回值 :pd.Series------CTI值序列(范围-1到+1)。
注意:pandas_ta中的CTI实现使用皮尔逊相关系数,而非原始的斯皮尔曼相关系数。在多数实际应用中两种方法给出的趋势方向判断是一致的。
3. 示例代码
python
import pandas as pd
import pandas_ta as ta
import numpy as np
import matplotlib.pyplot as plt
# ========== 第一步:准备数据 ==========
np.random.seed(42)
dates = pd.date_range(start='2024-01-01', end='2024-12-31', freq='D')
n = len(dates)
# 生成带趋势和周期波动的价格序列
trend = np.linspace(0, 25, n)
cycle = np.sin(np.linspace(0, 6 * np.pi, n)) * 8
noise = np.random.randn(n) * 2
price_series = 100 + trend + cycle + noise
df = pd.DataFrame(index=dates)
df['close'] = price_series
df['high'] = df['close'] + np.abs(np.random.randn(n)) * 2 + 1
df['low'] = df['close'] - np.abs(np.random.randn(n)) * 2 - 1
df['volume'] = np.random.randint(1000000, 10000000, n)
print("=" * 60)
print("数据预览:")
print(df.head())
print("\n" + "=" * 60 + "\n")
# ========== 第二步:计算CTI(基础用法) ==========
# 使用默认参数 length=12
df['CTI'] = ta.cti(df['close'])
print("CTI计算结果(最近10行):")
print(df[['close', 'CTI']].tail(10))
print("\n" + "=" * 60 + "\n")
# ========== 第三步:手动验证CTI计算 ==========
def manual_cti(close, length=12):
"""手动计算CTI验证pandas_ta结果"""
# 创建理想趋势线
trend_line = np.arange(1, length + 1)
n = length
cti_values = []
for i in range(len(close)):
if i < length - 1:
cti_values.append(np.nan)
continue
# 取最近length个价格
prices = close.iloc[i-length+1:i+1].values
# 计算皮尔逊相关系数
corr_matrix = np.corrcoef(prices, trend_line)
cti = corr_matrix[0, 1]
cti_values.append(cti)
return pd.Series(cti_values, index=close.index)
df['CTI_manual'] = manual_cti(df['close'])
# 验证一致性
diff = (df['CTI'] - df['CTI_manual']).abs().max()
print(f"pandas_ta与手动计算的最大差异:{diff:.10f}")
print("\n" + "=" * 60 + "\n")
# ========== 第四步:不同周期的CTI ==========
# 短线参数 (9)
df['CTI_9'] = ta.cti(df['close'], length=9)
# 标准参数 (12)
df['CTI_12'] = ta.cti(df['close'], length=12)
# 长线参数 (20)
df['CTI_20'] = ta.cti(df['close'], length=20)
print("不同周期CTI对比(最近5行):")
print(df[['close', 'CTI_9', 'CTI_12', 'CTI_20']].tail())
print("\n" + "=" * 60 + "\n")
# ========== 第五步:生成趋势强度信号 ==========
# 定义趋势强度阈值
STRONG_TREND = 0.5
WEAK_TREND = 0.2
df['trend_signal'] = ''
df.loc[df['CTI'] > STRONG_TREND, 'trend_signal'] = '强上升趋势'
df.loc[(df['CTI'] > WEAK_TREND) & (df['CTI'] <= STRONG_TREND), 'trend_signal'] = '中等上升趋势'
df.loc[(df['CTI'] > 0) & (df['CTI'] <= WEAK_TREND), 'trend_signal'] = '弱上升趋势'
df.loc[(df['CTI'] < 0) & (df['CTI'] >= -WEAK_TREND), 'trend_signal'] = '弱下降趋势'
df.loc[(df['CTI'] < -WEAK_TREND) & (df['CTI'] >= -STRONG_TREND), 'trend_signal'] = '中等下降趋势'
df.loc[df['CTI'] < -STRONG_TREND, 'trend_signal'] = '强下降趋势'
print("趋势信号统计:")
print(df['trend_signal'].value_counts())
print("\n" + "=" * 60 + "\n")
# ========== 第六步:零轴穿越信号 ==========
df['CTI_above_zero'] = df['CTI'] > 0
df['cross_above_zero'] = (df['CTI_above_zero'] == True) & (df['CTI_above_zero'].shift(1) == False)
df['cross_below_zero'] = (df['CTI_above_zero'] == False) & (df['CTI_above_zero'].shift(1) == True)
df['signal'] = ''
df.loc[df['cross_above_zero'], 'signal'] = '买入(上穿零轴)'
df.loc[df['cross_below_zero'], 'signal'] = '卖出(下穿零轴)'
print("零轴穿越信号(最近10个):")
signals = df[df['signal'] != ''].tail(10)
if not signals.empty:
print(signals[['close', 'CTI', 'signal']])
else:
print("近期无穿越信号")
print("\n" + "=" * 60 + "\n")
# ========== 第七步:趋势衰竭检测 ==========
# 检测CTI从高位回落的趋势衰竭信号
df['cti_peak'] = (df['CTI'] > 0.5) & (df['CTI'] < df['CTI'].shift(1)) & (df['CTI'].shift(1) > df['CTI'].shift(2))
df['trend_exhaustion'] = df['cti_peak'] & (df['CTI'].shift(1) > 0.6)
print("趋势衰竭信号统计:")
print(f"高位回落(趋势衰竭预警)数量:{df['trend_exhaustion'].sum()}")
print("\n" + "=" * 60 + "\n")
# ========== 第八步:背离检测(简化版) ==========
# 计算20日价格变化和CTI变化
df['price_change_20d'] = df['close'].diff(20)
df['cti_change_20d'] = df['CTI'].diff(20)
# 检测潜在背离
df['potential_bearish_div'] = (df['price_change_20d'] > 0) & (df['cti_change_20d'] < 0)
df['potential_bullish_div'] = (df['price_change_20d'] < 0) & (df['cti_change_20d'] > 0)
print("潜在背离信号统计(20日尺度):")
print(f"潜在顶背离(价格涨CTI跌)数量:{df['potential_bearish_div'].sum()}")
print(f"潜在底背离(价格跌CTI涨)数量:{df['potential_bullish_div'].sum()}")
print("注:完整背离需结合局部高/低点分析,此处为方向对比")
print("\n" + "=" * 60 + "\n")
# ========== 第九步:策略回测(零轴穿越策略) ==========
# 策略:CTI > 0 时持仓多头
df['position'] = (df['CTI'] > 0).astype(int)
# 计算收益
df['returns'] = df['close'].pct_change()
df['strategy_returns'] = df['position'].shift(1) * df['returns']
total_return_buyhold = (1 + df['returns']).prod() - 1
total_return_strategy = (1 + df['strategy_returns']).prod() - 1
win_rate = (df['strategy_returns'] > 0).sum() / (df['strategy_returns'] != 0).sum() if (df['strategy_returns'] != 0).sum() > 0 else 0
print("=" * 60)
print("策略绩效统计(CTI零轴穿越策略回测):")
print(f"买入持有策略总收益率:{total_return_buyhold:.2%}")
print(f"CTI零轴穿越策略总收益率:{total_return_strategy:.2%}")
print(f"策略胜率:{win_rate:.2%}")
print("\n注意:此为简化回测,仅供参考")
print("=" * 60 + "\n")
# ========== 第十步:可视化 ==========
plt.figure(figsize=(14, 10))
# 子图1:价格走势
plt.subplot(2, 1, 1)
plt.plot(df.index[-150:], df['close'][-150:], label='Close Price',
linewidth=1.5, color='black')
plt.title('Price Chart (Last 150 days)', fontsize=14)
plt.ylabel('Price')
plt.legend()
plt.grid(True, alpha=0.3)
# 子图2:CTI指标
plt.subplot(2, 1, 2)
plt.plot(df.index[-150:], df['CTI'][-150:], label='CTI (length=12)',
linewidth=1.5, color='blue')
plt.axhline(y=0, color='gray', linestyle='-', linewidth=1, label='零轴')
plt.axhline(y=0.5, color='green', linestyle='--', linewidth=1, label='强趋势线 (+0.5)')
plt.axhline(y=-0.5, color='red', linestyle='--', linewidth=1, label='强趋势线 (-0.5)')
plt.axhline(y=0.2, color='orange', linestyle=':', linewidth=0.8, label='弱趋势线 (+0.2)')
plt.axhline(y=-0.2, color='orange', linestyle=':', linewidth=0.8, label='弱趋势线 (-0.2)')
# 填充强趋势区域
plt.fill_between(df.index[-150:], 0.5, 1, alpha=0.1, color='green', label='强上升区')
plt.fill_between(df.index[-150:], -1, -0.5, alpha=0.1, color='red', label='强下降区')
# 标记买卖点
buy_signals = df[df['cross_above_zero']]
sell_signals = df[df['cross_below_zero']]
plt.scatter(buy_signals.index[-20:], buy_signals['CTI'][-20:],
color='green', marker='^', s=80, label='买入信号', alpha=0.8)
plt.scatter(sell_signals.index[-20:], sell_signals['CTI'][-20:],
color='red', marker='v', s=80, label='卖出信号', alpha=0.8)
plt.title('CTI (Correlation Trend Indicator) - Trend Strength Analysis', fontsize=14)
plt.xlabel('Date')
plt.ylabel('CTI Value')
plt.ylim(-1.1, 1.1)
plt.legend(loc='upper left')
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
# ========== 第十一步:不同周期CTI对比 ==========
plt.figure(figsize=(14, 6))
plt.plot(df.index[-150:], df['CTI_9'][-150:], label='CTI (9) - Fast',
linewidth=1, alpha=0.7)
plt.plot(df.index[-150:], df['CTI_12'][-150:], label='CTI (12) - Standard',
linewidth=1.5, color='blue')
plt.plot(df.index[-150:], df['CTI_20'][-150:], label='CTI (20) - Slow',
linewidth=1.5, color='green')
plt.axhline(y=0, color='gray', linestyle='-', linewidth=0.8)
plt.axhline(y=0.5, color='green', linestyle='--', linewidth=0.8)
plt.axhline(y=-0.5, color='red', linestyle='--', linewidth=0.8)
plt.title('CTI: Different Periods Comparison (9, 12, 20)', fontsize=14)
plt.xlabel('Date')
plt.ylabel('CTI Value')
plt.ylim(-1.1, 1.1)
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()
# ========== 第十二步:数据清洗提示 ==========
nan_count = df['CTI'].isna().sum()
print(f"\nCTI初始NaN数量:{nan_count}")
print("原因:CTI需要至少length个周期的数据才能计算相关系数")
print("处理建议:df_clean = df.iloc[12:].copy()")
五、总结
CTI(相关趋势指标)是一种基于统计相关性的趋势量化工具,其核心价值与定位如下:
| 维度 | 特点 |
|---|---|
| 核心创新 | 将统计相关系数引入技术分析,量化价格与理想趋势的拟合程度 |
| 理论来源 | John Ehlers(2020年),基于斯皮尔曼/皮尔逊相关系数 |
| 数值范围 | -1 到 +1,具有明确的统计意义 |
| 三大核心信号 | 零轴穿越、趋势强度等级、极值回落预警 |
| 最佳应用场景 | 趋势确认、趋势强度评估、多品种趋势比较 |
| 主要局限 | 滞后性、窗口敏感、震荡市假信号 |
实战使用三原则:
- 用CTI验证趋势,而非发现趋势:CTI更适合确认已识别的趋势强度,而非作为入场信号的唯一依据
- 大于0.5是强趋势门槛:当CTI值超过0.5时,表明价格走势与理想趋势高度相关,是较为可靠的趋势确认信号
- 高位回落是风险警示:当CTI从大于0.5的高位开始回落时,即使价格仍在上涨,也应警惕趋势强度正在衰减
最后提醒:CTI的最大特色在于其数学严谨性------它将"趋势强度"这一看似主观的概念量化为具有统计意义的相关系数。这使得不同品种、不同时间周期的趋势强度可以直接比较,为量化交易策略的开发提供了客观依据。然而,正如Ehlers本人所强调的,CTI并非完美的预测工具,而是帮助交易者"看到"市场当前状态的透镜。将其与移动平均线、成交量等传统工具配合使用,才能发挥最大价值。