Estrategia Avanzada de Supertrend
La Estrategia Avanzada de Supertrend mejora el indicador clásico Supertrend con filtros opcionales de RSI, media móvil y fuerza de tendencia. Entra largo cuando Supertrend cambia a alcista y entra corto cuando se vuelve bajista. El stop loss y take profit opcionales se derivan de múltiplos de ATR.
Detalles
- Criterios de entrada:
- Supertrend cambia de dirección (bajista→alcista para largo, alcista→bajista para corto).
- Filtros opcionales: RSI dentro de los límites establecidos, precio relativo a una media móvil, fuerza de tendencia y confirmación de ruptura.
- Largo/Corto: Ambos.
- Criterios de salida:
- Señal opuesta de Supertrend o niveles opcionales de stop-loss/take-profit.
- Stops: Stop loss y take profit opcionales basados en ATR.
- Valores predeterminados:
AtrLength= 6Multiplier= 3.0UseRsiFilter= falseRsiLength= 14RsiOverbought= 70RsiOversold= 30UseMaFilter= trueMaLength= 50MaType= WeightedUseStopLoss= trueSlMultiplier= 3.0UseTakeProfit= trueTpMultiplier= 9.0UseTrendStrength= falseMinTrendBars= 2UseBreakoutConfirmation= true
- Filtros:
- Categoría: Seguimiento de tendencia
- Dirección: Largo y Corto
- Indicadores: Supertrend, RSI, Media Móvil
- Stops: Basado en ATR
- Complejidad: Moderado
- Marco temporal: Cualquiera
- Estacionalidad: No
- Redes neuronales: No
- Divergencia: No
- Nivel de riesgo: Medio
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>
/// Advanced Supertrend strategy.
/// A Supertrend flip to bullish opens a long and a flip to bearish opens a short, subject to optional filters: RSI below
/// RsiOverbought for longs and above RsiOversold for shorts, the close on the trade side of a moving average, the previous trend
/// having lasted at least MinTrendBars bars, and a close beyond the previous candle's high or low. An opposite flip closes the
/// position, and optional stop loss and take profit sit SlMultiplier and TpMultiplier ATRs from the entry.
/// </summary>
public class AdvancedSupertrendStrategy : Strategy
{
/// <summary>
/// Moving average types of the filter.
/// </summary>
public enum MaTypes
{
/// <summary>
/// Simple moving average.
/// </summary>
Simple,
/// <summary>
/// Exponential moving average.
/// </summary>
Exponential,
/// <summary>
/// Weighted moving average.
/// </summary>
Weighted,
}
private readonly StrategyParam<int> _atrLength;
private readonly StrategyParam<decimal> _multiplier;
private readonly StrategyParam<bool> _useRsiFilter;
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<decimal> _rsiOverbought;
private readonly StrategyParam<decimal> _rsiOversold;
private readonly StrategyParam<bool> _useMaFilter;
private readonly StrategyParam<int> _maLength;
private readonly StrategyParam<MaTypes> _maType;
private readonly StrategyParam<bool> _useStopLoss;
private readonly StrategyParam<decimal> _slMultiplier;
private readonly StrategyParam<bool> _useTakeProfit;
private readonly StrategyParam<decimal> _tpMultiplier;
private readonly StrategyParam<bool> _useTrendStrength;
private readonly StrategyParam<int> _minTrendBars;
private readonly StrategyParam<bool> _useBreakoutConfirmation;
private readonly StrategyParam<DataType> _candleType;
private bool? _prevUpTrend;
private int _trendBars;
private decimal? _prevHigh;
private decimal? _prevLow;
private decimal? _stopPrice;
private decimal? _takePrice;
/// <summary>
/// ATR length of the Supertrend and of the stops.
/// </summary>
public int AtrLength
{
get => _atrLength.Value;
set => _atrLength.Value = value;
}
/// <summary>
/// Supertrend ATR multiplier.
/// </summary>
public decimal Multiplier
{
get => _multiplier.Value;
set => _multiplier.Value = value;
}
/// <summary>
/// Enable the RSI filter.
/// </summary>
public bool UseRsiFilter
{
get => _useRsiFilter.Value;
set => _useRsiFilter.Value = value;
}
/// <summary>
/// RSI period.
/// </summary>
public int RsiLength
{
get => _rsiLength.Value;
set => _rsiLength.Value = value;
}
/// <summary>
/// RSI level longs must stay below.
/// </summary>
public decimal RsiOverbought
{
get => _rsiOverbought.Value;
set => _rsiOverbought.Value = value;
}
/// <summary>
/// RSI level shorts must stay above.
/// </summary>
public decimal RsiOversold
{
get => _rsiOversold.Value;
set => _rsiOversold.Value = value;
}
/// <summary>
/// Enable the moving average filter.
/// </summary>
public bool UseMaFilter
{
get => _useMaFilter.Value;
set => _useMaFilter.Value = value;
}
/// <summary>
/// Moving average period.
/// </summary>
public int MaLength
{
get => _maLength.Value;
set => _maLength.Value = value;
}
/// <summary>
/// Moving average type.
/// </summary>
public MaTypes MaType
{
get => _maType.Value;
set => _maType.Value = value;
}
/// <summary>
/// Enable the ATR stop loss.
/// </summary>
public bool UseStopLoss
{
get => _useStopLoss.Value;
set => _useStopLoss.Value = value;
}
/// <summary>
/// ATR multiplier of the stop loss.
/// </summary>
public decimal SlMultiplier
{
get => _slMultiplier.Value;
set => _slMultiplier.Value = value;
}
/// <summary>
/// Enable the ATR take profit.
/// </summary>
public bool UseTakeProfit
{
get => _useTakeProfit.Value;
set => _useTakeProfit.Value = value;
}
/// <summary>
/// ATR multiplier of the take profit.
/// </summary>
public decimal TpMultiplier
{
get => _tpMultiplier.Value;
set => _tpMultiplier.Value = value;
}
/// <summary>
/// Enable the trend strength filter.
/// </summary>
public bool UseTrendStrength
{
get => _useTrendStrength.Value;
set => _useTrendStrength.Value = value;
}
/// <summary>
/// Bars the previous trend must have lasted.
/// </summary>
public int MinTrendBars
{
get => _minTrendBars.Value;
set => _minTrendBars.Value = value;
}
/// <summary>
/// Require a close beyond the previous candle's extreme.
/// </summary>
public bool UseBreakoutConfirmation
{
get => _useBreakoutConfirmation.Value;
set => _useBreakoutConfirmation.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public AdvancedSupertrendStrategy()
{
_atrLength = Param(nameof(AtrLength), 6)
.SetGreaterThanZero()
.SetDisplay("ATR Length", "ATR length of the Supertrend and of the stops", "Supertrend");
_multiplier = Param(nameof(Multiplier), 3.0m)
.SetGreaterThanZero()
.SetDisplay("Multiplier", "Supertrend ATR multiplier", "Supertrend");
_useRsiFilter = Param(nameof(UseRsiFilter), false)
.SetDisplay("Use RSI Filter", "Enable the RSI filter", "Filters");
_rsiLength = Param(nameof(RsiLength), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Length", "RSI period", "Filters");
_rsiOverbought = Param(nameof(RsiOverbought), 70m)
.SetDisplay("RSI Overbought", "RSI level longs must stay below", "Filters");
_rsiOversold = Param(nameof(RsiOversold), 30m)
.SetDisplay("RSI Oversold", "RSI level shorts must stay above", "Filters");
_useMaFilter = Param(nameof(UseMaFilter), true)
.SetDisplay("Use MA Filter", "Enable the moving average filter", "Filters");
_maLength = Param(nameof(MaLength), 50)
.SetGreaterThanZero()
.SetDisplay("MA Length", "Moving average period", "Filters");
_maType = Param(nameof(MaType), MaTypes.Weighted)
.SetDisplay("MA Type", "Moving average type", "Filters");
_useStopLoss = Param(nameof(UseStopLoss), true)
.SetDisplay("Use Stop Loss", "Enable the ATR stop loss", "Risk");
_slMultiplier = Param(nameof(SlMultiplier), 3.0m)
.SetNotNegative()
.SetDisplay("SL Multiplier", "ATR multiplier of the stop loss", "Risk");
_useTakeProfit = Param(nameof(UseTakeProfit), true)
.SetDisplay("Use Take Profit", "Enable the ATR take profit", "Risk");
_tpMultiplier = Param(nameof(TpMultiplier), 9.0m)
.SetNotNegative()
.SetDisplay("TP Multiplier", "ATR multiplier of the take profit", "Risk");
_useTrendStrength = Param(nameof(UseTrendStrength), false)
.SetDisplay("Use Trend Strength", "Enable the trend strength filter", "Filters");
_minTrendBars = Param(nameof(MinTrendBars), 2)
.SetGreaterThanZero()
.SetDisplay("Min Trend Bars", "Bars the previous trend must have lasted", "Filters");
_useBreakoutConfirmation = Param(nameof(UseBreakoutConfirmation), true)
.SetDisplay("Use Breakout Confirmation", "Require a close beyond the previous candle's extreme", "Filters");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).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();
ResetState();
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ResetState();
var superTrend = new SuperTrend { Length = AtrLength, Multiplier = Multiplier };
var atr = new AverageTrueRange { Length = AtrLength };
var rsi = new RelativeStrengthIndex { Length = RsiLength };
IIndicator ma = MaType switch
{
MaTypes.Simple => new SimpleMovingAverage { Length = MaLength },
MaTypes.Exponential => new ExponentialMovingAverage { Length = MaLength },
_ => new WeightedMovingAverage { Length = MaLength },
};
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(superTrend, atr, rsi, ma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, superTrend);
DrawIndicator(area, ma);
DrawOwnTrades(area);
}
}
private void ResetState()
{
_prevUpTrend = null;
_trendBars = 0;
_prevHigh = null;
_prevLow = null;
_stopPrice = null;
_takePrice = null;
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue superTrendValue, IIndicatorValue atrValue, IIndicatorValue rsiValue, IIndicatorValue maValue)
{
if (candle.State != CandleStates.Finished)
return;
var prevHigh = _prevHigh;
var prevLow = _prevLow;
_prevHigh = candle.HighPrice;
_prevLow = candle.LowPrice;
if (!superTrendValue.IsFormed || !atrValue.IsFormed || !rsiValue.IsFormed || !maValue.IsFormed)
return;
var upTrend = ((SuperTrendIndicatorValue)superTrendValue).IsUpTrend;
var prevUpTrend = _prevUpTrend;
var priorTrendBars = _trendBars;
if (prevUpTrend == upTrend)
_trendBars++;
else
_trendBars = 1;
_prevUpTrend = upTrend;
if (prevUpTrend is not bool wasUp || prevHigh is not decimal pHigh || prevLow is not decimal pLow)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var close = candle.ClosePrice;
if (Position > 0 && ((_stopPrice is decimal ls && candle.LowPrice <= ls) || (_takePrice is decimal lt && candle.HighPrice >= lt)))
{
SellMarket(Position);
ClearLevels();
return;
}
if (Position < 0 && ((_stopPrice is decimal ss && candle.HighPrice >= ss) || (_takePrice is decimal st && candle.LowPrice <= st)))
{
BuyMarket(-Position);
ClearLevels();
return;
}
var bullishFlip = upTrend && !wasUp;
var bearishFlip = !upTrend && wasUp;
if (!bullishFlip && !bearishFlip)
return;
var rsi = rsiValue.ToDecimal();
var ma = maValue.ToDecimal();
var strongTrend = !UseTrendStrength || priorTrendBars >= MinTrendBars;
var longOk = bullishFlip && strongTrend
&& (!UseRsiFilter || rsi < RsiOverbought)
&& (!UseMaFilter || close > ma)
&& (!UseBreakoutConfirmation || close > pHigh);
var shortOk = bearishFlip && strongTrend
&& (!UseRsiFilter || rsi > RsiOversold)
&& (!UseMaFilter || close < ma)
&& (!UseBreakoutConfirmation || close < pLow);
var atr = atrValue.ToDecimal();
if (longOk && Position <= 0)
{
BuyMarket(Volume + Math.Abs(Position));
SetLevels(close, atr, true);
}
else if (shortOk && Position >= 0)
{
SellMarket(Volume + Math.Abs(Position));
SetLevels(close, atr, false);
}
else if (bearishFlip && Position > 0)
{
SellMarket(Position);
ClearLevels();
}
else if (bullishFlip && Position < 0)
{
BuyMarket(-Position);
ClearLevels();
}
}
private void SetLevels(decimal price, decimal atr, bool isLong)
{
var stop = atr * SlMultiplier;
var take = atr * TpMultiplier;
_stopPrice = UseStopLoss && stop > 0 ? (isLong ? price - stop : price + stop) : null;
_takePrice = UseTakeProfit && take > 0 ? (isLong ? price + take : price - take) : null;
}
private void ClearLevels()
{
_stopPrice = null;
_takePrice = null;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
clr.AddReference("StockSharp.BusinessEntities")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import (SuperTrend, AverageTrueRange, RelativeStrengthIndex, SimpleMovingAverage,
ExponentialMovingAverage, WeightedMovingAverage)
from StockSharp.Algo.Strategies import Strategy
class MaTypes:
"""Moving average types of the filter."""
Simple = 0
Exponential = 1
Weighted = 2
class advanced_supertrend_strategy(Strategy):
"""
Advanced Supertrend strategy.
A Supertrend flip to bullish opens a long and a flip to bearish opens a short, subject to optional filters: RSI below
RsiOverbought for longs and above RsiOversold for shorts, the close on the trade side of a moving average, the previous trend
having lasted at least MinTrendBars bars, and a close beyond the previous candle's high or low. An opposite flip closes the
position, and optional stop loss and take profit sit SlMultiplier and TpMultiplier ATRs from the entry.
"""
def __init__(self):
super(advanced_supertrend_strategy, self).__init__()
self._atr_length = self.Param("AtrLength", 6).SetGreaterThanZero().SetDisplay("ATR Length", "ATR length of the Supertrend and of the stops", "Supertrend")
self._multiplier = self.Param("Multiplier", 3.0).SetGreaterThanZero().SetDisplay("Multiplier", "Supertrend ATR multiplier", "Supertrend")
self._use_rsi_filter = self.Param("UseRsiFilter", False).SetDisplay("Use RSI Filter", "Enable the RSI filter", "Filters")
self._rsi_length = self.Param("RsiLength", 14).SetGreaterThanZero().SetDisplay("RSI Length", "RSI period", "Filters")
self._rsi_overbought = self.Param("RsiOverbought", 70.0).SetDisplay("RSI Overbought", "RSI level longs must stay below", "Filters")
self._rsi_oversold = self.Param("RsiOversold", 30.0).SetDisplay("RSI Oversold", "RSI level shorts must stay above", "Filters")
self._use_ma_filter = self.Param("UseMaFilter", True).SetDisplay("Use MA Filter", "Enable the moving average filter", "Filters")
self._ma_length = self.Param("MaLength", 50).SetGreaterThanZero().SetDisplay("MA Length", "Moving average period", "Filters")
self._ma_type = self.Param("MaType", MaTypes.Weighted).SetDisplay("MA Type", "Moving average type", "Filters")
self._use_stop_loss = self.Param("UseStopLoss", True).SetDisplay("Use Stop Loss", "Enable the ATR stop loss", "Risk")
self._sl_multiplier = self.Param("SlMultiplier", 3.0).SetNotNegative().SetDisplay("SL Multiplier", "ATR multiplier of the stop loss", "Risk")
self._use_take_profit = self.Param("UseTakeProfit", True).SetDisplay("Use Take Profit", "Enable the ATR take profit", "Risk")
self._tp_multiplier = self.Param("TpMultiplier", 9.0).SetNotNegative().SetDisplay("TP Multiplier", "ATR multiplier of the take profit", "Risk")
self._use_trend_strength = self.Param("UseTrendStrength", False).SetDisplay("Use Trend Strength", "Enable the trend strength filter", "Filters")
self._min_trend_bars = self.Param("MinTrendBars", 2).SetGreaterThanZero().SetDisplay("Min Trend Bars", "Bars the previous trend must have lasted", "Filters")
self._use_breakout_confirmation = self.Param("UseBreakoutConfirmation", True).SetDisplay("Use Breakout Confirmation", "Require a close beyond the previous candle's extreme", "Filters")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._reset_state()
@property
def candle_type(self):
return self._candle_type.Value
def _reset_state(self):
self._prev_up_trend = None
self._trend_bars = 0
self._prev_high = None
self._prev_low = None
self._stop_price = None
self._take_price = None
def OnReseted(self):
super(advanced_supertrend_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(advanced_supertrend_strategy, self).OnStarted2(time)
self._reset_state()
super_trend = SuperTrend()
super_trend.Length = self._atr_length.Value
super_trend.Multiplier = Decimal(self._multiplier.Value)
atr = AverageTrueRange()
atr.Length = self._atr_length.Value
rsi = RelativeStrengthIndex()
rsi.Length = self._rsi_length.Value
ma_type = self._ma_type.Value
if ma_type == MaTypes.Simple:
ma = SimpleMovingAverage()
elif ma_type == MaTypes.Exponential:
ma = ExponentialMovingAverage()
else:
ma = WeightedMovingAverage()
ma.Length = self._ma_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(super_trend, atr, rsi, ma, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, super_trend)
self.DrawIndicator(area, ma)
self.DrawOwnTrades(area)
def _process_candle(self, candle, super_trend_value, atr_value, rsi_value, ma_value):
if candle.State != CandleStates.Finished:
return
prev_high = self._prev_high
prev_low = self._prev_low
self._prev_high = candle.HighPrice
self._prev_low = candle.LowPrice
if not super_trend_value.IsFormed or not atr_value.IsFormed or not rsi_value.IsFormed or not ma_value.IsFormed:
return
up_trend = super_trend_value.IsUpTrend
was_up = self._prev_up_trend
prior_trend_bars = self._trend_bars
if was_up == up_trend:
self._trend_bars += 1
else:
self._trend_bars = 1
self._prev_up_trend = up_trend
if was_up is None or prev_high is None or prev_low is None:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
close = candle.ClosePrice
if self.Position > 0 and ((self._stop_price is not None and candle.LowPrice <= self._stop_price)
or (self._take_price is not None and candle.HighPrice >= self._take_price)):
self.SellMarket(self.Position)
self._clear_levels()
return
if self.Position < 0 and ((self._stop_price is not None and candle.HighPrice >= self._stop_price)
or (self._take_price is not None and candle.LowPrice <= self._take_price)):
self.BuyMarket(-self.Position)
self._clear_levels()
return
bullish_flip = up_trend and not was_up
bearish_flip = not up_trend and was_up
if not bullish_flip and not bearish_flip:
return
rsi = rsi_value.GetValue[Decimal](None)
ma = ma_value.GetValue[Decimal](None)
strong_trend = not self._use_trend_strength.Value or prior_trend_bars >= self._min_trend_bars.Value
use_rsi = self._use_rsi_filter.Value
use_ma = self._use_ma_filter.Value
use_breakout = self._use_breakout_confirmation.Value
long_ok = (bullish_flip and strong_trend
and (not use_rsi or rsi < Decimal(self._rsi_overbought.Value))
and (not use_ma or close > ma)
and (not use_breakout or close > prev_high))
short_ok = (bearish_flip and strong_trend
and (not use_rsi or rsi > Decimal(self._rsi_oversold.Value))
and (not use_ma or close < ma)
and (not use_breakout or close < prev_low))
atr = atr_value.GetValue[Decimal](None)
if long_ok and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
self._set_levels(close, atr, True)
elif short_ok and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
self._set_levels(close, atr, False)
elif bearish_flip and self.Position > 0:
self.SellMarket(self.Position)
self._clear_levels()
elif bullish_flip and self.Position < 0:
self.BuyMarket(-self.Position)
self._clear_levels()
def _set_levels(self, price, atr, is_long):
stop = atr * Decimal(self._sl_multiplier.Value)
take = atr * Decimal(self._tp_multiplier.Value)
self._stop_price = (price - stop if is_long else price + stop) if self._use_stop_loss.Value and stop > 0 else None
self._take_price = (price + take if is_long else price - take) if self._use_take_profit.Value and take > 0 else None
def _clear_levels(self):
self._stop_price = None
self._take_price = None
def CreateClone(self):
return advanced_supertrend_strategy()