Estrategia TSI DeMarker
Estrategia que calcula el True Strength Index sobre el oscilador DeMarker. Se abre una posición larga cuando el TSI cruza por encima de su línea de señal de media móvil. Se abre una posición corta cuando el TSI cruza por debajo de la línea de señal.
El enfoque combina análisis de momentum y de sobrecompra/sobreventa.
Detalles
- Criterios de entrada:
- Largo:
TSI cruza por encima de la señal - Corto:
TSI cruza por debajo de la señal
- Largo:
- Largo/Corto: Ambos
- Criterios de salida: Señal opuesta
- Stops: No
- Valores predeterminados:
CandleType= TimeSpan.FromHours(8).TimeFrame()DemarkerPeriod= 25ShortLength= 5LongLength= 8SignalLength= 20
- Filtros:
- Categoría: Oscilador cruce
- Dirección: Ambos
- Indicadores: TSI, DeMarker
- Stops: No
- Complejidad: Intermedio
- Marco temporal: Medio plazo
- 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>
/// True Strength Index crossover strategy filtered by DeMarker.
/// </summary>
public class TSIDeMarkerStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _demarkerPeriod;
private readonly StrategyParam<decimal> _tsiSpread;
private readonly StrategyParam<decimal> _longDeMarkerLimit;
private readonly StrategyParam<decimal> _shortDeMarkerLimit;
private readonly StrategyParam<int> _cooldownBars;
private decimal? _prevTsi;
private decimal? _prevSignal;
private int _cooldownRemaining;
/// <summary>
/// Candle type for processing.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Period for DeMarker indicator.
/// </summary>
public int DemarkerPeriod
{
get => _demarkerPeriod.Value;
set => _demarkerPeriod.Value = value;
}
/// <summary>
/// Minimum absolute spread between TSI and its signal line.
/// </summary>
public decimal TsiSpread
{
get => _tsiSpread.Value;
set => _tsiSpread.Value = value;
}
/// <summary>
/// Maximum DeMarker value allowed for long entries.
/// </summary>
public decimal LongDeMarkerLimit
{
get => _longDeMarkerLimit.Value;
set => _longDeMarkerLimit.Value = value;
}
/// <summary>
/// Minimum DeMarker value allowed for short entries.
/// </summary>
public decimal ShortDeMarkerLimit
{
get => _shortDeMarkerLimit.Value;
set => _shortDeMarkerLimit.Value = value;
}
/// <summary>
/// Number of completed candles to wait after a position change.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="TSIDeMarkerStrategy"/> class.
/// </summary>
public TSIDeMarkerStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for strategy", "General");
_demarkerPeriod = Param(nameof(DemarkerPeriod), 14)
.SetDisplay("DeMarker Period", "Period for DeMarker", "Indicators");
_tsiSpread = Param(nameof(TsiSpread), 2m)
.SetDisplay("TSI Spread", "Minimum spread between TSI and its signal line", "Filters");
_longDeMarkerLimit = Param(nameof(LongDeMarkerLimit), 0.55m)
.SetDisplay("Long DeMarker", "Maximum DeMarker for long entries", "Filters");
_shortDeMarkerLimit = Param(nameof(ShortDeMarkerLimit), 0.45m)
.SetDisplay("Short DeMarker", "Minimum DeMarker for short entries", "Filters");
_cooldownBars = Param(nameof(CooldownBars), 6)
.SetDisplay("Cooldown Bars", "Completed candles to wait after a position change", "Trading");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevTsi = null;
_prevSignal = null;
_cooldownRemaining = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var tsi = new TrueStrengthIndex();
var demarker = new DeMarker { Length = DemarkerPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(tsi, demarker, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, tsi);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue tsiValue, IIndicatorValue demarkerValue)
{
if (candle.State != CandleStates.Finished || !tsiValue.IsFinal || !demarkerValue.IsFinal)
return;
if (_cooldownRemaining > 0)
_cooldownRemaining--;
var tsiPair = (ITrueStrengthIndexValue)tsiValue;
if (tsiPair.Tsi is not decimal tsi || tsiPair.Signal is not decimal signal)
return;
var demarker = demarkerValue.ToDecimal();
if (_prevTsi is not decimal prevTsi || _prevSignal is not decimal prevSignal)
{
_prevTsi = tsi;
_prevSignal = signal;
return;
}
var crossUp = prevTsi <= prevSignal && tsi > signal && Math.Abs(tsi - signal) >= TsiSpread;
var crossDown = prevTsi >= prevSignal && tsi < signal && Math.Abs(tsi - signal) >= TsiSpread;
if (_cooldownRemaining == 0)
{
if (crossUp && demarker <= LongDeMarkerLimit && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
_cooldownRemaining = CooldownBars;
}
else if (crossDown && demarker >= ShortDeMarkerLimit && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
_cooldownRemaining = CooldownBars;
}
}
_prevTsi = tsi;
_prevSignal = signal;
}
}
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 TrueStrengthIndex, DeMarker
from StockSharp.Algo.Strategies import Strategy
class tsi_de_marker_strategy(Strategy):
def __init__(self):
super(tsi_de_marker_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe for strategy", "General")
self._demarker_period = self.Param("DemarkerPeriod", 14) \
.SetDisplay("DeMarker Period", "Period for DeMarker", "Indicators")
self._tsi_spread = self.Param("TsiSpread", 2.0) \
.SetDisplay("TSI Spread", "Minimum spread between TSI and its signal line", "Filters")
self._long_demarker_limit = self.Param("LongDeMarkerLimit", 0.55) \
.SetDisplay("Long DeMarker", "Maximum DeMarker for long entries", "Filters")
self._short_demarker_limit = self.Param("ShortDeMarkerLimit", 0.45) \
.SetDisplay("Short DeMarker", "Minimum DeMarker for short entries", "Filters")
self._cooldown_bars = self.Param("CooldownBars", 6) \
.SetDisplay("Cooldown Bars", "Completed candles to wait after a position change", "Trading")
self._prev_tsi = None
self._prev_signal = None
self._cooldown_remaining = 0
@property
def candle_type(self):
return self._candle_type.Value
@property
def demarker_period(self):
return self._demarker_period.Value
@property
def tsi_spread(self):
return self._tsi_spread.Value
@property
def long_demarker_limit(self):
return self._long_demarker_limit.Value
@property
def short_demarker_limit(self):
return self._short_demarker_limit.Value
@property
def cooldown_bars(self):
return self._cooldown_bars.Value
def OnReseted(self):
super(tsi_de_marker_strategy, self).OnReseted()
self._prev_tsi = None
self._prev_signal = None
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(tsi_de_marker_strategy, self).OnStarted2(time)
tsi = TrueStrengthIndex()
demarker = DeMarker()
demarker.Length = self.demarker_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(tsi, demarker, self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, tsi)
self.DrawOwnTrades(area)
def process_candle(self, candle, tsi_value, demarker_value):
if candle.State != CandleStates.Finished or not tsi_value.IsFinal or not demarker_value.IsFinal:
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
tsi_pair = tsi_value
tsi_val = tsi_pair.Tsi
signal_val = tsi_pair.Signal
if tsi_val is None or signal_val is None:
return
tsi_val = float(tsi_val)
signal_val = float(signal_val)
demarker = float(demarker_value)
if self._prev_tsi is None or self._prev_signal is None:
self._prev_tsi = tsi_val
self._prev_signal = signal_val
return
tsi_spread_val = float(self.tsi_spread)
cross_up = self._prev_tsi <= self._prev_signal and tsi_val > signal_val and abs(tsi_val - signal_val) >= tsi_spread_val
cross_down = self._prev_tsi >= self._prev_signal and tsi_val < signal_val and abs(tsi_val - signal_val) >= tsi_spread_val
if self._cooldown_remaining == 0:
if cross_up and demarker <= float(self.long_demarker_limit) and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._cooldown_remaining = self.cooldown_bars
elif cross_down and demarker >= float(self.short_demarker_limit) and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._cooldown_remaining = self.cooldown_bars
self._prev_tsi = tsi_val
self._prev_signal = signal_val
def CreateClone(self):
return tsi_de_marker_strategy()