在 GitHub 上查看

Sidus 鳄鱼策略

概述

Sidus 策略在 StockSharp 中复刻了 MetaTrader 平台上著名的“Sidus”专家顾问。策略将比尔·威廉姆斯的鳄鱼指标与 14 周期 RSI 结合使用:只有当 RSI 穿越 50 中线并且鳄鱼的三条平滑均线同时向同一方向张开时才会产生信号。进场后立即计算保护性止损和可选的跟踪止损,所有距离均以“点”为单位并自动适配标的的报价精度。

指标与数据

  • 鳄鱼指标:针对每根蜡烛的中值价格(最高价 + 最低价 ÷ 2)计算的三条平滑移动平均线。颚线、齿线与唇线拥有独立的长度与前移偏移量,用于分析最近两根已完成蜡烛之间的坡度差。
  • RSI:14 周期的相对强弱指数,基于收盘价计算。策略只处理已经完成的蜡烛,避免未来函数。
  • 蜡烛数据:通过 CandleType 参数指定任意时间框架,默认订阅 1 分钟蜡烛。

交易逻辑

  1. RSI 确认
    • 多头信号:RSI[t-2] < 50RSI[t-1] > 50,即 RSI 向上穿越 50。
    • 空头信号:RSI[t-2] > 50RSI[t-1] < 50,即 RSI 向下跌破 50。
  2. 鳄鱼坡度过滤
    • 多头要求颚线、齿线和唇线在最近两根有效数据之间的差值都大于 Delta,表明均线同时向上扩张。
    • 空头要求上述差值小于 Delta,意味着均线向下收敛或转跌。
  3. 持仓管理
    • 当出现多头信号时,如果 CloseOpposite = true 会先平掉空头,再以市场价买入 OrderVolume 指定的数量。
    • 出现空头信号时,若允许则先平掉多头,再以市场价卖出同样的数量。

离场与风险控制

  • 初始止损:以上一根蜡烛的极值加减 OffsetPips 计算(点值会根据价格步长自动换算)。若止损距离不合理则放弃该交易。
  • 止盈目标:由 TakeProfitPips 设置,参数为 0 时不使用目标。
  • 跟踪止损:当 TrailingStopPipsTrailingStepPips 均为正值时启用;当价格向有利方向行进至少 TrailingStopPips + TrailingStepPips 点后,止损被移动到距离当根蜡烛最高点/最低点 TrailingStopPips 的位置。
  • 离场检测:每根完成的蜡烛都会检查是否触及止损、止盈或跟踪止损,使用蜡烛的高低价模拟盘中触发。

参数说明

  • OrderVolume(默认 0.1):下单手数或合约数量。
  • OffsetPips(默认 3):相对于上一根蜡烛极值的初始止损距离。
  • TakeProfitPips(默认 75):止盈距离,设为 0 时关闭。
  • TrailingStopPips(默认 5):基础跟踪止损距离。
  • TrailingStepPips(默认 15):每次推进跟踪止损前所需的额外盈利点数。
  • Delta(默认 0.00003):鳄鱼三条线的最小坡度差阈值。
  • CloseOpposite(默认 false):为 true 时在开新仓前先平掉反向仓位;为 false 时仅在当前仓位归零后再入场。
  • JawPeriodTeethPeriodLipsPeriod:鳄鱼颚线/齿线/唇线的平滑长度(默认 13/8/5)。
  • JawShiftTeethShiftLipsShift:对应的前移偏移量(默认 8/5/3)。
  • RsiPeriod(默认 14):RSI 周期。
  • CandleType:所使用的蜡烛类型/时间框架。

实现细节

  • 所有以点为单位的距离都会根据 Security.PriceStep 自动换算;对于 3 位或 5 位小数报价,会额外乘以 10 以匹配 MetaTrader 的点值定义。
  • 为了重现原策略,仅维护每条鳄鱼线在所需偏移下的有限历史值,无需构建完整的时间序列集合。
  • 下单通过高层 API BuyMarketSellMarket 完成,让策略专注于信号生成,而执行细节由 StockSharp 负责。
using System;
using System.Collections.Generic;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Sidus Alligator strategy using triple EMA alignment.
/// </summary>
public class SidusAlligatorStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _jawPeriod;
	private readonly StrategyParam<int> _teethPeriod;
	private readonly StrategyParam<int> _lipsPeriod;

	private decimal? _prevLips;
	private decimal? _prevTeeth;

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public int JawPeriod
	{
		get => _jawPeriod.Value;
		set => _jawPeriod.Value = value;
	}

	public int TeethPeriod
	{
		get => _teethPeriod.Value;
		set => _teethPeriod.Value = value;
	}

	public int LipsPeriod
	{
		get => _lipsPeriod.Value;
		set => _lipsPeriod.Value = value;
	}

	public SidusAlligatorStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe", "General");

		_jawPeriod = Param(nameof(JawPeriod), 50)
			.SetGreaterThanZero()
			.SetDisplay("Jaw Period", "Slow EMA (Jaw)", "Indicators");

		_teethPeriod = Param(nameof(TeethPeriod), 25)
			.SetGreaterThanZero()
			.SetDisplay("Teeth Period", "Medium EMA (Teeth)", "Indicators");

		_lipsPeriod = Param(nameof(LipsPeriod), 10)
			.SetGreaterThanZero()
			.SetDisplay("Lips Period", "Fast EMA (Lips)", "Indicators");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevLips = null;
		_prevTeeth = null;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_prevLips = null;
		_prevTeeth = null;

		var jaw = new ExponentialMovingAverage { Length = JawPeriod };
		var teeth = new ExponentialMovingAverage { Length = TeethPeriod };
		var lips = new ExponentialMovingAverage { Length = LipsPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(jaw, teeth, lips, ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, jaw);
			DrawIndicator(area, teeth);
			DrawIndicator(area, lips);
			DrawOwnTrades(area);
		}
	}

	private void ProcessCandle(ICandleMessage candle, decimal jawVal, decimal teethVal, decimal lipsVal)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (!IsFormedAndOnlineAndAllowTrading())
		{
			_prevLips = lipsVal;
			_prevTeeth = teethVal;
			return;
		}

		if (_prevLips == null || _prevTeeth == null)
		{
			_prevLips = lipsVal;
			_prevTeeth = teethVal;
			return;
		}

		// Lips crosses above teeth with alligator aligned (lips > teeth > jaw)
		if (_prevLips.Value <= _prevTeeth.Value && lipsVal > teethVal && lipsVal > jawVal)
		{
			if (Position < 0)
				BuyMarket();
			if (Position <= 0)
				BuyMarket();
		}
		// Lips crosses below teeth with alligator aligned (lips < teeth < jaw)
		else if (_prevLips.Value >= _prevTeeth.Value && lipsVal < teethVal && lipsVal < jawVal)
		{
			if (Position > 0)
				SellMarket();
			if (Position >= 0)
				SellMarket();
		}

		_prevLips = lipsVal;
		_prevTeeth = teethVal;
	}
}