Trailing Stop And Take Strategy
Overview
The Trailing Stop And Take Strategy is a direct StockSharp adaptation of the MetaTrader expert advisor from MQL/19963. It focuses on active trade management: once a position is open, the strategy attaches initial stop-loss and take-profit levels and then trails both levels as price moves. Trailing adjustments respect configurable minimum step sizes, breakeven protection, and the option to avoid trailing while a trade is still losing.
The strategy operates on a single security using finished candles. When the strategy is flat it opens a position in the direction of the most recent candle body (bullish closes lead to longs, bearish closes lead to shorts). This mirrors the original test behavior used by the MQL script and provides a continuous flow of positions for the trailing engine to manage.
How It Works
- Subscribe to the configured candle type and process only finished candles.
- When no position is open, enter long on bullish candles or short on bearish candles (respecting the position type filter).
- On a new position, initialize stop-loss and take-profit distances using
InitialStopLossPoints/InitialTakeProfitPoints. If those are zero, the trailing distances are used instead. - On each candle close, compute updated trailing targets:
- Stops move closer to price only after the market advances by the trailing step.
- Take profits move closer when price retraces by at least the trailing step.
- Breakeven protection prevents moving levels into a loss zone when
AllowTrailingLossis disabled.
- When price crosses a trailing stop or take-profit level, exit via market order and reset all stored levels.
Trailing Logic
Long Positions
- Initial stop is clamped to at least
SpreadMultiplier * PriceStepaway from entry. - Initial take profit is positioned at least the same minimum distance above entry.
- Trailing stop follows the close price downward by
TrailingStopLossPointswhile respecting the trailing step and optional breakeven filter. - Trailing take profit tightens when price retraces, never moving below the breakeven level when trailing losses are disallowed.
Short Positions
- Initial stop is set above entry, no closer than the spread multiplier distance.
- Initial take profit starts below entry with the same minimum distance rule.
- Trailing stop lowers as price falls, but will not move higher than breakeven unless loss trailing is permitted.
- Trailing take profit rises toward price on retracements, clamped to breakeven when needed.
Parameters
| Parameter | Description |
|---|---|
CandleType |
Candle aggregation used for price evaluation. |
Volume |
Default order volume for entries and exits. |
PositionType |
Restricts the engine to manage long positions, short positions, or both. |
InitialStopLossPoints |
Initial stop-loss size in price points (uses trailing distance if zero). |
InitialTakeProfitPoints |
Initial take-profit size in price points (uses trailing distance if zero). |
TrailingStopLossPoints |
Distance between price and trailing stop. |
TrailingTakeProfitPoints |
Distance between price and trailing take profit. |
TrailingStepPoints |
Minimum movement in points required before adjusting stops or targets. |
AllowTrailingLoss |
Enables trailing while the trade is still below breakeven. |
BreakevenPoints |
Offset in points added to the entry price to form the breakeven barrier. |
SpreadMultiplier |
Multiplier for the minimal stop distance approximation (simulates the MQL StopLevel). |
Notes
- Stops and targets are executed with market orders when triggered, which keeps the implementation simple and mirrors the original stop modifications.
SpreadMultiplierapproximates the MQL behavior where stop levels cannot be placed closer than the current spread. Adjust this value to match the execution venue.- The strategy intentionally avoids a Python version and focuses solely on the C# implementation, as requested.
- Consider combining the trailing engine with your own entry filter by disabling the built-in entries and injecting external orders if required.
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()