LazyBot V1 — это конвертация оригинального советника MetaTrader 5 в формате StockSharp. Стратегия работает на дневных свечах: каждый торговый день выставляется пара отложенных заявок на пробой диапазона предыдущей свечи, а открытые позиции защищаются плавающим стоп-лоссом с фиксированным расстоянием в пунктах.
Логика работы
Ожидание закрытия свечи выбранного таймфрейма (по умолчанию день).
В начале нового дня стратегия проверяет, не попадает ли текущий серверный час в выходные и, при необходимости, в разрешённый торговый интервал.
Все ранее созданные отложенные ордера пробоя отменяются.
Заявка Buy Stop размещается выше максимума предыдущего дня, а Sell Stop — ниже минимума. Параметр «Breakout Offset (pips)» добавляет дополнительное смещение от экстремумов.
После активации любой заявки стоп-лосс устанавливается на заданное расстояние и подтягивается вслед за ценой, когда прибыль превышает заданный порог.
Объём следующей заявки рассчитывается либо как фиксированный, либо на основе процента риска от текущей стоимости портфеля.
Параметры
Параметр
Описание
Candle Type
Таймфрейм для построения ориентирных свечей (по умолчанию дневной).
Bot Name
Комментарий, записываемый в заявки.
Stop Loss (pips)
Расстояние стоп-лосса и шага трейлинг-стопа в пунктах.
Breakout Offset (pips)
Дополнительное смещение от максимумов/минимумов предыдущего дня.
Max Spread (pips)
Максимально допустимый спред перед созданием новых ордеров (0 — без ограничения).
Use Trading Hours
Включение фильтра по времени начала торговли.
Start Hour
Час, начиная с которого разрешено размещать новые ордера.
End Hour
Час окончания размещения новых ордеров. Если равен Start Hour, фильтр работает только на нижнюю границу.
Use Risk %
Переключатель между фиксированным объёмом и расчётом по риску.
Risk %
Процент от стоимости портфеля, используемый для расчёта объёма при активном Use Risk %.
Fixed Volume
Фиксированный объём сделки. При нуле используется глобальный параметр Volume (по умолчанию 0.01).
Управление рисками
Трейлинг-стоп повторяет механику исходного советника: стоп-лосс удерживается на расстоянии Stop Loss (pips) от лучшего бид/аск и подтягивается только при достижении более выгодной цены.
Спред-фильтр предотвращает появление новых ордеров при невыгодных рыночных условиях.
Расчёт объёма по риску делит допустимый денежный риск (equity * Risk %) на ценовое расстояние до стопа и никогда не опускается ниже базового лота.
Дополнительные сведения
Комментарии ордеров имеют формат BotName;SymbolId;YYYYMMDD, что облегчает идентификацию заявок разных дней.
Стратегия подписывается на поток Level1 для контроля спреда и оперативного подтягивания стоп-лосса.
Перерасчёт трейлинг-стопа выполняется после каждой закрытой свечи и сразу после сделок, что соответствует логике оригинальной реализации.
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()