KDJ指标
由small_q创建,最终由small_q 被浏览 3 用户
因子原理
今天我们来聊聊 KDJ 指标,这个在股市中常被提及的技术分析工具,它可以帮助你更好地理解市场趋势。
KDJ 是由 K 线、D 线和 J 线组成的指标,主要用于判断市场的超买超卖情况。简单来说,KDJ指标可以评估股票是被高估还是被低估,从而做出更明智的投资选择。
我们先来看看KDJ指标是如何计算出来的:
首先,计算第N日的RSV值
其中C是N日收盘价,L是N日最低价,H是N日最高价。
再次,计算K值、D值、J值。
K线:反映了短期价格变化的敏感度。等式右边的K为前一日的K值, 初始K通常设置为50。
D线:是 K 线的平滑移动平均,反映了中期趋势。等式右边的D为前一日的D值, 与K一样,初始值通常设定为50。
J线:是 K 线与 D 线的差值,常用来识别买入和卖出信号。
KDJ的用法决定了它的研究方法并不契合因子投资理论。我们无法单独拿一个K、D或者J值按照往常一样使用因子投资的分析体系,所以我们对该因子的研究需要使用新方法。
因子研究
用法1:看似金叉多死叉空,实则背离
“金叉死叉”是技术分析中一个耳熟能详的用法。金叉指K值上穿D值,被视为多头信号,预示上涨;死叉则是K值下穿D值,为空头信号,预示下跌。
我们借助“金叉死叉”来完善KDJ的投研架构:
-
在bigquant上调用股票行情数据,计算个股的KDJ指标值与未来一天的收益率;
import dai import talib import numpy as np import pandas as pd import matplotlib.pyplot as plt
sd = '2018-01-01' ed = '2024-12-31' sql = """ SELECT date, instrument, close, open, high, low, volume, m_lead(open, 2) / m_lead(open, 1) - 1 AS future_return FROM cn_stock_prefactors WHERE is_zz500 = 1 AND is_risk_warning = 0 AND st_status = 0 AND suspended = 0 QUALIFY COLUMNS (*) IS NOT NULL ORDER BY date """ data = dai.query(sql, filters={'date': [sd, ed]}).df()
-
标记每天市场中发生“金叉”和“死叉”的股票;
def KDJ(df): df.sort_values('date', inplace=True) df['k'], df['d'] = talib.STOCH( df['high'].values, df['low'].values, df['close'].values, fastk_period=9, slowk_period=3, slowk_matype=0, slowd_period=3, slowd_matype=0 ) df['j'] = 3 * df['k'] - 2 * df['d'] df['signal'] = np.where( (df['k']>df['d']) & (df['k'].shift(1) < df['d'].shift(1)), 1, # 金叉 np.where( (df['k']<df['d']) & (df['k'].shift(1) > df['d'].shift(1)), # 死叉 -1, 0 ) ) return df df = data.groupby('instrument').apply(KDJ).reset_index(drop=True).dropna() df
-
将所有金叉个股的未来一天收益率取平均作为当期金叉的预期收益(若当天不存在金叉,则定义为0),将所有死叉个股的未来一天收益率取平均作为当期死叉预期收益(若当天没有个股发生死叉,则定义为0);分别计算累计收益。
# 统计每天金叉死叉的累计收益曲线 def calc_return(df): """ |date|gold_cross_return|dead_cross_return| |... | ... | ... | """ # 计算金叉的平均收益 gold = df[df['signal']==1] if len(gold)==0: gold_return = 0 else: gold_return = gold['future_return'].mean() # 计算死叉的平均收益 death = df[df['signal']==-1] if len(death)==0: death_return = 0 else: death_return = death['future_return'].mean() return pd.DataFrame( { "date": [df['date'].iloc[0]], "gold_cross_return" : [gold_return], "dead_cross_return": [death_return] } ) cross_return = df.groupby('date').apply(calc_return).reset_index(drop=True) cross_return
如图所示,为2018年到2024年金叉和死叉股票的累计收益趋势:
我们发现,金叉的累计收益从18年以来一路向下,相较之下,死叉的累计收益表现较为平稳,这与传统的“金叉死叉”法则是背离的。所以简单地使用KDJ的金叉死叉开仓平仓是不现实的。
用法2:超买状态空头、超卖多头
为了挖掘KDJ的潜在价值,我们转而关注超买超卖区域的表现。
首先,我们依旧需要根据公式计算KDJ指标与个股未来一天收益率;
fig = plt.figure()
plt.plot(cross_return['date'], cross_return['gold_cross_return'].cumsum(), label='gold_cross_return')
plt.plot(cross_return['date'], cross_return['dead_cross_return'].cumsum(), label='death_cross_return')
plt.legend()
plt.show()
接着,我们定义超买和超卖状态,当K值<10,D值<20,J值<0时,为超卖状态;当K值>90,D值>80,J值>100时,为超买状态;
最后,分别统计超买、超卖状态下未来一天的收益均值和累计收益。
def calc_return_2(df):
# 计算超卖组合的预期收益率
over_sell = df[(df['k']<10) & (df['d']<20) & (df['j']<0)]
if len(over_sell)==0:
over_sell_return = 0
else:
over_sell_return = over_sell['future_return'].mean()
# 计算超买组合的预期收益率
over_buy = df[(df['k']>90) & (df['d']>80) & (df['j']>100)]
if len(over_buy)==0:
over_buy_return = 0
else:
over_buy_return = over_buy['future_return'].mean()
return pd.DataFrame(
{
'date': [df['date'].iloc[0]],
'over_sell_return': [over_sell_return],
'over_buy_return': [over_buy_return]
}
)
over_return = df.groupby('date').apply(calc_return_2).reset_index(drop=True)
over_return
从图中可以发现,用法2更适合做空机制,即看涨“超卖”,看跌“超买”。
图中”超卖“的累计收益在19年9月之后位于”超买“上方,说明用法2基本正确,但是超卖区域的累计收益依旧为负,所以我们需要在此基础上优化这个指标的用法。
用法3:低位金叉多头,高位死叉空头
我们以传统金叉进、死叉出的策略,将前两种用法结合,在低位统计金叉信号的表现,在高位统计死叉信号的表现。
首先,我们定义低位超卖、高位超买,计算每只股票的金叉死叉信号;
再次,我们统计低位金叉、高位死叉的累积收益。
def calc_return_3(df):
# 高位死叉
temp_1 = df[(df['signal']==1) & ((df['k']>90) & (df['d']>80) & (df['j']>100))]
if len(temp_1)==0:
over_buy_death_return = 0
else:
over_buy_death_return = temp_1['future_return'].mean()
# 低位金叉
temp_2 = df[(df['signal']==-1) & ((df['k']<10) & (df['d']<20) & (df['j']<0))]
if len(temp_2)==0:
over_sell_gold_return = 0
else:
over_sell_gold_return = temp_2['future_return'].mean()
return pd.DataFrame(
{
"date": [df['date'].iloc[0]],
"over_buy_death_return": [over_buy_death_return],
"over_sell_gold_return": [over_sell_gold_return]
}
)
melt = df.groupby('date').apply(calc_return_3).reset_index(drop=True)
可以发现,低位金叉表现出明显的多头信号,而高位死叉则强化了下跌信号。
用法3不仅符合我们对KDJ指标的预期认知,提升了策略的可操作性,同时累计收益曲线也得到了明显改善。
因子分析源码
完整因子分析源码,请查看:
https://bigquant.com/codesharev3/7296e6e2-26be-4333-9445-05ab28aff97b
\