Стратегия TSI Cloud Cross
Стратегия TSI Cloud Cross сравнивает индикатор True Strength Index (TSI) с его задержанной копией, образуя облако. Длинная позиция открывается, когда TSI пересекает сверху вверх смещённую линию, что указывает на бычий импульс. Короткая позиция открывается, когда TSI пересекает смещённую линию сверху вниз. Сигналы можно инвертировать и при необходимости закрывать противоположные позиции.
Подробности
- Условия входа:
- TSI пересекает смещённую линию снизу вверх (лонг).
- TSI пересекает смещённую линию сверху вниз (шорт).
- Направление: лонг и шорт.
- Условия выхода:
- По желанию закрытие при противоположном сигнале.
- Стопы: нет.
- Параметры по умолчанию:
LongLength= 25ShortLength= 13TriggerShift= 1Invert= false
- Фильтры:
- Категория: моментум-осциллятор
- Направление: лонг/шорт
- Индикаторы: True Strength Index
- Стопы: нет
- Сложность: низкая
- Таймфрейм: любой
- Сезонность: нет
- Нейросети: нет
- Дивергенция: нет
- Уровень риска: низкий
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 cross with shifted TSI line.
/// Opens long when TSI crosses above its shifted value and short on opposite cross.
/// </summary>
public class TsiCloudCrossStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _firstLength;
private readonly StrategyParam<int> _secondLength;
private readonly StrategyParam<int> _triggerShift;
private TrueStrengthIndex _tsi;
private readonly Queue<decimal> _tsiValues = new();
private decimal _prevTsi;
private decimal _prevTrigger;
private bool _isInitialized;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int FirstLength { get => _firstLength.Value; set => _firstLength.Value = value; }
public int SecondLength { get => _secondLength.Value; set => _secondLength.Value = value; }
public int TriggerShift { get => _triggerShift.Value; set => _triggerShift.Value = value; }
public TsiCloudCrossStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
_firstLength = Param(nameof(FirstLength), 25)
.SetDisplay("First Length", "First smoothing period for TSI", "TSI")
.SetGreaterThanZero();
_secondLength = Param(nameof(SecondLength), 13)
.SetDisplay("Second Length", "Second smoothing period for TSI", "TSI")
.SetGreaterThanZero();
_triggerShift = Param(nameof(TriggerShift), 1)
.SetDisplay("Trigger Shift", "Bars to shift TSI for trigger", "TSI")
.SetGreaterThanZero();
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_tsi = null;
_tsiValues.Clear();
_prevTsi = 0;
_prevTrigger = 0;
_isInitialized = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_tsiValues.Clear();
_isInitialized = false;
_tsi = new TrueStrengthIndex
{
FirstLength = FirstLength,
SecondLength = SecondLength,
};
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(_tsi, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _tsi);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue value)
{
if (candle.State != CandleStates.Finished)
return;
if (value is not ITrueStrengthIndexValue tsiVal || tsiVal.Tsi is not decimal tsiValue)
return;
if (!_tsi.IsFormed)
return;
_tsiValues.Enqueue(tsiValue);
if (_tsiValues.Count > TriggerShift + 1)
_tsiValues.Dequeue();
if (_tsiValues.Count < TriggerShift + 1)
{
_prevTsi = tsiValue;
_prevTrigger = tsiValue;
return;
}
var trigger = _tsiValues.Peek();
if (!_isInitialized)
{
_prevTsi = tsiValue;
_prevTrigger = trigger;
_isInitialized = true;
return;
}
var crossUp = _prevTsi <= _prevTrigger && tsiValue > trigger;
var crossDown = _prevTsi >= _prevTrigger && tsiValue < trigger;
_prevTsi = tsiValue;
_prevTrigger = trigger;
if (crossUp && Position <= 0)
BuyMarket();
else if (crossDown && 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
from StockSharp.Algo.Indicators import TrueStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class tsi_cloud_cross_strategy(Strategy):
def __init__(self):
super(tsi_cloud_cross_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._first_length = self.Param("FirstLength", 25) \
.SetDisplay("First Length", "First smoothing period for TSI", "TSI")
self._second_length = self.Param("SecondLength", 13) \
.SetDisplay("Second Length", "Second smoothing period for TSI", "TSI")
self._trigger_shift = self.Param("TriggerShift", 1) \
.SetDisplay("Trigger Shift", "Bars to shift TSI for trigger", "TSI")
self._tsi = None
self._tsi_values = []
self._prev_tsi = 0.0
self._prev_trigger = 0.0
self._is_initialized = False
@property
def candle_type(self):
return self._candle_type.Value
@property
def first_length(self):
return self._first_length.Value
@property
def second_length(self):
return self._second_length.Value
@property
def trigger_shift(self):
return self._trigger_shift.Value
def OnReseted(self):
super(tsi_cloud_cross_strategy, self).OnReseted()
self._tsi = None
self._tsi_values = []
self._prev_tsi = 0.0
self._prev_trigger = 0.0
self._is_initialized = False
def OnStarted2(self, time):
super(tsi_cloud_cross_strategy, self).OnStarted2(time)
self._tsi_values = []
self._is_initialized = False
self._tsi = TrueStrengthIndex()
self._tsi.FirstLength = self.first_length
self._tsi.SecondLength = self.second_length
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(self._tsi, self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, self._tsi)
self.DrawOwnTrades(area)
def process_candle(self, candle, value):
if candle.State != CandleStates.Finished:
return
tsi_val = value.Tsi
if tsi_val is None:
return
tsi_value = float(tsi_val)
if not self._tsi.IsFormed:
return
shift = int(self.trigger_shift)
self._tsi_values.append(tsi_value)
if len(self._tsi_values) > shift + 1:
self._tsi_values.pop(0)
if len(self._tsi_values) < shift + 1:
self._prev_tsi = tsi_value
self._prev_trigger = tsi_value
return
trigger = self._tsi_values[0]
if not self._is_initialized:
self._prev_tsi = tsi_value
self._prev_trigger = trigger
self._is_initialized = True
return
cross_up = self._prev_tsi <= self._prev_trigger and tsi_value > trigger
cross_down = self._prev_tsi >= self._prev_trigger and tsi_value < trigger
self._prev_tsi = tsi_value
self._prev_trigger = trigger
if cross_up and self.Position <= 0:
self.BuyMarket()
elif cross_down and self.Position >= 0:
self.SellMarket()
def CreateClone(self):
return tsi_cloud_cross_strategy()