在 GitHub 上查看
AMS ES RSI 策略
概述
AMS ES RSI 策略在 StockSharp 中复刻了 MetaTrader 顾问 Expert_AMS_ES_RSI 的逻辑。策略将晨星/暮星形态与 RSI 指标结合使用:当出现多头晨星并且 RSI 处于超卖区域时做多;当出现空头暮星且 RSI 显示超买时做空。只要 RSI 回落/回升穿越预设阈值,就立即平仓。
市场假设
- 适用于提供标准 OHLC 蜡烛的任何交易品种,原始版本主要面向外汇与指数期货。
- 前提是价格走势相对平滑,蜡烛形态具有统计意义。极度噪声的超短周期图表可能无法生成可靠信号。
入场逻辑
- 订阅参数中设定的周期(默认 1 小时),等待三根完整闭合的蜡烛。
- 计算最近 BodyAveragePeriod 根蜡烛(默认 3)的平均实体长度。
- 当满足以下条件时判定为 晨星:
- 第三根蜡烛为大阴线(
Open - Close 大于平均实体)。
- 第二根蜡烛实体很小(小于平均值的一半)并向下跳空。
- 第一根蜡烛收盘价高于第三根蜡烛实体的中点。
- 满足对称条件时判定为 暮星(做空信号)。
- 若当前 RSI 低于 LongEntryRsi(默认 40)则确认做多;若 RSI 高于 ShortEntryRsi(默认 60)则确认做空。
- 使用策略的
Volume 参数发送市价单。
出场逻辑
- 当 RSI 向下穿越 UpperExitRsi(默认 70)或 LowerExitRsi(默认 30)时平掉所有多单。
- 当 RSI 向上穿越上述任一水平时平掉所有空单。
- 策略不包含固定止损/止盈,风险控制应通过外部仓位管理或调整阈值完成。
参数
| 名称 |
说明 |
默认值 |
范围 |
CandleType |
订阅的蜡烛类型。 |
1 小时周期 |
任意受支持的蜡烛 |
RsiPeriod |
RSI 计算周期。 |
47 |
可优化 (10–70) |
BodyAveragePeriod |
计算平均实体长度所用的蜡烛数量。 |
3 |
可优化 (2–6) |
LongEntryRsi |
做多所允许的最大 RSI。 |
40 |
可优化 (20–50) |
ShortEntryRsi |
做空所需的最小 RSI。 |
60 |
可优化 (50–80) |
LowerExitRsi |
RSI 向上穿越时的平仓下限。 |
30 |
可优化 (20–40) |
UpperExitRsi |
RSI 向下穿越时的平仓上限。 |
70 |
可优化 (60–80) |
实现要点
- 使用 StockSharp 高级 API,自动处理蜡烛订阅。
- 通过
Bind 获取指标值,完全遵循“不调用 GetValue”的项目规范。
- 只保留最近三根蜡烛用于形态识别,内存占用极小。
- 在
OnStarted 中调用 StartProtection() 以启用平台自带的保护机制。
使用建议
- 将策略附加到目标证券和组合,确认连接器能够提供所需的蜡烛数据。
- 根据标的波动率调整 RSI 阈值:阈值越宽,交易次数越少但信号越稳健。
- 如需模拟原版顾问的固定手数,可结合外部仓位管理或资金管理模块。
- 回测时请确保历史蜡烛包含合理的跳空,以便正确识别晨星/暮星形态。
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// Morning/Evening Star + RSI strategy.
/// Buys on morning star with low RSI, sells on evening star with high RSI.
/// </summary>
public class AmsEsRsiStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<decimal> _rsiLow;
private readonly StrategyParam<decimal> _rsiHigh;
private ICandleMessage _prevCandle;
private ICandleMessage _prevPrevCandle;
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 AmsEsRsiStrategy()
{
_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", "Signals");
_rsiHigh = Param(nameof(RsiHigh), 60m)
.SetDisplay("RSI High", "RSI overbought threshold", "Signals");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevCandle = null;
_prevPrevCandle = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevCandle = null;
_prevPrevCandle = null;
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;
if (_prevCandle != null && _prevPrevCandle != null)
{
var prevBody = Math.Abs(_prevCandle.ClosePrice - _prevCandle.OpenPrice);
var prevRange = _prevCandle.HighPrice - _prevCandle.LowPrice;
var isSmallBody = prevRange > 0 && prevBody < prevRange * 0.3m;
var firstBearish = _prevPrevCandle.OpenPrice > _prevPrevCandle.ClosePrice;
var currBullish = candle.ClosePrice > candle.OpenPrice;
var isMorningStar = firstBearish && isSmallBody && currBullish;
var firstBullish = _prevPrevCandle.ClosePrice > _prevPrevCandle.OpenPrice;
var currBearish = candle.OpenPrice > candle.ClosePrice;
var isEveningStar = firstBullish && isSmallBody && currBearish;
if (isMorningStar && rsiValue < RsiLow && Position <= 0)
BuyMarket();
else if (isEveningStar && rsiValue > RsiHigh && Position >= 0)
SellMarket();
}
_prevPrevCandle = _prevCandle;
_prevCandle = candle;
}
}
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 ams_es_rsi_strategy(Strategy):
def __init__(self):
super(ams_es_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)
self._prev_candle = None
self._prev_prev_candle = None
@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 OnReseted(self):
super(ams_es_rsi_strategy, self).OnReseted()
self._prev_candle = None
self._prev_prev_candle = None
def OnStarted2(self, time):
super(ams_es_rsi_strategy, self).OnStarted2(time)
self._prev_candle = None
self._prev_prev_candle = None
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)
if self._prev_candle is not None and self._prev_prev_candle is not None:
prev_body = abs(float(self._prev_candle.ClosePrice) - float(self._prev_candle.OpenPrice))
prev_range = float(self._prev_candle.HighPrice) - float(self._prev_candle.LowPrice)
is_small_body = prev_range > 0 and prev_body < prev_range * 0.3
first_bearish = float(self._prev_prev_candle.OpenPrice) > float(self._prev_prev_candle.ClosePrice)
curr_bullish = float(candle.ClosePrice) > float(candle.OpenPrice)
is_morning_star = first_bearish and is_small_body and curr_bullish
first_bullish = float(self._prev_prev_candle.ClosePrice) > float(self._prev_prev_candle.OpenPrice)
curr_bearish = float(candle.OpenPrice) > float(candle.ClosePrice)
is_evening_star = first_bullish and is_small_body and curr_bearish
if is_morning_star and rsi_val < self.RsiLow and self.Position <= 0:
self.BuyMarket()
elif is_evening_star and rsi_val > self.RsiHigh and self.Position >= 0:
self.SellMarket()
self._prev_prev_candle = self._prev_candle
self._prev_candle = candle
def CreateClone(self):
return ams_es_rsi_strategy()