Estrategia de Envolventes de Tendencia
Estrategia de seguimiento de tendencia basada en el indicador TrendEnvelopes. Combina una EMA con bandas basadas en ATR para detectar rupturas. Las posiciones largas se abren cuando el precio rompe por encima de la banda superior y aparece una señal de compra. Las posiciones cortas se abren en rupturas por debajo de la banda inferior con una señal de venta. Las bandas opuestas activan el cierre de posiciones.
Detalles
- Criterios de entrada:
- Largo: el precio cierra por encima del envolvente superior y genera una señal de compra
- Corto: el precio cierra por debajo del envolvente inferior y genera una señal de venta
- Largo/Corto: Ambos
- Criterios de salida: Señal de tendencia opuesta
- Stops: Sí (take profit y stop loss)
- Valores predeterminados:
MaPeriod= 14Deviation= 0.2mAtrPeriod= 15AtrSensitivity= 0.5mTakeProfit= 2000 puntosStopLoss= 1000 puntosCandleType= TimeSpan.FromHours(4).TimeFrame()
- Filtros:
- Categoría: Seguimiento de tendencia
- Dirección: Ambos
- Indicadores: EMA, ATR
- Stops: Sí
- Complejidad: Intermedio
- Marco temporal: 4h
- Estacionalidad: No
- Redes neuronales: No
- Divergencia: No
- Nivel de riesgo: Medio
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 TrendEnvelopes indicator with ATR-based signals.
/// </summary>
public class TrendEnvelopesStrategy : Strategy
{
private readonly StrategyParam<int> _maPeriod;
private readonly StrategyParam<decimal> _deviation;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<decimal> _atrSensitivity;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<bool> _buyEntry;
private readonly StrategyParam<bool> _sellEntry;
private readonly StrategyParam<bool> _buyExit;
private readonly StrategyParam<bool> _sellExit;
private readonly StrategyParam<int> _takeProfit;
private readonly StrategyParam<int> _stopLoss;
private ExponentialMovingAverage _ma;
private AverageTrueRange _atr;
private decimal _prevSmax;
private decimal _prevSmin;
private int _prevTrend;
private bool _initialized;
/// <summary>
/// Moving average period.
/// </summary>
public int MaPeriod
{
get => _maPeriod.Value;
set => _maPeriod.Value = value;
}
/// <summary>
/// Percentage deviation for envelopes.
/// </summary>
public decimal Deviation
{
get => _deviation.Value;
set => _deviation.Value = value;
}
/// <summary>
/// ATR period.
/// </summary>
public int AtrPeriod
{
get => _atrPeriod.Value;
set => _atrPeriod.Value = value;
}
/// <summary>
/// ATR shift sensitivity.
/// </summary>
public decimal AtrSensitivity
{
get => _atrSensitivity.Value;
set => _atrSensitivity.Value = value;
}
/// <summary>
/// Candle type for calculations.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Whether long entries are allowed.
/// </summary>
public bool BuyEntry
{
get => _buyEntry.Value;
set => _buyEntry.Value = value;
}
/// <summary>
/// Whether short entries are allowed.
/// </summary>
public bool SellEntry
{
get => _sellEntry.Value;
set => _sellEntry.Value = value;
}
/// <summary>
/// Whether long positions can be closed.
/// </summary>
public bool BuyExit
{
get => _buyExit.Value;
set => _buyExit.Value = value;
}
/// <summary>
/// Whether short positions can be closed.
/// </summary>
public bool SellExit
{
get => _sellExit.Value;
set => _sellExit.Value = value;
}
/// <summary>
/// Take profit in points.
/// </summary>
public int TakeProfit
{
get => _takeProfit.Value;
set => _takeProfit.Value = value;
}
/// <summary>
/// Stop loss in points.
/// </summary>
public int StopLoss
{
get => _stopLoss.Value;
set => _stopLoss.Value = value;
}
/// <summary>
/// Initializes a new instance of the strategy.
/// </summary>
public TrendEnvelopesStrategy()
{
_maPeriod = Param(nameof(MaPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("MA Period", "Moving average length", "Indicator");
_deviation = Param(nameof(Deviation), 0.2m)
.SetGreaterThanZero()
.SetDisplay("Deviation", "Percent offset for envelopes", "Indicator");
_atrPeriod = Param(nameof(AtrPeriod), 15)
.SetGreaterThanZero()
.SetDisplay("ATR Period", "ATR calculation length", "Indicator");
_atrSensitivity = Param(nameof(AtrSensitivity), 0.5m)
.SetGreaterThanZero()
.SetDisplay("ATR Sensitivity", "Multiplier for signal shift", "Indicator");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for strategy", "General");
_buyEntry = Param(nameof(BuyEntry), true)
.SetDisplay("Enable Long Entry", "Allow opening long positions", "Trading");
_sellEntry = Param(nameof(SellEntry), true)
.SetDisplay("Enable Short Entry", "Allow opening short positions", "Trading");
_buyExit = Param(nameof(BuyExit), true)
.SetDisplay("Enable Long Exit", "Allow closing long positions", "Trading");
_sellExit = Param(nameof(SellExit), true)
.SetDisplay("Enable Short Exit", "Allow closing short positions", "Trading");
_takeProfit = Param(nameof(TakeProfit), 2000)
.SetGreaterThanZero()
.SetDisplay("Take Profit", "Target in points", "Protection");
_stopLoss = Param(nameof(StopLoss), 1000)
.SetGreaterThanZero()
.SetDisplay("Stop Loss", "Loss limit in points", "Protection");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_ma = default;
_atr = default;
_prevSmax = 0;
_prevSmin = 0;
_prevTrend = 0;
_initialized = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_ma = new ExponentialMovingAverage { Length = MaPeriod };
_atr = new AverageTrueRange { Length = AtrPeriod };
Indicators.Add(_ma);
Indicators.Add(_atr);
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
var step = Security.PriceStep ?? 1m;
StartProtection(
takeProfit: new Unit(TakeProfit * step, UnitTypes.Absolute),
stopLoss: new Unit(StopLoss * step, UnitTypes.Absolute));
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _ma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var maResult = _ma.Process(candle.ClosePrice, candle.OpenTime, true);
var atrResult = _atr.Process(candle);
if (!maResult.IsFormed || !atrResult.IsFormed)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var maValue = maResult.ToDecimal();
var atrValue = atrResult.ToDecimal();
var smax = (1m + Deviation / 100m) * maValue;
var smin = (1m - Deviation / 100m) * maValue;
var trend = _prevTrend;
if (_initialized)
{
if (candle.ClosePrice > _prevSmax)
trend = 1;
if (candle.ClosePrice < _prevSmin)
trend = -1;
}
decimal? upSignal = null;
decimal? downSignal = null;
var upTrend = false;
var downTrend = false;
if (!_initialized)
{
_prevSmax = smax;
_prevSmin = smin;
_prevTrend = 0;
_initialized = true;
return;
}
if (trend > 0)
{
if (smin < _prevSmin)
smin = _prevSmin;
upTrend = true;
if (_prevTrend <= 0)
upSignal = smin - AtrSensitivity * atrValue;
}
else if (trend < 0)
{
if (smax > _prevSmax)
smax = _prevSmax;
downTrend = true;
if (_prevTrend >= 0)
downSignal = smax + AtrSensitivity * atrValue;
}
_prevSmax = smax;
_prevSmin = smin;
_prevTrend = trend;
if (BuyExit && downTrend && Position > 0)
SellMarket();
if (SellExit && upTrend && Position < 0)
BuyMarket();
if (BuyEntry && upSignal.HasValue && Position <= 0)
BuyMarket();
if (SellEntry && downSignal.HasValue && Position >= 0)
SellMarket();
}
}
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, Unit, UnitTypes
from StockSharp.Algo.Indicators import ExponentialMovingAverage, AverageTrueRange, CandleIndicatorValue
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class trend_envelopes_strategy(Strategy):
def __init__(self):
super(trend_envelopes_strategy, self).__init__()
self._ma_period = self.Param("MaPeriod", 14) \
.SetDisplay("MA Period", "Moving average length", "Indicator")
self._deviation = self.Param("Deviation", 0.2) \
.SetDisplay("Deviation", "Percent offset for envelopes", "Indicator")
self._atr_period = self.Param("AtrPeriod", 15) \
.SetDisplay("ATR Period", "ATR calculation length", "Indicator")
self._atr_sensitivity = self.Param("AtrSensitivity", 0.5) \
.SetDisplay("ATR Sensitivity", "Multiplier for signal shift", "Indicator")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe for strategy", "General")
self._buy_entry = self.Param("BuyEntry", True) \
.SetDisplay("Enable Long Entry", "Allow opening long positions", "Trading")
self._sell_entry = self.Param("SellEntry", True) \
.SetDisplay("Enable Short Entry", "Allow opening short positions", "Trading")
self._buy_exit = self.Param("BuyExit", True) \
.SetDisplay("Enable Long Exit", "Allow closing long positions", "Trading")
self._sell_exit = self.Param("SellExit", True) \
.SetDisplay("Enable Short Exit", "Allow closing short positions", "Trading")
self._take_profit = self.Param("TakeProfit", 2000) \
.SetDisplay("Take Profit", "Target in points", "Protection")
self._stop_loss = self.Param("StopLoss", 1000) \
.SetDisplay("Stop Loss", "Loss limit in points", "Protection")
self._ma = None
self._atr = None
self._prev_smax = 0.0
self._prev_smin = 0.0
self._prev_trend = 0
self._initialized = False
@property
def ma_period(self):
return self._ma_period.Value
@property
def deviation(self):
return self._deviation.Value
@property
def atr_period(self):
return self._atr_period.Value
@property
def atr_sensitivity(self):
return self._atr_sensitivity.Value
@property
def candle_type(self):
return self._candle_type.Value
@property
def buy_entry(self):
return self._buy_entry.Value
@property
def sell_entry(self):
return self._sell_entry.Value
@property
def buy_exit(self):
return self._buy_exit.Value
@property
def sell_exit(self):
return self._sell_exit.Value
@property
def take_profit(self):
return self._take_profit.Value
@property
def stop_loss(self):
return self._stop_loss.Value
def OnReseted(self):
super(trend_envelopes_strategy, self).OnReseted()
self._ma = None
self._atr = None
self._prev_smax = 0.0
self._prev_smin = 0.0
self._prev_trend = 0
self._initialized = False
def OnStarted2(self, time):
super(trend_envelopes_strategy, self).OnStarted2(time)
self._ma = ExponentialMovingAverage()
self._ma.Length = self.ma_period
self._atr = AverageTrueRange()
self._atr.Length = self.atr_period
self.Indicators.Add(self._ma)
self.Indicators.Add(self._atr)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self.process_candle).Start()
step = self.Security.PriceStep
if step is None or float(step) == 0:
step = 1.0
else:
step = float(step)
self.StartProtection(
takeProfit=Unit(float(self.take_profit) * step, UnitTypes.Absolute),
stopLoss=Unit(float(self.stop_loss) * step, UnitTypes.Absolute))
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, self._ma)
self.DrawOwnTrades(area)
def process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
ma_result = process_float(self._ma, candle.ClosePrice, candle.OpenTime, True)
div_atr = CandleIndicatorValue(self._atr, candle)
div_atr.IsFinal = True
atr_result = self._atr.Process(div_atr)
if not ma_result.IsFormed or not atr_result.IsFormed:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
ma_value = float(ma_result)
atr_value = float(atr_result)
dev = float(self.deviation)
smax = (1.0 + dev / 100.0) * ma_value
smin = (1.0 - dev / 100.0) * ma_value
trend = self._prev_trend
close = float(candle.ClosePrice)
if self._initialized:
if close > self._prev_smax:
trend = 1
if close < self._prev_smin:
trend = -1
if not self._initialized:
self._prev_smax = smax
self._prev_smin = smin
self._prev_trend = 0
self._initialized = True
return
up_signal = False
down_signal = False
up_trend = False
down_trend = False
atr_sens = float(self.atr_sensitivity)
if trend > 0:
if smin < self._prev_smin:
smin = self._prev_smin
up_trend = True
if self._prev_trend <= 0:
up_signal = True
elif trend < 0:
if smax > self._prev_smax:
smax = self._prev_smax
down_trend = True
if self._prev_trend >= 0:
down_signal = True
self._prev_smax = smax
self._prev_smin = smin
self._prev_trend = trend
if self.buy_exit and down_trend and self.Position > 0:
self.SellMarket()
if self.sell_exit and up_trend and self.Position < 0:
self.BuyMarket()
if self.buy_entry and up_signal and self.Position <= 0:
self.BuyMarket()
if self.sell_entry and down_signal and self.Position >= 0:
self.SellMarket()
def CreateClone(self):
return trend_envelopes_strategy()