LazyBot V1 is a daily breakout strategy converted from the original MetaTrader 5 expert advisor. Every trading day it places a pair of pending stop orders around the previous day's price range and uses a trailing stop to protect open positions. The conversion leverages the high-level StockSharp API with candle subscriptions and automatic order management.
Trading Logic
Wait for a completed candle of the configured timeframe (daily by default).
On a new day, optionally ensure the current server time is inside the allowed trading window and skip weekends.
Cancel any existing breakout pending orders created by the strategy.
Place a buy stop above the previous day's high and a sell stop below the previous day's low. The Breakout Offset (pips) parameter adds extra distance to both breakout levels.
When either order is triggered, keep the protective stop-loss at a fixed distance and trail it whenever the price advances in the trade's favor by more than the configured pip distance.
Recompute volume for the next orders using either a fixed lot size or the risk-based sizing module.
Parameters
Name
Description
Candle Type
Timeframe used to gather the reference candles (daily by default).
Bot Name
Value written into order comments for easier tracking.
Stop Loss (pips)
Distance used for both the initial and trailing stop.
Breakout Offset (pips)
Extra distance applied to the previous high/low when placing the pending orders.
Max Spread (pips)
Maximum allowed spread before creating new breakout orders. Set to 0 to disable the check.
Use Trading Hours
Enables the start hour filter similar to the original EA.
Start Hour
First hour (inclusive) when new orders may be placed.
End Hour
Hour at which new orders stop being scheduled. When equal to the start hour the filter acts as a simple lower bound.
Use Risk %
Enables risk-based volume calculation.
Risk %
Percentage of the portfolio equity used to size positions when Use Risk % is enabled.
Fixed Volume
Fixed order volume used when risk sizing is disabled. When zero the strategy falls back to the global Volume property (defaults to 0.01).
Risk Management
The trailing stop mirrors the MetaTrader trailing logic by keeping the stop loss Stop Loss (pips) away from the best bid/ask and only tightening when a better price is reached.
The spread filter protects the strategy from submitting new breakout orders when the market is too wide.
Risk-based sizing divides the allowed monetary risk (equity * Risk %) by the stop distance expressed in price units and never goes below the fixed lot size.
Additional Notes
Order comments follow the format BotName;SymbolId;YYYYMMDD, which makes it easy to distinguish pending orders created on different days.
The strategy subscribes to Level1 data to evaluate the current spread for the filter and to trail stops with the latest bid/ask values.
Trailing stops are reapplied on every candle update and immediately after fills to match the original EA behavior.
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Simplified from "LazyBot V1" MetaTrader expert.
/// Daily breakout strategy using previous N-bar high/low range.
/// Buys when price breaks above previous high, sells when below previous low.
/// </summary>
public class LazyBotV1Strategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _lookback;
private readonly Queue<decimal> _highs = new();
private readonly Queue<decimal> _lows = new();
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int Lookback
{
get => _lookback.Value;
set => _lookback.Value = value;
}
public LazyBotV1Strategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for breakout detection", "General");
_lookback = Param(nameof(Lookback), 30)
.SetGreaterThanZero()
.SetDisplay("Lookback", "Number of bars for high/low range", "Indicators");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_highs.Clear();
_lows.Clear();
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
// Build range from previous bars (not including current)
if (_highs.Count >= Lookback)
{
var highs = _highs.ToArray();
var lows = _lows.ToArray();
decimal highest = decimal.MinValue;
decimal lowest = decimal.MaxValue;
foreach (var h in highs)
if (h > highest) highest = h;
foreach (var l in lows)
if (l < lowest) lowest = l;
var close = candle.ClosePrice;
var volume = Volume;
if (volume <= 0)
volume = 1;
var padding = (highest - lowest) * 0.05m;
if (close > highest + padding)
{
if (Position <= 0)
BuyMarket(Position < 0 ? Math.Abs(Position) + volume : volume);
}
else if (close < lowest - padding)
{
if (Position >= 0)
SellMarket(Position > 0 ? Math.Abs(Position) + volume : volume);
}
}
_highs.Enqueue(candle.HighPrice);
_lows.Enqueue(candle.LowPrice);
if (_highs.Count > Lookback)
{
_highs.Dequeue();
_lows.Dequeue();
}
}
/// <inheritdoc />
protected override void OnReseted()
{
_highs.Clear();
_lows.Clear();
base.OnReseted();
}
}
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.Strategies import Strategy
class lazy_bot_v1_strategy(Strategy):
def __init__(self):
super(lazy_bot_v1_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(60)))
self._lookback = self.Param("Lookback", 30)
self._highs = []
self._lows = []
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def Lookback(self):
return self._lookback.Value
@Lookback.setter
def Lookback(self, value):
self._lookback.Value = value
def OnReseted(self):
super(lazy_bot_v1_strategy, self).OnReseted()
self._highs = []
self._lows = []
def OnStarted2(self, time):
super(lazy_bot_v1_strategy, self).OnStarted2(time)
self._highs = []
self._lows = []
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._process_candle).Start()
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
lookback = self.Lookback
if len(self._highs) >= lookback:
highest = max(self._highs)
lowest = min(self._lows)
close = float(candle.ClosePrice)
padding = (highest - lowest) * 0.05
if close > highest + padding:
if self.Position <= 0:
self.BuyMarket()
elif close < lowest - padding:
if self.Position >= 0:
self.SellMarket()
self._highs.append(float(candle.HighPrice))
self._lows.append(float(candle.LowPrice))
while len(self._highs) > lookback:
self._highs.pop(0)
self._lows.pop(0)
def CreateClone(self):
return lazy_bot_v1_strategy()