3. RSI多周期超买超卖信号
任务目标
基于同一只股票的日线、周线、月线收盘价,分别计算RSI(14),生成各周期的超买(RSI≥70)与超卖(RSI≤30)信号,并融合为综合信号。综合信号规则:任一周期超卖则综合为买入信号;任一周期超买则综合为卖出信号;若同时出现超买与超卖(极端背离),以月线信号优先,其次周线,最后日线。
环境与依赖
仅需Python 3.8+与pandas、numpy。无需外部数据文件,使用内置示例数据直接验证。
关键完整代码
import pandas as pd
import numpy as np
def compute_rsi(close: pd.Series, period: int = 14) -> pd.Series:
delta = close.diff()
gain = delta.clip(lower=0)
loss = -delta.clip(upper=0)
avg_gain = gain.ewm(alpha=1/period, min_periods=period).mean()
avg_loss = loss.ewm(alpha=1/period, min_periods=period).mean()
rs = avg_gain / avg_loss
rsi = 100 - (100 / (1 + rs))
return rsi
def resample_weekly_monthly(df: pd.DataFrame):
df = df.set_index('date').sort_index()
weekly = df['close'].resample('W-FRI').last().dropna()
monthly = df['close'].resample('ME').last().dropna()
return weekly, monthly
def generate_signals(df: pd.DataFrame) -> pd.DataFrame:
weekly, monthly = resample_weekly_monthly(df)
df = df.set_index('date').sort_index()
df['rsi_daily'] = compute_rsi(df['close'])
df['rsi_weekly'] = compute_rsi(weekly).reindex(df.index, method='ffill')
df['rsi_monthly'] = compute_rsi(monthly).reindex(df.index, method='ffill')
def signal_from_rsi(rsi):
if rsi >= 70:
return 'overbought'
elif rsi <= 30:
return 'oversold'
return 'neutral'
df['sig_d'] = df['rsi_daily'].apply(signal_from_rsi)
df['sig_w'] = df['rsi_weekly'].apply(signal_from_rsi)
df['sig_m'] = df['rsi_monthly'].apply(signal_from_rsi)
def combine(row):
has_ob = row['sig_d'] == 'overbought' or row['sig_w'] == 'overbought' or row['sig_m'] == 'overbought'
has_os = row['sig_d'] == 'oversold' or row['sig_w'] == 'oversold' or row['sig_m'] == 'oversold'
if has_ob and has_os:
if row['sig_m'] != 'neutral':
return 'sell' if row['sig_m'] == 'overbought' else 'buy'
if row['sig_w'] != 'neutral':
return 'sell' if row['sig_w'] == 'overbought' else 'buy'
return 'sell' if row['sig_d'] == 'overbought' else 'buy'
if has_ob:
return 'sell'
if has_os:
return 'buy'
return 'hold'
df['combo_signal'] = df.apply(combine, axis=1)
return df.reset_index()
# 示例数据(2023-2024两年日线)
dates = pd.date_range('2023-01-01', '2024-12-31', freq='B')
np.random.seed(42)
close = 100 + np.cumsum(np.random.normal(0.2, 1.5, len(dates)))
sample_df = pd.DataFrame({'date': dates, 'close': close})
result = generate_signals(sample_df)
print(result[['date', 'close', 'rsi_daily', 'rsi_weekly', 'rsi_monthly', 'sig_d', 'sig_w', 'sig_m', 'combo_signal']].tail(10))
运行与验证
运行方式:将上述代码整体保存为 rsi_combo.py,在命令行执行 python rsi_combo.py。
验证方法:程序输出最后10个交易日的完整信号明细。检查以下三点确认实现正确:
- 各周期RSI值均在0~100区间内,且日线RSI与直接调用
compute_rsi(sample_df['close'])结果一致。 - 当某日任一周期RSI≤30时,
combo_signal为buy;任一周期RSI≥70时为sell;全部处于30~70之间时为hold。 - 若同一日既有超买又有超卖(本例随机数据可能出现),按月线→周线→日线的优先级决定最终买卖方向。
预期输出特征:由于使用随机游走数据,输出中三类信号(buy/sell/hold)均会出现,且每行信号与RSI数值逻辑自洽。例如某行rsi_daily=25.3、rsi_weekly=45.1、rsi_monthly=52.0时,该行sig_d='oversold'、combo_signal='buy'。
常见问题
- 周线/月线RSI前几行为NaN:属于正常现象,RSI需要至少14个周期数据才能计算,重采样后前期数据不足。代码已用
ffill将最近可得值向前填充,不影响信号生成逻辑。 - 若使用真实股票数据:将
sample_df替换为包含date(datetime类型)和close列的DataFrame即可,无需其他修改。