DSS Bressert Strategy
This strategy uses the Double Smoothed Stochastic (DSS) Bressert indicator. Two lines are calculated:
- DSS line – stochastic value smoothed twice with exponential moving average.
- MIT line – intermediate value after the first smoothing.
A trade is opened when these lines cross:
- Buy when the DSS line crosses below the MIT line after being above it.
- Sell when the MIT line crosses below the DSS line after being above it.
Parameters
| Parameter | Description |
|---|---|
EmaPeriod |
EMA smoothing period (default: 8) |
StoPeriod |
Stochastic calculation period (default: 13) |
TakeProfitPercent |
Take profit percentage for protective orders (default: 2) |
StopLossPercent |
Stop loss percentage for protective orders (default: 1) |
CandleType |
Timeframe used for calculations (default: 4 hours) |
Notes
- Strategy works on closed candles only.
- Protection uses percentage based stop loss and take profit.
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy based on the Double Smoothed Stochastic (DSS) Bressert indicator.
/// Trades when DSS line crosses the MIT line.
/// </summary>
public class DssBressertStrategy : Strategy
{
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<int> _stoPeriod;
private readonly StrategyParam<decimal> _takeProfitPercent;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<int> _cooldownBars;
private readonly StrategyParam<DataType> _candleType;
private DssBressertIndicator _dss = null!;
private decimal _prevDss;
private decimal _prevMit;
private int _barsSinceTrade;
/// <summary>
/// EMA period used for smoothing.
/// </summary>
public int EmaPeriod
{
get => _emaPeriod.Value;
set => _emaPeriod.Value = value;
}
/// <summary>
/// Stochastic period.
/// </summary>
public int StoPeriod
{
get => _stoPeriod.Value;
set => _stoPeriod.Value = value;
}
/// <summary>
/// Take profit level in percent.
/// </summary>
public decimal TakeProfitPercent
{
get => _takeProfitPercent.Value;
set => _takeProfitPercent.Value = value;
}
/// <summary>
/// Stop loss level in percent.
/// </summary>
public decimal StopLossPercent
{
get => _stopLossPercent.Value;
set => _stopLossPercent.Value = value;
}
/// <summary>
/// Bars to wait after a completed trade.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Candle type used by the strategy.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes strategy parameters.
/// </summary>
public DssBressertStrategy()
{
_emaPeriod = Param(nameof(EmaPeriod), 8)
.SetGreaterThanZero()
.SetDisplay("EMA Period", "EMA smoothing period", "Indicators")
.SetOptimize(5, 20, 1);
_stoPeriod = Param(nameof(StoPeriod), 13)
.SetGreaterThanZero()
.SetDisplay("Stochastic Period", "Stochastic calculation period", "Indicators")
.SetOptimize(5, 30, 1);
_takeProfitPercent = Param(nameof(TakeProfitPercent), 2m)
.SetGreaterThanZero()
.SetDisplay("Take Profit %", "Take profit level in percent", "Risk")
.SetOptimize(1m, 5m, 0.5m);
_stopLossPercent = Param(nameof(StopLossPercent), 1m)
.SetGreaterThanZero()
.SetDisplay("Stop Loss %", "Stop loss level in percent", "Risk")
.SetOptimize(0.5m, 5m, 0.5m);
_cooldownBars = Param(nameof(CooldownBars), 1)
.SetDisplay("Cooldown Bars", "Bars to wait after a completed trade", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevDss = 0m;
_prevMit = 0m;
_barsSinceTrade = CooldownBars;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_dss = new DssBressertIndicator
{
EmaPeriod = EmaPeriod,
StoPeriod = StoPeriod
};
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_dss, ProcessCandle)
.Start();
StartProtection(
takeProfit: new Unit(TakeProfitPercent, UnitTypes.Percent),
stopLoss: new Unit(StopLossPercent, UnitTypes.Percent));
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _dss);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal dssValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnline())
return;
if (_barsSinceTrade < CooldownBars)
_barsSinceTrade++;
var currentMit = _dss.LastMit;
if (_barsSinceTrade >= CooldownBars && _prevDss <= _prevMit && dssValue > currentMit && Position <= 0)
{
BuyMarket(Volume + Math.Abs(Position));
_barsSinceTrade = 0;
}
else if (_barsSinceTrade >= CooldownBars && _prevDss >= _prevMit && dssValue < currentMit && Position >= 0)
{
SellMarket(Volume + Math.Abs(Position));
_barsSinceTrade = 0;
}
_prevDss = dssValue;
_prevMit = currentMit;
}
}
/// <summary>
/// Double Smoothed Stochastic Bressert indicator.
/// Calculates DSS and MIT lines. Returns DSS value as decimal output.
/// </summary>
public class DssBressertIndicator : BaseIndicator
{
private static readonly object _sync = new();
/// <summary>
/// EMA smoothing period.
/// </summary>
public int EmaPeriod { get; set; } = 8;
/// <summary>
/// Stochastic period.
/// </summary>
public int StoPeriod { get; set; } = 13;
/// <summary>
/// Last MIT value for external access.
/// </summary>
public decimal LastMit { get; private set; } = 50m;
private readonly Queue<decimal> _high = new();
private readonly Queue<decimal> _low = new();
private readonly Queue<decimal> _close = new();
private readonly Queue<decimal> _mit = new();
private decimal _prevMit = 50m;
private decimal _prevDss = 50m;
/// <inheritdoc />
public override void Reset()
{
base.Reset();
_high.Clear();
_low.Clear();
_close.Clear();
_mit.Clear();
_prevMit = 50m;
_prevDss = 50m;
LastMit = 50m;
}
/// <inheritdoc />
protected override IIndicatorValue OnProcess(IIndicatorValue input)
{
lock (_sync)
{
var candle = input.GetValue<ICandleMessage>();
if (candle == null)
return new DecimalIndicatorValue(this, _prevDss, input.Time);
_high.Enqueue(candle.HighPrice);
_low.Enqueue(candle.LowPrice);
_close.Enqueue(candle.ClosePrice);
if (_high.Count > StoPeriod)
{
_high.Dequeue();
_low.Dequeue();
_close.Dequeue();
}
if (_high.Count >= StoPeriod)
IsFormed = true;
var highRange = GetMax(_high);
var lowRange = GetMin(_low);
if (highRange == lowRange)
return new DecimalIndicatorValue(this, _prevDss, input.Time);
var delta = candle.ClosePrice - lowRange;
var mitRaw = delta / (highRange - lowRange) * 100m;
var coeff = 2m / (1m + EmaPeriod);
var mitValue = _prevMit + coeff * (mitRaw - _prevMit);
_prevMit = mitValue;
LastMit = mitValue;
_mit.Enqueue(mitValue);
if (_mit.Count > StoPeriod)
_mit.Dequeue();
var highMit = GetMax(_mit);
var lowMit = GetMin(_mit);
if (highMit == lowMit)
return new DecimalIndicatorValue(this, _prevDss, input.Time);
var deltaMit = mitValue - lowMit;
var dssRaw = deltaMit / (highMit - lowMit) * 100m;
var dssValue = _prevDss + coeff * (dssRaw - _prevDss);
_prevDss = dssValue;
return new DecimalIndicatorValue(this, dssValue, input.Time);
}
}
private static decimal GetMax(IEnumerable<decimal> values)
{
var max = decimal.MinValue;
foreach (var v in values)
{
if (v > max)
max = v;
}
return max;
}
private static decimal GetMin(IEnumerable<decimal> values)
{
var min = decimal.MaxValue;
foreach (var v in values)
{
if (v < min)
min = v;
}
return min;
}
}
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 collections import deque
from StockSharp.Messages import DataType, Unit, UnitTypes, CandleStates
from StockSharp.Algo.Strategies import Strategy
class dss_bressert_strategy(Strategy):
def __init__(self):
super(dss_bressert_strategy, self).__init__()
self._ema_period = self.Param("EmaPeriod", 8) \
.SetDisplay("EMA Period", "EMA smoothing period", "Indicators")
self._sto_period = self.Param("StoPeriod", 13) \
.SetDisplay("Stochastic Period", "Stochastic calculation period", "Indicators")
self._take_profit_percent = self.Param("TakeProfitPercent", 2.0) \
.SetDisplay("Take Profit %", "Take profit level in percent", "Risk")
self._stop_loss_percent = self.Param("StopLossPercent", 1.0) \
.SetDisplay("Stop Loss %", "Stop loss level in percent", "Risk")
self._cooldown_bars = self.Param("CooldownBars", 1) \
.SetDisplay("Cooldown Bars", "Bars to wait after a completed trade", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._prev_dss = 0.0
self._prev_mit = 0.0
self._bars_since_trade = 0
self._high_buf = deque()
self._low_buf = deque()
self._mit_buf = deque()
self._prev_mit_ema = 50.0
self._prev_dss_ema = 50.0
self._last_mit = 50.0
self._dss_formed = False
@property
def EmaPeriod(self):
return self._ema_period.Value
@EmaPeriod.setter
def EmaPeriod(self, value):
self._ema_period.Value = value
@property
def StoPeriod(self):
return self._sto_period.Value
@StoPeriod.setter
def StoPeriod(self, value):
self._sto_period.Value = value
@property
def TakeProfitPercent(self):
return self._take_profit_percent.Value
@TakeProfitPercent.setter
def TakeProfitPercent(self, value):
self._take_profit_percent.Value = value
@property
def StopLossPercent(self):
return self._stop_loss_percent.Value
@StopLossPercent.setter
def StopLossPercent(self, value):
self._stop_loss_percent.Value = value
@property
def CooldownBars(self):
return self._cooldown_bars.Value
@CooldownBars.setter
def CooldownBars(self, value):
self._cooldown_bars.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def _compute_dss(self, candle):
high = float(candle.HighPrice)
low = float(candle.LowPrice)
close = float(candle.ClosePrice)
sto_period = self.StoPeriod
ema_period = self.EmaPeriod
self._high_buf.append(high)
self._low_buf.append(low)
if len(self._high_buf) > sto_period:
self._high_buf.popleft()
self._low_buf.popleft()
if len(self._high_buf) >= sto_period:
self._dss_formed = True
high_range = max(self._high_buf)
low_range = min(self._low_buf)
if high_range == low_range:
return self._prev_dss_ema
delta = close - low_range
mit_raw = delta / (high_range - low_range) * 100.0
coeff = 2.0 / (1.0 + ema_period)
mit_value = self._prev_mit_ema + coeff * (mit_raw - self._prev_mit_ema)
self._prev_mit_ema = mit_value
self._last_mit = mit_value
self._mit_buf.append(mit_value)
if len(self._mit_buf) > sto_period:
self._mit_buf.popleft()
high_mit = max(self._mit_buf)
low_mit = min(self._mit_buf)
if high_mit == low_mit:
return self._prev_dss_ema
delta_mit = mit_value - low_mit
dss_raw = delta_mit / (high_mit - low_mit) * 100.0
dss_value = self._prev_dss_ema + coeff * (dss_raw - self._prev_dss_ema)
self._prev_dss_ema = dss_value
return dss_value
def OnStarted2(self, time):
super(dss_bressert_strategy, self).OnStarted2(time)
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self.ProcessCandle).Start()
self.StartProtection(
takeProfit=Unit(self.TakeProfitPercent, UnitTypes.Percent),
stopLoss=Unit(self.StopLossPercent, UnitTypes.Percent))
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def ProcessCandle(self, candle):
if candle.State != CandleStates.Finished:
return
dss_value = self._compute_dss(candle)
if not self._dss_formed:
return
if self._bars_since_trade < self.CooldownBars:
self._bars_since_trade += 1
current_mit = self._last_mit
if (self._bars_since_trade >= self.CooldownBars
and self._prev_dss <= self._prev_mit
and dss_value > current_mit
and self.Position <= 0):
self.BuyMarket(self.Volume + abs(self.Position))
self._bars_since_trade = 0
elif (self._bars_since_trade >= self.CooldownBars
and self._prev_dss >= self._prev_mit
and dss_value < current_mit
and self.Position >= 0):
self.SellMarket(self.Volume + abs(self.Position))
self._bars_since_trade = 0
self._prev_dss = dss_value
self._prev_mit = current_mit
def OnReseted(self):
super(dss_bressert_strategy, self).OnReseted()
self._prev_dss = 0.0
self._prev_mit = 0.0
self._bars_since_trade = self.CooldownBars
self._high_buf = deque()
self._low_buf = deque()
self._mit_buf = deque()
self._prev_mit_ema = 50.0
self._prev_dss_ema = 50.0
self._last_mit = 50.0
self._dss_formed = False
def CreateClone(self):
return dss_bressert_strategy()