Estrategia SMI Correct
Descripción general
La Estrategia SMI Correct implementa un sistema de trading basado en el indicador Stochastic Momentum Index (SMI). La estrategia observa la línea SMI y su línea de señal de media móvil. Se abre una posición larga cuando el SMI cruza por debajo de la línea de señal. Se abre una posición corta cuando el SMI cruza por encima de la línea de señal.
Parámetros
- Candle Type – marco temporal de las velas utilizadas para los cálculos.
- SMI Length – número de períodos para el cálculo del Estocástico.
- Signal Length – período de suavizado para la línea de señal.
Cómo funciona
- La estrategia se suscribe a las velas del tipo especificado.
- Para cada vela completada, actualiza el oscilador Estocástico y la media móvil de señal.
- Cuando el SMI cruza por debajo de la línea de señal, se cierra cualquier posición corta y se abre una posición larga.
- Cuando el SMI cruza por encima de la línea de señal, se cierra cualquier posición larga y se abre una posición corta.
El ejemplo también dibuja las velas y las líneas del indicador en un gráfico para su visualización.
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>
/// Strategy based on Stochastic Momentum Index crossings.
/// Uses Stochastic %K with a signal line (SMA of %K).
/// Buys when K crosses above signal, sells when K crosses below.
/// </summary>
public class SmiCorrectStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _smiLength;
private readonly StrategyParam<int> _signalLength;
private StochasticOscillator _stochastic;
private SimpleMovingAverage _signal;
private decimal? _prevSmi;
private decimal? _prevSignal;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int SmiLength
{
get => _smiLength.Value;
set => _smiLength.Value = value;
}
public int SignalLength
{
get => _signalLength.Value;
set => _signalLength.Value = value;
}
public SmiCorrectStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_smiLength = Param(nameof(SmiLength), 13)
.SetGreaterThanZero()
.SetDisplay("SMI Length", "Period for SMI calculation", "SMI");
_signalLength = Param(nameof(SignalLength), 5)
.SetGreaterThanZero()
.SetDisplay("Signal Length", "Smoothing period", "SMI");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_stochastic = null;
_signal = null;
_prevSmi = null;
_prevSignal = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevSmi = null;
_prevSignal = null;
_stochastic = new StochasticOscillator
{
K = { Length = SmiLength },
D = { Length = 1 }
};
_signal = new SimpleMovingAverage { Length = SignalLength };
Indicators.Add(_stochastic);
Indicators.Add(_signal);
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandleNew)
.Start();
StartProtection(
takeProfit: new Unit(2, UnitTypes.Percent),
stopLoss: new Unit(1, UnitTypes.Percent));
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _stochastic);
DrawOwnTrades(area);
}
}
private void ProcessCandleNew(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var stochResult = _stochastic.Process(candle);
if (!_stochastic.IsFormed)
return;
var stochTyped = (StochasticOscillatorValue)stochResult;
if (stochTyped.K is not decimal k)
return;
var signalResult = _signal.Process(new DecimalIndicatorValue(_signal, k, candle.OpenTime) { IsFinal = true });
if (!_signal.IsFormed)
{
_prevSmi = k;
return;
}
var signal = signalResult.ToDecimal();
if (_prevSmi is null || _prevSignal is null)
{
_prevSmi = k;
_prevSignal = signal;
return;
}
var crossUp = _prevSmi < _prevSignal && k >= signal;
var crossDown = _prevSmi > _prevSignal && k <= signal;
if (crossUp && Position == 0)
BuyMarket();
else if (crossDown && Position == 0)
SellMarket();
_prevSmi = k;
_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
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes
from StockSharp.Algo.Indicators import StochasticOscillator, SimpleMovingAverage, CandleIndicatorValue
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class smi_correct_strategy(Strategy):
def __init__(self):
super(smi_correct_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._smi_length = self.Param("SmiLength", 13) \
.SetDisplay("SMI Length", "Period for SMI calculation", "SMI")
self._signal_length = self.Param("SignalLength", 5) \
.SetDisplay("Signal Length", "Smoothing period", "SMI")
self._stochastic = None
self._signal = None
self._prev_smi = None
self._prev_signal = None
@property
def candle_type(self):
return self._candle_type.Value
@property
def smi_length(self):
return self._smi_length.Value
@property
def signal_length(self):
return self._signal_length.Value
def OnReseted(self):
super(smi_correct_strategy, self).OnReseted()
self._stochastic = None
self._signal = None
self._prev_smi = None
self._prev_signal = None
def OnStarted2(self, time):
super(smi_correct_strategy, self).OnStarted2(time)
self._prev_smi = None
self._prev_signal = None
self._stochastic = StochasticOscillator()
self._stochastic.K.Length = self.smi_length
self._stochastic.D.Length = 1
self._signal = SimpleMovingAverage()
self._signal.Length = self.signal_length
self.Indicators.Add(self._stochastic)
self.Indicators.Add(self._signal)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self.process_candle).Start()
self.StartProtection(
takeProfit=Unit(2, UnitTypes.Percent),
stopLoss=Unit(1, UnitTypes.Percent))
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, self._stochastic)
self.DrawOwnTrades(area)
def process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
stoch_input = CandleIndicatorValue(self._stochastic, candle)
stoch_input.IsFinal = True
stoch_result = self._stochastic.Process(stoch_input)
if not self._stochastic.IsFormed:
return
k = stoch_result.K
if k is None:
return
k = float(k)
signal_result = process_float(self._signal, k, candle.OpenTime, True)
if not self._signal.IsFormed:
self._prev_smi = k
return
signal = float(signal_result)
if self._prev_smi is None or self._prev_signal is None:
self._prev_smi = k
self._prev_signal = signal
return
cross_up = self._prev_smi < self._prev_signal and k >= signal
cross_down = self._prev_smi > self._prev_signal and k <= signal
if cross_up and self.Position == 0:
self.BuyMarket()
elif cross_down and self.Position == 0:
self.SellMarket()
self._prev_smi = k
self._prev_signal = signal
def CreateClone(self):
return smi_correct_strategy()