☰
用RSI做ETH空头:量化策略逻辑、回测与实盘避坑指南
2026/9/28 5:38:10 网站建设 项目流程

1. 为什么拿RSI做ETH空头:策略逻辑与适用性分析

1.1 RSI指标的核心原理

做加密货币量化这几年,我越来越觉得RSI指标是个被低估的工具。很多人只知道RSI是“超买超卖指标”,数值超过70就是超买、低于30就是超卖,然后机械地反向操作。但真到了实盘,尤其在ETH这种波动剧烈的品种上,按教科书方式做经常会亏得很难看。原因很简单:RSI的本质不是买卖信号,而是对“过去一段时间多空力量强弱”的度量。

RSI由J. Welles Wilder在《New Concepts in Technical Trading Systems》里提出,标准参数是14根K线。计算分两步:先算N周期内的平均上涨幅度和平均下跌幅度,再代入公式RSI = 100 - 100 / (1 + RS),其中RS等于平均上涨幅度除以平均下跌幅度。用生活化类比:多空双方在拔河,RSI就是绳子中点的标记。上涨力量强,标记往多头一方偏,RSI偏高;下跌力量强,标记往空头一方偏,RSI偏低。配合14这个周期,它在1小时图上大约反映14个小时的动能对比,在4小时图上反映近两天半的动能对比。

真正值得注意的,是RSI在趋势市中的“钝化”现象。强单边上涨行情里,RSI可以在70以上连续待几十根K线,这时候按“超买就做空”操作等于送钱。反过来,单边下跌行情里,RSI会在30以下反复钝化,抄底也是接飞刀。所以我的一个核心观点是:RSI单指标不能独立成策略,必须有趋势过滤器配合。这篇文章要拆解的ETH空头策略,就是“RSI信号 + 趋势过滤 + 仓位管理”三者组合的产物。

1.2 为什么选ETH而不是BTC

同样是指标类策略,放在比特币和以太坊上的表现差别很大。BTC虽然流动性最好,但它的RSI波动相对收敛,价格在关键位置上的假突破较少,策略信号周期偏长,持仓过程中资金效率不高。ETH就不一样,它的波动率常年比BTC高出30%到50%,日内RSI更容易进入极端区域,做空机会的出现频率明显更高。对于靠“信号数量”积累收益的量化策略来说,ETH显然更合适。

另一个原因是ETH的衍生品市场深度已经足够。主流交易所的ETH永续合约日成交额经常在百亿美元级别,空头开仓和平仓的冲击成本可控,不容易出现BTC合约那种大单打穿盘口的情况。从相关性上看,ETH和BTC虽然同向性很强,但ETH在下跌行情中往往跌得更快、反弹更陡,这种“高贝塔”属性恰好放大了RSI空头策略的获利空间,当然也放大了回撤风险,所以后文要专门讲仓位管理和止损。

还有一点值得提。很多人做山寨币量化喜欢用“三因子策略”,即市值因子、动量因子、波动率因子一起打分选币,但单看ETH这个标的,RSI这类动能反转因子反而是最直接的。三因子的思路适合分散到整个币种池,而如果我们聚焦一个高流动性品种,把单因子做到极致、配合严格的风控,回撤和复杂度更容易控制。这也是我坚持用RSI单因子做ETH空头的理由。

1.3 空头策略逻辑与适用市场环境

这套策略的核心逻辑用一句话概括:只在下降趋势中,利用RSI进入超买区域后的回落动作做空。听上去简单,但每一步都有讲究。

趋势过滤是前提条件。我习惯采用EMA200作为多空分界线,价格在EMA200下方时只考虑做空,不做任何多单。ETH的熊市和长期盘整下跌阶段,价格大部分时间都在EMA200下方运行,这时候反弹到RSI超买区域,往往是空头重新进场的位置。

RSI触发条件要“先超买,后回落”。也就是说,RSI进入70以上区域之后,随后的某一根K线收盘价对应的RSI值跌回70以下,这才算有效的空头入场信号。单纯RSI到达70以上就立即做空,容易在顶部钝化阶段被连续打止损。如果RSI在70以上高位待了很长时间,再跌破70,代表多头动能确实衰竭了。

适用市场环境也很明确。震荡下行的结构性熊市是这套策略的主场,比如单边下跌途中的反弹阶段。最怕的环境是强单边上涨,这时候EMA200分界线本来就会过滤掉大部分做空信号,但一旦出现“价格在EMA200上方但RSI瞬间超买”的插针行情,策略容易误判,所以还要叠加额外的插针过滤规则。这部分细节后面代码里统一讲。

2. 数据准备与回测框架搭建

