Стратегия Kolier SuperTrend X2
Обзор
Стратегия повторяет оригинальный эксперт MetaTrader, сочетая два фильтра SuperTrend на разных таймфреймах. SuperTrend старшего таймфрейма определяет доминирующее направление рынка, а SuperTrend младшего таймфрейма ищет синхронные развороты для входов. В портировании на StockSharp используются привязки высокоуровневого API, поэтому индикаторы получают свечи напрямую и ведут собственную историю.
Логика торговли
- Фильтр тренда: SuperTrend старшего таймфрейма должен подтвердить восходящий или нисходящий тренд. Задержка подтверждения задаётся параметром
TrendSignalShift, а режим (TrendMode) определяет, нужна ли всего одна свеча (NewWay) либо два последовательных бара (остальные режимы). - Сигналы входа: SuperTrend младшего таймфрейма ждёт смены направления в сторону текущего фильтра. Параметр
EntrySignalShiftоткладывает момент оценки до закрытых баров, аEntryModeопределяет реакцию сразу после смены (NewWay) либо только после подтверждённого разворота (прочие режимы). - Лонг: допускается, если
EnableBuyEntries = true, трендовый фильтр бычий и входной SuperTrend переключился вверх в соответствии с выбранным режимом. Перед открытием закрывается возможная короткая позиция, затем покупается объёмVolume + |Position|. - Шорт: допускается, если
EnableSellEntries = true, трендовый фильтр медвежий и входной SuperTrend переключился вниз. Перед входом закрывается длинная позиция. - Выходы:
- Разворот на старшем таймфрейме закрывает лонги (
CloseBuyOnTrendFlip) или шорты (CloseSellOnTrendFlip). - Смена направления на младшем таймфрейме закрывает позиции при включении
CloseBuyOnEntryFlip/CloseSellOnEntryFlip. - Опциональные фиксированные стопы (
StopLossPoints,TakeProfitPoints) рассчитываются в кратныхSecurity.PriceStep.
- Разворот на старшем таймфрейме закрывает лонги (
Индикаторы
- Два экземпляра StockSharp
SuperTrend(один для фильтра тренда, второй для входов).
Параметры
TrendCandleType– таймфрейм фильтра тренда.EntryCandleType– таймфрейм сигналов входа.TrendAtrPeriod,TrendAtrMultiplier– настройки ATR для трендового SuperTrend.EntryAtrPeriod,EntryAtrMultiplier– настройки ATR для входного SuperTrend.TrendMode,EntryMode– режимы подтверждения:NewWayреагирует после одного бара, остальные требуют два подряд (Visual и ExpertSignal в порте работают как классический SuperTrend).TrendSignalShift,EntrySignalShift– количество закрытых баров до использования значений индикаторов.EnableBuyEntries,EnableSellEntries– разрешения на лонги и шорты.CloseBuyOnTrendFlip,CloseSellOnTrendFlip– выход по сигналу противоположного тренда.CloseBuyOnEntryFlip,CloseSellOnEntryFlip– выход по смене направления на входном таймфрейме.StopLossPoints,TakeProfitPoints– расстояния защитных ордеров в шагах цены (0 отключает уровни).Volume– базовый объём сделок.Slippage– параметр-заглушка, оставленный для совместимости с оригиналом.
Примечания
- Порт использует высокоуровневый рабочий процесс StockSharp: подписка на свечи через
SubscribeCandles, привязка индикаторовBindEx, хранение только минимального состояния (направление тренда и актуальные стопы). - Метод
StartProtection()вызывается один раз для активации стандартной защиты позиций StockSharp.
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>
/// Kolier SuperTrend X2 strategy that combines higher timeframe trend filtering and lower timeframe entries.
/// </summary>
public class KolierSuperTrendX2Strategy : Strategy
{
private readonly StrategyParam<DataType> _trendCandleType;
private readonly StrategyParam<DataType> _entryCandleType;
private readonly StrategyParam<int> _trendAtrPeriod;
private readonly StrategyParam<decimal> _trendAtrMultiplier;
private readonly StrategyParam<int> _entryAtrPeriod;
private readonly StrategyParam<decimal> _entryAtrMultiplier;
private readonly StrategyParam<KolierTrendModes> _trendMode;
private readonly StrategyParam<KolierTrendModes> _entryMode;
private readonly StrategyParam<int> _trendSignalShift;
private readonly StrategyParam<int> _entrySignalShift;
private readonly StrategyParam<bool> _enableBuyEntries;
private readonly StrategyParam<bool> _enableSellEntries;
private readonly StrategyParam<bool> _closeBuyOnTrendFlip;
private readonly StrategyParam<bool> _closeSellOnTrendFlip;
private readonly StrategyParam<bool> _closeBuyOnEntryFlip;
private readonly StrategyParam<bool> _closeSellOnEntryFlip;
private readonly StrategyParam<decimal> _stopLossPoints;
private readonly StrategyParam<decimal> _takeProfitPoints;
private SuperTrend _trendSuperTrend = null!;
private SuperTrend _entrySuperTrend = null!;
private readonly List<int> _trendDirections = new();
private readonly List<int> _entryDirections = new();
private int _trendDirection;
private decimal? _stopLossPrice;
private decimal? _takeProfitPrice;
/// <summary>
/// Initializes a new instance of <see cref="KolierSuperTrendX2Strategy"/>.
/// </summary>
public KolierSuperTrendX2Strategy()
{
_trendCandleType = Param(nameof(TrendCandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Trend Timeframe", "Timeframe for trend SuperTrend", "Data");
_entryCandleType = Param(nameof(EntryCandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Entry Timeframe", "Timeframe for entry SuperTrend", "Data");
_trendAtrPeriod = Param(nameof(TrendAtrPeriod), 10)
.SetGreaterThanZero()
.SetDisplay("Trend ATR Period", "ATR period for trend SuperTrend", "Trend");
_trendAtrMultiplier = Param(nameof(TrendAtrMultiplier), 3m)
.SetGreaterThanZero()
.SetDisplay("Trend ATR Multiplier", "ATR multiplier for trend SuperTrend", "Trend");
_entryAtrPeriod = Param(nameof(EntryAtrPeriod), 10)
.SetGreaterThanZero()
.SetDisplay("Entry ATR Period", "ATR period for entry SuperTrend", "Entry");
_entryAtrMultiplier = Param(nameof(EntryAtrMultiplier), 3m)
.SetGreaterThanZero()
.SetDisplay("Entry ATR Multiplier", "ATR multiplier for entry SuperTrend", "Entry");
_trendMode = Param(nameof(TrendMode), KolierTrendModes.NewWay)
.SetDisplay("Trend Mode", "Mode used for trend confirmation", "Trend");
_entryMode = Param(nameof(EntryMode), KolierTrendModes.NewWay)
.SetDisplay("Entry Mode", "Mode used for entry detection", "Entry");
_trendSignalShift = Param(nameof(TrendSignalShift), 1)
.SetRange(0, 10)
.SetDisplay("Trend Signal Shift", "Bars to delay trend confirmation", "Trend");
_entrySignalShift = Param(nameof(EntrySignalShift), 1)
.SetRange(0, 10)
.SetDisplay("Entry Signal Shift", "Bars to delay entry confirmation", "Entry");
_enableBuyEntries = Param(nameof(EnableBuyEntries), true)
.SetDisplay("Enable Long Entries", "Allow opening long positions", "Trading");
_enableSellEntries = Param(nameof(EnableSellEntries), true)
.SetDisplay("Enable Short Entries", "Allow opening short positions", "Trading");
_closeBuyOnTrendFlip = Param(nameof(CloseBuyOnTrendFlip), true)
.SetDisplay("Close Long On Trend Flip", "Close longs when higher timeframe turns bearish", "Exits");
_closeSellOnTrendFlip = Param(nameof(CloseSellOnTrendFlip), true)
.SetDisplay("Close Short On Trend Flip", "Close shorts when higher timeframe turns bullish", "Exits");
_closeBuyOnEntryFlip = Param(nameof(CloseBuyOnEntryFlip), false)
.SetDisplay("Close Long On Entry Flip", "Close longs when entry SuperTrend flips down", "Exits");
_closeSellOnEntryFlip = Param(nameof(CloseSellOnEntryFlip), false)
.SetDisplay("Close Short On Entry Flip", "Close shorts when entry SuperTrend flips up", "Exits");
_stopLossPoints = Param(nameof(StopLossPoints), 1000m)
.SetDisplay("Stop Loss (steps)", "Stop loss distance in price steps", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 2000m)
.SetDisplay("Take Profit (steps)", "Take profit distance in price steps", "Risk");
}
/// <summary>
/// Candle type used for the trend filter.
/// </summary>
public DataType TrendCandleType
{
get => _trendCandleType.Value;
set => _trendCandleType.Value = value;
}
/// <summary>
/// Candle type used for entry signals.
/// </summary>
public DataType EntryCandleType
{
get => _entryCandleType.Value;
set => _entryCandleType.Value = value;
}
/// <summary>
/// ATR period for the trend SuperTrend filter.
/// </summary>
public int TrendAtrPeriod
{
get => _trendAtrPeriod.Value;
set => _trendAtrPeriod.Value = value;
}
/// <summary>
/// ATR multiplier for the trend SuperTrend filter.
/// </summary>
public decimal TrendAtrMultiplier
{
get => _trendAtrMultiplier.Value;
set => _trendAtrMultiplier.Value = value;
}
/// <summary>
/// ATR period for the entry SuperTrend filter.
/// </summary>
public int EntryAtrPeriod
{
get => _entryAtrPeriod.Value;
set => _entryAtrPeriod.Value = value;
}
/// <summary>
/// ATR multiplier for the entry SuperTrend filter.
/// </summary>
public decimal EntryAtrMultiplier
{
get => _entryAtrMultiplier.Value;
set => _entryAtrMultiplier.Value = value;
}
/// <summary>
/// Confirmation mode for the trend timeframe.
/// </summary>
public KolierTrendModes TrendMode
{
get => _trendMode.Value;
set => _trendMode.Value = value;
}
/// <summary>
/// Confirmation mode for the entry timeframe.
/// </summary>
public KolierTrendModes EntryMode
{
get => _entryMode.Value;
set => _entryMode.Value = value;
}
/// <summary>
/// Bars to shift when confirming trend direction.
/// </summary>
public int TrendSignalShift
{
get => _trendSignalShift.Value;
set => _trendSignalShift.Value = value;
}
/// <summary>
/// Bars to shift when confirming entry signals.
/// </summary>
public int EntrySignalShift
{
get => _entrySignalShift.Value;
set => _entrySignalShift.Value = value;
}
/// <summary>
/// Allow opening long positions.
/// </summary>
public bool EnableBuyEntries
{
get => _enableBuyEntries.Value;
set => _enableBuyEntries.Value = value;
}
/// <summary>
/// Allow opening short positions.
/// </summary>
public bool EnableSellEntries
{
get => _enableSellEntries.Value;
set => _enableSellEntries.Value = value;
}
/// <summary>
/// Close long positions when the higher timeframe turns bearish.
/// </summary>
public bool CloseBuyOnTrendFlip
{
get => _closeBuyOnTrendFlip.Value;
set => _closeBuyOnTrendFlip.Value = value;
}
/// <summary>
/// Close short positions when the higher timeframe turns bullish.
/// </summary>
public bool CloseSellOnTrendFlip
{
get => _closeSellOnTrendFlip.Value;
set => _closeSellOnTrendFlip.Value = value;
}
/// <summary>
/// Close long positions when the entry SuperTrend flips bearish.
/// </summary>
public bool CloseBuyOnEntryFlip
{
get => _closeBuyOnEntryFlip.Value;
set => _closeBuyOnEntryFlip.Value = value;
}
/// <summary>
/// Close short positions when the entry SuperTrend flips bullish.
/// </summary>
public bool CloseSellOnEntryFlip
{
get => _closeSellOnEntryFlip.Value;
set => _closeSellOnEntryFlip.Value = value;
}
/// <summary>
/// Stop loss distance expressed in price steps.
/// </summary>
public decimal StopLossPoints
{
get => _stopLossPoints.Value;
set => _stopLossPoints.Value = value;
}
/// <summary>
/// Take profit distance expressed in price steps.
/// </summary>
public decimal TakeProfitPoints
{
get => _takeProfitPoints.Value;
set => _takeProfitPoints.Value = value;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, EntryCandleType);
if (!TrendCandleType.Equals(EntryCandleType))
{
yield return (Security, TrendCandleType);
}
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_trendDirections.Clear();
_entryDirections.Clear();
_trendDirection = 0;
_stopLossPrice = null;
_takeProfitPrice = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_trendSuperTrend = new SuperTrend
{
Length = TrendAtrPeriod,
Multiplier = TrendAtrMultiplier
};
_entrySuperTrend = new SuperTrend
{
Length = EntryAtrPeriod,
Multiplier = EntryAtrMultiplier
};
var entrySubscription = SubscribeCandles(EntryCandleType);
if (TrendCandleType.Equals(EntryCandleType))
{
entrySubscription
.BindEx(_trendSuperTrend, ProcessTrendCandle)
.BindEx(_entrySuperTrend, ProcessEntryCandle)
.Start();
}
else
{
entrySubscription
.BindEx(_entrySuperTrend, ProcessEntryCandle)
.Start();
var trendSubscription = SubscribeCandles(TrendCandleType);
trendSubscription
.BindEx(_trendSuperTrend, ProcessTrendCandle)
.Start();
}
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, entrySubscription);
DrawIndicator(area, _entrySuperTrend);
DrawOwnTrades(area);
}
}
private void ProcessTrendCandle(ICandleMessage candle, IIndicatorValue value)
{
if (candle.State != CandleStates.Finished)
return;
if (!value.IsFormed || value is not SuperTrendIndicatorValue trendValue)
return;
if (!_trendSuperTrend.IsFormed)
return;
var direction = trendValue.IsUpTrend ? 1 : -1;
if (direction == 0)
return;
UpdateHistory(_trendDirections, direction, TrendSignalShift + 3);
var current = GetDirection(_trendDirections, TrendSignalShift);
var previous = GetDirection(_trendDirections, TrendSignalShift + 1);
if (current is null || previous is null)
return;
switch (TrendMode)
{
case KolierTrendModes.NewWay:
_trendDirection = current.Value;
break;
default:
if (current == previous)
{
_trendDirection = current.Value;
}
break;
}
}
private void ProcessEntryCandle(ICandleMessage candle, IIndicatorValue value)
{
if (candle.State != CandleStates.Finished)
return;
if (!value.IsFormed || value is not SuperTrendIndicatorValue entryValue)
return;
if (!_entrySuperTrend.IsFormed)
return;
var direction = entryValue.IsUpTrend ? 1 : -1;
if (direction == 0)
return;
UpdateHistory(_entryDirections, direction, EntrySignalShift + 3);
if (HandleStops(candle))
return;
var current = GetDirection(_entryDirections, EntrySignalShift);
var previous = GetDirection(_entryDirections, EntrySignalShift + 1);
if (current is null || previous is null)
return;
var flipToUp = IsFlipTo(1, current, previous);
var flipToDown = IsFlipTo(-1, current, previous);
var closeLong = Position > 0 && ((CloseBuyOnTrendFlip && _trendDirection < 0) || (CloseBuyOnEntryFlip && current < 0));
var closeShort = Position < 0 && ((CloseSellOnTrendFlip && _trendDirection > 0) || (CloseSellOnEntryFlip && current > 0));
if (closeLong)
{
SellMarket();
ResetStops();
}
if (closeShort)
{
BuyMarket();
ResetStops();
}
if (EnableBuyEntries && _trendDirection > 0 && flipToUp && Position <= 0)
{
BuyMarket();
UpdateStops(candle.ClosePrice, true);
}
else if (EnableSellEntries && _trendDirection < 0 && flipToDown && Position >= 0)
{
SellMarket();
UpdateStops(candle.ClosePrice, false);
}
}
private bool HandleStops(ICandleMessage candle)
{
if (Position > 0)
{
if (_stopLossPrice is decimal sl && candle.LowPrice <= sl)
{
SellMarket();
ResetStops();
return true;
}
if (_takeProfitPrice is decimal tp && candle.HighPrice >= tp)
{
SellMarket();
ResetStops();
return true;
}
}
else if (Position < 0)
{
var absPos = Math.Abs(Position);
if (_stopLossPrice is decimal sl && candle.HighPrice >= sl)
{
BuyMarket(absPos);
ResetStops();
return true;
}
if (_takeProfitPrice is decimal tp && candle.LowPrice <= tp)
{
BuyMarket(absPos);
ResetStops();
return true;
}
}
return false;
}
private void UpdateStops(decimal entryPrice, bool isLong)
{
var step = Security?.PriceStep ?? 0m;
if (step <= 0m)
{
_stopLossPrice = null;
_takeProfitPrice = null;
return;
}
_stopLossPrice = StopLossPoints > 0m
? isLong ? entryPrice - StopLossPoints * step : entryPrice + StopLossPoints * step
: null;
_takeProfitPrice = TakeProfitPoints > 0m
? isLong ? entryPrice + TakeProfitPoints * step : entryPrice - TakeProfitPoints * step
: null;
}
private void ResetStops()
{
_stopLossPrice = null;
_takeProfitPrice = null;
}
private static void UpdateHistory(List<int> history, int direction, int maxLength)
{
history.Insert(0, direction);
while (history.Count > maxLength && history.Count > 0)
{
try { history.RemoveAt(history.Count - 1); }
catch { break; }
}
}
private static int? GetDirection(List<int> history, int offset)
{
return history.Count > offset ? history[offset] : null;
}
private bool IsFlipTo(int targetDirection, int? current, int? previous)
{
if (current != targetDirection)
return false;
return EntryMode switch
{
KolierTrendModes.NewWay => previous != targetDirection,
_ => previous == -targetDirection,
};
}
/// <summary>
/// Enumeration mirroring the original MQL SuperTrend modes.
/// </summary>
public enum KolierTrendModes
{
/// <summary>
/// Classic SuperTrend confirmation requiring consecutive candles.
/// </summary>
SuperTrend,
/// <summary>
/// Faster confirmation that reacts after a single candle flip.
/// </summary>
NewWay,
/// <summary>
/// Visual mode (treated like classic confirmation in this port).
/// </summary>
Visual,
/// <summary>
/// Expert signal mode (treated like classic confirmation in this port).
/// </summary>
ExpertSignal,
}
}
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, Unit, UnitTypes
from StockSharp.Algo.Indicators import SuperTrend
from StockSharp.Algo.Strategies import Strategy
MODE_SUPERTREND = 0
MODE_NEWWAY = 1
MODE_VISUAL = 2
MODE_EXPERT = 3
class kolier_super_trend_x2_strategy(Strategy):
def __init__(self):
super(kolier_super_trend_x2_strategy, self).__init__()
self._trend_candle_type = self.Param("TrendCandleType", DataType.TimeFrame(TimeSpan.FromHours(4)))
self._entry_candle_type = self.Param("EntryCandleType", DataType.TimeFrame(TimeSpan.FromHours(4)))
self._trend_atr_period = self.Param("TrendAtrPeriod", 10)
self._trend_atr_multiplier = self.Param("TrendAtrMultiplier", 3.0)
self._entry_atr_period = self.Param("EntryAtrPeriod", 10)
self._entry_atr_multiplier = self.Param("EntryAtrMultiplier", 3.0)
self._trend_mode = self.Param("TrendMode", MODE_NEWWAY)
self._entry_mode = self.Param("EntryMode", MODE_NEWWAY)
self._trend_signal_shift = self.Param("TrendSignalShift", 1)
self._entry_signal_shift = self.Param("EntrySignalShift", 1)
self._enable_buy_entries = self.Param("EnableBuyEntries", True)
self._enable_sell_entries = self.Param("EnableSellEntries", True)
self._close_buy_on_trend_flip = self.Param("CloseBuyOnTrendFlip", True)
self._close_sell_on_trend_flip = self.Param("CloseSellOnTrendFlip", True)
self._close_buy_on_entry_flip = self.Param("CloseBuyOnEntryFlip", False)
self._close_sell_on_entry_flip = self.Param("CloseSellOnEntryFlip", False)
self._stop_loss_points = self.Param("StopLossPoints", 1000.0)
self._take_profit_points = self.Param("TakeProfitPoints", 2000.0)
self._trend_directions = []
self._entry_directions = []
self._trend_direction = 0
self._stop_loss_price = None
self._take_profit_price = None
@property
def TrendCandleType(self):
return self._trend_candle_type.Value
@TrendCandleType.setter
def TrendCandleType(self, value):
self._trend_candle_type.Value = value
@property
def EntryCandleType(self):
return self._entry_candle_type.Value
@EntryCandleType.setter
def EntryCandleType(self, value):
self._entry_candle_type.Value = value
@property
def TrendAtrPeriod(self):
return self._trend_atr_period.Value
@TrendAtrPeriod.setter
def TrendAtrPeriod(self, value):
self._trend_atr_period.Value = value
@property
def TrendAtrMultiplier(self):
return self._trend_atr_multiplier.Value
@TrendAtrMultiplier.setter
def TrendAtrMultiplier(self, value):
self._trend_atr_multiplier.Value = value
@property
def EntryAtrPeriod(self):
return self._entry_atr_period.Value
@EntryAtrPeriod.setter
def EntryAtrPeriod(self, value):
self._entry_atr_period.Value = value
@property
def EntryAtrMultiplier(self):
return self._entry_atr_multiplier.Value
@EntryAtrMultiplier.setter
def EntryAtrMultiplier(self, value):
self._entry_atr_multiplier.Value = value
@property
def TrendMode(self):
return self._trend_mode.Value
@TrendMode.setter
def TrendMode(self, value):
self._trend_mode.Value = value
@property
def EntryMode(self):
return self._entry_mode.Value
@EntryMode.setter
def EntryMode(self, value):
self._entry_mode.Value = value
@property
def TrendSignalShift(self):
return self._trend_signal_shift.Value
@TrendSignalShift.setter
def TrendSignalShift(self, value):
self._trend_signal_shift.Value = value
@property
def EntrySignalShift(self):
return self._entry_signal_shift.Value
@EntrySignalShift.setter
def EntrySignalShift(self, value):
self._entry_signal_shift.Value = value
@property
def EnableBuyEntries(self):
return self._enable_buy_entries.Value
@EnableBuyEntries.setter
def EnableBuyEntries(self, value):
self._enable_buy_entries.Value = value
@property
def EnableSellEntries(self):
return self._enable_sell_entries.Value
@EnableSellEntries.setter
def EnableSellEntries(self, value):
self._enable_sell_entries.Value = value
@property
def CloseBuyOnTrendFlip(self):
return self._close_buy_on_trend_flip.Value
@CloseBuyOnTrendFlip.setter
def CloseBuyOnTrendFlip(self, value):
self._close_buy_on_trend_flip.Value = value
@property
def CloseSellOnTrendFlip(self):
return self._close_sell_on_trend_flip.Value
@CloseSellOnTrendFlip.setter
def CloseSellOnTrendFlip(self, value):
self._close_sell_on_trend_flip.Value = value
@property
def CloseBuyOnEntryFlip(self):
return self._close_buy_on_entry_flip.Value
@CloseBuyOnEntryFlip.setter
def CloseBuyOnEntryFlip(self, value):
self._close_buy_on_entry_flip.Value = value
@property
def CloseSellOnEntryFlip(self):
return self._close_sell_on_entry_flip.Value
@CloseSellOnEntryFlip.setter
def CloseSellOnEntryFlip(self, value):
self._close_sell_on_entry_flip.Value = value
@property
def StopLossPoints(self):
return self._stop_loss_points.Value
@StopLossPoints.setter
def StopLossPoints(self, value):
self._stop_loss_points.Value = value
@property
def TakeProfitPoints(self):
return self._take_profit_points.Value
@TakeProfitPoints.setter
def TakeProfitPoints(self, value):
self._take_profit_points.Value = value
def OnStarted2(self, time):
super(kolier_super_trend_x2_strategy, self).OnStarted2(time)
self._trend_directions = []
self._entry_directions = []
self._trend_direction = 0
self._stop_loss_price = None
self._take_profit_price = None
self._trend_st = SuperTrend()
self._trend_st.Length = self.TrendAtrPeriod
self._trend_st.Multiplier = self.TrendAtrMultiplier
self._entry_st = SuperTrend()
self._entry_st.Length = self.EntryAtrPeriod
self._entry_st.Multiplier = self.EntryAtrMultiplier
entry_sub = self.SubscribeCandles(self.EntryCandleType)
if str(self.TrendCandleType) == str(self.EntryCandleType):
entry_sub.BindEx(self._trend_st, self._process_trend_candle)
entry_sub.BindEx(self._entry_st, self._process_entry_candle)
entry_sub.Start()
else:
entry_sub.BindEx(self._entry_st, self._process_entry_candle)
entry_sub.Start()
trend_sub = self.SubscribeCandles(self.TrendCandleType)
trend_sub.BindEx(self._trend_st, self._process_trend_candle)
trend_sub.Start()
def _process_trend_candle(self, candle, value):
if candle.State != CandleStates.Finished:
return
if not value.IsFormed:
return
if not self._trend_st.IsFormed:
return
direction = 1 if value.IsUpTrend else -1
trend_shift = int(self.TrendSignalShift)
max_len = trend_shift + 3
self._trend_directions.insert(0, direction)
while len(self._trend_directions) > max_len:
self._trend_directions.pop()
current = self._get_direction(self._trend_directions, trend_shift)
previous = self._get_direction(self._trend_directions, trend_shift + 1)
if current is None or previous is None:
return
trend_mode = int(self.TrendMode)
if trend_mode == MODE_NEWWAY:
self._trend_direction = current
else:
if current == previous:
self._trend_direction = current
def _process_entry_candle(self, candle, value):
if candle.State != CandleStates.Finished:
return
if not value.IsFormed:
return
if not self._entry_st.IsFormed:
return
direction = 1 if value.IsUpTrend else -1
entry_shift = int(self.EntrySignalShift)
max_len = entry_shift + 3
self._entry_directions.insert(0, direction)
while len(self._entry_directions) > max_len:
self._entry_directions.pop()
if self._handle_stops(candle):
return
current = self._get_direction(self._entry_directions, entry_shift)
previous = self._get_direction(self._entry_directions, entry_shift + 1)
if current is None or previous is None:
return
flip_to_up = self._is_flip_to(1, current, previous)
flip_to_down = self._is_flip_to(-1, current, previous)
close_long = self.Position > 0 and ((self.CloseBuyOnTrendFlip and self._trend_direction < 0) or
(self.CloseBuyOnEntryFlip and current < 0))
close_short = self.Position < 0 and ((self.CloseSellOnTrendFlip and self._trend_direction > 0) or
(self.CloseSellOnEntryFlip and current > 0))
if close_long:
self.SellMarket()
self._reset_stops()
if close_short:
self.BuyMarket()
self._reset_stops()
if self.EnableBuyEntries and self._trend_direction > 0 and flip_to_up and self.Position <= 0:
self.BuyMarket()
self._update_stops(float(candle.ClosePrice), True)
elif self.EnableSellEntries and self._trend_direction < 0 and flip_to_down and self.Position >= 0:
self.SellMarket()
self._update_stops(float(candle.ClosePrice), False)
def _handle_stops(self, candle):
if self.Position > 0:
if self._stop_loss_price is not None and float(candle.LowPrice) <= self._stop_loss_price:
self.SellMarket()
self._reset_stops()
return True
if self._take_profit_price is not None and float(candle.HighPrice) >= self._take_profit_price:
self.SellMarket()
self._reset_stops()
return True
elif self.Position < 0:
if self._stop_loss_price is not None and float(candle.HighPrice) >= self._stop_loss_price:
self.BuyMarket()
self._reset_stops()
return True
if self._take_profit_price is not None and float(candle.LowPrice) <= self._take_profit_price:
self.BuyMarket()
self._reset_stops()
return True
return False
def _update_stops(self, entry_price, is_long):
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 0.0
if step <= 0.0:
self._stop_loss_price = None
self._take_profit_price = None
return
sl_pts = float(self.StopLossPoints)
tp_pts = float(self.TakeProfitPoints)
if sl_pts > 0.0:
self._stop_loss_price = entry_price - sl_pts * step if is_long else entry_price + sl_pts * step
else:
self._stop_loss_price = None
if tp_pts > 0.0:
self._take_profit_price = entry_price + tp_pts * step if is_long else entry_price - tp_pts * step
else:
self._take_profit_price = None
def _reset_stops(self):
self._stop_loss_price = None
self._take_profit_price = None
def _get_direction(self, history, offset):
if len(history) > offset:
return history[offset]
return None
def _is_flip_to(self, target_direction, current, previous):
if current != target_direction:
return False
entry_mode = int(self.EntryMode)
if entry_mode == MODE_NEWWAY:
return previous != target_direction
else:
return previous == -target_direction
def OnReseted(self):
super(kolier_super_trend_x2_strategy, self).OnReseted()
self._trend_directions = []
self._entry_directions = []
self._trend_direction = 0
self._stop_loss_price = None
self._take_profit_price = None
def CreateClone(self):
return kolier_super_trend_x2_strategy()