Bill Williams
Bill Williams combines the Alligator indicator with fractal breakouts. The jaws, teeth and lips must diverge before a breakout of the most recent fractal triggers an order.
Details
- Data: Price candles.
- Entry Criteria:
- Calculate fractal highs and lows from the last 5 candles.
- The distance between Jaw and Teeth must exceed
GatorDivSlowPoints. - The distance between Lips and Teeth must exceed
GatorDivFastPoints. - Long: Price closes above the last up fractal by at least
FilterPointspoints and the candle is bullish. - Short: Price closes below the last down fractal by at least
FilterPointspoints and the candle is bearish.
- Exit Criteria:
- Opposite breakout.
- Trailing stop at the latest opposite fractal.
- Stops: Fractal-based trailing stop.
- Default Values:
FilterPoints= 30GatorDivSlowPoints= 250GatorDivFastPoints= 150CandleType= 1 hour candles
using System;
using System.Linq;
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>
/// Bill Williams strategy combining Alligator divergence with fractal breakouts.
/// </summary>
public class BillWilliamsStrategy : Strategy
{
private readonly StrategyParam<decimal> _filterPct;
private readonly StrategyParam<decimal> _gatorDivSlowPct;
private readonly StrategyParam<decimal> _gatorDivFastPct;
private readonly StrategyParam<DataType> _candleType;
private SmoothedMovingAverage _jaw;
private SmoothedMovingAverage _teeth;
private SmoothedMovingAverage _lips;
private readonly Queue<decimal> _highs = new();
private readonly Queue<decimal> _lows = new();
private decimal? _fractalUp;
private decimal? _fractalDown;
public decimal FilterPct { get => _filterPct.Value; set => _filterPct.Value = value; }
public decimal GatorDivSlowPct { get => _gatorDivSlowPct.Value; set => _gatorDivSlowPct.Value = value; }
public decimal GatorDivFastPct { get => _gatorDivFastPct.Value; set => _gatorDivFastPct.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public BillWilliamsStrategy()
{
_filterPct = Param(nameof(FilterPct), 0.05m)
.SetDisplay("Filter %", "Minimal price offset as percentage", "General");
_gatorDivSlowPct = Param(nameof(GatorDivSlowPct), 0.3m)
.SetDisplay("Jaw-Teeth %", "Required jaw-teeth distance as % of price", "Alligator");
_gatorDivFastPct = Param(nameof(GatorDivFastPct), 0.15m)
.SetDisplay("Lips-Teeth %", "Required lips-teeth distance as % of price", "Alligator");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_highs.Clear();
_lows.Clear();
_fractalUp = default;
_fractalDown = default;
_jaw = default;
_teeth = default;
_lips = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_jaw = new SmoothedMovingAverage { Length = 13 };
_teeth = new SmoothedMovingAverage { Length = 8 };
_lips = new SmoothedMovingAverage { Length = 5 };
Indicators.Add(_jaw);
Indicators.Add(_teeth);
Indicators.Add(_lips);
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
_highs.Enqueue(candle.HighPrice);
_lows.Enqueue(candle.LowPrice);
if (_highs.Count > 5) _highs.Dequeue();
if (_lows.Count > 5) _lows.Dequeue();
if (_highs.Count == 5)
{
var hs = _highs.ToArray();
if (hs[2] > hs[0] && hs[2] > hs[1] && hs[2] > hs[3] && hs[2] > hs[4])
_fractalUp = hs[2];
}
if (_lows.Count == 5)
{
var ls = _lows.ToArray();
if (ls[2] < ls[0] && ls[2] < ls[1] && ls[2] < ls[3] && ls[2] < ls[4])
_fractalDown = ls[2];
}
var median = (candle.HighPrice + candle.LowPrice) / 2m;
var jawVal = _jaw.Process(median, candle.OpenTime, true);
var teethVal = _teeth.Process(median, candle.OpenTime, true);
var lipsVal = _lips.Process(median, candle.OpenTime, true);
if (!jawVal.IsFormed || !teethVal.IsFormed || !lipsVal.IsFormed)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var jaw = jawVal.ToDecimal();
var teeth = teethVal.ToDecimal();
var lips = lipsVal.ToDecimal();
var price = candle.ClosePrice;
var filter = FilterPct / 100m * price;
var slowThreshold = GatorDivSlowPct / 100m * price;
var fastThreshold = GatorDivFastPct / 100m * price;
var slowDiff = Math.Abs(jaw - teeth);
var fastDiff = Math.Abs(lips - teeth);
var alligatorOpen = slowDiff >= slowThreshold && fastDiff >= fastThreshold;
if (Position <= 0 && alligatorOpen && _fractalUp is decimal up &&
candle.HighPrice >= up + filter && candle.ClosePrice > candle.OpenPrice && up >= teeth)
{
if (Position < 0) BuyMarket();
BuyMarket();
}
else if (Position >= 0 && alligatorOpen && _fractalDown is decimal down &&
candle.LowPrice <= down - filter && candle.ClosePrice < candle.OpenPrice && down <= teeth)
{
if (Position > 0) SellMarket();
SellMarket();
}
// Fractal stop
if (Position > 0 && _fractalDown is decimal longStop && candle.ClosePrice <= longStop - filter)
{
SellMarket();
}
else if (Position < 0 && _fractalUp is decimal shortStop && candle.ClosePrice >= shortStop + filter)
{
BuyMarket();
}
}
}
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 System.Collections.Generic import Queue
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import SmoothedMovingAverage
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class bill_williams_strategy(Strategy):
def __init__(self):
super(bill_williams_strategy, self).__init__()
self._filter_pct = self.Param("FilterPct", 0.05) \
.SetDisplay("Filter %", "Minimal price offset as percentage", "General")
self._gator_div_slow_pct = self.Param("GatorDivSlowPct", 0.3) \
.SetDisplay("Jaw-Teeth %", "Required jaw-teeth distance as % of price", "Alligator")
self._gator_div_fast_pct = self.Param("GatorDivFastPct", 0.15) \
.SetDisplay("Lips-Teeth %", "Required lips-teeth distance as % of price", "Alligator")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._highs = []
self._lows = []
self._fractal_up = None
self._fractal_down = None
self._jaw = None
self._teeth = None
self._lips = None
@property
def filter_pct(self):
return self._filter_pct.Value
@property
def gator_div_slow_pct(self):
return self._gator_div_slow_pct.Value
@property
def gator_div_fast_pct(self):
return self._gator_div_fast_pct.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(bill_williams_strategy, self).OnReseted()
self._highs = []
self._lows = []
self._fractal_up = None
self._fractal_down = None
self._jaw = None
self._teeth = None
self._lips = None
def OnStarted2(self, time):
super(bill_williams_strategy, self).OnStarted2(time)
self._jaw = SmoothedMovingAverage()
self._jaw.Length = 13
self._teeth = SmoothedMovingAverage()
self._teeth.Length = 8
self._lips = SmoothedMovingAverage()
self._lips.Length = 5
self.Indicators.Add(self._jaw)
self.Indicators.Add(self._teeth)
self.Indicators.Add(self._lips)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self.process_candle).Start()
def process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
self._highs.append(float(candle.HighPrice))
self._lows.append(float(candle.LowPrice))
if len(self._highs) > 5:
self._highs.pop(0)
if len(self._lows) > 5:
self._lows.pop(0)
if len(self._highs) == 5:
hs = self._highs
if hs[2] > hs[0] and hs[2] > hs[1] and hs[2] > hs[3] and hs[2] > hs[4]:
self._fractal_up = hs[2]
if len(self._lows) == 5:
ls = self._lows
if ls[2] < ls[0] and ls[2] < ls[1] and ls[2] < ls[3] and ls[2] < ls[4]:
self._fractal_down = ls[2]
median = (candle.HighPrice + candle.LowPrice) / 2
t = candle.OpenTime
jaw_val = process_float(self._jaw, median, t, True)
teeth_val = process_float(self._teeth, median, t, True)
lips_val = process_float(self._lips, median, t, True)
if not jaw_val.IsFormed or not teeth_val.IsFormed or not lips_val.IsFormed:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
jaw = float(jaw_val)
teeth = float(teeth_val)
lips = float(lips_val)
price = float(candle.ClosePrice)
filt = float(self.filter_pct) / 100.0 * price
slow_threshold = float(self.gator_div_slow_pct) / 100.0 * price
fast_threshold = float(self.gator_div_fast_pct) / 100.0 * price
slow_diff = abs(jaw - teeth)
fast_diff = abs(lips - teeth)
alligator_open = slow_diff >= slow_threshold and fast_diff >= fast_threshold
if (self.Position <= 0 and alligator_open and self._fractal_up is not None and
float(candle.HighPrice) >= self._fractal_up + filt and
float(candle.ClosePrice) > float(candle.OpenPrice) and self._fractal_up >= teeth):
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif (self.Position >= 0 and alligator_open and self._fractal_down is not None and
float(candle.LowPrice) <= self._fractal_down - filt and
float(candle.ClosePrice) < float(candle.OpenPrice) and self._fractal_down <= teeth):
if self.Position > 0:
self.SellMarket()
self.SellMarket()
if self.Position > 0 and self._fractal_down is not None and float(candle.ClosePrice) <= self._fractal_down - filt:
self.SellMarket()
elif self.Position < 0 and self._fractal_up is not None and float(candle.ClosePrice) >= self._fractal_up + filt:
self.BuyMarket()
def CreateClone(self):
return bill_williams_strategy()