Estrategia SMI Ergódica Adaptativa
La estrategia SMI Ergódica Adaptativa utiliza el oscilador True Strength Index (TSI) con una línea de señal EMA para detectar reversiones desde extremos de sobrecompra o sobreventa. Se abre una posición larga cuando el TSI cruza por encima del umbral de sobreventa mientras se mantiene por encima de su línea de señal. Se abre una posición corta cuando el TSI cruza por debajo del umbral de sobrecompra y está por debajo de la línea de señal.
Detalles
- Criterios de entrada:
- TSI cruza por encima de sobreventa y TSI > señal (largo).
- TSI cruza por debajo de sobrecompra y TSI < señal (corto).
- Largo/Corto: Ambos.
- Criterios de salida:
- La señal inversa activa la operación opuesta.
- Stops: Ninguno.
- Valores predeterminados:
LongLength= 12ShortLength= 5SignalLength= 5OversoldThreshold= -0.4OverboughtThreshold= 0.4
- Filtros:
- Categoría: Oscilador de momentum
- Dirección: Largo/Corto
- Indicadores: True Strength Index, EMA
- Stops: No
- Complejidad: Bajo
- Marco temporal: Cualquiera
- Estacionalidad: No
- Redes neuronales: No
- Divergencia: No
- Nivel de riesgo: Bajo
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>
/// Adaptive SMI Ergodic strategy.
/// The True Strength Index (long smoothing LongLength, short smoothing ShortLength) is compared with its EMA signal line. A long opens
/// when TSI crosses above OversoldThreshold while above the signal line, a short when it crosses below OverboughtThreshold while below
/// the signal line; the opposite signal reverses the position. Thresholds are on the -1..1 ergodic scale.
/// </summary>
public class AdaptiveSmiErgodicStrategy : Strategy
{
private readonly StrategyParam<int> _longLength;
private readonly StrategyParam<int> _shortLength;
private readonly StrategyParam<int> _signalLength;
private readonly StrategyParam<decimal> _oversoldThreshold;
private readonly StrategyParam<decimal> _overboughtThreshold;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prevTsi;
/// <summary>
/// Long smoothing length of TSI.
/// </summary>
public int LongLength
{
get => _longLength.Value;
set => _longLength.Value = value;
}
/// <summary>
/// Short smoothing length of TSI.
/// </summary>
public int ShortLength
{
get => _shortLength.Value;
set => _shortLength.Value = value;
}
/// <summary>
/// EMA length of the signal line.
/// </summary>
public int SignalLength
{
get => _signalLength.Value;
set => _signalLength.Value = value;
}
/// <summary>
/// Oversold level on the -1..1 scale.
/// </summary>
public decimal OversoldThreshold
{
get => _oversoldThreshold.Value;
set => _oversoldThreshold.Value = value;
}
/// <summary>
/// Overbought level on the -1..1 scale.
/// </summary>
public decimal OverboughtThreshold
{
get => _overboughtThreshold.Value;
set => _overboughtThreshold.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public AdaptiveSmiErgodicStrategy()
{
_longLength = Param(nameof(LongLength), 12)
.SetGreaterThanZero()
.SetDisplay("Long Length", "Long smoothing length of TSI", "TSI");
_shortLength = Param(nameof(ShortLength), 5)
.SetGreaterThanZero()
.SetDisplay("Short Length", "Short smoothing length of TSI", "TSI");
_signalLength = Param(nameof(SignalLength), 5)
.SetGreaterThanZero()
.SetDisplay("Signal Length", "EMA length of the signal line", "TSI");
_oversoldThreshold = Param(nameof(OversoldThreshold), -0.4m)
.SetDisplay("Oversold Threshold", "Oversold level on the -1..1 scale", "Levels");
_overboughtThreshold = Param(nameof(OverboughtThreshold), 0.4m)
.SetDisplay("Overbought Threshold", "Overbought level on the -1..1 scale", "Levels");
_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();
_prevTsi = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevTsi = null;
var tsi = new TrueStrengthIndex
{
FirstLength = LongLength,
SecondLength = ShortLength,
SignalLength = SignalLength,
};
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(tsi, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
var oscillators = CreateChartArea();
if (oscillators != null)
DrawIndicator(oscillators, tsi);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue tsiValue)
{
if (candle.State != CandleStates.Finished)
return;
if (tsiValue is not ITrueStrengthIndexValue { Tsi: decimal rawTsi, Signal: decimal rawSignal })
return;
// The indicator reports TSI in percent; the thresholds use the -1..1 ergodic scale.
var tsi = rawTsi / 100m;
var signal = rawSignal / 100m;
var prevTsi = _prevTsi;
_prevTsi = tsi;
if (prevTsi is not decimal prev || !IsFormedAndOnlineAndAllowTrading())
return;
var crossAboveOversold = prev <= OversoldThreshold && tsi > OversoldThreshold;
var crossBelowOverbought = prev >= OverboughtThreshold && tsi < OverboughtThreshold;
if (crossAboveOversold && tsi > signal && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
else if (crossBelowOverbought && tsi < signal && Position >= 0)
SellMarket(Volume + Math.Abs(Position));
}
}
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 TrueStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class adaptive_smi_ergodic_strategy(Strategy):
"""
Adaptive SMI Ergodic strategy.
The True Strength Index (long smoothing LongLength, short smoothing ShortLength) is compared with its EMA signal line. A long opens
when TSI crosses above OversoldThreshold while above the signal line, a short when it crosses below OverboughtThreshold while below
the signal line; the opposite signal reverses the position. Thresholds are on the -1..1 ergodic scale.
"""
def __init__(self):
super(adaptive_smi_ergodic_strategy, self).__init__()
self._long_length = self.Param("LongLength", 12).SetGreaterThanZero().SetDisplay("Long Length", "Long smoothing length of TSI", "TSI")
self._short_length = self.Param("ShortLength", 5).SetGreaterThanZero().SetDisplay("Short Length", "Short smoothing length of TSI", "TSI")
self._signal_length = self.Param("SignalLength", 5).SetGreaterThanZero().SetDisplay("Signal Length", "EMA length of the signal line", "TSI")
self._oversold_threshold = self.Param("OversoldThreshold", -0.4).SetDisplay("Oversold Threshold", "Oversold level on the -1..1 scale", "Levels")
self._overbought_threshold = self.Param("OverboughtThreshold", 0.4).SetDisplay("Overbought Threshold", "Overbought level on the -1..1 scale", "Levels")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._prev_tsi = None
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(adaptive_smi_ergodic_strategy, self).OnReseted()
self._prev_tsi = None
def OnStarted2(self, time):
super(adaptive_smi_ergodic_strategy, self).OnStarted2(time)
self._prev_tsi = None
tsi = TrueStrengthIndex()
tsi.FirstLength = self._long_length.Value
tsi.SecondLength = self._short_length.Value
tsi.SignalLength = self._signal_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(tsi, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
oscillators = self.CreateChartArea()
if oscillators is not None:
self.DrawIndicator(oscillators, tsi)
def _process_candle(self, candle, tsi_value):
if candle.State != CandleStates.Finished:
return
if tsi_value.Tsi is None or tsi_value.Signal is None:
return
# The indicator reports TSI in percent; the thresholds use the -1..1 ergodic scale.
tsi = tsi_value.Tsi / Decimal(100)
signal = tsi_value.Signal / Decimal(100)
prev = self._prev_tsi
self._prev_tsi = tsi
if prev is None or not self.IsFormedAndOnlineAndAllowTrading():
return
oversold = Decimal(self._oversold_threshold.Value)
overbought = Decimal(self._overbought_threshold.Value)
cross_above_oversold = prev <= oversold and tsi > oversold
cross_below_overbought = prev >= overbought and tsi < overbought
if cross_above_oversold and tsi > signal and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
elif cross_below_overbought and tsi < signal and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
def CreateClone(self):
return adaptive_smi_ergodic_strategy()