This strategy is an adaptation of the MetaTrader expert advisor "MyLineOrder" for the StockSharp API. It allows a trader to define horizontal price levels that trigger automatic market orders when touched. Optional stop loss, take profit and trailing stop distances are expressed in pips, and the trade volume can be configured.
When the market price reaches the BuyPrice level, the strategy enters a long position. Hitting the SellPrice level opens a short position. After entry, the strategy monitors the position and exits when one of the protective conditions is met: stop loss, take profit or trailing stop.
Details
Entry Criteria:
Long: Price touches or exceeds BuyPrice.
Short: Price touches or falls below SellPrice.
Long/Short: Both.
Exit Criteria:
Stop loss, take profit or trailing stop.
Stops:
StopLossPips, TakeProfitPips, TrailingStopPips.
Filters:
None.
Parameters:
BuyPrice – level for long entry.
SellPrice – level for short entry.
StopLossPips – stop-loss distance in pips.
TakeProfitPips – take-profit distance in pips.
TrailingStopPips – trailing stop distance in pips.
TradeVolume – order volume.
CandleType – timeframe of candles for price monitoring.
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>
/// Dual-level breakout strategy using SMA and ATR for dynamic support/resistance levels.
/// Buys when price breaks above SMA + ATR, sells when breaks below SMA - ATR.
/// Exits on mean reversion to SMA.
/// </summary>
public class LineOrderDualLevelStrategy : Strategy
{
private readonly StrategyParam<int> _smaPeriod;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<decimal> _atrMult;
private readonly StrategyParam<DataType> _candleType;
private decimal _entryPrice;
public int SmaPeriod { get => _smaPeriod.Value; set => _smaPeriod.Value = value; }
public int AtrPeriod { get => _atrPeriod.Value; set => _atrPeriod.Value = value; }
public decimal AtrMult { get => _atrMult.Value; set => _atrMult.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public LineOrderDualLevelStrategy()
{
_smaPeriod = Param(nameof(SmaPeriod), 10)
.SetGreaterThanZero()
.SetDisplay("SMA Period", "SMA period", "Indicators");
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("ATR Period", "ATR period", "Indicators");
_atrMult = Param(nameof(AtrMult), 1.0m)
.SetDisplay("ATR Mult", "ATR multiplier for levels", "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();
_entryPrice = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var sma = new SimpleMovingAverage { Length = SmaPeriod };
var atr = new StandardDeviation { Length = AtrPeriod };
SubscribeCandles(CandleType)
.Bind(sma, atr, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal smaVal, decimal atrVal)
{
if (candle.State != CandleStates.Finished) return;
if (atrVal <= 0) return;
var close = candle.ClosePrice;
var upperLevel = smaVal + atrVal * AtrMult;
var lowerLevel = smaVal - atrVal * AtrMult;
// Breakout above upper level => long
if (close > upperLevel && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
_entryPrice = close;
}
// Breakout below lower level => short
else if (close < lowerLevel && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
_entryPrice = close;
}
// Exit long at SMA or if loss > 2*ATR
else if (Position > 0)
{
if (close <= smaVal || (_entryPrice > 0 && close <= _entryPrice - atrVal * 2))
{
SellMarket();
_entryPrice = 0;
}
}
// Exit short at SMA or if loss > 2*ATR
else if (Position < 0)
{
if (close >= smaVal || (_entryPrice > 0 && close >= _entryPrice + atrVal * 2))
{
BuyMarket();
_entryPrice = 0;
}
}
}
}
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 SimpleMovingAverage, StandardDeviation
from StockSharp.Algo.Strategies import Strategy
class line_order_dual_level_strategy(Strategy):
def __init__(self):
super(line_order_dual_level_strategy, self).__init__()
self._sma_period = self.Param("SmaPeriod", 10) \
.SetDisplay("SMA Period", "SMA period", "Indicators")
self._atr_period = self.Param("AtrPeriod", 14) \
.SetDisplay("ATR Period", "ATR period", "Indicators")
self._atr_mult = self.Param("AtrMult", 1.0) \
.SetDisplay("ATR Mult", "ATR multiplier for levels", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._entry_price = 0.0
@property
def sma_period(self):
return self._sma_period.Value
@property
def atr_period(self):
return self._atr_period.Value
@property
def atr_mult(self):
return self._atr_mult.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(line_order_dual_level_strategy, self).OnReseted()
self._entry_price = 0.0
def OnStarted2(self, time):
super(line_order_dual_level_strategy, self).OnStarted2(time)
sma = SimpleMovingAverage()
sma.Length = self.sma_period
atr = StandardDeviation()
atr.Length = self.atr_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(sma, atr, self.on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def on_process(self, candle, sma_val, atr_val):
if candle.State != CandleStates.Finished:
return
if atr_val <= 0:
return
close = candle.ClosePrice
upper_level = sma_val + atr_val * self.atr_mult
lower_level = sma_val - atr_val * self.atr_mult
# Breakout above upper level => long
if close > upper_level and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
# Breakout below lower level => short
elif close < lower_level and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
# Exit long at SMA or if loss > 2*ATR
elif self.Position > 0:
if close <= sma_val or (self._entry_price > 0 and close <= self._entry_price - atr_val * 2):
self.SellMarket()
self._entry_price = 0
# Exit short at SMA or if loss > 2*ATR
elif self.Position < 0:
if close >= sma_val or (self._entry_price > 0 and close >= self._entry_price + atr_val * 2):
self.BuyMarket()
self._entry_price = 0
def CreateClone(self):
return line_order_dual_level_strategy()