Estratégia Kolier SuperTrend X2
Visão geral
A estratégia reproduz o expert original do MetaTrader combinando dois filtros SuperTrend que operam em períodos distintos. O SuperTrend do período superior define o viés dominante do mercado, enquanto o SuperTrend do período inferior busca rompimentos sincronizados para acionar entradas. O port para StockSharp usa vinculações de API de alto nível, de modo que os indicadores recebem atualizações de candles diretamente e mantêm seu próprio histórico.
Lógica de negociação
- Filtro de tendência: o SuperTrend do período superior deve confirmar uma tendência de alta ou baixa. O atraso de confirmação é controlado por
TrendSignalShift, e o modo (TrendMode) define se uma única barra (NewWay) ou duas barras consecutivas (todos os outros modos) são necessárias. - Sinais de entrada: o SuperTrend do período inferior aguarda uma virada de direção alinhada com o filtro de tendência atual.
EntrySignalShiftatrasa o sinal para depender de barras completamente fechadas, eEntryModecontrola se a estratégia reage imediatamente (NewWay) ou apenas após uma reversão confirmada (outros modos). - Entrada comprado: permitida quando
EnableBuyEntriesétrue, o filtro de tendência está em alta e o SuperTrend de entrada vira para alta de acordo com o modo selecionado. A exposição vendida existente é encerrada primeiro; depois, abre-se uma posição comprada com volumeVolume + |Position|. - Entrada vendido: permitida quando
EnableSellEntriesétrue, o filtro de tendência está em baixa e o SuperTrend de entrada vira para baixa. A exposição comprada existente é coberta antes de entrar vendido. - Saídas:
- A reversão no período superior fecha comprados (
CloseBuyOnTrendFlip) ou vendidos (CloseSellOnTrendFlip). - Viradas no período de entrada também podem fechar posições quando
CloseBuyOnEntryFlip/CloseSellOnEntryFlipestão habilitados. - Stops fixos opcionais (
StopLossPoints,TakeProfitPoints) são aplicados como múltiplos deSecurity.PriceStep.
- A reversão no período superior fecha comprados (
Indicadores
- Duas instâncias do StockSharp
SuperTrend(uma para o período de tendência, outra para entradas).
Parâmetros
TrendCandleType– período para o filtro de tendência.EntryCandleType– período para os sinais de entrada.TrendAtrPeriod,TrendAtrMultiplier– configurações ATR para o SuperTrend de tendência.EntryAtrPeriod,EntryAtrMultiplier– configurações ATR para o SuperTrend de entrada.TrendMode,EntryMode– modos de confirmação:NewWayreage após uma barra; outros modos exigem duas barras consecutivas (Visual e ExpertSignal se comportam como o SuperTrend clássico neste port).TrendSignalShift,EntrySignalShift– número de barras fechadas a aguardar antes de usar os valores do indicador.EnableBuyEntries,EnableSellEntries– habilitar operações compradas/vendidas.CloseBuyOnTrendFlip,CloseSellOnTrendFlip– sair com sinais opostos do filtro de tendência.CloseBuyOnEntryFlip,CloseSellOnEntryFlip– sair com sinais opostos do período de entrada.StopLossPoints,TakeProfitPoints– distância em passos de preço para ordens de proteção (0 para desabilitar).Volume– volume base para novas posições.Slippage– parâmetro de marcador de posição mantido por compatibilidade com o expert original.
Notas
- O port foca no fluxo de trabalho de alto nível do StockSharp: candles são assinados via
SubscribeCandles, indicadores são vinculados viaBindEx, e a estratégia mantém apenas estado mínimo (direção de tendência, níveis de stop). StartProtection()é invocado uma vez para ativar o auxiliar padrão de proteção de posições do 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()