Double Supertrend
Double Supertrend employs two ATR‑based moving averages with different periods and multipliers. The first line sets the trade direction, while the second can act as a target or trailing exit. This combination allows flexible trend following with defined profit and risk parameters.
When price moves above both lines and the strategy is set to trade long, a position is opened. For short trades the conditions are mirrored. Exits depend on the selected take‑profit type or a percentage stop loss.
Details
- Data: Price candles.
- Entry Criteria: Price crosses supertrend lines in the allowed
Direction. - Exit Criteria: Opposite line break, take‑profit (
TPType/TPPercent) or stop‑loss (SLPercent). - Stops: Percentage stop based on
SLPercent. - Default Values:
ATRPeriod1= 10Factor1= 3.0ATRPeriod2= 20Factor2= 5.0Direction= "Long"TPType= "Supertrend"TPPercent= 1.5SLPercent= 10.0
- Filters:
- Category: Trend following
- Direction: Configurable
- Indicators: ATR‑based Supertrend
- Complexity: Advanced
- Risk level: Medium
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>
/// Double Supertrend Strategy.
/// Uses two SuperTrend indicators with different parameters.
/// Enters long when both SuperTrends are bullish.
/// Enters short when both SuperTrends are bearish.
/// </summary>
public class DoubleSupertrendStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleTypeParam;
private readonly StrategyParam<int> _atrPeriod1;
private readonly StrategyParam<decimal> _factor1;
private readonly StrategyParam<int> _atrPeriod2;
private readonly StrategyParam<decimal> _factor2;
private readonly StrategyParam<int> _cooldownBars;
private SuperTrend _st1;
private SuperTrend _st2;
private bool _prevUpTrend1;
private bool _prevUpTrend2;
private bool _hasPrev;
private int _cooldownRemaining;
public DoubleSupertrendStrategy()
{
_candleTypeParam = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
.SetDisplay("Candle type", "Candle type for strategy calculation.", "General");
_atrPeriod1 = Param(nameof(ATRPeriod1), 10)
.SetGreaterThanZero()
.SetDisplay("ST1 Period", "First SuperTrend ATR period", "SuperTrend 1");
_factor1 = Param(nameof(Factor1), 2.0m)
.SetDisplay("ST1 Factor", "First SuperTrend multiplier", "SuperTrend 1");
_atrPeriod2 = Param(nameof(ATRPeriod2), 20)
.SetGreaterThanZero()
.SetDisplay("ST2 Period", "Second SuperTrend ATR period", "SuperTrend 2");
_factor2 = Param(nameof(Factor2), 4.0m)
.SetDisplay("ST2 Factor", "Second SuperTrend multiplier", "SuperTrend 2");
_cooldownBars = Param(nameof(CooldownBars), 10)
.SetDisplay("Cooldown Bars", "Bars to wait between trades", "Risk");
}
public DataType CandleType
{
get => _candleTypeParam.Value;
set => _candleTypeParam.Value = value;
}
public int ATRPeriod1
{
get => _atrPeriod1.Value;
set => _atrPeriod1.Value = value;
}
public decimal Factor1
{
get => _factor1.Value;
set => _factor1.Value = value;
}
public int ATRPeriod2
{
get => _atrPeriod2.Value;
set => _atrPeriod2.Value = value;
}
public decimal Factor2
{
get => _factor2.Value;
set => _factor2.Value = value;
}
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_st1 = null;
_st2 = null;
_prevUpTrend1 = false;
_prevUpTrend2 = false;
_hasPrev = false;
_cooldownRemaining = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_st1 = new SuperTrend { Length = ATRPeriod1, Multiplier = Factor1 };
_st2 = new SuperTrend { Length = ATRPeriod2, Multiplier = Factor2 };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(_st1, _st2, OnProcess)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _st1);
DrawIndicator(area, _st2);
DrawOwnTrades(area);
}
}
private void OnProcess(ICandleMessage candle, IIndicatorValue st1Value, IIndicatorValue st2Value)
{
if (candle.State != CandleStates.Finished)
return;
if (!_st1.IsFormed || !_st2.IsFormed)
return;
if (st1Value.IsEmpty || st2Value.IsEmpty)
return;
var stv1 = (SuperTrendIndicatorValue)st1Value;
var stv2 = (SuperTrendIndicatorValue)st2Value;
var upTrend1 = stv1.IsUpTrend;
var upTrend2 = stv2.IsUpTrend;
if (!IsFormedAndOnlineAndAllowTrading())
{
_prevUpTrend1 = upTrend1;
_prevUpTrend2 = upTrend2;
_hasPrev = true;
return;
}
if (_cooldownRemaining > 0)
{
_cooldownRemaining--;
_prevUpTrend1 = upTrend1;
_prevUpTrend2 = upTrend2;
_hasPrev = true;
return;
}
if (!_hasPrev)
{
_prevUpTrend1 = upTrend1;
_prevUpTrend2 = upTrend2;
_hasPrev = true;
return;
}
// Both bullish
var bothBullish = upTrend1 && upTrend2;
// Both bearish
var bothBearish = !upTrend1 && !upTrend2;
// Trend changed to both bullish
var bullishSignal = bothBullish && (!_prevUpTrend1 || !_prevUpTrend2);
// Trend changed to both bearish
var bearishSignal = bothBearish && (_prevUpTrend1 || _prevUpTrend2);
// Buy when both SuperTrends turn bullish
if (bullishSignal && Position <= 0)
{
if (Position < 0)
BuyMarket(Math.Abs(Position));
BuyMarket(Volume);
_cooldownRemaining = CooldownBars;
}
// Sell when both SuperTrends turn bearish
else if (bearishSignal && Position >= 0)
{
if (Position > 0)
SellMarket(Math.Abs(Position));
SellMarket(Volume);
_cooldownRemaining = CooldownBars;
}
// Exit long if either SuperTrend turns bearish
else if (Position > 0 && !bothBullish && (_prevUpTrend1 && _prevUpTrend2))
{
SellMarket(Math.Abs(Position));
_cooldownRemaining = CooldownBars;
}
// Exit short if either SuperTrend turns bullish
else if (Position < 0 && !bothBearish && (!_prevUpTrend1 && !_prevUpTrend2))
{
BuyMarket(Math.Abs(Position));
_cooldownRemaining = CooldownBars;
}
_prevUpTrend1 = upTrend1;
_prevUpTrend2 = upTrend2;
}
}
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 SuperTrend
from StockSharp.Algo.Strategies import Strategy
class double_supertrend_strategy(Strategy):
"""Double SuperTrend Strategy.
Uses two SuperTrend indicators with different parameters.
Enters long when both SuperTrends are bullish.
Enters short when both SuperTrends are bearish."""
def __init__(self):
super(double_supertrend_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))) \
.SetDisplay("Candle type", "Candle type for strategy calculation.", "General")
self._atr_period1 = self.Param("ATRPeriod1", 10) \
.SetDisplay("ST1 Period", "First SuperTrend ATR period", "SuperTrend 1")
self._factor1 = self.Param("Factor1", 2.0) \
.SetDisplay("ST1 Factor", "First SuperTrend multiplier", "SuperTrend 1")
self._atr_period2 = self.Param("ATRPeriod2", 20) \
.SetDisplay("ST2 Period", "Second SuperTrend ATR period", "SuperTrend 2")
self._factor2 = self.Param("Factor2", 4.0) \
.SetDisplay("ST2 Factor", "Second SuperTrend multiplier", "SuperTrend 2")
self._cooldown_bars = self.Param("CooldownBars", 10) \
.SetDisplay("Cooldown Bars", "Bars to wait between trades", "Risk")
self._st1 = None
self._st2 = None
self._prev_up_trend1 = False
self._prev_up_trend2 = False
self._has_prev = False
self._cooldown_remaining = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(double_supertrend_strategy, self).OnReseted()
self._st1 = None
self._st2 = None
self._prev_up_trend1 = False
self._prev_up_trend2 = False
self._has_prev = False
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(double_supertrend_strategy, self).OnStarted2(time)
self._st1 = SuperTrend()
self._st1.Length = int(self._atr_period1.Value)
self._st1.Multiplier = float(self._factor1.Value)
self._st2 = SuperTrend()
self._st2.Length = int(self._atr_period2.Value)
self._st2.Multiplier = float(self._factor2.Value)
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(self._st1, self._st2, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, self._st1)
self.DrawIndicator(area, self._st2)
self.DrawOwnTrades(area)
def _on_process(self, candle, st1_value, st2_value):
if candle.State != CandleStates.Finished:
return
if not self._st1.IsFormed or not self._st2.IsFormed:
return
if st1_value.IsEmpty or st2_value.IsEmpty:
return
up_trend1 = st1_value.IsUpTrend
up_trend2 = st2_value.IsUpTrend
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_up_trend1 = up_trend1
self._prev_up_trend2 = up_trend2
self._has_prev = True
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
self._prev_up_trend1 = up_trend1
self._prev_up_trend2 = up_trend2
self._has_prev = True
return
if not self._has_prev:
self._prev_up_trend1 = up_trend1
self._prev_up_trend2 = up_trend2
self._has_prev = True
return
cooldown = int(self._cooldown_bars.Value)
both_bullish = up_trend1 and up_trend2
both_bearish = not up_trend1 and not up_trend2
bullish_signal = both_bullish and (not self._prev_up_trend1 or not self._prev_up_trend2)
bearish_signal = both_bearish and (self._prev_up_trend1 or self._prev_up_trend2)
if bullish_signal and self.Position <= 0:
if self.Position < 0:
self.BuyMarket(Math.Abs(self.Position))
self.BuyMarket(self.Volume)
self._cooldown_remaining = cooldown
elif bearish_signal and self.Position >= 0:
if self.Position > 0:
self.SellMarket(Math.Abs(self.Position))
self.SellMarket(self.Volume)
self._cooldown_remaining = cooldown
elif self.Position > 0 and not both_bullish and (self._prev_up_trend1 and self._prev_up_trend2):
self.SellMarket(Math.Abs(self.Position))
self._cooldown_remaining = cooldown
elif self.Position < 0 and not both_bearish and (not self._prev_up_trend1 and not self._prev_up_trend2):
self.BuyMarket(Math.Abs(self.Position))
self._cooldown_remaining = cooldown
self._prev_up_trend1 = up_trend1
self._prev_up_trend2 = up_trend2
def CreateClone(self):
return double_supertrend_strategy()