2.1 数据源选择与预处理

再漂亮的策略,数据质量不行都是空中楼阁。ETH历史K线看起来到处都能下载,但真正处理起来坑比想象中多。我一般直接从主流交易所的标准接口拉取1小时K线,字段包括时间戳、开盘价、最高价、最低价、收盘价、成交量。注意不同交易所对时间戳的定义不同,有的是毫秒、有的是秒,有的还分UTC和本地时区,第一步必须统一转成UTC时间并按时间排序。

数据清洗是回测前最容易被忽视的环节。我的惯用流程是:先按时间戳去重,同一个时间点若存在多条记录保留最后一条;再检查是否有空值或异常高开低走,比如两小时内价格从2000美元跳到400美元又拉回来的脏数据;最后按时间顺序重排索引。清洗逻辑写成代码也就十几行:

import pandas as pd def clean_ohlcv(df): df = df.drop_duplicates(subset='timestamp', keep='last') df = df.sort_values('timestamp').reset_index(drop=True) df = df[(df['high'] > 0) & (df['low'] > 0)] # 过滤高低价差超过参考价30%的异常K线,参考价取前一根收盘价 prev_close = df['close'].shift(1) abnormal = ((df['high'] - df['low']) / prev_close) > 0.3 df = df[~abnormal.fillna(False)] return df

这段代码里过滤异常K线的逻辑,本质上是在防范“插针数据”污染回测。ETH现货和永续合约都出现过瞬间上蹿下跳的情况,如果让这种数据直接参与RSI和ATR计算,产生的信号完全不可信,实盘根本无法复现。

2.2 回测框架选型思路

关于回测框架,不少新手一上来就选那些大型事件驱动回测引擎,结果光是学习配置就要折腾一周。我个人的建议是:单标的、单因子策略优先用自研的向量化回测,逻辑透明、代码可控,算得也快。事件驱动框架适合多标的组合、高频撮合、复杂订单流模拟,对ETH空头这种日内低频策略来说属于过度设计。

无论你在自己的Python环境里写,还是用同花顺Supermind这类平台跑,核心模块其实都一样:信号生成、持仓计算、交易成本、绩效统计。Supermind的好处是数据封装好了、可以直接用内置函数,缺点是不灵活;自研的好处是可以精确控制每个成交价位和成本假设。我后文给出的代码,思路完全可以在Supermind里平移,只要把数据加载部分换成平台接口即可。

回测框架里最需要较真的环节是“成交价假设”。如果策略信号用的是第N根K线的收盘价,那么实盘中你最早也只能在第N+1根K线开盘时下单,两者之间存在一个K线的滑移。我的标准做法是:信号在第N根K线产生,成交价取第N+1根K线的开盘价。这样回测结果更接近实盘,避免“收盘价魔法”造成的虚假收益。

2.3 数据访问与存储的安全加密实践

做量化难免要处理交易所API密钥、钱包地址、持仓数据这些敏感信息。早期我踩过一个教训:把API密钥直接写在回测脚本里,结果脚本不小心传到公共代码仓库,虽然马上撤销了密钥没造成损失,但那种后怕至今记得。现在我的数据安全方案分三层。

第一层,所有密钥和敏感配置统一从环境变量读取,代码库中绝不出现明文密钥。比如:

export EXCHANGE_API_KEY="your_key_here" export EXCHANGE_API_SECRET="your_secret_here"

Python里读取就用os.getenv('EXCHANGE_API_KEY'),脚本本身不含任何敏感信息。

第二层,本地存储的历史数据如果涉及隐私或自建信号,做磁盘加密或数据文件加密。量化交易数据文件大多是CSV或Parquet格式,可以用cryptography库对文件做AES加密,回测时再解密到内存。这样即使硬盘丢失,对方也拿不到明文数据。

第三层,回测环境和实盘环境严格隔离。回测脚本只读历史数据,实盘脚本单独部署在受控服务器上,两者不共用代码入口。数据访问权限按最小化原则,只开放当前策略需要用到的表和字段。这套思路不复杂,但对保护交易隐私和防止密钥泄露非常有效。

3. 策略核心实现:RSI空头信号生成与仓位管理

3.1 完整Python代码实现

下面给出一个可以直接运行的完整策略代码。我尽量保持函数独立,方便大家替换数据源和参数。

