在 GitHub 上查看

Live Alligator 策略

该策略利用动态配置的 Alligator 指标和多条 EMA 过滤器来捕捉趋势反转。 当 Alligator 线条改变方向且五条 EMA 确认趋势时开仓。 可选的交易时段过滤器限制在指定时间内入场。 当价格跌破或突破基于 TrailPeriod 的平滑移动平均线时平仓。

  • 入场条件
    • Lips 在 Jaw 之上、Teeth 在 Jaw 之下且前一根柱子的 Lips 位于 Jaw 下方时,在空头趋势后开多。
    • Lips 在 Jaw 之下、Teeth 在 Jaw 之上且前一根柱子的 Lips 位于 Jaw 上方时,在多头趋势后开空。
    • 基于收盘价、加权价、典型价、中位价和开盘价的五条 EMA 必须按趋势方向严格排列。
  • 出场条件
    • 价格穿越 TrailPeriod 平滑移动平均线。
    • 开仓时可选择设置止损。
  • 使用的指标
    • Alligator 三条线及其基于 SMMA 的跟踪止损。
    • 不同价格类型上的 EMA。

参数可配置 Alligator 基准周期、EMA 确认周期、跟踪周期、止损和交易时间窗口。

using System;
using System.Collections.Generic;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Alligator strategy using three smoothed moving averages (jaw, teeth, lips).
/// Buys when lips cross above jaw; sells when lips cross below jaw.
/// Uses a trailing SMA for exit.
/// </summary>
public class LiveAlligatorStrategy : Strategy
{
	private readonly StrategyParam<int> _jawLength;
	private readonly StrategyParam<int> _teethLength;
	private readonly StrategyParam<int> _lipsLength;
	private readonly StrategyParam<int> _trailLength;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevLips;
	private decimal _prevJaw;
	private decimal _prevTrail;
	private bool _hasPrev;

	public int JawLength { get => _jawLength.Value; set => _jawLength.Value = value; }
	public int TeethLength { get => _teethLength.Value; set => _teethLength.Value = value; }
	public int LipsLength { get => _lipsLength.Value; set => _lipsLength.Value = value; }
	public int TrailLength { get => _trailLength.Value; set => _trailLength.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public LiveAlligatorStrategy()
	{
		_jawLength = Param(nameof(JawLength), 21)
			.SetGreaterThanZero()
			.SetDisplay("Jaw", "Alligator Jaw length", "Indicators");

		_teethLength = Param(nameof(TeethLength), 13)
			.SetGreaterThanZero()
			.SetDisplay("Teeth", "Alligator Teeth length", "Indicators");

		_lipsLength = Param(nameof(LipsLength), 8)
			.SetGreaterThanZero()
			.SetDisplay("Lips", "Alligator Lips length", "Indicators");

		_trailLength = Param(nameof(TrailLength), 50)
			.SetGreaterThanZero()
			.SetDisplay("Trail", "Trailing SMA length", "Indicators");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Type of candles", "General");
	}

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

	protected override void OnReseted()
	{
		base.OnReseted();
		_prevLips = 0;
		_prevJaw = 0;
		_prevTrail = 0;
		_hasPrev = false;
	}

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

		var jaw = new SmoothedMovingAverage { Length = JawLength };
		var teeth = new SmoothedMovingAverage { Length = TeethLength };
		var lips = new SmoothedMovingAverage { Length = LipsLength };
		var trail = new SimpleMovingAverage { Length = TrailLength };

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

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

		if (!_hasPrev)
		{
			_prevLips = lipsVal;
			_prevJaw = jawVal;
			_prevTrail = trailVal;
			_hasPrev = true;
			return;
		}

		var close = candle.ClosePrice;

		// Lips cross above jaw -> uptrend start
		if (_prevLips <= _prevJaw && lipsVal > jawVal && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		// Lips cross below jaw -> downtrend start
		else if (_prevLips >= _prevJaw && lipsVal < jawVal && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}

		// Trail exit: close below trail for longs
		if (Position > 0 && close < _prevTrail)
		{
			SellMarket();
		}
		// Trail exit: close above trail for shorts
		else if (Position < 0 && close > _prevTrail)
		{
			BuyMarket();
		}

		_prevLips = lipsVal;
		_prevJaw = jawVal;
		_prevTrail = trailVal;
	}
}