Vietnamese 3x Supertrend Strategy
This strategy stacks three SuperTrend indicators with different ATR lengths and multipliers. It scales into long positions when the slow trend is bearish and faster trends show pullback opportunities. An optional break-even stop protects profits once price moves favorably.
Details
- Entry Criteria:
- Slow SuperTrend in downtrend.
- Long 1: Medium uptrend and fast downtrend.
- Long 2: Medium downtrend and price above fast SuperTrend line.
- Long 3: Fast downtrend and breakout above highest high during the fast downtrend.
- Long/Short: Long only.
- Exit Criteria:
- All SuperTrends turn up and the candle closes bearish.
- Average entry price above current close.
- Optional break-even stop if enabled.
- Stops: Optional break-even stop.
- Default Values:
FastAtrLength= 10FastMultiplier= 1MediumAtrLength= 11MediumMultiplier= 2SlowAtrLength= 12SlowMultiplier= 3UseHighestOfTwoRedCandles= FalseUseEntryStopLoss= TrueUseAllDowntrendExit= TrueUseAvgPriceInLoss= True
- Filters:
- Category: Trend following
- Direction: Long
- Indicators: SuperTrend
- Stops: Optional
- Complexity: Medium
- Timeframe: Any
- Seasonality: No
- Neural networks: No
- Divergence: No
- Risk level: Medium
namespace StockSharp.Samples.Strategies;
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
/// <summary>
/// Vietnamese 3x SuperTrend strategy.
/// Uses three SuperTrend indicators with different settings.
/// Enters long when SuperTrend conditions align.
/// Exits based on break-even, all-uptrend-red-candle, or avg price in loss.
/// </summary>
public class Vietnamese3xSupertrendStrategy : Strategy
{
private readonly StrategyParam<int> _fastAtrLength;
private readonly StrategyParam<decimal> _fastMultiplier;
private readonly StrategyParam<int> _mediumAtrLength;
private readonly StrategyParam<decimal> _mediumMultiplier;
private readonly StrategyParam<int> _slowAtrLength;
private readonly StrategyParam<decimal> _slowMultiplier;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cooldownBars;
private decimal _highestGreen;
private bool _breakEvenActive;
private decimal _avgEntryPrice;
private int _entryCount;
private int _cooldownRemaining;
public int FastAtrLength { get => _fastAtrLength.Value; set => _fastAtrLength.Value = value; }
public decimal FastMultiplier { get => _fastMultiplier.Value; set => _fastMultiplier.Value = value; }
public int MediumAtrLength { get => _mediumAtrLength.Value; set => _mediumAtrLength.Value = value; }
public decimal MediumMultiplier { get => _mediumMultiplier.Value; set => _mediumMultiplier.Value = value; }
public int SlowAtrLength { get => _slowAtrLength.Value; set => _slowAtrLength.Value = value; }
public decimal SlowMultiplier { get => _slowMultiplier.Value; set => _slowMultiplier.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int CooldownBars { get => _cooldownBars.Value; set => _cooldownBars.Value = value; }
public Vietnamese3xSupertrendStrategy()
{
_fastAtrLength = Param(nameof(FastAtrLength), 10)
.SetDisplay("Fast ATR Length", "ATR length for fast SuperTrend", "SuperTrend");
_fastMultiplier = Param(nameof(FastMultiplier), 1m)
.SetDisplay("Fast Multiplier", "ATR multiplier for fast SuperTrend", "SuperTrend");
_mediumAtrLength = Param(nameof(MediumAtrLength), 11)
.SetDisplay("Medium ATR Length", "ATR length for medium SuperTrend", "SuperTrend");
_mediumMultiplier = Param(nameof(MediumMultiplier), 2m)
.SetDisplay("Medium Multiplier", "ATR multiplier for medium SuperTrend", "SuperTrend");
_slowAtrLength = Param(nameof(SlowAtrLength), 12)
.SetDisplay("Slow ATR Length", "ATR length for slow SuperTrend", "SuperTrend");
_slowMultiplier = Param(nameof(SlowMultiplier), 3m)
.SetDisplay("Slow Multiplier", "ATR multiplier for slow SuperTrend", "SuperTrend");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_cooldownBars = Param(nameof(CooldownBars), 10)
.SetDisplay("Cooldown Bars", "Bars between trades", "Risk");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_highestGreen = 0;
_breakEvenActive = false;
_avgEntryPrice = 0;
_entryCount = 0;
_cooldownRemaining = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var fast = new SuperTrend { Length = FastAtrLength, Multiplier = FastMultiplier };
var medium = new SuperTrend { Length = MediumAtrLength, Multiplier = MediumMultiplier };
var slow = new SuperTrend { Length = SlowAtrLength, Multiplier = SlowMultiplier };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(fast, medium, slow, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fast);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue fastVal, IIndicatorValue medVal, IIndicatorValue slowVal)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var fastSt = (SuperTrendIndicatorValue)fastVal;
var medSt = (SuperTrendIndicatorValue)medVal;
var slowSt = (SuperTrendIndicatorValue)slowVal;
var dir1 = fastSt.IsUpTrend ? 1 : -1;
var dir2 = medSt.IsUpTrend ? 1 : -1;
var dir3 = slowSt.IsUpTrend ? 1 : -1;
// Track highest green candle for breakout entry
if (dir1 < 0 && _highestGreen == 0)
_highestGreen = candle.HighPrice;
if (_highestGreen > 0 && dir1 < 0)
_highestGreen = Math.Max(_highestGreen, candle.HighPrice);
if (dir1 >= 0)
_highestGreen = 0;
// Exit logic for longs
if (Position > 0)
{
// Break-even stop
if (dir1 > 0 && dir2 < 0 && dir3 < 0)
{
if (!_breakEvenActive && candle.LowPrice > _avgEntryPrice)
_breakEvenActive = true;
if (_breakEvenActive && candle.LowPrice <= _avgEntryPrice)
{
SellMarket(Math.Abs(Position));
ResetEntries();
_cooldownRemaining = CooldownBars;
return;
}
}
// All uptrend + red candle exit
if (dir3 > 0 && dir2 > 0 && dir1 > 0 && candle.ClosePrice < candle.OpenPrice)
{
SellMarket(Math.Abs(Position));
ResetEntries();
_cooldownRemaining = CooldownBars;
return;
}
// Avg price in loss exit
if (_avgEntryPrice > candle.ClosePrice)
{
SellMarket(Math.Abs(Position));
ResetEntries();
_cooldownRemaining = CooldownBars;
return;
}
}
if (_cooldownRemaining > 0)
{
_cooldownRemaining--;
return;
}
// Entry logic - max 3 entries
if (_entryCount < 3)
{
if (dir3 < 0)
{
if (dir2 > 0 && dir1 < 0)
{
BuyMarket(Volume);
AddEntry(candle.ClosePrice);
_cooldownRemaining = CooldownBars;
}
else if (dir2 < 0 && candle.ClosePrice > fastSt.Value)
{
BuyMarket(Volume);
AddEntry(candle.ClosePrice);
_cooldownRemaining = CooldownBars;
}
}
else
{
if (dir1 < 0 && _highestGreen > 0 && candle.ClosePrice > _highestGreen)
{
BuyMarket(Volume);
AddEntry(candle.ClosePrice);
_cooldownRemaining = CooldownBars;
}
}
}
}
private void AddEntry(decimal price)
{
_avgEntryPrice = (_avgEntryPrice * _entryCount + price) / (_entryCount + 1);
_entryCount++;
}
private void ResetEntries()
{
_avgEntryPrice = 0;
_entryCount = 0;
_breakEvenActive = false;
}
}
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, IndicatorHelper
from StockSharp.Algo.Strategies import Strategy
class vietnamese_3x_supertrend_strategy(Strategy):
"""Vietnamese 3x SuperTrend Strategy."""
def __init__(self):
super(vietnamese_3x_supertrend_strategy, self).__init__()
self._fast_atr_length = self.Param("FastAtrLength", 10) \
.SetDisplay("Fast ATR Length", "ATR length for fast SuperTrend", "SuperTrend")
self._fast_multiplier = self.Param("FastMultiplier", 1.0) \
.SetDisplay("Fast Multiplier", "ATR multiplier for fast SuperTrend", "SuperTrend")
self._medium_atr_length = self.Param("MediumAtrLength", 11) \
.SetDisplay("Medium ATR Length", "ATR length for medium SuperTrend", "SuperTrend")
self._medium_multiplier = self.Param("MediumMultiplier", 2.0) \
.SetDisplay("Medium Multiplier", "ATR multiplier for medium SuperTrend", "SuperTrend")
self._slow_atr_length = self.Param("SlowAtrLength", 12) \
.SetDisplay("Slow ATR Length", "ATR length for slow SuperTrend", "SuperTrend")
self._slow_multiplier = self.Param("SlowMultiplier", 3.0) \
.SetDisplay("Slow Multiplier", "ATR multiplier for slow SuperTrend", "SuperTrend")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._cooldown_bars = self.Param("CooldownBars", 10) \
.SetDisplay("Cooldown Bars", "Bars between trades", "Risk")
self._highest_green = 0.0
self._break_even_active = False
self._avg_entry_price = 0.0
self._entry_count = 0
self._cooldown_remaining = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(vietnamese_3x_supertrend_strategy, self).OnReseted()
self._highest_green = 0.0
self._break_even_active = False
self._avg_entry_price = 0.0
self._entry_count = 0
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(vietnamese_3x_supertrend_strategy, self).OnStarted2(time)
fast = SuperTrend()
fast.Length = int(self._fast_atr_length.Value)
fast.Multiplier = self._fast_multiplier.Value
medium = SuperTrend()
medium.Length = int(self._medium_atr_length.Value)
medium.Multiplier = self._medium_multiplier.Value
slow = SuperTrend()
slow.Length = int(self._slow_atr_length.Value)
slow.Multiplier = self._slow_multiplier.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(fast, medium, slow, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast)
self.DrawOwnTrades(area)
def _on_process(self, candle, fast_val, med_val, slow_val):
if candle.State != CandleStates.Finished:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
dir1 = 1 if fast_val.IsUpTrend else -1
dir2 = 1 if med_val.IsUpTrend else -1
dir3 = 1 if slow_val.IsUpTrend else -1
high_price = float(candle.HighPrice)
close_price = float(candle.ClosePrice)
open_price = float(candle.OpenPrice)
low_price = float(candle.LowPrice)
cooldown = int(self._cooldown_bars.Value)
# Track highest green candle for breakout entry
if dir1 < 0 and self._highest_green == 0.0:
self._highest_green = high_price
if self._highest_green > 0 and dir1 < 0:
self._highest_green = max(self._highest_green, high_price)
if dir1 >= 0:
self._highest_green = 0.0
# Exit logic for longs
if self.Position > 0:
# Break-even stop
if dir1 > 0 and dir2 < 0 and dir3 < 0:
if not self._break_even_active and low_price > self._avg_entry_price:
self._break_even_active = True
if self._break_even_active and low_price <= self._avg_entry_price:
self.SellMarket(Math.Abs(self.Position))
self._reset_entries()
self._cooldown_remaining = cooldown
return
# All uptrend + red candle exit
if dir3 > 0 and dir2 > 0 and dir1 > 0 and close_price < open_price:
self.SellMarket(Math.Abs(self.Position))
self._reset_entries()
self._cooldown_remaining = cooldown
return
# Avg price in loss exit
if self._avg_entry_price > close_price:
self.SellMarket(Math.Abs(self.Position))
self._reset_entries()
self._cooldown_remaining = cooldown
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
return
# Entry logic - max 3 entries
if self._entry_count < 3:
if dir3 < 0:
if dir2 > 0 and dir1 < 0:
self.BuyMarket(self.Volume)
self._add_entry(close_price)
self._cooldown_remaining = cooldown
elif dir2 < 0 and close_price > float(IndicatorHelper.ToDecimal(fast_val)):
self.BuyMarket(self.Volume)
self._add_entry(close_price)
self._cooldown_remaining = cooldown
else:
if dir1 < 0 and self._highest_green > 0 and close_price > self._highest_green:
self.BuyMarket(self.Volume)
self._add_entry(close_price)
self._cooldown_remaining = cooldown
def _add_entry(self, price):
self._avg_entry_price = (self._avg_entry_price * self._entry_count + price) / (self._entry_count + 1)
self._entry_count += 1
def _reset_entries(self):
self._avg_entry_price = 0.0
self._entry_count = 0
self._break_even_active = False
def CreateClone(self):
return vietnamese_3x_supertrend_strategy()