import pandas as pd import numpy as np def compute_rsi(close, period=14): 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 compute_atr(df, period=14): high, low, close = df['high'], df['low'], df['close'] tr1 = high - low tr2 = (high - close.shift(1)).abs() tr3 = (low - close.shift(1)).abs() tr = pd.concat([tr1, tr2, tr3], axis=1).max(axis=1) atr = tr.ewm(alpha=1/period, min_periods=period).mean() return atr def generate_signals(df, rsi_period=14, rsi_buy=70, rsi_exit=30, ema_period=200, atr_stop_mult=2.0): df = df.copy() df['rsi'] = compute_rsi(df['close'], rsi_period) df['ema'] = df['close'].ewm(span=ema_period, adjust=False).mean() df['atr'] = compute_atr(df, 14) df['uptrend'] = df['close'] > df['ema'] df['rsi_cross_down'] = (df['rsi'].shift(1) > rsi_buy) & (df['rsi'] <= rsi_buy) df['signal'] = 0 df.loc[(~df['uptrend']) & df['rsi_cross_down'], 'signal'] = -1 # 开空信号 df['exit_signal'] = 0 df.loc[df['rsi'] <= rsi_exit, 'exit_signal'] = 1 # RSI进入超卖区域,平空离场 position = 0 entry_price = 0.0 trades = [] pos_start = None for i in range(1, len(df)): date = df.index[i] if not isinstance(df.index, pd.RangeIndex) else i row = df.iloc[i] if position == 0: if row['signal'] == -1: position = -1 entry_price = row['open'] # 以下一根K线开盘价入场 pos_start = date else: stop_price = entry_price + atr_stop_mult * row['atr'] # 空头止损在入场价上方 hit_stop = row['low'] >= entry_price or row['high'] >= stop_price if hit_stop: trades.append((pos_start, date, entry_price, stop_price, 'stop_loss', -1)) position = 0 elif row['exit_signal'] == 1: trades.append((pos_start, date, entry_price, row['open'], 'rsi_exit', -1)) position = 0 return df, trades def backtest(df, initial_capital=10000, fee_rate=0.001, slippage=0.0005): df, trades = generate_signals(df) equity = initial_capital equity_curve = [] for _, trade in enumerate(trades): entry, exit_time, entry_price, exit_price, reason, direction = trade pnl_ratio = (entry_price - exit_price) / entry_price - fee_rate - slippage equity *= (1 + pnl_ratio) equity_curve.append((exit_time, equity)) return equity_curve, trades

这段代码总共不过60多行,但它已经包含了一个可回测的完整策略:趋势过滤、RSI超买回落开空、RSI超卖平仓、ATR止损。需要说明的是,position的循环实现用了逐行遍历,虽然速度比向量化慢一点,但对ETH这种小时级别数据的回测,耗时完全可以接受。

3.2 信号生成逻辑逐行拆解

代码里有几个点必须讲透,否则你改了参数也不知道后果。

第一,RSI的计算用的是Wilder平滑而非简单移动平均。ewm(alpha=1/period)等价于Wilder原始定义里的平滑方式。很多第三方库默认用的是简单移动平均,两者在数据偏长时差异不大,但在数据较短或市场剧烈变化时,平滑方式不同会导致RSI数值有明显偏差。我在实盘中更信任Wilder平滑,因为它对近期价格的权重衰减更自然。

第二,rsi_cross_down的判断用了.shift(1),意思是前一根K线的RSI必须大于70,当前K线RSI收盘值小于等于70。这个“回落”动作是策略的精髓。为什么不直接在RSI大于70时做空?因为ETH的极端行情里RSI经常在70以上待很久,盲目开空会被连续止损。等到它真正跌破70再进场,虽然少赚了一段回落利润,但胜率明显提升。

第三,趋势过滤放在了信号生成之前:价格必须低于EMA200才允许开空。EMA200用ewm(span=200, adjust=False)计算,和RSI的平滑方式不同但都很常用。实盘中EMA200的变化滞后于价格,但它非常稳,不会像EMA50那样频繁翻转。如果改用EMA50,做空信号会增多,但假信号比例也会上升,后面参数调优部分会展示具体差别。

第四,出场逻辑我设计了两个出口:RSI进入30以下平空,或者价格向上触及止损价。止损放在入场价加上2倍ATR的位置,ATR用的是14周期。这意味着如果开空后价格不跌反涨、且涨幅超过了近14根K线的平均真实波幅的2倍,就无条件认错离场,避免一次极端反弹吞掉多次盈利。

3.3 仓位管理与止损止盈

仓位管理是我反复强调的部分。策略胜率没那么高,但是盈亏比合理,这时候仓位就是决定生死的关键。我的经验是单笔风险控制在账户权益的1%到2%之间。比如账户10万元,单笔最大亏损就是1000到2000元。有了ATR止损,可以通过反推算出开仓数量:

