Drag SL/TP Manager Strategy
This strategy automatically places stop-loss and take-profit orders at a fixed distance from the executed trade price. It is useful when manual positions should be protected immediately after entry.
Parameters
- Auto Set SL (
bool): enable automatic stop-loss placement. - SL Points (
decimal): stop-loss distance in price steps. - Auto Set TP (
bool): enable automatic take-profit placement. - TP Points (
decimal): take-profit distance in price steps.
Behavior
When the strategy starts it calls StartProtection with the selected distances. Any position opened while the strategy is running will immediately receive the corresponding protective orders. The distances are measured in price steps (Security.PriceStep).
The strategy itself does not generate trade signals; it simply manages protective orders for positions opened manually or by other strategies.
Notes
- Designed for high-level API usage.
- Only the finished candle state should trigger trading actions in extended versions.
- No graphical dragging feature from the original MQL script is implemented.
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy that uses EMA crossover for entries with stop-loss and take-profit.
/// </summary>
public class DragSlTpStrategy : Strategy
{
private readonly StrategyParam<decimal> _slPoints;
private readonly StrategyParam<decimal> _tpPoints;
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevFast;
private decimal _prevSlow;
private bool _isInitialized;
private decimal _entryPrice;
public decimal SlPoints { get => _slPoints.Value; set => _slPoints.Value = value; }
public decimal TpPoints { get => _tpPoints.Value; set => _tpPoints.Value = value; }
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public DragSlTpStrategy()
{
_slPoints = Param(nameof(SlPoints), 500m)
.SetGreaterThanZero()
.SetDisplay("SL Points", "Stop-loss distance", "Risk");
_tpPoints = Param(nameof(TpPoints), 1000m)
.SetGreaterThanZero()
.SetDisplay("TP Points", "Take-profit distance", "Risk");
_fastPeriod = Param(nameof(FastPeriod), 10)
.SetGreaterThanZero()
.SetDisplay("Fast Period", "Fast EMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 30)
.SetGreaterThanZero()
.SetDisplay("Slow Period", "Slow EMA period", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
protected override void OnReseted()
{
base.OnReseted();
_prevFast = 0;
_prevSlow = 0;
_isInitialized = false;
_entryPrice = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var fastEma = new ExponentialMovingAverage { Length = FastPeriod };
var slowEma = new ExponentialMovingAverage { Length = SlowPeriod };
SubscribeCandles(CandleType)
.Bind(fastEma, slowEma, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)
{
if (candle.State != CandleStates.Finished) return;
if (!_isInitialized)
{
_prevFast = fast;
_prevSlow = slow;
_isInitialized = true;
return;
}
var crossUp = _prevFast <= _prevSlow && fast > slow;
var crossDown = _prevFast >= _prevSlow && fast < slow;
_prevFast = fast;
_prevSlow = slow;
if (Position == 0)
{
if (crossUp)
{
BuyMarket();
_entryPrice = candle.ClosePrice;
}
else if (crossDown)
{
SellMarket();
_entryPrice = candle.ClosePrice;
}
}
else if (Position > 0)
{
var price = candle.ClosePrice;
if (price - _entryPrice >= TpPoints || _entryPrice - price >= SlPoints || crossDown)
{
SellMarket();
if (crossDown)
{
SellMarket();
_entryPrice = candle.ClosePrice;
}
}
}
else if (Position < 0)
{
var price = candle.ClosePrice;
if (_entryPrice - price >= TpPoints || price - _entryPrice >= SlPoints || crossUp)
{
BuyMarket();
if (crossUp)
{
BuyMarket();
_entryPrice = candle.ClosePrice;
}
}
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class drag_sl_tp_strategy(Strategy):
def __init__(self):
super(drag_sl_tp_strategy, self).__init__()
self._sl_points = self.Param("SlPoints", 500.0) \
.SetDisplay("SL Points", "Stop-loss distance", "Risk")
self._tp_points = self.Param("TpPoints", 1000.0) \
.SetDisplay("TP Points", "Take-profit distance", "Risk")
self._fast_period = self.Param("FastPeriod", 10) \
.SetDisplay("Fast Period", "Fast EMA period", "Indicators")
self._slow_period = self.Param("SlowPeriod", 30) \
.SetDisplay("Slow Period", "Slow EMA period", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._prev_fast = 0.0
self._prev_slow = 0.0
self._is_initialized = False
self._entry_price = 0.0
@property
def sl_points(self):
return self._sl_points.Value
@property
def tp_points(self):
return self._tp_points.Value
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(drag_sl_tp_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._is_initialized = False
self._entry_price = 0.0
def OnStarted2(self, time):
super(drag_sl_tp_strategy, self).OnStarted2(time)
fast_ema = ExponentialMovingAverage()
fast_ema.Length = self.fast_period
slow_ema = ExponentialMovingAverage()
slow_ema.Length = self.slow_period
self.SubscribeCandles(self.candle_type).Bind(fast_ema, slow_ema, self.process_candle).Start()
def process_candle(self, candle, fast, slow):
if candle.State != CandleStates.Finished:
return
fv = float(fast)
sv = float(slow)
if not self._is_initialized:
self._prev_fast = fv
self._prev_slow = sv
self._is_initialized = True
return
cross_up = self._prev_fast <= self._prev_slow and fv > sv
cross_down = self._prev_fast >= self._prev_slow and fv < sv
self._prev_fast = fv
self._prev_slow = sv
tp = float(self.tp_points)
sl = float(self.sl_points)
price = float(candle.ClosePrice)
if self.Position == 0:
if cross_up:
self.BuyMarket()
self._entry_price = price
elif cross_down:
self.SellMarket()
self._entry_price = price
elif self.Position > 0:
if price - self._entry_price >= tp or self._entry_price - price >= sl or cross_down:
self.SellMarket()
if cross_down:
self.SellMarket()
self._entry_price = price
elif self.Position < 0:
if self._entry_price - price >= tp or price - self._entry_price >= sl or cross_up:
self.BuyMarket()
if cross_up:
self.BuyMarket()
self._entry_price = price
def CreateClone(self):
return drag_sl_tp_strategy()