在 GitHub 上查看
AH HM RSI 策略
概述
该策略是 MetaTrader 智能交易系统 Expert_AH_HM_RSI 的 StockSharp 移植版本。策略在已收盘的 K 线中寻找锤子线或上吊线,并使用相对强弱指数(RSI)进行确认。风险管理与原版 EA 一致:当出现新的反向信号时立即反手。
交易逻辑
- 趋势过滤:使用一个非常短周期的简单移动平均线(默认周期为 2)来判断市场处于微弱上升还是下降趋势。
- K 线形态:分析最新收盘的 K 线。
- 锤子线 条件:K 线实体位于全日波动范围的上三分之一,开收盘价格低于前一根 K 线,并且该 K 线的中点位于移动平均线下方。
- 上吊线 条件:K 线实体位于上三分之一,开收盘价格高于前一根 K 线,并且该 K 线的中点位于移动平均线上方。
- RSI 过滤:
- 做多信号要求 RSI 低于锤子线阈值(默认 40)。
- 做空信号要求 RSI 高于上吊线阈值(默认 60)。
- 执行方式:当信号成立时,以
Volume + |Position| 的数量下单,使得当前持仓在出现反向信号时会立刻反转,与原始 EA 行为一致。
- 离场规则:当 RSI 向上突破下轨(默认 30)或向下跌破上轨(默认 70)时,平掉相应方向的仓位,再现原始代码中的“投票”退出逻辑。
指标
- RelativeStrengthIndex(默认周期 33)。
- SimpleMovingAverage(默认周期 2,基于收盘价)。
参数
| 名称 |
说明 |
默认值 |
Volume |
每次开仓使用的数量。 |
1 |
RsiPeriod |
RSI 的计算周期。 |
33 |
MaPeriod |
趋势过滤用的移动平均周期。 |
2 |
HammerRsiThreshold |
允许锤子线进场的 RSI 上限。 |
40 |
HangingManRsiThreshold |
允许上吊线进场的 RSI 下限。 |
60 |
LowerExitLevel |
RSI 向上突破时平空的阈值。 |
30 |
UpperExitLevel |
RSI 向下跌破时平多的阈值。 |
70 |
CandleType |
使用的 K 线周期。 |
1 小时 K 线 |
所有参数都可以通过 StockSharp 的参数界面进行优化。
使用提示
- 策略仅在确认收盘的 K 线上工作,确保所选周期的数据是完整的。
- 由于使用
Volume + |Position| 下单,反向信号会立即反手。
- 启动时会调用
StartProtection(),只需执行一次即可激活内置保护模块。
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// Hammer/Hanging Man + RSI strategy.
/// Buys on hammer with low RSI, sells on hanging man with high RSI.
/// </summary>
public class AhHmRsiStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<decimal> _rsiLow;
private readonly StrategyParam<decimal> _rsiHigh;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
public decimal RsiLow { get => _rsiLow.Value; set => _rsiLow.Value = value; }
public decimal RsiHigh { get => _rsiHigh.Value; set => _rsiHigh.Value = value; }
public AhHmRsiStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Period", "RSI period", "Indicators");
_rsiLow = Param(nameof(RsiLow), 40m)
.SetDisplay("RSI Low", "RSI oversold threshold for buy", "Signals");
_rsiHigh = Param(nameof(RsiHigh), 60m)
.SetDisplay("RSI High", "RSI overbought threshold for sell", "Signals");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(rsi, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal rsiValue)
{
if (candle.State != CandleStates.Finished) return;
var body = Math.Abs(candle.ClosePrice - candle.OpenPrice);
var range = candle.HighPrice - candle.LowPrice;
if (range <= 0 || body <= 0) return;
var upperShadow = candle.HighPrice - Math.Max(candle.OpenPrice, candle.ClosePrice);
var lowerShadow = Math.Min(candle.OpenPrice, candle.ClosePrice) - candle.LowPrice;
var isHammer = lowerShadow > body * 2 && upperShadow < body;
var isHangingMan = upperShadow > body * 2 && lowerShadow < body;
if (isHammer && rsiValue < RsiLow && Position <= 0)
BuyMarket();
else if (isHangingMan && rsiValue > RsiHigh && Position >= 0)
SellMarket();
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class ah_hm_rsi_strategy(Strategy):
def __init__(self):
super(ah_hm_rsi_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5)))
self._rsi_period = self.Param("RsiPeriod", 14)
self._rsi_low = self.Param("RsiLow", 40.0)
self._rsi_high = self.Param("RsiHigh", 60.0)
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def RsiPeriod(self):
return self._rsi_period.Value
@RsiPeriod.setter
def RsiPeriod(self, value):
self._rsi_period.Value = value
@property
def RsiLow(self):
return self._rsi_low.Value
@RsiLow.setter
def RsiLow(self, value):
self._rsi_low.Value = value
@property
def RsiHigh(self):
return self._rsi_high.Value
@RsiHigh.setter
def RsiHigh(self, value):
self._rsi_high.Value = value
def OnStarted2(self, time):
super(ah_hm_rsi_strategy, self).OnStarted2(time)
rsi = RelativeStrengthIndex()
rsi.Length = self.RsiPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(rsi, self._process_candle).Start()
def _process_candle(self, candle, rsi_value):
if candle.State != CandleStates.Finished:
return
rsi_val = float(rsi_value)
body = abs(float(candle.ClosePrice) - float(candle.OpenPrice))
rng = float(candle.HighPrice) - float(candle.LowPrice)
if rng <= 0 or body <= 0:
return
upper_shadow = float(candle.HighPrice) - max(float(candle.OpenPrice), float(candle.ClosePrice))
lower_shadow = min(float(candle.OpenPrice), float(candle.ClosePrice)) - float(candle.LowPrice)
is_hammer = lower_shadow > body * 2 and upper_shadow < body
is_hanging_man = upper_shadow > body * 2 and lower_shadow < body
if is_hammer and rsi_val < self.RsiLow and self.Position <= 0:
self.BuyMarket()
elif is_hanging_man and rsi_val > self.RsiHigh and self.Position >= 0:
self.SellMarket()
def CreateClone(self):
return ah_hm_rsi_strategy()