Стратегия реализует подход Bill Williams CandlesticksBW. Каждая свеча окрашивается по изменению индикаторов Awesome Oscillator (AO) и Accelerator Oscillator (AC). Открытие и закрытие позиций происходит при переходе между бычьими и медвежьими цветами.
Принцип работы
AO вычисляется как разница SMA(5) и SMA(34) по медианной цене.
AC = AO минус SMA(5) от AO.
Свечи делятся на шесть цветов в зависимости от роста/падения AO и AC и направления свечи.
Если предпоследняя свеча имеет цвет <2 и последняя цвет >1, открывается покупка и закрываются продажи.
Если предпоследняя свеча имеет цвет >3 и последняя цвет <4, открывается продажа и закрываются покупки.
Стопы и тейки задаются через StartProtection.
Параметры
CandleType – таймфрейм свечей.
SignalBar – смещение бара для анализа.
StopLoss – расстояние стоп-лосса в пунктах.
TakeProfit – расстояние тейк-профита в пунктах.
BuyPosOpen – разрешить открытие покупок.
SellPosOpen – разрешить открытие продаж.
BuyPosClose – разрешить закрытие покупок.
SellPosClose – разрешить закрытие продаж.
Индикаторы
Awesome Oscillator (на основе SMA).
Accelerator Oscillator.
Правила торговли
Покупка: предпоследний цвет <2 и последний цвет >1.
Продажа: предпоследний цвет >3 и последний цвет <4.
Закрытие покупок: выполняется при условии продажи, если позиция >0.
Закрытие продаж: выполняется при условии покупки, если позиция <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()