Стратегия Trailing Stop And Take
Обзор
Trailing Stop And Take Strategy — это адаптация эксперта из MQL/19963 на платформу StockSharp. Стратегия сосредоточена на управлении уже открытыми позициями: после входа выставляются стартовые StopLoss и TakeProfit, а затем уровни подтягиваются вслед за ценой. Корректировки учитывают настраиваемый минимальный шаг, защиту уровня безубыточности и возможность запретить работу в зоне убытка.
Стратегия работает с одним инструментом и анализирует только закрытые свечи. При отсутствии позиций она открывает сделку в направлении тела последней свечи (бычья — покупка, медвежья — продажа), что повторяет тестовый режим из оригинального MQL-робота и обеспечивает постоянные сделки для модуля сопровождения.
Последовательность работы
- Подписка на выбранный тип свечей и обработка только закрытых баров.
- При отсутствии позиции выполняется вход по направлению свечи (с учётом фильтра по типу позиции).
- Для новой сделки рассчитываются стартовые стоп и тейк при помощи
InitialStopLossPoints/InitialTakeProfitPoints; если значения равны нулю, используются параметры трейлинга. - На каждом закрытии свечи вычисляются новые уровни сопровождения:
- Стоп подтягивается ближе к цене лишь после движения на величину трейлингового шага.
- Тейк подтягивается после отката минимум на величину шага.
- При выключенном
AllowTrailingLossуровни не смещаются ниже границы безубыточности.
- При достижении ценой стопа или тейка позиция закрывается рыночной заявкой и все уровни сбрасываются.
Логика сопровождения
Длинные позиции
- Стартовый стоп располагается не ближе, чем на
SpreadMultiplier * PriceStepот цены входа. - Стартовый тейк находится не ближе того же минимального расстояния выше цены входа.
- Трейлинг-стоп следует за ценой, опираясь на
TrailingStopLossPoints, и учитывает шаг и ограничение по безубыточности. - Трейлинг-тейк подтягивается при откатах и не опускается ниже безубыточности, если запрет на работу в убытке включён.
Короткие позиции
- Стартовый стоп размещается над ценой входа на расстоянии не меньше, чем множитель спреда.
- Стартовый тейк располагается ниже входа с тем же минимальным зазором.
- Трейлинг-стоп снижается вместе с ценой, но не поднимается выше уровня безубыточности при запрете работы в убытке.
- Трейлинг-тейк поднимается к цене при откатах и ограничивается безубыточностью при необходимости.
Параметры
| Параметр | Описание |
|---|---|
CandleType |
Тип свечей, используемых в расчётах. |
Volume |
Объём сделок для входа и выхода. |
PositionType |
Управление только лонгами, только шортами или обоими направлениями. |
InitialStopLossPoints |
Размер стартового стопа в пунктах (при 0 берётся расстояние трейлинга). |
InitialTakeProfitPoints |
Размер стартового тейка в пунктах (при 0 берётся расстояние трейлинга). |
TrailingStopLossPoints |
Дистанция между ценой и трейлинг-стопом. |
TrailingTakeProfitPoints |
Дистанция между ценой и трейлинг-тейком. |
TrailingStepPoints |
Минимальное движение цены для обновления уровней. |
AllowTrailingLoss |
Разрешение сопровождать позицию в зоне убытка. |
BreakevenPoints |
Смещение в пунктах для расчёта уровня безубыточности. |
SpreadMultiplier |
Множитель минимальной дистанции (аналог MQL StopLevel). |
Примечания
- Закрытие по стопу или тейку выполняется рыночными заявками, что упрощает реализацию и повторяет логику модификации позиций в MQL.
SpreadMultiplierимитирует ограничение брокера на минимальное расстояние до цены, подбирайте значение под ваш инструмент.- По заданию реализована только C# версия, Python-вариант не создавался.
- При необходимости можно отключить встроенные входы и использовать стратегию как модуль сопровождения собственных сигналов.
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>
/// Strategy that manages trailing stop-loss and take-profit levels similar to the original MQL Expert Advisor.
/// </summary>
public class TrailingStopAndTakeStrategy : Strategy
{
public enum TrailingPositionTypes
{
All,
Long,
Short,
}
private readonly StrategyParam<decimal> _epsilon;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<TrailingPositionTypes> _positionType;
private readonly StrategyParam<decimal> _initialStopLossPoints;
private readonly StrategyParam<decimal> _initialTakeProfitPoints;
private readonly StrategyParam<decimal> _trailingStopLossPoints;
private readonly StrategyParam<decimal> _trailingTakeProfitPoints;
private readonly StrategyParam<decimal> _trailingStepPoints;
private readonly StrategyParam<bool> _allowTrailingLoss;
private readonly StrategyParam<decimal> _breakevenPoints;
private readonly StrategyParam<int> _spreadMultiplier;
private decimal _priceStep;
private decimal _previousPosition;
private decimal? _longStop;
private decimal? _longTake;
private decimal? _shortStop;
private decimal? _shortTake;
private decimal _entryPrice;
/// <summary>
/// Initializes a new instance of the <see cref="TrailingStopAndTakeStrategy"/>.
/// </summary>
public TrailingStopAndTakeStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromDays(1).TimeFrame())
.SetDisplay("Candle Type", "Candle aggregation used for trailing decisions", "General");
_positionType = Param(nameof(PositionType), TrailingPositionTypes.All)
.SetDisplay("Position Filter", "Positions managed by the trailing engine", "Trading");
_initialStopLossPoints = Param(nameof(InitialStopLossPoints), 400m)
.SetRange(0m, 10000m)
.SetDisplay("Initial Stop", "Initial stop-loss size in price points", "Risk")
;
_initialTakeProfitPoints = Param(nameof(InitialTakeProfitPoints), 400m)
.SetRange(0m, 10000m)
.SetDisplay("Initial Take", "Initial take-profit size in price points", "Risk")
;
_trailingStopLossPoints = Param(nameof(TrailingStopLossPoints), 200m)
.SetRange(0m, 10000m)
.SetDisplay("Trailing Stop", "Trailing stop distance in price points", "Risk")
;
_trailingTakeProfitPoints = Param(nameof(TrailingTakeProfitPoints), 200m)
.SetRange(0m, 10000m)
.SetDisplay("Trailing Take", "Trailing take-profit distance in price points", "Risk")
;
_trailingStepPoints = Param(nameof(TrailingStepPoints), 10m)
.SetRange(0m, 1000m)
.SetDisplay("Trailing Step", "Minimum movement required before adjusting targets", "Risk");
_epsilon = Param(nameof(Epsilon), 0.0000001m)
.SetGreaterThanZero()
.SetDisplay("Trailing Epsilon", "Minimum trailing step size", "Risk");
_allowTrailingLoss = Param(nameof(AllowTrailingLoss), false)
.SetDisplay("Trail In Loss", "Allow trailing while position is not yet profitable", "Risk");
_breakevenPoints = Param(nameof(BreakevenPoints), 6m)
.SetRange(0m, 1000m)
.SetDisplay("Breakeven Points", "Profit offset used for breakeven protection", "Risk");
_spreadMultiplier = Param(nameof(SpreadMultiplier), 2)
.SetRange(1, 20)
.SetDisplay("Spread Multiplier", "Multiplier applied to minimal stop distance", "Execution");
}
/// <summary>
/// Candle type used by the strategy.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Position filter managed by the trailing logic.
/// </summary>
public TrailingPositionTypes PositionType
{
get => _positionType.Value;
set => _positionType.Value = value;
}
/// <summary>
/// Initial stop-loss size expressed in price points.
/// </summary>
public decimal InitialStopLossPoints
{
get => _initialStopLossPoints.Value;
set => _initialStopLossPoints.Value = value;
}
/// <summary>
/// Initial take-profit size expressed in price points.
/// </summary>
public decimal InitialTakeProfitPoints
{
get => _initialTakeProfitPoints.Value;
set => _initialTakeProfitPoints.Value = value;
}
/// <summary>
/// Trailing stop distance expressed in price points.
/// </summary>
public decimal TrailingStopLossPoints
{
get => _trailingStopLossPoints.Value;
set => _trailingStopLossPoints.Value = value;
}
/// <summary>
/// Trailing take-profit distance expressed in price points.
/// </summary>
public decimal TrailingTakeProfitPoints
{
get => _trailingTakeProfitPoints.Value;
set => _trailingTakeProfitPoints.Value = value;
}
/// <summary>
/// Minimum movement required before stops or targets are updated.
/// </summary>
public decimal TrailingStepPoints
{
get => _trailingStepPoints.Value;
set => _trailingStepPoints.Value = value;
}
/// <summary>
/// Minimum trailing step size used as a floor.
/// </summary>
public decimal Epsilon
{
get => _epsilon.Value;
set => _epsilon.Value = value;
}
/// <summary>
/// Enables trailing adjustments while the position remains in the loss zone.
/// </summary>
public bool AllowTrailingLoss
{
get => _allowTrailingLoss.Value;
set => _allowTrailingLoss.Value = value;
}
/// <summary>
/// Profit offset in points used to define the breakeven level.
/// </summary>
public decimal BreakevenPoints
{
get => _breakevenPoints.Value;
set => _breakevenPoints.Value = value;
}
/// <summary>
/// Multiplier applied to the minimal stop distance approximation.
/// </summary>
public int SpreadMultiplier
{
get => _spreadMultiplier.Value;
set => _spreadMultiplier.Value = value;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, CandleType);
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_priceStep = Security?.PriceStep ?? 1m;
if (_priceStep <= 0m)
_priceStep = 1m;
_previousPosition = 0m;
ResetLevels();
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_priceStep = 0m;
_previousPosition = 0m;
_entryPrice = 0m;
ResetLevels();
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
// Handle long positions first.
if (Position > 0m)
{
if (PositionType == TrailingPositionTypes.Short)
{
ResetLongLevels();
}
else
{
if (_previousPosition <= 0m)
ResetShortLevels();
EnsureLongInitialized();
UpdateLongTrailing(candle);
ManageLongExits(candle);
}
}
else if (Position < 0m)
{
if (PositionType == TrailingPositionTypes.Long)
{
ResetShortLevels();
}
else
{
if (_previousPosition >= 0m)
ResetLongLevels();
EnsureShortInitialized();
UpdateShortTrailing(candle);
ManageShortExits(candle);
}
}
else
{
ResetLevels();
}
// Try to open a new position once flat.
TryEnter(candle);
_previousPosition = Position;
}
private void TryEnter(ICandleMessage candle)
{
if (Position != 0m || Volume <= 0m)
return;
// Simple directional entry mirroring the tester behavior from the MQL script.
if (PositionType == TrailingPositionTypes.Long)
{
if (candle.ClosePrice > candle.OpenPrice)
BuyMarket(Volume);
}
else if (PositionType == TrailingPositionTypes.Short)
{
if (candle.ClosePrice < candle.OpenPrice)
SellMarket(Volume);
}
else
{
if (candle.ClosePrice > candle.OpenPrice)
BuyMarket(Volume);
else if (candle.ClosePrice < candle.OpenPrice)
SellMarket(Volume);
}
}
private void EnsureLongInitialized()
{
if (Position <= 0m)
return;
var entryPrice = _entryPrice;
if (entryPrice <= 0m)
return;
var minDistance = GetMinStopDistance();
if (_longStop == null)
{
var points = InitialStopLossPoints > 0m
? InitialStopLossPoints
: TrailingStopLossPoints > 0m ? TrailingStopLossPoints : 0m;
if (points > 0m)
{
var candidate = entryPrice - points * _priceStep;
var minAllowed = entryPrice - minDistance;
_longStop = Math.Min(candidate, minAllowed);
}
}
if (_longTake == null)
{
var points = InitialTakeProfitPoints > 0m
? InitialTakeProfitPoints
: TrailingTakeProfitPoints > 0m ? TrailingTakeProfitPoints : 0m;
if (points > 0m)
{
var candidate = entryPrice + points * _priceStep;
var minAllowed = entryPrice + minDistance;
_longTake = Math.Max(candidate, minAllowed);
}
}
}
private void EnsureShortInitialized()
{
if (Position >= 0m)
return;
var entryPrice = _entryPrice;
if (entryPrice <= 0m)
return;
var minDistance = GetMinStopDistance();
if (_shortStop == null)
{
var points = InitialStopLossPoints > 0m
? InitialStopLossPoints
: TrailingStopLossPoints > 0m ? TrailingStopLossPoints : 0m;
if (points > 0m)
{
var candidate = entryPrice + points * _priceStep;
var minAllowed = entryPrice + minDistance;
_shortStop = Math.Max(candidate, minAllowed);
}
}
if (_shortTake == null)
{
var points = InitialTakeProfitPoints > 0m
? InitialTakeProfitPoints
: TrailingTakeProfitPoints > 0m ? TrailingTakeProfitPoints : 0m;
if (points > 0m)
{
var candidate = entryPrice - points * _priceStep;
var minAllowed = entryPrice - minDistance;
_shortTake = Math.Min(candidate, minAllowed);
}
}
}
private void UpdateLongTrailing(ICandleMessage candle)
{
var entryPrice = _entryPrice;
if (entryPrice <= 0m)
return;
var breakeven = entryPrice + BreakevenPoints * _priceStep;
var trailingStep = Math.Max(TrailingStepPoints * _priceStep, Epsilon);
var minDistance = GetMinStopDistance();
if (TrailingStopLossPoints > 0m)
{
var candidate = candle.ClosePrice - TrailingStopLossPoints * _priceStep;
var minAllowed = candle.ClosePrice - minDistance;
var newStop = Math.Min(candidate, minAllowed);
if (!AllowTrailingLoss && newStop < breakeven)
{
// Skip moving the stop into the loss area when disabled.
}
else if (_longStop == null || newStop > _longStop.Value + trailingStep)
{
_longStop = newStop;
}
}
if (TrailingTakeProfitPoints > 0m)
{
var candidate = candle.ClosePrice + TrailingTakeProfitPoints * _priceStep;
var minAllowed = candle.ClosePrice + minDistance;
var newTake = Math.Max(candidate, minAllowed);
if (!AllowTrailingLoss && newTake < breakeven)
newTake = breakeven;
if (_longTake == null || newTake < _longTake.Value - trailingStep)
_longTake = newTake;
}
}
private void UpdateShortTrailing(ICandleMessage candle)
{
var entryPrice = _entryPrice;
if (entryPrice <= 0m)
return;
var breakeven = entryPrice - BreakevenPoints * _priceStep;
var trailingStep = Math.Max(TrailingStepPoints * _priceStep, Epsilon);
var minDistance = GetMinStopDistance();
if (TrailingStopLossPoints > 0m)
{
var candidate = candle.ClosePrice + TrailingStopLossPoints * _priceStep;
var minAllowed = candle.ClosePrice + minDistance;
var newStop = Math.Max(candidate, minAllowed);
if (!AllowTrailingLoss && newStop > breakeven)
{
// Skip moving the stop into the loss area when disabled.
}
else if (_shortStop == null || newStop < _shortStop.Value - trailingStep)
{
_shortStop = newStop;
}
}
if (TrailingTakeProfitPoints > 0m)
{
var candidate = candle.ClosePrice - TrailingTakeProfitPoints * _priceStep;
var minAllowed = candle.ClosePrice - minDistance;
var newTake = Math.Min(candidate, minAllowed);
if (!AllowTrailingLoss && newTake > breakeven)
newTake = breakeven;
if (_shortTake == null || newTake > _shortTake.Value + trailingStep)
_shortTake = newTake;
}
}
private void ManageLongExits(ICandleMessage candle)
{
if (_longStop.HasValue && candle.LowPrice <= _longStop.Value)
{
SellMarket(Position);
ResetLongLevels();
return;
}
if (_longTake.HasValue && candle.HighPrice >= _longTake.Value)
{
SellMarket(Position);
ResetLongLevels();
}
}
private void ManageShortExits(ICandleMessage candle)
{
if (_shortStop.HasValue && candle.HighPrice >= _shortStop.Value)
{
BuyMarket(Math.Abs(Position));
ResetShortLevels();
return;
}
if (_shortTake.HasValue && candle.LowPrice <= _shortTake.Value)
{
BuyMarket(Math.Abs(Position));
ResetShortLevels();
}
}
private decimal GetMinStopDistance()
{
var multiplier = SpreadMultiplier < 1 ? 1 : SpreadMultiplier;
return _priceStep * multiplier;
}
private void ResetLevels()
{
ResetLongLevels();
ResetShortLevels();
}
private void ResetLongLevels()
{
_longStop = null;
_longTake = null;
}
private void ResetShortLevels()
{
_shortStop = null;
_shortTake = null;
}
/// <inheritdoc />
protected override void OnOwnTradeReceived(MyTrade trade)
{
base.OnOwnTradeReceived(trade);
if (trade?.Trade == null) return;
if (Position != 0 && _entryPrice == 0m)
_entryPrice = trade.Trade.Price;
if (Position == 0)
_entryPrice = 0m;
}
}
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
from StockSharp.Algo.Strategies import Strategy
class trailing_stop_and_take_strategy(Strategy):
POS_ALL = 0
POS_LONG = 1
POS_SHORT = 2
def __init__(self):
super(trailing_stop_and_take_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromDays(1)))
self._position_type = self.Param("PositionType", self.POS_ALL)
self._initial_stop_loss_points = self.Param("InitialStopLossPoints", 400.0)
self._initial_take_profit_points = self.Param("InitialTakeProfitPoints", 400.0)
self._trailing_stop_loss_points = self.Param("TrailingStopLossPoints", 200.0)
self._trailing_take_profit_points = self.Param("TrailingTakeProfitPoints", 200.0)
self._trailing_step_points = self.Param("TrailingStepPoints", 10.0)
self._epsilon = self.Param("Epsilon", 0.0000001)
self._allow_trailing_loss = self.Param("AllowTrailingLoss", False)
self._breakeven_points = self.Param("BreakevenPoints", 6.0)
self._spread_multiplier = self.Param("SpreadMultiplier", 2)
self._price_step = 1.0
self._previous_position = 0.0
self._long_stop = None
self._long_take = None
self._short_stop = None
self._short_take = None
self._entry_price = 0.0
@property
def CandleType(self):
return self._candle_type.Value
@property
def PositionType(self):
return self._position_type.Value
@property
def InitialStopLossPoints(self):
return self._initial_stop_loss_points.Value
@property
def InitialTakeProfitPoints(self):
return self._initial_take_profit_points.Value
@property
def TrailingStopLossPoints(self):
return self._trailing_stop_loss_points.Value
@property
def TrailingTakeProfitPoints(self):
return self._trailing_take_profit_points.Value
@property
def TrailingStepPoints(self):
return self._trailing_step_points.Value
@property
def Epsilon(self):
return self._epsilon.Value
@property
def AllowTrailingLoss(self):
return self._allow_trailing_loss.Value
@property
def BreakevenPoints(self):
return self._breakeven_points.Value
@property
def SpreadMultiplier(self):
return self._spread_multiplier.Value
def OnStarted2(self, time):
super(trailing_stop_and_take_strategy, self).OnStarted2(time)
sec = self.Security
self._price_step = float(sec.PriceStep) if sec is not None and sec.PriceStep is not None and float(sec.PriceStep) > 0 else 1.0
self._previous_position = 0.0
self._reset_levels()
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
pos = float(self.Position)
if pos > 0:
if self.PositionType == self.POS_SHORT:
self._reset_long_levels()
else:
if self._previous_position <= 0:
self._reset_short_levels()
self._ensure_long_initialized()
self._update_long_trailing(candle)
self._manage_long_exits(candle)
elif pos < 0:
if self.PositionType == self.POS_LONG:
self._reset_short_levels()
else:
if self._previous_position >= 0:
self._reset_long_levels()
self._ensure_short_initialized()
self._update_short_trailing(candle)
self._manage_short_exits(candle)
else:
self._reset_levels()
self._entry_price = 0.0
self._try_enter(candle)
self._previous_position = float(self.Position)
def _try_enter(self, candle):
pos = float(self.Position)
vol = float(self.Volume)
if pos != 0 or vol <= 0:
return
close = float(candle.ClosePrice)
if self.PositionType == self.POS_LONG:
if close > float(candle.OpenPrice):
self.BuyMarket(vol)
self._entry_price = close
elif self.PositionType == self.POS_SHORT:
if close < float(candle.OpenPrice):
self.SellMarket(vol)
self._entry_price = close
else:
if close > float(candle.OpenPrice):
self.BuyMarket(vol)
self._entry_price = close
elif close < float(candle.OpenPrice):
self.SellMarket(vol)
self._entry_price = close
def _ensure_long_initialized(self):
if float(self.Position) <= 0:
return
entry = self._entry_price
if entry <= 0:
return
min_dist = self._get_min_stop_distance()
if self._long_stop is None:
pts = float(self.InitialStopLossPoints) if float(self.InitialStopLossPoints) > 0 else (float(self.TrailingStopLossPoints) if float(self.TrailingStopLossPoints) > 0 else 0.0)
if pts > 0:
candidate = entry - pts * self._price_step
min_allowed = entry - min_dist
self._long_stop = min(candidate, min_allowed)
if self._long_take is None:
pts = float(self.InitialTakeProfitPoints) if float(self.InitialTakeProfitPoints) > 0 else (float(self.TrailingTakeProfitPoints) if float(self.TrailingTakeProfitPoints) > 0 else 0.0)
if pts > 0:
candidate = entry + pts * self._price_step
min_allowed = entry + min_dist
self._long_take = max(candidate, min_allowed)
def _ensure_short_initialized(self):
if float(self.Position) >= 0:
return
entry = self._entry_price
if entry <= 0:
return
min_dist = self._get_min_stop_distance()
if self._short_stop is None:
pts = float(self.InitialStopLossPoints) if float(self.InitialStopLossPoints) > 0 else (float(self.TrailingStopLossPoints) if float(self.TrailingStopLossPoints) > 0 else 0.0)
if pts > 0:
candidate = entry + pts * self._price_step
min_allowed = entry + min_dist
self._short_stop = max(candidate, min_allowed)
if self._short_take is None:
pts = float(self.InitialTakeProfitPoints) if float(self.InitialTakeProfitPoints) > 0 else (float(self.TrailingTakeProfitPoints) if float(self.TrailingTakeProfitPoints) > 0 else 0.0)
if pts > 0:
candidate = entry - pts * self._price_step
min_allowed = entry - min_dist
self._short_take = min(candidate, min_allowed)
def _update_long_trailing(self, candle):
entry = self._entry_price
if entry <= 0:
return
breakeven = entry + float(self.BreakevenPoints) * self._price_step
trailing_step = max(float(self.TrailingStepPoints) * self._price_step, float(self.Epsilon))
min_dist = self._get_min_stop_distance()
if float(self.TrailingStopLossPoints) > 0:
candidate = float(candle.ClosePrice) - float(self.TrailingStopLossPoints) * self._price_step
min_allowed = float(candle.ClosePrice) - min_dist
new_stop = min(candidate, min_allowed)
if not self.AllowTrailingLoss and new_stop < breakeven:
pass
elif self._long_stop is None or new_stop > self._long_stop + trailing_step:
self._long_stop = new_stop
if float(self.TrailingTakeProfitPoints) > 0:
candidate = float(candle.ClosePrice) + float(self.TrailingTakeProfitPoints) * self._price_step
min_allowed = float(candle.ClosePrice) + min_dist
new_take = max(candidate, min_allowed)
if not self.AllowTrailingLoss and new_take < breakeven:
new_take = breakeven
if self._long_take is None or new_take < self._long_take - trailing_step:
self._long_take = new_take
def _update_short_trailing(self, candle):
entry = self._entry_price
if entry <= 0:
return
breakeven = entry - float(self.BreakevenPoints) * self._price_step
trailing_step = max(float(self.TrailingStepPoints) * self._price_step, float(self.Epsilon))
min_dist = self._get_min_stop_distance()
if float(self.TrailingStopLossPoints) > 0:
candidate = float(candle.ClosePrice) + float(self.TrailingStopLossPoints) * self._price_step
min_allowed = float(candle.ClosePrice) + min_dist
new_stop = max(candidate, min_allowed)
if not self.AllowTrailingLoss and new_stop > breakeven:
pass
elif self._short_stop is None or new_stop < self._short_stop - trailing_step:
self._short_stop = new_stop
if float(self.TrailingTakeProfitPoints) > 0:
candidate = float(candle.ClosePrice) - float(self.TrailingTakeProfitPoints) * self._price_step
min_allowed = float(candle.ClosePrice) - min_dist
new_take = min(candidate, min_allowed)
if not self.AllowTrailingLoss and new_take > breakeven:
new_take = breakeven
if self._short_take is None or new_take > self._short_take + trailing_step:
self._short_take = new_take
def _manage_long_exits(self, candle):
if self._long_stop is not None and float(candle.LowPrice) <= self._long_stop:
self.SellMarket(float(self.Position))
self._reset_long_levels()
return
if self._long_take is not None and float(candle.HighPrice) >= self._long_take:
self.SellMarket(float(self.Position))
self._reset_long_levels()
def _manage_short_exits(self, candle):
if self._short_stop is not None and float(candle.HighPrice) >= self._short_stop:
self.BuyMarket(abs(float(self.Position)))
self._reset_short_levels()
return
if self._short_take is not None and float(candle.LowPrice) <= self._short_take:
self.BuyMarket(abs(float(self.Position)))
self._reset_short_levels()
def _get_min_stop_distance(self):
mult = self.SpreadMultiplier if self.SpreadMultiplier >= 1 else 1
return self._price_step * mult
def _reset_levels(self):
self._reset_long_levels()
self._reset_short_levels()
def _reset_long_levels(self):
self._long_stop = None
self._long_take = None
def _reset_short_levels(self):
self._short_stop = None
self._short_take = None
def OnOwnTradeReceived(self, trade):
super(trailing_stop_and_take_strategy, self).OnOwnTradeReceived(trade)
if trade is None or trade.Trade is None:
return
pos = float(self.Position)
if pos != 0 and self._entry_price == 0:
self._entry_price = float(trade.Trade.Price)
if pos == 0:
self._entry_price = 0.0
def OnReseted(self):
super(trailing_stop_and_take_strategy, self).OnReseted()
self._price_step = 0.0
self._previous_position = 0.0
self._entry_price = 0.0
self._reset_levels()
def CreateClone(self):
return trailing_stop_and_take_strategy()