在 GitHub 上查看

AMS ES RSI 策略

概述

AMS ES RSI 策略在 StockSharp 中复刻了 MetaTrader 顾问 Expert_AMS_ES_RSI 的逻辑。策略将晨星/暮星形态与 RSI 指标结合使用:当出现多头晨星并且 RSI 处于超卖区域时做多;当出现空头暮星且 RSI 显示超买时做空。只要 RSI 回落/回升穿越预设阈值,就立即平仓。

市场假设

  • 适用于提供标准 OHLC 蜡烛的任何交易品种,原始版本主要面向外汇与指数期货。
  • 前提是价格走势相对平滑,蜡烛形态具有统计意义。极度噪声的超短周期图表可能无法生成可靠信号。

入场逻辑

  1. 订阅参数中设定的周期(默认 1 小时),等待三根完整闭合的蜡烛。
  2. 计算最近 BodyAveragePeriod 根蜡烛(默认 3)的平均实体长度。
  3. 当满足以下条件时判定为 晨星
    • 第三根蜡烛为大阴线(Open - Close 大于平均实体)。
    • 第二根蜡烛实体很小(小于平均值的一半)并向下跳空。
    • 第一根蜡烛收盘价高于第三根蜡烛实体的中点。
  4. 满足对称条件时判定为 暮星(做空信号)。
  5. 若当前 RSI 低于 LongEntryRsi(默认 40)则确认做多;若 RSI 高于 ShortEntryRsi(默认 60)则确认做空。
  6. 使用策略的 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() 以启用平台自带的保护机制。

使用建议

  1. 将策略附加到目标证券和组合,确认连接器能够提供所需的蜡烛数据。
  2. 根据标的波动率调整 RSI 阈值:阈值越宽,交易次数越少但信号越稳健。
  3. 如需模拟原版顾问的固定手数,可结合外部仓位管理或资金管理模块。
  4. 回测时请确保历史蜡烛包含合理的跳空,以便正确识别晨星/暮星形态。
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;
	}
}