RSI超买/超卖交易策略
一、交易策略解释
核心思想
RSI超买/超卖反转策略的核心思想是基于相对强弱指标(Relative Strength Index, RSI)在达到极端水平时,市场可能出现价格反转的原理。该策略认为:
当RSI值超过70进入超买区域后,再回落至70以下时,表明市场可能已达到短期高点,价格有下跌趋势的可能,产生做空信号。
当RSI值低于30进入超卖区域后,再回升至30以上时,表明市场可能已达到短期低点,价格有上涨趋势的可能,产生做多信号。
这种策略本质上是一种均值回归(Mean Reversion)思想,认为价格在短期内大幅偏离其均值后,有回归至均值的趋势。RSI作为一个动量指标,帮助交易者识别价格动量的极端值及其随后的反转。
理论基础
RSI指标由J. Welles Wilder Jr.于1978年在其著作《新型技术交易系统》中首次提出。该指标的理论基础建立在以下几个方面:
-
价格动量与反转理论:市场过度扩张理论(Market Overextension Theory)指出,价格在短期内不可能无限制地向一个方向发展,当达到极端水平时,会因供需平衡的调整而反转。
-
实证研究支持:多项学术研究证实了RSI在预测短期市场反转方面的有效性。例如,Andrew Lo和Jasmina Hasanhodzic在《市场的适应性本质》中发现,RSI在识别超买和超卖条件方面具有统计显著性。
-
技术分析理论支持:价格与震荡指标的背离(Divergence)理论认为,当价格趋势与技术指标趋势不一致时,往往预示着价格趋势可能即将逆转。
策略适用场景
-
区间震荡市场:在缺乏明显趋势的横盘市场中,价格往往在一定区间内震荡,RSI指标能有效捕捉区间的上下边界,提供较为准确的反转信号。
-
避免强趋势市场:在强烈的单向趋势市场中,RSI可能长时间保持在超买或超卖区域,反转信号可能导致过早入场,因此该策略不适合强趋势市场。
-
与其他指标结合使用:结合其他技术指标如移动平均线、MACD或布林带等,可以提高信号的可靠性。例如,只在价格位于移动平均线下方时考虑RSI超卖信号。
二、天勤介绍
天勤平台概述
天勤(TqSdk)是一个由信易科技开发的开源量化交易系统,为期货、期权等衍生品交易提供专业的量化交易解决方案。平台具有以下特 点:
- 丰富的行情数据: 提供所有可交易合约的全部Tick和K线数据,基于内存数据库实现零延迟访问。
- 一站式的解决方案: 从历史数据分析到实盘交易的完整工具链,打通开发、回测、模拟到实盘的全流程。
- 专业的技术支持: 近百个技术指标源码,深度集成pandas和numpy,采用单线程异步模型保证性能。
策略开发流程
- 环境准备
- 安装Python环境(推荐Python 3.6或以上版本)
- 安装tqsdk包:pip install tqsdk
- 注册天勤账户获取访问密钥
- 数据准备
- 订阅近月与远月合约行情
- 获取历史K线或者Tick数据(用于分析与行情推进)
- 策略编写
- 设计信号生成逻辑(基于价差、均值和标准差)
- 编写交易执行模块(开仓、平仓逻辑)
- 实现风险控制措施(止损、资金管理)
- 回测验证
- 设置回测时间区间和初始资金
- 运行策略获取回测结果
- 分析绩效指标(胜率、收益率、夏普率等)
- 策略优化
- 调整参数(标准差倍数、窗口大小等)
- 添加过滤条件(成交量、波动率等)
- 完善风险控制机制
三、天勤实现策略
策略原理
RSI指标的标准公式为:
RSI = 100 - (100 / (1 + RS))
RS = 平均上涨点数 / 平均下跌点数
本策略中,使用天勤SDK的内置RSI函数计算相对强弱指标,并通过缩短RSI周期(6日),使指标对价格变化更加敏感,并调整传统的超买超卖阈值(65/35)以获取更多交易信号。
交易逻辑
入场条件:
- 做多信号:RSI从低于35回升至35以上,且之前的RSI值已经进入过超卖区域(RSI < 35)
- 做空信号:RSI从高于65回落至65以下,且之前的RSI值已经进入过超买区域(RSI > 65)
出场条件:
- 止盈:当RSI达到反向的极端区域
- 多头持仓时RSI升至65以上
- 空头持仓时RSI降至35以下
- 止损:使用固定止损,设置为入场点的1%
- 时间止损:如果持仓超过10个交易日没有触发止盈或止损,则强制平仓
风险管理:
- 资金风险控制:每次交易风险敞口不超过账户总资金的2%,通过止损比例和头寸大小配合实现
- 指标确认机制:通过跟踪RSI是否真正进入过超买/超卖区域,避免边界波动产生的错误信号
回测
回测初始设置
- 测试周期: 2020 年 4 月 20 日 - 2020 年 11 月 20 日
- 交易品种: DCE.a2101
- 初始资金: 1000万元
回测结果
上述回测累计收益走势图
完整代码示例
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__author__ = "Chaos"
from datetime import date
from tqsdk import TqApi, TqAuth, TqBacktest, TargetPosTask, BacktestFinished
from tqsdk.ta import RSI
from tqsdk.tafunc import time_to_str
# ===== 全局参数设置 =====
SYMBOL = "DCE.a2101"
POSITION_SIZE = 500 # 每次交易手数
START_DATE = date(2020, 4, 20) # 回测开始日期
END_DATE = date(2020, 11, 20) # 回测结束日期
# RSI参数
RSI_PERIOD = 6 # RSI计算周期,
OVERBOUGHT_THRESHOLD = 65 # 超买阈值
OVERSOLD_THRESHOLD = 35 # 超卖阈值
# 风控参数
STOP_LOSS_PERCENT = 0.01 # 止损百分比
TIME_STOP_DAYS = 10 # 缩短时间止损天数
# ===== 全局变量 =====
current_direction = 0 # 当前持仓方向:1=多头,-1=空头,0=空仓
entry_price = 0 # 开仓价格
stop_loss_price = 0 # 止损价格
entry_date = None # 开仓日期
was_overbought = False # 是否曾进入超买区域
was_oversold = False # 是否曾进入超卖区域
# ===== 策略开始 =====
print("开始运行RSI超买/超卖反转策略...")
# 创建API实例
api = TqApi(backtest=TqBacktest(start_dt=START_DATE, end_dt=END_DATE),
auth=TqAuth("快期账号", "快期密码"))
# 订阅合约的K线数据
klines = api.get_kline_serial(SYMBOL, 60 * 60 * 24) # 日线数据
# 创建目标持仓任务
target_pos = TargetPosTask(api, SYMBOL)
try:
while True:
# 等待更新
api.wait_update()
# 如果K线有更新
if api.is_changing(klines.iloc[-1], "datetime"):
# 确保有足够的数据计算指标
if len(klines) < RSI_PERIOD + 10:
continue
# 使用天勤的RSI函数计算
rsi = RSI(klines, RSI_PERIOD)
current_rsi = float(rsi.iloc[-1].iloc[0])
previous_rsi = float(rsi.iloc[-2].iloc[0])
# 更新超买/超卖状态
if previous_rsi > OVERBOUGHT_THRESHOLD:
was_overbought = True
if previous_rsi < OVERSOLD_THRESHOLD:
was_oversold = True
# 获取最新数据
current_price = float(klines.close.iloc[-1])
current_timestamp = klines.datetime.iloc[-1]
current_datetime = time_to_str(current_timestamp) # 使用time_to_str转换时间
# 打印当前状态
print(f"日期: {current_datetime}, 价格: {current_price:.2f}, RSI: {current_rsi:.2f}")
# ===== 交易逻辑 =====
# 空仓状态 - 寻找开仓机会
if current_direction == 0:
# 多头开仓条件:RSI从超卖区域回升
if was_oversold and previous_rsi < OVERSOLD_THRESHOLD and current_rsi > OVERSOLD_THRESHOLD:
current_direction = 1
target_pos.set_target_volume(POSITION_SIZE)
entry_price = current_price
stop_loss_price = entry_price * (1 - STOP_LOSS_PERCENT)
entry_date = current_timestamp # 存储时间戳
print(f"多头开仓: 价格={entry_price:.2f}, 止损={stop_loss_price:.2f}")
was_oversold = False # 重置超卖状态
# 空头开仓条件:RSI从超买区域回落
elif was_overbought and previous_rsi > OVERBOUGHT_THRESHOLD and current_rsi < OVERBOUGHT_THRESHOLD:
current_direction = -1
target_pos.set_target_volume(-POSITION_SIZE)
entry_price = current_price
stop_loss_price = entry_price * (1 + STOP_LOSS_PERCENT)
entry_date = current_timestamp # 存储时间戳
print(f"空头开仓: 价格={entry_price:.2f}, 止损={stop_loss_price:.2f}")
was_overbought = False # 重置超买状态
# 多头持仓 - 检查平仓条件
elif current_direction == 1:
# 止损条件
if current_price <= stop_loss_price:
profit_pct = (current_price - entry_price) / entry_price * 100
target_pos.set_target_volume(0)
current_direction = 0
print(f"多头止损平仓: 价格={current_price:.2f}, 盈亏={profit_pct:.2f}%")
# 止盈条件:RSI进入超买区域
elif current_rsi > OVERBOUGHT_THRESHOLD:
profit_pct = (current_price - entry_price) / entry_price * 100
target_pos.set_target_volume(0)
current_direction = 0
print(f"多头止盈平仓: 价格={current_price:.2f}, 盈亏={profit_pct:.2f}%")
# 时间止损
elif (current_timestamp - entry_date) / (60 * 60 * 24) >= TIME_STOP_DAYS: # 计算天数差
profit_pct = (current_price - entry_price) / entry_price * 100
target_pos.set_target_volume(0)
current_direction = 0
print(f"多头时间止损: 价格={current_price:.2f}, 盈亏={profit_pct:.2f}%")
# 空头持仓 - 检查平仓条件
elif current_direction == -1:
# 止损条件
if current_price >= stop_loss_price:
profit_pct = (entry_price - current_price) / entry_price * 100
target_pos.set_target_volume(0)
current_direction = 0
print(f"空头止损平仓: 价格={current_price:.2f}, 盈亏={profit_pct:.2f}%")
# 止盈条件:RSI进入超卖区域
elif current_rsi < OVERSOLD_THRESHOLD:
profit_pct = (entry_price - current_price) / entry_price * 100
target_pos.set_target_volume(0)
current_direction = 0
print(f"空头止盈平仓: 价格={current_price:.2f}, 盈亏={profit_pct:.2f}%")
# 时间止损
elif (current_timestamp - entry_date) / (60 * 60 * 24) >= TIME_STOP_DAYS: # 计算天数差
profit_pct = (entry_price - current_price) / entry_price * 100
target_pos.set_target_volume(0)
current_direction = 0
print(f"空头时间止损: 价格={current_price:.2f}, 盈亏={profit_pct:.2f}%")
except BacktestFinished as e:
print("回测结束")
api.close()