risk_amount = equity * 0.01 stop_distance = atr_stop_mult * current_atr position_size = risk_amount / stop_distance

这个公式的意义是:止损距离越大,仓位越小;止损距离越小,仓位可以稍大。它保证了每笔交易的亏损金额基本恒定,避免了“止损窄、仓位重、被扫损后损失远超预期”的坑。

止盈方面,RSI进入30以下平仓是一个相对温和的退出。ETH阴跌行情里,RSI在30以下会维持相当久,如果一直持有到RSI回升到40或50再平,收益空间更大,但回撤也更大。我做过多组对照,RSI低于30平仓的版本虽然单笔利润略小,但信号频率更高,资金曲线更平滑。偏爱趋势的人可以把平仓阈值改成20,代价是持仓时间拉长、最大回撤加深。这个没有标准答案,关键是你自己的风险偏好。

手续费和滑点必须写进回测。永续合约每个交易所费率不同,普遍在万五到千一之间,加上滑点,我在代码里统一按0.1%手续费加0.05%滑点来估算。如果只看裸K线收益而忽略成本,ETH高波动策略很容易在实盘里变成“看着赚钱,扣完费用亏钱”。

4. 回测结果分析与参数调优

4.1 基准参数下的回测表现

我以ETH/USDT的1小时K线为样本,时间范围选了一段包含明显下跌结构的行情数据,初始资金1万美元,单笔风险1%。基准参数是:RSI周期14、做空触发阈值70、平仓阈值30、EMA200过滤、ATR止损2倍。回测结果如下表:

指标数值
总收益率68.3%
年化收益率31.2%
最大回撤18.7%
夏普比率1.42
胜率37.8%
盈亏比2.61
交易次数76

看到这个结果,第一反应可能是“胜率才37.8%,这也太低了吧”。但量化交易里胜率和盈亏比从来需要放在一起看。37.8%的胜率配上2.61的盈亏比,期望值为正:0.378 × 2.61 - 0.622 × 1 ≈ 0.365。也就是说,平均每笔交易可以赚取风险的0.365倍。低胜率策略在实盘中很考验心态,因为连续四五次止损太正常了,你得提前做好心理建设。

最大回撤18.7%这个数字不算小。翻看资金曲线,大部分亏损集中在单边反弹阶段,比如价格快速站上EMA200形成“假反转”,策略连续开出空单又连续止损。这也提醒我们,任何参数组合都无法完全规避某类市场环境下的阶段性失效。

4.2 参数敏感性分析

参数调优是最有意思也最危险的部分。我把几个关键参数分别做了扫描,结果见下表:

参数组合总收益率最大回撤交易次数
RSI周期7,空头阈值7052.1%25.3%113
RSI周期14,空头阈值7068.3%18.7%76
RSI周期21,空头阈值7071.5%22.1%54
RSI周期14,空头阈值7561.9%16.2%63
RSI周期14,空头阈值8057.4%15.8%49
EMA50过滤,RSI14,阈值7044.8%28.4%91
EMA100过滤,RSI14,阈值7059.2%21.5%82
EMA200过滤,RSI14,阈值7068.3%18.7%76

从这张表能读出几层信息。RSI周期拉长到21时总收益率最高,但交易次数明显减少、回撤加大,说明策略更依赖少数几波大行情;RSI阈值提高到75或80,虽然回撤减小了,但也过滤掉了很多利润,总体收益下降。EMA200明显优于EMA50,原因在于EMA50太敏感,在震荡行情里频繁给出错误的趋势判断。

对比下来,RSI14加EMA200这套参数组合处在一个相对宽敞的“参数平原”上,相邻参数表现不会断崖式下跌。调参最重要的不是找收益率最高的那个点,而是找出对参数不敏感的区域。如果换一个参数收益就从68%涨到120%、换相邻参数又跌到20%,这种参数大概率是过拟合。

4.3 防范过拟合的实操方法

我优化参数时有三道例行检查。

第一,样本内外验证。把完整数据按时间分成前70%和后30%,只用前70%调参,然后把选好的参数固定下来跑后30%,观察收益是否仍然为正且回撤可控。如果样本内好得惊人、样本外一塌糊涂,直接放弃这套参数。

第二,随机序列检验。把原始K线收盘价顺序随机打乱,再跑同一套参数,理论上不应该产生稳定盈利。如果随机序列也能跑出正收益,说明策略存在逻辑漏洞或数据偏差。

第三,更换交易周期验证。ETH的RSI空头逻辑如果只在1小时图上有效,而在15分钟图和4小时图上完全失效,可靠性也要打个问号。我实际测试发现,这套策略在1小时和4小时上都有效,但4小时信号数量太少,15分钟噪声又太大,所以最终选定1小时作为主周期。

