Levels With Trail Strategy
Converted from MQL script levels_with_trail.mq4. The strategy opens trades when price crosses a specified level and can trail the stop loss.
How it works
- Subscribes to candles of the chosen timeframe.
- When there is no open position and the closing price is above
Level Price, it buys; if the price is below, it sells. - If
Trail Stopis enabled, the stop loss follows the price when the position is profitable. - Positions are closed when the stop loss, take profit, or an opposite breakout signal is triggered.
Parameters
Stop Loss– stop loss size in price units.Take Profit– take profit size in price units.Level Price– breakout level to watch.Trail Stop– enable or disable the trailing stop loss.Candle Type– candle timeframe used for analysis.
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy trading price level breakouts with trailing stop loss.
/// Uses SMA as dynamic level, enters on breakout, trails stop on winning positions.
/// </summary>
public class LevelsWithTrailStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _maPeriod;
private readonly StrategyParam<decimal> _trailPct;
private decimal _entryPrice;
private decimal _bestPrice;
private decimal _prevPrice;
private decimal _prevMa;
private bool _hasPrev;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int MaPeriod
{
get => _maPeriod.Value;
set => _maPeriod.Value = value;
}
public decimal TrailPct
{
get => _trailPct.Value;
set => _trailPct.Value = value;
}
public LevelsWithTrailStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candle type", "General");
_maPeriod = Param(nameof(MaPeriod), 50)
.SetDisplay("MA Period", "Moving average period for level", "Parameters");
_trailPct = Param(nameof(TrailPct), 1m)
.SetDisplay("Trail %", "Trailing stop percent", "Risk");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_entryPrice = 0;
_bestPrice = 0;
_prevPrice = 0;
_prevMa = 0;
_hasPrev = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_entryPrice = 0;
_bestPrice = 0;
_prevPrice = 0;
_prevMa = 0;
_hasPrev = false;
var ma = new ExponentialMovingAverage { Length = MaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal maValue)
{
if (candle.State != CandleStates.Finished)
return;
var price = candle.ClosePrice;
// Trailing stop management
if (Position > 0)
{
if (price > _bestPrice)
_bestPrice = price;
var stopLevel = _bestPrice * (1 - TrailPct / 100m);
if (price <= stopLevel)
{
SellMarket();
_entryPrice = 0;
}
}
else if (Position < 0)
{
if (price < _bestPrice)
_bestPrice = price;
var stopLevel = _bestPrice * (1 + TrailPct / 100m);
if (price >= stopLevel)
{
BuyMarket();
_entryPrice = 0;
}
}
if (!_hasPrev)
{
_prevPrice = price;
_prevMa = maValue;
_hasPrev = true;
return;
}
// Entry: price crosses above MA
if (_prevPrice < _prevMa && price >= maValue && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
_entryPrice = price;
_bestPrice = price;
}
// Entry: price crosses below MA
else if (_prevPrice > _prevMa && price <= maValue && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
_entryPrice = price;
_bestPrice = price;
}
_prevPrice = price;
_prevMa = maValue;
}
}
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 levels_with_trail_strategy(Strategy):
def __init__(self):
super(levels_with_trail_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Candle type", "General")
self._ma_period = self.Param("MaPeriod", 50) \
.SetDisplay("MA Period", "Moving average period for level", "Parameters")
self._trail_pct = self.Param("TrailPct", 1.0) \
.SetDisplay("Trail %", "Trailing stop percent", "Risk")
self._entry_price = 0.0
self._best_price = 0.0
self._prev_price = 0.0
self._prev_ma = 0.0
self._has_prev = False
@property
def candle_type(self):
return self._candle_type.Value
@property
def ma_period(self):
return self._ma_period.Value
@property
def trail_pct(self):
return self._trail_pct.Value
def OnReseted(self):
super(levels_with_trail_strategy, self).OnReseted()
self._entry_price = 0.0
self._best_price = 0.0
self._prev_price = 0.0
self._prev_ma = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(levels_with_trail_strategy, self).OnStarted2(time)
self._entry_price = 0.0
self._best_price = 0.0
self._prev_price = 0.0
self._prev_ma = 0.0
self._has_prev = False
ma = ExponentialMovingAverage()
ma.Length = self.ma_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(ma, self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ma)
self.DrawOwnTrades(area)
def process_candle(self, candle, ma_value):
if candle.State != CandleStates.Finished:
return
ma_value = float(ma_value)
price = float(candle.ClosePrice)
trail = float(self.trail_pct)
# Trailing stop management
if self.Position > 0:
if price > self._best_price:
self._best_price = price
stop_level = self._best_price * (1.0 - trail / 100.0)
if price <= stop_level:
self.SellMarket()
self._entry_price = 0.0
elif self.Position < 0:
if price < self._best_price:
self._best_price = price
stop_level = self._best_price * (1.0 + trail / 100.0)
if price >= stop_level:
self.BuyMarket()
self._entry_price = 0.0
if not self._has_prev:
self._prev_price = price
self._prev_ma = ma_value
self._has_prev = True
return
# Entry: price crosses above MA
if self._prev_price < self._prev_ma and price >= ma_value and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = price
self._best_price = price
# Entry: price crosses below MA
elif self._prev_price > self._prev_ma and price <= ma_value and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = price
self._best_price = price
self._prev_price = price
self._prev_ma = ma_value
def CreateClone(self):
return levels_with_trail_strategy()