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");
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.BusinessEntities")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import DateTime, Decimal, Math, TimeSpan, InvalidOperationException
from System.Globalization import CultureInfo, DateTimeStyles
from System.IO import File
from StockSharp.Algo.Indicators import ExponentialMovingAverage, SimpleMovingAverage, AverageTrueRange, AlligatorLine, CandleIndicatorValue
from StockSharp.Algo.Strategies import Strategy
from StockSharp.BusinessEntities import Subscription
from StockSharp.Messages import Level1Fields, ITickTradeMessage, CandleStates, DataType, OrderStates, OrderTypes, Sides, Unit, UnitTypes
class williams_alligator_atr_strategy(Strategy):
"""Long-only shifted median-price Alligator crosses with an entry-frozen native ATR stop."""
def __init__(self):
super(williams_alligator_atr_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))) \
.SetDisplay("CandleType", "Finished signal candle type.", "Parameters")
self._jaw_length = self.Param("JawLength", 13).SetRange(1, 2147483647) \
.SetDisplay("JawLength", "Jaw smoothed median-price window.", "Parameters")
self._teeth_length = self.Param("TeethLength", 8).SetRange(1, 2147483647) \
.SetDisplay("TeethLength", "Teeth smoothed median-price window.", "Parameters")
self._lips_length = self.Param("LipsLength", 5).SetRange(1, 2147483647) \
.SetDisplay("LipsLength", "Lips smoothed median-price window.", "Parameters")
self._jaw_shift = self.Param("JawShift", 8).SetRange(1, 2147483647) \
.SetDisplay("JawShift", "Completed bars delaying the Jaw line.", "Parameters")
self._teeth_shift = self.Param("TeethShift", 5).SetRange(1, 2147483647) \
.SetDisplay("TeethShift", "Completed bars delaying the Teeth line.", "Parameters")
self._lips_shift = self.Param("LipsShift", 3).SetRange(1, 2147483647) \
.SetDisplay("LipsShift", "Completed bars delaying the Lips line.", "Parameters")
self._atr_period = self.Param("AtrPeriod", 14).SetRange(1, 2147483647) \
.SetDisplay("AtrPeriod", "Wilder true-range window.", "Parameters")
self._atr_multiplier = self.Param("AtrMultiplier", Decimal(2)).SetRange(Decimal(0.000001), Decimal.MaxValue) \
.SetDisplay("AtrMultiplier", "Entry-frozen ATR stop multiplier.", "Parameters")
self._clear_state()
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def JawLength(self):
return self._jaw_length.Value
@JawLength.setter
def JawLength(self, value):
self._jaw_length.Value = value
@property
def TeethLength(self):
return self._teeth_length.Value
@TeethLength.setter
def TeethLength(self, value):
self._teeth_length.Value = value
@property
def LipsLength(self):
return self._lips_length.Value
@LipsLength.setter
def LipsLength(self, value):
self._lips_length.Value = value
@property
def JawShift(self):
return self._jaw_shift.Value
@JawShift.setter
def JawShift(self, value):
self._jaw_shift.Value = value
@property
def TeethShift(self):
return self._teeth_shift.Value
@TeethShift.setter
def TeethShift(self, value):
self._teeth_shift.Value = value
@property
def LipsShift(self):
return self._lips_shift.Value
@LipsShift.setter
def LipsShift(self, value):
self._lips_shift.Value = value
@property
def AtrPeriod(self):
return self._atr_period.Value
@AtrPeriod.setter
def AtrPeriod(self, value):
self._atr_period.Value = value
@property
def AtrMultiplier(self):
return self._atr_multiplier.Value
@AtrMultiplier.setter
def AtrMultiplier(self, value):
self._atr_multiplier.Value = value
def _clear_state(self):
self._pending = None
self._jaw = None
self._teeth = None
self._lips = None
self._atr = None
self._previous_spread = None
def OnReseted(self):
super(williams_alligator_atr_strategy, self).OnReseted()
self._clear_state()
def GetWorkingSecurities(self):
return [(self.Security, self.CandleType)]
def OnStarted2(self, time):
super(williams_alligator_atr_strategy, self).OnStarted2(time)
for field in (Level1Fields.BestBidPrice, Level1Fields.BestAskPrice):
quote = Subscription(DataType.Level1, self.Security)
quote.MarketData.BuildField = field
self.SubscribeLevel1(quote).Start()
self._jaw = AlligatorLine()
self._jaw.Length = self.JawLength
self._jaw.Shift = self.JawShift
self._teeth = AlligatorLine()
self._teeth.Length = self.TeethLength
self._teeth.Shift = self.TeethShift
self._lips = AlligatorLine()
self._lips.Length = self.LipsLength
self._lips.Shift = self.LipsShift
self._atr = AverageTrueRange()
self._atr.Length = self.AtrPeriod
self.SubscribeCandles(self.CandleType).Bind(self._process_candle).Start()
def _is_pending(self):
return self._pending is not None and self._pending.State not in (OrderStates.Done, OrderStates.Failed)
def _submit(self, side, quantity, comment):
self._pending = self.CreateOrder(side, Decimal(0), quantity)
self._pending.Type = OrderTypes.Market
self._pending.Comment = comment
self.RegisterOrder(self._pending)
@staticmethod
def _process(indicator, candle):
value = CandleIndicatorValue(indicator, candle)
value.IsFinal = True
result = indicator.Process(value)
return None if result.IsEmpty else result.GetValue[Decimal](None)
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
jaw = self._process(self._jaw, candle)
teeth = self._process(self._teeth, candle)
lips = self._process(self._lips, candle)
atr = self._process(self._atr, candle)
if not self._jaw.IsFormed or not self._teeth.IsFormed or not self._lips.IsFormed or not self._atr.IsFormed or jaw is None or teeth is None or lips is None or atr is None:
return
spread = lips - jaw
previous = self._previous_spread
self._previous_spread = spread
if previous is None or self._is_pending() or not self.IsFormedAndOnlineAndAllowTrading():
return
if previous >= 0 and spread < 0 and self.Position > 0:
self._submit(Sides.Sell, self.Position, "0512CrossExit")
elif previous <= 0 and spread > 0 and self.Position == 0 and atr > 0:
self.StartProtection(Unit(), Unit(atr * self.AtrMultiplier, UnitTypes.Absolute), useMarketOrders=True, isLocalStop=True)
self._submit(Sides.Buy, self.Volume, "0512Entry")
def CreateClone(self):
return williams_alligator_atr_strategy()