在 GitHub 上查看

Williams Alligator ATR

交易规则

三条 Alligator 线均平滑中间价 (high+low)/2;Length、Shift 控制平滑及完整蜡烛延迟。Teeth 必须计算并形成,但不另加开仓过滤。全部线和 Wilder ATR 形成后,Lips−Jaw 从 <=0 跨到 >0 时空仓买入 Volume,从 >=0 跨到 <0 时平掉真实全部多仓,从不做空。入场固定 ATR×AtrMultiplier 为距真实成交价的原生本地市价止损距离,买卖报价可在蜡烛之间触发。无冷却、止盈或随后 ATR 移动止损。

参数

参数 默认值
CandleType TimeSpan.FromMinutes(30).TimeFrame()
JawLength 13
TeethLength 8
LipsLength 5
JawShift 8
TeethShift 5
LipsShift 3
AtrPeriod 14
AtrMultiplier 2

验证

两种语言使用随附 BTC/TON,检查独立公式、订单方向/数量、真实成交、仓位与退出。带日期的价格、因子、日历数据验证机制,重定位 OHLC/成交量不是多年历史。两个排名组、TopN=1、SMA=2 是明确的小证券池测试配置,长窗口和原始默认值另行检查。不承诺盈利。

namespace StockSharp.Samples.Strategies;

using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;

using Ecng.Common;

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

/// <summary>Long-only shifted median-price Alligator crosses with an entry-frozen native ATR stop.</summary>
public class WilliamsAlligatorAtrStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _jawLength;
	private readonly StrategyParam<int> _teethLength;
	private readonly StrategyParam<int> _lipsLength;
	private readonly StrategyParam<int> _jawShift;
	private readonly StrategyParam<int> _teethShift;
	private readonly StrategyParam<int> _lipsShift;
	private readonly StrategyParam<int> _atrPeriod;
	private readonly StrategyParam<decimal> _atrMultiplier;
	private Order _pending;
	private AlligatorLine _jaw;
	private AlligatorLine _teeth;
	private AlligatorLine _lips;
	private AverageTrueRange _atr;
	private decimal? _previousSpread;
	/// <summary>Finished signal candle type.</summary>
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	/// <summary>Jaw smoothed median-price window.</summary>
	public int JawLength { get => _jawLength.Value; set => _jawLength.Value = value; }

	/// <summary>Teeth smoothed median-price window.</summary>
	public int TeethLength { get => _teethLength.Value; set => _teethLength.Value = value; }

	/// <summary>Lips smoothed median-price window.</summary>
	public int LipsLength { get => _lipsLength.Value; set => _lipsLength.Value = value; }

	/// <summary>Completed bars delaying the Jaw line.</summary>
	public int JawShift { get => _jawShift.Value; set => _jawShift.Value = value; }

	/// <summary>Completed bars delaying the Teeth line.</summary>
	public int TeethShift { get => _teethShift.Value; set => _teethShift.Value = value; }

	/// <summary>Completed bars delaying the Lips line.</summary>
	public int LipsShift { get => _lipsShift.Value; set => _lipsShift.Value = value; }

	/// <summary>Wilder true-range window.</summary>
	public int AtrPeriod { get => _atrPeriod.Value; set => _atrPeriod.Value = value; }

	/// <summary>Entry-frozen ATR stop multiplier.</summary>
	public decimal AtrMultiplier { get => _atrMultiplier.Value; set => _atrMultiplier.Value = value; }

	/// <summary>Initialize the strategy parameters.</summary>
	public WilliamsAlligatorAtrStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
			.SetDisplay("CandleType", "Finished signal candle type.", "Parameters");
		_jawLength = Param(nameof(JawLength), 13).SetRange(1, int.MaxValue)
			.SetDisplay("JawLength", "Jaw smoothed median-price window.", "Parameters");
		_teethLength = Param(nameof(TeethLength), 8).SetRange(1, int.MaxValue)
			.SetDisplay("TeethLength", "Teeth smoothed median-price window.", "Parameters");
		_lipsLength = Param(nameof(LipsLength), 5).SetRange(1, int.MaxValue)
			.SetDisplay("LipsLength", "Lips smoothed median-price window.", "Parameters");
		_jawShift = Param(nameof(JawShift), 8).SetRange(1, int.MaxValue)
			.SetDisplay("JawShift", "Completed bars delaying the Jaw line.", "Parameters");
		_teethShift = Param(nameof(TeethShift), 5).SetRange(1, int.MaxValue)
			.SetDisplay("TeethShift", "Completed bars delaying the Teeth line.", "Parameters");
		_lipsShift = Param(nameof(LipsShift), 3).SetRange(1, int.MaxValue)
			.SetDisplay("LipsShift", "Completed bars delaying the Lips line.", "Parameters");
		_atrPeriod = Param(nameof(AtrPeriod), 14).SetRange(1, int.MaxValue)
			.SetDisplay("AtrPeriod", "Wilder true-range window.", "Parameters");
		_atrMultiplier = Param(nameof(AtrMultiplier), 2m).SetRange(0.000001m, decimal.MaxValue)
			.SetDisplay("AtrMultiplier", "Entry-frozen ATR stop multiplier.", "Parameters");
	}

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_pending = null;
		_jaw = null;
		_teeth = null;
		_lips = null;
		_atr = null;
		_previousSpread = null;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{

		base.OnStarted2(time);
		foreach (var field in new[] { Level1Fields.BestBidPrice, Level1Fields.BestAskPrice })
		{
			var quote = new Subscription(DataType.Level1, Security);
			quote.MarketData.BuildField = field;
			SubscribeLevel1(quote).Start();
		}
		_jaw = new AlligatorLine { Length = JawLength, Shift = JawShift };
		_teeth = new AlligatorLine { Length = TeethLength, Shift = TeethShift };
		_lips = new AlligatorLine { Length = LipsLength, Shift = LipsShift };
		_atr = new AverageTrueRange { Length = AtrPeriod };
		SubscribeCandles(CandleType).Bind(ProcessCandle).Start();
	}

	private bool IsPending => _pending is not null && _pending.State is not (OrderStates.Done or OrderStates.Failed);

	private void Submit(Sides side, decimal quantity, string comment)
	{
		_pending = CreateOrder(side, 0m, quantity);
		_pending.Type = OrderTypes.Market;
		_pending.Comment = comment;
		RegisterOrder(_pending);
	}

	private void ProcessCandle(ICandleMessage candle)
	{
		if (candle.State != CandleStates.Finished) return;
		var jaw = _jaw.Process(candle);
		var teeth = _teeth.Process(candle);
		var lips = _lips.Process(candle);
		var atr = _atr.Process(candle);
		if (!_jaw.IsFormed || !_teeth.IsFormed || !_lips.IsFormed || !_atr.IsFormed
			|| jaw.IsEmpty || teeth.IsEmpty || lips.IsEmpty || atr.IsEmpty) return;
		var spread = lips.ToDecimal() - jaw.ToDecimal();
		var previous = _previousSpread;
		_previousSpread = spread;
		if (previous is null || IsPending || !IsFormedAndOnlineAndAllowTrading()) return;
		if (previous >= 0m && spread < 0m && Position > 0m)
			Submit(Sides.Sell, Position, "0512CrossExit");
		else if (previous <= 0m && spread > 0m && Position == 0m && atr.ToDecimal() > 0m)
		{
			StartProtection(new Unit(), new Unit(atr.ToDecimal() * AtrMultiplier, UnitTypes.Absolute), useMarketOrders: true, isLocalStop: true);
			Submit(Sides.Buy, Volume, "0512Entry");
		}
	}

}