日线RSI的计算与展示

用户头像sh_***388rgt
2025-11-07 发布

RSI ,即相对强弱指标,是由韦尔斯.怀尔德(Welles Wilder)提出的,是衡量证券自身内在相对强度的指标。相对强弱指数RSI是根据一定时期内上涨和下跌幅度之和的比率制作出的一种技术曲线,能够反映出市场在一定时期内的景气程度。因为投资的一般原理认为,投资者的买卖行为是各种因素综合结果的反映,行情的变化最终取决于供求关系,而RSI指标正是根据供求平衡的原理,通过测量某一个期间内股价上涨总幅度占股价变化总幅度平均值的百分比,来评估多空力量的强弱程度,进而提示具体操作的。

RSI公式不仅能够提供这种平滑特征,而且可以产生一个能够在0-100之间固定区域变动的指标。怀尔德推荐的默认时间跨度是14天,他论证了应用月周期28日的一半是有效的。

计算公式:

N日RSI =N日内收盘涨幅的平均值/(N日内收盘涨幅均值+N日内收盘跌幅均值) ×100

由上面算式可知RSI指标的技术含义,即以向上的力量与向下的力量进行比较,若向上的力量较大,则计算出来的指标上升;若向下的力量较大,则指标下降,由此测算出市场走势的强弱。

市场上一般的规则:(快速RSI指14日的RSI,慢速RSI指6日的RSI)

  1. RSI 金叉:快速 RSI 从下往上突破慢速 RSI 时,认为是买进机会。
  2. RSI 死叉:快速 RSI 从上往下跌破慢速 RSI 时,认为是卖出机会
  3. 慢速RSI<20 为超卖状态,为买进机会。
  4. 慢速RSI>80 为超买状态,为卖出机会。

接下来,我将通过python程序调用baostock实现RSI计算,RSI超卖和超买提示的功能。

具体代码如下:

import baostock as bs
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from datetime import datetime
import matplotlib as mpl

# 设置中文字体 - 解决中文显示问题
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'DejaVu Sans']  # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号

# 可选:设置其他matplotlib参数
plt.rcParams['figure.figsize'] = [12, 8]
plt.rcParams['figure.dpi'] = 100


def calculate_RSI(prices, period=14):
    """纯Python实现RSI指标计算"""
    deltas = np.diff(prices)
    seed = deltas[:period]

    up = seed[seed >= 0].sum() / period
    down = -seed[seed < 0].sum() / period

    rs = up / down if down != 0 else 0
    rsi = np.zeros_like(prices)
    rsi[:period] = 100.0 - 100.0 / (1.0 + rs)

    for i in range(period, len(prices)):
        delta = deltas[i - 1]

        if delta > 0:
            up_val = delta
            down_val = 0.0
        else:
            up_val = 0.0
            down_val = -delta

        up = (up * (period - 1) + up_val) / period
        down = (down * (period - 1) + down_val) / period

        rs = up / down if down != 0 else 0
        rsi[i] = 100.0 - 100.0 / (1.0 + rs)

    return rsi


def computeRSI(code, startdate, enddate):
    """计算证券在起止时间内的RSI指标"""
    # 登录baostock系统
    lg = bs.login()
    if lg.error_code != '0':
        print(f'登录失败: {lg.error_msg}')
        return None

    try:
        # 获取股票数据
        rs = bs.query_history_k_data_plus(code,
                                          "date,code,close,tradestatus,volume",
                                          start_date=startdate,
                                          end_date=enddate,
                                          frequency="d",
                                          adjustflag="3")

        if rs.error_code != '0':
            print(f'查询失败: {rs.error_msg}')
            return None

        # 处理数据
        data_list = []
        while (rs.error_code == '0') & rs.next():
            data_list.append(rs.get_row_data())

        if not data_list:
            print("未获取到数据")
            return None

        df = pd.DataFrame(data_list, columns=rs.fields)

        # 数据清洗
        df = df[df['tradestatus'] == '1'].copy()
        if df.empty:
            print("无有效交易数据")
            return None

        # 数据类型转换
        df['close'] = df['close'].astype(float)
        df['volume'] = df['volume'].astype(float)
        df['date'] = pd.to_datetime(df['date'])
        df = df.sort_values('date').reset_index(drop=True)

        # 计算RSI指标
        close_prices = df['close'].values
        df['rsi_6'] = calculate_RSI(close_prices, 6)
        df['rsi_12'] = calculate_RSI(close_prices, 12)
        df['rsi_24'] = calculate_RSI(close_prices, 24)

        # 识别超买超卖信号
        df['signal'] = ''
        rsi_6_values = df['rsi_6'].values

        for i in range(1, len(df)):
            if not np.isnan(rsi_6_values[i]):
                if rsi_6_values[i] > 80 and rsi_6_values[i - 1] <= 80:
                    df.loc[i, 'signal'] = '超买'
                elif rsi_6_values[i] < 20 and rsi_6_values[i - 1] >= 20:
                    df.loc[i, 'signal'] = '超卖'

        return df

    except Exception as e:
        print(f"计算过程中发生错误: {e}")
        return None
    finally:
        bs.logout()


