This strategy replicates the Bill Williams CandlesticksBW approach. It colors each candle using Awesome Oscillator (AO) and Accelerator Oscillator (AC) momentum. The strategy opens or closes positions based on transitions between bullish and bearish colors.
How it works
Computes AO as the difference between 5- and 34-period SMAs of the median price.
Computes AC as AO minus a 5-period SMA of AO.
Each candle is classified into six colors depending on AO/AC growth and candle direction.
A bullish setup occurs when the penultimate candle is bullish (color 0 or 1). If the last candle's color is above 1, a long position is opened and short positions are closed.
A bearish setup occurs when the penultimate candle is bearish (color 4 or 5). If the last candle's color is below 4, a short position is opened and long positions are closed.
Stops and targets are applied via StartProtection.
Parameters
CandleType – candle timeframe.
SignalBar – offset bar for signal evaluation.
StopLoss – stop loss distance in points.
TakeProfit – take profit distance in points.
BuyPosOpen – allow opening long positions.
SellPosOpen – allow opening short positions.
BuyPosClose – allow closing long positions.
SellPosClose – allow closing short positions.
Indicators
Awesome Oscillator (derived from SMAs).
Accelerator Oscillator.
Trading rules
Long entry: penultimate candle color <2 and last color >1.
Short entry: penultimate candle color >3 and last color <4.
Long exit: on short entry condition if position >0.
Short exit: on long entry condition if position <0.
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>
/// CandlesticksBW strategy based on Bill Williams' color classification of candles.
/// Uses Awesome and Accelerator oscillators to detect momentum shifts.
/// </summary>
public class CandlesticksBwStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private SimpleMovingAverage _aoFast;
private SimpleMovingAverage _aoSlow;
private SimpleMovingAverage _acMa;
private decimal _prevAo;
private decimal _prevAc;
private bool _hasPrev;
private int _prevColor = -1;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public CandlesticksBwStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Time frame for analysis", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
protected override void OnReseted()
{
base.OnReseted();
_aoFast = null;
_aoSlow = null;
_acMa = null;
_prevAo = 0;
_prevAc = 0;
_hasPrev = false;
_prevColor = -1;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevAo = 0;
_prevAc = 0;
_hasPrev = false;
_prevColor = -1;
_aoFast = new SimpleMovingAverage { Length = 5 };
_aoSlow = new SimpleMovingAverage { Length = 34 };
_acMa = new SimpleMovingAverage { Length = 5 };
Indicators.Add(_aoFast);
Indicators.Add(_aoSlow);
Indicators.Add(_acMa);
var sma = new SimpleMovingAverage { Length = 1 };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(sma, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal _unused)
{
if (candle.State != CandleStates.Finished)
return;
var hl2 = (candle.HighPrice + candle.LowPrice) / 2m;
var t = candle.CloseTime;
var aoFastResult = _aoFast.Process(hl2, t, true);
var aoSlowResult = _aoSlow.Process(hl2, t, true);
if (!_aoFast.IsFormed || !_aoSlow.IsFormed)
return;
var ao = aoFastResult.GetValue<decimal>() - aoSlowResult.GetValue<decimal>();
var acMaResult = _acMa.Process(ao, t, true);
if (!_acMa.IsFormed)
return;
var ac = ao - acMaResult.GetValue<decimal>();
// Bill Williams candle color classification:
// 0 = green (bullish candle + AO up + AC up)
// 1 = fade (bearish candle + AO up + AC up)
// 2 = squat green (bullish, mixed)
// 3 = squat red (bearish, mixed)
// 4 = fake (bullish candle + AO down + AC down)
// 5 = red (bearish candle + AO down + AC down)
int color;
if (_hasPrev && ao >= _prevAo && ac >= _prevAc)
color = candle.OpenPrice <= candle.ClosePrice ? 0 : 1;
else if (_hasPrev && ao <= _prevAo && ac <= _prevAc)
color = candle.OpenPrice >= candle.ClosePrice ? 5 : 4;
else
color = candle.OpenPrice <= candle.ClosePrice ? 2 : 3;
_prevAo = ao;
_prevAc = ac;
_hasPrev = true;
if (!IsFormedAndOnline())
{
_prevColor = color;
return;
}
if (_prevColor < 0)
{
_prevColor = color;
return;
}
// Bullish signal: prev was up momentum (0 or 1), current transitions
if (_prevColor < 2 && color > 1 && Position <= 0)
BuyMarket();
// Bearish signal: prev was down momentum (4 or 5), current transitions
else if (_prevColor > 3 && color < 4 && Position >= 0)
SellMarket();
_prevColor = color;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class candlesticks_bw_strategy(Strategy):
def __init__(self):
super(candlesticks_bw_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Time frame for analysis", "General")
self._ao_fast = None
self._ao_slow = None
self._ac_ma = None
self._prev_ao = 0.0
self._prev_ac = 0.0
self._has_prev = False
self._prev_color = -1
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(candlesticks_bw_strategy, self).OnReseted()
self._ao_fast = None
self._ao_slow = None
self._ac_ma = None
self._prev_ao = 0.0
self._prev_ac = 0.0
self._has_prev = False
self._prev_color = -1
def OnStarted2(self, time):
super(candlesticks_bw_strategy, self).OnStarted2(time)
self._prev_ao = 0.0
self._prev_ac = 0.0
self._has_prev = False
self._prev_color = -1
self._ao_fast = SimpleMovingAverage()
self._ao_fast.Length = 5
self._ao_slow = SimpleMovingAverage()
self._ao_slow.Length = 34
self._ac_ma = SimpleMovingAverage()
self._ac_ma.Length = 5
self.Indicators.Add(self._ao_fast)
self.Indicators.Add(self._ao_slow)
self.Indicators.Add(self._ac_ma)
sma = SimpleMovingAverage()
sma.Length = 1
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(sma, self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def process_candle(self, candle, _unused):
if candle.State != CandleStates.Finished:
return
hl2 = (float(candle.HighPrice) + float(candle.LowPrice)) / 2.0
t = candle.CloseTime
ao_fast_result = process_float(self._ao_fast, hl2, t, True)
ao_slow_result = process_float(self._ao_slow, hl2, t, True)
if not self._ao_fast.IsFormed or not self._ao_slow.IsFormed:
return
ao = float(ao_fast_result) - float(ao_slow_result)
ac_ma_result = process_float(self._ac_ma, ao, t, True)
if not self._ac_ma.IsFormed:
return
ac = ao - float(ac_ma_result)
open_price = float(candle.OpenPrice)
close_price = float(candle.ClosePrice)
if self._has_prev and ao >= self._prev_ao and ac >= self._prev_ac:
color = 0 if open_price <= close_price else 1
elif self._has_prev and ao <= self._prev_ao and ac <= self._prev_ac:
color = 5 if open_price >= close_price else 4
else:
color = 2 if open_price <= close_price else 3
self._prev_ao = ao
self._prev_ac = ac
self._has_prev = True
if not self.IsFormedAndOnline():
self._prev_color = color
return
if self._prev_color < 0:
self._prev_color = color
return
if self._prev_color < 2 and color > 1 and self.Position <= 0:
self.BuyMarket()
elif self._prev_color > 3 and color < 4 and self.Position >= 0:
self.SellMarket()
self._prev_color = color
def CreateClone(self):
return candlesticks_bw_strategy()