Williams Alligator ATR Strategy
This strategy uses the Williams Alligator indicator combined with an ATR-based stop-loss. A long position is opened when the Lips line crosses above the Jaw line. The position is closed when the Lips cross below the Jaw or when price falls to an ATR-based stop level.
Details
- Entry Criteria: Lips crossing above Jaw.
- Exit Criteria: Lips crossing below Jaw or ATR stop-loss.
- Indicators: Smoothed Moving Averages, Average True Range.
- Type: Long only.
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>
/// Williams Alligator strategy with ATR-based stop-loss.
/// Uses three smoothed moving averages (Jaw, Teeth, Lips) for trend detection.
/// </summary>
public class WilliamsAlligatorAtrStrategy : Strategy
{
private readonly StrategyParam<int> _jawLength;
private readonly StrategyParam<int> _teethLength;
private readonly StrategyParam<int> _lipsLength;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<decimal> _atrMultiplier;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cooldownBars;
private bool _prevLipsAboveJaw;
private bool _prevLipsBelowJaw;
private bool _isInitialized;
private decimal _entryPrice;
private int _cooldownRemaining;
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 AtrPeriod { get => _atrPeriod.Value; set => _atrPeriod.Value = value; }
public decimal AtrMultiplier { get => _atrMultiplier.Value; set => _atrMultiplier.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int CooldownBars { get => _cooldownBars.Value; set => _cooldownBars.Value = value; }
public WilliamsAlligatorAtrStrategy()
{
_jawLength = Param(nameof(JawLength), 13)
.SetGreaterThanZero()
.SetDisplay("Jaw Length", "Alligator jaw period", "Alligator");
_teethLength = Param(nameof(TeethLength), 8)
.SetGreaterThanZero()
.SetDisplay("Teeth Length", "Alligator teeth period", "Alligator");
_lipsLength = Param(nameof(LipsLength), 5)
.SetGreaterThanZero()
.SetDisplay("Lips Length", "Alligator lips period", "Alligator");
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("ATR Period", "ATR period for stop-loss", "ATR");
_atrMultiplier = Param(nameof(AtrMultiplier), 2m)
.SetGreaterThanZero()
.SetDisplay("ATR Multiplier", "ATR multiplier for stop-loss", "ATR");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
_cooldownBars = Param(nameof(CooldownBars), 10)
.SetDisplay("Cooldown Bars", "Bars between trades", "Risk");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevLipsAboveJaw = false;
_prevLipsBelowJaw = false;
_isInitialized = false;
_entryPrice = 0m;
_cooldownRemaining = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var jaw = new SmoothedMovingAverage { Length = JawLength };
var lips = new SmoothedMovingAverage { Length = LipsLength };
var atr = new AverageTrueRange { Length = AtrPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(jaw, lips, atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, jaw);
DrawIndicator(area, lips);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal jawVal, decimal lipsVal, decimal atrVal)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
{
_prevLipsAboveJaw = lipsVal > jawVal;
_prevLipsBelowJaw = lipsVal < jawVal;
_isInitialized = true;
return;
}
if (!_isInitialized)
{
_prevLipsAboveJaw = lipsVal > jawVal;
_prevLipsBelowJaw = lipsVal < jawVal;
_isInitialized = true;
return;
}
var lipsAboveJaw = lipsVal > jawVal;
var lipsBelowJaw = lipsVal < jawVal;
// Check ATR stop for existing positions
if (Position > 0 && _entryPrice > 0 && atrVal > 0)
{
if (candle.ClosePrice <= _entryPrice - AtrMultiplier * atrVal)
{
SellMarket(Math.Abs(Position));
_entryPrice = 0m;
_cooldownRemaining = CooldownBars;
_prevLipsAboveJaw = lipsAboveJaw;
_prevLipsBelowJaw = lipsBelowJaw;
return;
}
}
else if (Position < 0 && _entryPrice > 0 && atrVal > 0)
{
if (candle.ClosePrice >= _entryPrice + AtrMultiplier * atrVal)
{
BuyMarket(Math.Abs(Position));
_entryPrice = 0m;
_cooldownRemaining = CooldownBars;
_prevLipsAboveJaw = lipsAboveJaw;
_prevLipsBelowJaw = lipsBelowJaw;
return;
}
}
if (_cooldownRemaining > 0)
{
_cooldownRemaining--;
_prevLipsAboveJaw = lipsAboveJaw;
_prevLipsBelowJaw = lipsBelowJaw;
return;
}
// Long entry: lips crosses above jaw
if (!_prevLipsAboveJaw && lipsAboveJaw && Position <= 0)
{
if (Position < 0)
BuyMarket(Math.Abs(Position));
BuyMarket(Volume);
_entryPrice = candle.ClosePrice;
_cooldownRemaining = CooldownBars;
}
// Short entry: lips crosses below jaw
else if (!_prevLipsBelowJaw && lipsBelowJaw && Position >= 0)
{
if (Position > 0)
SellMarket(Math.Abs(Position));
SellMarket(Volume);
_entryPrice = candle.ClosePrice;
_cooldownRemaining = CooldownBars;
}
_prevLipsAboveJaw = lipsAboveJaw;
_prevLipsBelowJaw = lipsBelowJaw;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import SmoothedMovingAverage, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class williams_alligator_atr_strategy(Strategy):
def __init__(self):
super(williams_alligator_atr_strategy, self).__init__()
self._jaw_length = self.Param("JawLength", 13) \
.SetGreaterThanZero() \
.SetDisplay("Jaw Length", "Alligator jaw period", "Alligator")
self._lips_length = self.Param("LipsLength", 5) \
.SetGreaterThanZero() \
.SetDisplay("Lips Length", "Alligator lips period", "Alligator")
self._atr_period = self.Param("AtrPeriod", 14) \
.SetGreaterThanZero() \
.SetDisplay("ATR Period", "ATR period for stop-loss", "ATR")
self._atr_multiplier = self.Param("AtrMultiplier", 2.0) \
.SetGreaterThanZero() \
.SetDisplay("ATR Multiplier", "ATR multiplier for stop-loss", "ATR")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._cooldown_bars = self.Param("CooldownBars", 10) \
.SetDisplay("Cooldown Bars", "Bars between trades", "Risk")
self._prev_lips_above_jaw = False
self._prev_lips_below_jaw = False
self._is_initialized = False
self._entry_price = 0.0
self._cooldown_remaining = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(williams_alligator_atr_strategy, self).OnReseted()
self._prev_lips_above_jaw = False
self._prev_lips_below_jaw = False
self._is_initialized = False
self._entry_price = 0.0
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(williams_alligator_atr_strategy, self).OnStarted2(time)
jaw = SmoothedMovingAverage()
jaw.Length = int(self._jaw_length.Value)
lips = SmoothedMovingAverage()
lips.Length = int(self._lips_length.Value)
atr = AverageTrueRange()
atr.Length = int(self._atr_period.Value)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(jaw, lips, atr, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, jaw)
self.DrawIndicator(area, lips)
self.DrawOwnTrades(area)
def _on_process(self, candle, jaw_val, lips_val, atr_val):
if candle.State != CandleStates.Finished:
return
jaw_v = float(jaw_val)
lips_v = float(lips_val)
atr_v = float(atr_val)
close = float(candle.ClosePrice)
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_lips_above_jaw = lips_v > jaw_v
self._prev_lips_below_jaw = lips_v < jaw_v
self._is_initialized = True
return
if not self._is_initialized:
self._prev_lips_above_jaw = lips_v > jaw_v
self._prev_lips_below_jaw = lips_v < jaw_v
self._is_initialized = True
return
lips_above_jaw = lips_v > jaw_v
lips_below_jaw = lips_v < jaw_v
atr_mult = float(self._atr_multiplier.Value)
cooldown = int(self._cooldown_bars.Value)
# Check ATR stop for existing positions
if self.Position > 0 and self._entry_price > 0 and atr_v > 0:
if close <= self._entry_price - atr_mult * atr_v:
self.SellMarket(Math.Abs(self.Position))
self._entry_price = 0.0
self._cooldown_remaining = cooldown
self._prev_lips_above_jaw = lips_above_jaw
self._prev_lips_below_jaw = lips_below_jaw
return
elif self.Position < 0 and self._entry_price > 0 and atr_v > 0:
if close >= self._entry_price + atr_mult * atr_v:
self.BuyMarket(Math.Abs(self.Position))
self._entry_price = 0.0
self._cooldown_remaining = cooldown
self._prev_lips_above_jaw = lips_above_jaw
self._prev_lips_below_jaw = lips_below_jaw
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
self._prev_lips_above_jaw = lips_above_jaw
self._prev_lips_below_jaw = lips_below_jaw
return
if not self._prev_lips_above_jaw and lips_above_jaw and self.Position <= 0:
if self.Position < 0:
self.BuyMarket(Math.Abs(self.Position))
self.BuyMarket(self.Volume)
self._entry_price = close
self._cooldown_remaining = cooldown
elif not self._prev_lips_below_jaw and lips_below_jaw and self.Position >= 0:
if self.Position > 0:
self.SellMarket(Math.Abs(self.Position))
self.SellMarket(self.Volume)
self._entry_price = close
self._cooldown_remaining = cooldown
self._prev_lips_above_jaw = lips_above_jaw
self._prev_lips_below_jaw = lips_below_jaw
def CreateClone(self):
return williams_alligator_atr_strategy()