def plot_RSI_analysis(df, code):
    """绘制RSI指标分析图表"""
    if df is None or df.empty:
        print("无有效数据可绘制")
        return

    # 创建图表
    fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(14, 10))

    # 子图1:价格走势
    ax1.plot(df['date'], df['close'], label='收盘价', linewidth=2, color='#1f77b4')
    ax1.set_title(f'{code} - 价格走势与RSI信号', fontsize=14, fontweight='bold')
    ax1.set_ylabel('价格 (元)', fontsize=12)
    ax1.legend()
    ax1.grid(True, alpha=0.3)

    # 标记超买超卖点
    buy_signals = df[df['signal'] == '超卖']
    sell_signals = df[df['signal'] == '超买']

    ax1.scatter(sell_signals['date'], sell_signals['close'],
                color='red', marker='v', s=100, label='超买信号', zorder=5)
    ax1.scatter(buy_signals['date'], buy_signals['close'],
                color='green', marker='^', s=100, label='超卖信号', zorder=5)

    # 子图2:RSI指标
    ax2.plot(df['date'], df['rsi_6'], label='RSI 6日', linewidth=2, color='#ff7f0e')
    ax2.plot(df['date'], df['rsi_12'], label='RSI 12日', linewidth=2, color='#2ca02c')
    ax2.plot(df['date'], df['rsi_24'], label='RSI 24日', linewidth=2, color='#d62728')

    # 添加RSI参考线
    ax2.axhline(y=80, color='red', linestyle='--', alpha=0.7, label='超买线 (80)')
    ax2.axhline(y=50, color='gray', linestyle='--', alpha=0.5, label='均衡线 (50)')
    ax2.axhline(y=20, color='green', linestyle='--', alpha=0.7, label='超卖线 (20)')

    ax2.set_title('RSI相对强弱指标', fontsize=14, fontweight='bold')
    ax2.set_ylabel('RSI值', fontsize=12)
    ax2.set_xlabel('日期', fontsize=12)
    ax2.legend()
    ax2.grid(True, alpha=0.3)
    ax2.set_ylim(0, 100)

    # 标记RSI超买超卖区域
    ax2.fill_between(df['date'], 80, 100, color='red', alpha=0.1, label='超买区域')
    ax2.fill_between(df['date'], 0, 20, color='green', alpha=0.1, label='超卖区域')

    plt.tight_layout()
    plt.show()


if __name__ == '__main__':
    # 参数设置
    code = "sh.600000"
    startdate = "2025-01-01"
    enddate = "2025-11-06"

    print("开始计算RSI指标...")

    # 计算RSI
    df = computeRSI(code, startdate, enddate)

    if df is not None:
        # 显示最新数据
        print("\n最新5个交易日的RSI数据:")
        recent_data = df[['date', 'close', 'rsi_6', 'rsi_12', 'rsi_24', 'signal']].tail()
        print(recent_data.to_string(index=False))

        # 统计信号
        buy_signals = len(df[df['signal'] == '超卖'])
        sell_signals = len(df[df['signal'] == '超买'])
        print(f"\n信号统计: 超买信号 {sell_signals} 次, 超卖信号 {buy_signals} 次")

        # 绘制图表
        plot_RSI_analysis(df, code)

        # 保存数据
        output_file = f"D:/rsi_analysis_{code.replace('.', '_')}.csv"
        df.to_csv(output_file, encoding='gbk', index=False)
        print(f"\n数据已保存至: {output_file}")
    else:
        print("RSI计算失败")

结果画图如下:

5f8aab558bae0435ead3657c87f3b710.png

生成的CSV文件内容如下:

7625e6b6a1dc6eda63343653cd3c344e.png

至于RSI的金叉和死叉的实现,我在之前KDJ里有类似的方法,请大家自己动手。现在我们已经实现了MACD、KDJ、RSI这三个技术指标。可能有部分人认为计算的结果和交易软件给出的不一样。首先,交易软件给出的是从上市以来的计算结果;其次,我也对比了好几个,发现我计算的结果和新浪财经,腾讯财经提供的指标值比较接近。若还有什么问题,欢迎提问啊。

评论