SampleTrailingStopStrategy — это порт советника MetaTrader SampleTrailingstop.mq4 на платформу StockSharp. Стратегия не открывает позиции самостоятельно, а следит за уже существующими сделками и поддерживает защитные заявки стоп-лосс и тейк-профит. Логика полностью повторяет оригинал: передвигает стоп по мере роста прибыли и учитывает брокерские ограничения по stop level и freeze level.
Когда длинная позиция становится прибыльной и лучшая цена Bid отходит от цены входа достаточно далеко, стратегия сначала переносит стоп-лосс на минимально допустимое расстояние под Bid. После этого стоп тянется за ценой на заданное количество пунктов с учётом брокерских буферов. Для коротких позиций применяется зеркальный алгоритм — стоп размещается над Ask. При каждом обновлении дополнительно пересчитывается целевой уровень тейк-профита (если он включён).
Поток данных
Подписка на поток Level1 для получения лучших цен Bid/Ask.
Использование встроенного API стратегии для доступа к средней цене текущей позиции.
Повторная регистрация защитных заявок при каждом пересчёте новых уровней.
Параметры
Параметр
Значение по умолчанию
Описание
TrailingStopPoints
200
Расстояние от рынка до трейлинг-стопа в пунктах. Добавляется к брокерским буферам при вычислениях.
TakeProfitPoints
1000
Расстояние до тейк-профита в пунктах. Значение 0 отключает управление тейк-профитом.
StopLevelPoints
0
Минимальный stop level брокера в пунктах. Прибавляется к расстоянию трейлинга, чтобы заявка была валидна.
FreezeLevelPoints
0
Freeze level брокера в пунктах. Трейлинг активируется только после выхода цены за этот буфер от цены входа.
Все расстояния переводятся в денежные значения с помощью минимального шага цены инструмента, что соответствует константе _Point в MetaTrader.
Алгоритм работы
Проверка позиции — трейлинг выполняется только при наличии позиции и актуальных котировок Bid/Ask.
Контроль прибыли — для лонга требуется Bid > цена входа, для шорта — Ask < цена входа, а также выход цены за freeze level.
Первичный перенос — если трейлинг ещё не активен, при достаточном удалении цены стоп переносится к рынку на минимально допустимое расстояние.
Подтягивание стопа — пока позиция прибыльна, стоп перемещается следом за ценой на величину TrailingStopPoints плюс брокерские буферы. Тейк-профит обновляется при каждом шаге.
Обслуживание заявок — защитные заявки создаются, перерегистрируются или отменяются через высокоуровневые методы, чтобы брокер всегда видел актуальные значения.
Рекомендации по использованию
Запускайте стратегию вместе с другим модулем, который открывает сделки, либо используйте её для сопровождения вручную открытых позиций.
Убедитесь, что у инструмента корректно заданы шаг цены и объёма — стратегия нормализует значения согласно биржевым требованиям.
При смене направления позиции старые защитные заявки удаляются, после чего трейлинг запускается заново для нового направления.
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// Sample Trailing Stop strategy: Smoothed MA crossover.
/// Buys when close crosses above Smoothed MA, sells when crosses below.
/// </summary>
public class SampleTrailingStopStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _period;
private decimal _prevClose;
private decimal _prevSma;
private bool _hasPrev;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int Period { get => _period.Value; set => _period.Value = value; }
public SampleTrailingStopStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_period = Param(nameof(Period), 50)
.SetGreaterThanZero()
.SetDisplay("Period", "Smoothed MA period", "Indicators");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevClose = 0;
_prevSma = 0;
_hasPrev = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevClose = 0;
_prevSma = 0;
_hasPrev = false;
var smma = new ExponentialMovingAverage { Length = Period };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(smma, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal smmaValue)
{
if (candle.State != CandleStates.Finished) return;
if (_hasPrev)
{
if (_prevClose <= _prevSma && candle.ClosePrice > smmaValue && Position <= 0)
BuyMarket();
else if (_prevClose >= _prevSma && candle.ClosePrice < smmaValue && Position >= 0)
SellMarket();
}
_prevClose = candle.ClosePrice;
_prevSma = smmaValue;
_hasPrev = true;
}
}
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 sample_trailing_stop_strategy(Strategy):
def __init__(self):
super(sample_trailing_stop_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(60)))
self._period = self.Param("Period", 50)
self._prev_close = 0.0
self._prev_sma = 0.0
self._has_prev = False
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def Period(self):
return self._period.Value
@Period.setter
def Period(self, value):
self._period.Value = value
def OnReseted(self):
super(sample_trailing_stop_strategy, self).OnReseted()
self._prev_close = 0.0
self._prev_sma = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(sample_trailing_stop_strategy, self).OnStarted2(time)
self._prev_close = 0.0
self._prev_sma = 0.0
self._has_prev = False
smma = ExponentialMovingAverage()
smma.Length = self.Period
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(smma, self._process_candle).Start()
def _process_candle(self, candle, smma_value):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
smma_val = float(smma_value)
if self._has_prev:
if self._prev_close <= self._prev_sma and close > smma_val and self.Position <= 0:
self.BuyMarket()
elif self._prev_close >= self._prev_sma and close < smma_val and self.Position >= 0:
self.SellMarket()
self._prev_close = close
self._prev_sma = smma_val
self._has_prev = True
def CreateClone(self):
return sample_trailing_stop_strategy()