Стратегия является переносом советника «Sensitive» для MetaTrader 5 на платформу StockSharp. Основная идея — отрабатывать пересечения линий MACD вблизи нулевой отметки и сопровождать позицию набором защитных инструментов: фиксированными стоп-лоссом и тейк-профитом, а также ступенчатым трейлинг-стопом в пунктах. Вся логика работает только на закрытых свечах и использует высокоуровневый API.
Используемые данные и индикаторы
MACD (Moving Average Convergence Divergence) с настраиваемыми периодами быстрой, медленной EMA и сигнальной линии.
Новый сигнал анализируется только после завершения очередной свечи.
Из привязанного MACD получаем значения основной и сигнальной линий.
Покупка выполняется, если выполняются условия:
Линия MACD пересекла сигнальную снизу вверх (текущее macd > signal, предыдущее macd < signal).
Значение MACD остаётся отрицательным.
Модуль MACD больше MacdOpenLevel * Point, что отсекает слабые колебания.
Текущая позиция не длинная (Position <= 0). При наличии короткой позиции выполняется разворот за один рыночный ордер.
Продажа симметрична покупкам:
MACD пересекает сигнал сверху вниз, оставаясь положительным.
Модуль MACD превышает порог.
Нет активной короткой позиции (Position >= 0), при необходимости выполняется разворот из лонга.
Управление сделкой
Тейк-профит рассчитывается в момент входа по параметру TakeProfitPips. Длинная позиция закрывается, когда максимум свечи достигает целевой цены, короткая — когда минимум уходит ниже целевого уровня.
Стоп-лосс строится аналогично по StopLossPips. Для лонга выход происходит при касании стоп-уровня минимумом свечи, для шорта — при пробое максимума.
Трейлинг-стоп активируется, если TrailingStopPips > 0. После того как цена прошла в сторону позиции не менее TrailingStopPips + TrailingStepPips пунктов, стоп подтягивается вслед за ценой, сохраняя расстояние TrailingStopPips. Каждый следующий шаг выполняется только при дополнительном продвижении на величину TrailingStepPips. Если трейлинг включён, но шаг равен нулю, стратегия выводит сообщение об ошибке и останавливается.
После закрытия позиции внутренние переменные (_entryPrice, _stopLoss, _takeProfit) очищаются, чтобы избежать повторного срабатывания старых уровней.
Объём сделок
Количество контрактов задаётся параметром Volume (по умолчанию 0.1). При развороте позиция закрывается и открывается новое направление одной рыночной заявкой: к целевому объёму прибавляется абсолютное значение текущей позиции.
Параметры
Параметр
Назначение
Значение по умолчанию
FastLength
Период быстрой EMA в MACD
12
SlowLength
Период медленной EMA в MACD
26
SignalLength
Период сигнальной EMA
9
MacdOpenLevel
Минимальная величина MACD (в ценовых пунктах) для открытия сделки
3
StopLossPips
Дистанция стоп-лосса в пунктах
35
TakeProfitPips
Дистанция тейк-профита в пунктах
75
TrailingStopPips
Базовое расстояние трейлинг-стопа (0 — выключено)
5
TrailingStepPips
Минимальный шаг цены для обновления трейлинг-стопа
5
CandleType
Тип и таймфрейм свечей
Свечи 1 минуты
Volume
Торговый объём (лоты/контракты)
0.1
Дополнительные сведения
Пункты и величина Point вычисляются из шага цены и количества знаков инструмента; для трёх- и пятизнаковых форекс-символов величина пункта автоматически масштабируется.
Все комментарии в коде на английском языке, реализация использует только высокоуровневые методы, как предписано в инструкциях.
Стратегия предполагает мгновенное исполнение рыночных ордеров. Если необходимо учитывать частичное исполнение или проскальзывание, логику можно доработать.
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>
/// Sensitive MACD Trailing strategy. Uses EMA crossover (5/15).
/// </summary>
public class SensitiveMacdTrailingStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private decimal? _prevFast;
private decimal? _prevSlow;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public SensitiveMacdTrailingStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame()).SetDisplay("Candle Type", "Timeframe", "General");
_fastPeriod = Param(nameof(FastPeriod), 5).SetGreaterThanZero().SetDisplay("Fast EMA", "Fast EMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 15).SetGreaterThanZero().SetDisplay("Slow EMA", "Slow EMA period", "Indicators");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevFast = null;
_prevSlow = null;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevFast = null; _prevSlow = null;
var fast = new ExponentialMovingAverage { Length = FastPeriod };
var slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(fast, slow, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null) { DrawCandles(area, subscription); DrawIndicator(area, fast); DrawIndicator(area, slow); DrawOwnTrades(area); }
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)
{
if (candle.State != CandleStates.Finished) return;
if (!IsFormedAndOnlineAndAllowTrading()) { _prevFast = fast; _prevSlow = slow; return; }
if (_prevFast == null || _prevSlow == null) { _prevFast = fast; _prevSlow = slow; return; }
var prevAbove = _prevFast.Value > _prevSlow.Value;
var currAbove = fast > slow;
_prevFast = fast; _prevSlow = slow;
if (!prevAbove && currAbove && Position <= 0) { if (Position < 0) BuyMarket(); BuyMarket(); }
else if (prevAbove && !currAbove && Position >= 0) { if (Position > 0) SellMarket(); SellMarket(); }
}
}
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 sensitive_macd_trailing_strategy(Strategy):
def __init__(self):
super(sensitive_macd_trailing_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._fast_period = self.Param("FastPeriod", 5) \
.SetDisplay("Fast EMA", "Fast EMA period", "Indicators")
self._slow_period = self.Param("SlowPeriod", 15) \
.SetDisplay("Slow EMA", "Slow EMA period", "Indicators")
self._prev_fast = None
self._prev_slow = None
@property
def CandleType(self):
return self._candle_type.Value
@property
def FastPeriod(self):
return self._fast_period.Value
@property
def SlowPeriod(self):
return self._slow_period.Value
def OnReseted(self):
super(sensitive_macd_trailing_strategy, self).OnReseted()
self._prev_fast = None
self._prev_slow = None
def OnStarted2(self, time):
super(sensitive_macd_trailing_strategy, self).OnStarted2(time)
self._prev_fast = None
self._prev_slow = None
fast = ExponentialMovingAverage()
fast.Length = self.FastPeriod
slow = ExponentialMovingAverage()
slow.Length = self.SlowPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(fast, slow, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast)
self.DrawIndicator(area, slow)
self.DrawOwnTrades(area)
def _on_process(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fv = float(fast_value)
sv = float(slow_value)
if self._prev_fast is None or self._prev_slow is None:
self._prev_fast = fv
self._prev_slow = sv
return
prev_above = self._prev_fast > self._prev_slow
curr_above = fv > sv
self._prev_fast = fv
self._prev_slow = sv
if not prev_above and curr_above and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif prev_above and not curr_above and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
def CreateClone(self):
return sensitive_macd_trailing_strategy()