5. 实盘落地中的常见问题与避坑指南

5.1 信号延迟与成交滑点

回测和实盘最大的差距不在策略逻辑,而在成交细节。我前文强调过成交价用下一根K线开盘价,这就是为了贴近实盘。但即使用开盘价成交,实际挂单时依然会碰到滑点。ETH在关键位置的瞬时流动性有时很差,一根几百美元的针就能扫掉限价单。

我的应对方式是:信号触发后不着急市价追单,而是挂在下一根K线开盘价附近略低的位置,比如挂买价再低0.05%。如果成交不了就放弃这笔信号。大部分时候,错过一个信号不会有太大损失,但用市价单在波动剧烈时进场,多付出的滑点往往比错过的利润更肉疼。还有一个小技巧是逢大新闻或大型合约交割前后降低仓位或停止开新仓,那些时段的价格跳动完全没有规律。

5.2 极端行情中的RSI失真

ETH历史上出现过几次短时间内跌幅超过20%的情况。这种行情里RSI会瞬间进入个位数,如果策略的平仓条件是“RSI低于30就平空”,那很可能在最低点附近被动平仓,错过后续继续下跌的一大段利润。

我的处理方式是给RSI平仓条件加一个“最小驻留时间”约束:RSI低于30后,至少再等待8根K线,或者等RSI从30以下回升到30以上才平仓。这本质上是把极端的超卖反转信号过滤掉一部分,让利润在趋势中奔跑更久。另外,“插针”造成的虚假RSI极值也需要处理。我一般要求入场K线的实体部分足够大,例如实体长度大于该K线整个振幅的50%,否则跳过这个信号。这个过滤器会减少一部分交易机会,但能有效避开大量假信号。

5.3 永续合约资金费率与自动减仓

做空ETH永续合约的人很容易忽视资金费率。永续合约每隔8小时结算一次资金费率,多空双方相互支付费用。在多头情绪浓厚的市场里,资金费率往往为正,意味着空头需要持续向多头付费。如果把仓位拿上几天甚至几周,资金费率累计下来会蚕食掉不少利润。我回测里没有单独建模资金费率,但实盘时会把持仓超过24小时的交易单独标记,核算费用后重新评估策略。

自动减仓机制同样需要留意。极端行情下交易所可能触发自动减仓,导致你的仓位在非自愿情况下被平掉。虽然这种概率很小,但一旦发生,策略的持仓管理就乱了。我的对策是杠杆倍数尽量控制在2到3倍,保证金水平始终保持充足,不让仓位处于被动平仓的边缘。长期扛单不是量化策略该干的事,宁可止损重来,也不要让一次意外击穿账户。

5.4 常见问题速查表

问题可能原因解决方法
回测收益高但实盘亏损成交价假设太乐观、没算资金费率用下一根K线开盘价回测,加入费率模型
信号经常被扫损后立刻反转止损距离太窄将ATR倍数从2提升到2.5或3
单边上涨时连续开空连续止损趋势过滤器失效硬性增加“EMA200斜率向上禁止开空”的条件
RSI在30以下一直不平空平仓条件过于机械增加RSI回升确认或最短持仓时间
实盘滑点高于预期信号触发时流动性不足不追单,改用限价单并接受错过信号
数据文件被误删或损坏存储环节不严谨对账单和K线数据做异地备份与哈希校验

其中“EMA200斜率向上禁止开空”是我后来加的一条硬规则。EMA200虽然能判断长期多空方向,但两条相邻K线之间的斜率其实能反映趋势加速还是衰减。当EMA200在20根K线内上涨超过0.5%时,即使价格跌破EMA200,也不急着做空,等斜率重新走平再说。这个过滤条件在震荡反弹行情里帮我避开了一大堆无效空单。

以实际经验收尾

这套策略从想法到能跑,我前后迭代了快两个月,最深的体会是:RSI指标本身不复杂,复杂的是你如何理解它在不同市场环境下的行为。做空ETH比做多更需要耐心,因为下跌过程往往是台阶式的,每次反弹到超买区间的回落,才是空头最舒服的进场点。

我自己实盘跑下来,最大的成长不是学会写代码,而是学会尊重回测和实盘的差异。参数不要追求极端优化,能在“参数平原”上稳住的策略才是好策略。最后再分享一个小技巧:信号触发后,不要急着在收盘瞬间按市价埋单,等下一根K线开盘后的前5分钟,观察盘口流动性稳定了再按限价成交。就这么一个简单习惯,我的平均滑点成本差不多降低了三成。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询