不使用pandas ewm函数计算MACD及解决KeyError:'close'问题
手动计算MACD触发KeyError:'close'的问题解决
我尝试手动计算交易领域的技术指标MACD(Moving Average Convergence Divergence),不使用pandas的ewm函数。按照EMA(指数移动平均线)和MACD的计算步骤编写了代码,但运行时持续出现KeyError:'close'错误,尤其在计算Signal Line传入df['macd']时触发。
计算逻辑
EMA计算步骤
- 计算简单移动平均线(SMA):
(周期内数值之和 / 周期数) - 计算乘数:
2 / (周期数 + 1)(例如周期为5时,乘数为2/(5+1)≈33.33%) - 计算EMA:
- 第一个EMA值使用前一日的SMA代替前一日EMA
- 后续EMA公式:
EMA = (当日收盘价 - 前一日EMA) × 乘数 + 前一日EMA
MACD计算步骤
- 计算短期EMA:通常为12期EMA
- 计算长期EMA:通常为26期EMA
- 计算MACD线:短期EMA减去长期EMA
- 计算信号线:对MACD线计算9期EMA
出错代码
import numpy as np import pandas as pd # EMA计算逻辑注释 # 1. 计算SMA:(周期内数值之和 / 周期数) # 2. 计算乘数:2 / (周期数 + 1),比如周期5时乘数≈33.33% # 3. 计算EMA:第一个EMA用前一日SMA,后续用公式:EMA=(收盘价-前一日EMA)*乘数+前一日EMA # MACD计算逻辑注释 # 1. 计算12期短期EMA # 2. 计算26期长期EMA # 3. MACD线=短期EMA-长期EMA # 4. 信号线=MACD线的9期EMA def compute_ema(df,n): df['sma'] = df['close'].rolling(n).mean() multiplier = 2/(n + 1) df.dropna(inplace=True) df['ema'] = np.nan for i in range(len(df)): if i == 1: df.loc[df.index[i], 'ema'] = (df.loc[df.index[i],'close'] - df.loc[df.index[i - 1],'sma']) * multiplier + df.loc[df.index[i - 1], 'sma'] else: df.loc[df.index[i], 'ema'] = (df.loc[df.index[i],'close'] - df.loc[df.index[i - 1],'ema']) * multiplier + df.loc[df.index[i - 1], 'ema'] return df df['fast'] = compute_ema(df, n=12) df['slow'] = compute_ema(df, n=26) df['macd'] = df['fast'] - df['slow'] df['signal'] = compute_ema(df['macd'],n)
报错信息
KeyError: 'close' The above exception was the direct cause of the following exception: KeyError Traceback (most recent call last) Cell In[41], line 1 ----> 1 df['signal'] = compute_ema(df['macd'],n) Cell In[21], line 24 23 def compute_ema(df,n): ... 3815 # InvalidIndexError. Otherwise we fall through and re-raise 3816 # the TypeError. 3817 self._check_indexing_error(key)
问题原因
- 函数参数不兼容:
compute_ema函数默认接收完整DataFrame并依赖df['close']列,但计算信号线时传入的是df['macd'](一个Series),此时访问df['close']必然触发KeyError。 - 数据修改逻辑错误:函数内使用
df.dropna(inplace=True)直接修改原DataFrame,且返回整个DataFrame,导致df['fast']和df['slow']被赋值为完整DataFrame,后续计算MACD时会出现数据结构不匹配的问题。 - EMA起始计算错误:代码中
i == 1的判断逻辑错误,第一个EMA值应该对应第一个有效的SMA位置,而非索引为1的位置。
修复后的代码
import numpy as np import pandas as pd def compute_ema(series, n): # 计算周期SMA作为EMA初始值 sma = series.rolling(n).mean() multiplier = 2 / (n + 1) # 初始化EMA数组,保留原索引 ema = np.full_like(series, np.nan) first_valid_idx = sma.first_valid_index() if not first_valid_idx: return pd.Series(ema, index=series.index) # 第一个EMA值等于对应位置的SMA ema[series.index.get_loc(first_valid_idx)] = sma[first_valid_idx] # 从第一个有效SMA的下一个位置开始迭代计算 for idx in series.index[series.index.get_loc(first_valid_idx)+1:]: prev_idx = series.index[series.index.get_loc(idx)-1] ema[series.index.get_loc(idx)] = (series[idx] - ema[series.index.get_loc(prev_idx)]) * multiplier + ema[series.index.get_loc(prev_idx)] return pd.Series(ema, index=series.index) # 假设df包含'close'列,示例计算 df['fast'] = compute_ema(df['close'], n=12) df['slow'] = compute_ema(df['close'], n=26) df['macd'] = df['fast'] - df['slow'] # 计算信号线时传入macd序列,函数可正确处理 df['signal'] = compute_ema(df['macd'], n=9)
修复说明
- 函数改为接收Series作为输入,不再依赖特定列名,可灵活计算任意序列的EMA(包括MACD线)
- 避免修改原数据,返回独立的EMA结果Series,保证原数据完整性
- 修正EMA起始值的计算逻辑,从第一个有效的SMA位置开始逐步计算后续EMA值
内容的提问来源于stack exchange,提问作者dev0419
相关产品推荐
相关产品推荐

