Suffic369 — трендовая пробойная стратегия, сочетающая две короткие скользящие средние и широкие полосы Боллинджера. Длинная позиция открывается, когда быстрая SMA по ценам закрытия пересекает сверху SMA по максимумам и при этом цена закрытия находится ниже нижней полосы Боллинджера. Короткая позиция появляется, когда быстрая SMA опускается ниже SMA по минимумам, а цена закрытия располагается над верхней полосой. Перенос в StockSharp полностью повторяет оригинальную логику MQL и строится на высокоуровневых подписках на свечи.
Индикаторы
Быстрая SMA (Close, период 3) — отслеживает краткосрочное направление цены закрытия.
SMA по максимумам (High, период 5) — отражает локальные уровни сопротивления.
SMA по минимумам (Low, период 5) — используется как ориентир поддержки для шортов.
Полосы Боллинджера (период 156, отклонение 1) — выделяют экстремальные значения цены относительно волатильности.
Все индикаторы обрабатываются только на завершённых свечах. Предыдущие значения кэшируются, чтобы воспроизвести сдвиг на один бар, применённый в MetaTrader.
Правила торговли
Вход в лонг
Предыдущее значение быстрой SMA (close) ниже предыдущей SMA по максимумам.
Текущее значение быстрой SMA (close) выше текущей SMA по максимумам.
Цена закрытия свечи ниже нижней полосы Боллинджера.
Вход в шорт
Предыдущая быстрая SMA (close) выше предыдущей SMA по минимумам.
Текущая быстрая SMA (close) ниже текущей SMA по минимумам.
Цена закрытия свечи выше верхней полосы Боллинджера.
Выходы
Противоположный сигнал: открытая позиция закрывается при появлении сигнала противоположного направления.
Стоп-лосс: опциональное ограничение убытков в шагах цены.
Тейк-профит: опциональная фиксация прибыли в шагах цены.
Трейлинг-стоп: опциональное подтягивание стопа вслед за движением цены, полностью повторяющее логику модификации ордеров из MQL.
Одновременно удерживается только одна позиция. После срабатывания стопа, тейка или противоположного сигнала новые входы рассматриваются только на следующей завершённой свече.
Параметры
Имя
Значение по умолчанию
Описание
FastMaLength
3
Период быстрой SMA по закрытиям.
HighMaLength
5
Период SMA по максимумам.
LowMaLength
5
Период SMA по минимумам.
BollingerLength
156
Окно расчёта полос Боллинджера.
BollingerDeviation
1
Множитель стандартного отклонения для полос.
UseStopLoss
true
Включить блок стоп-лосса.
StopLossPoints
30
Дистанция стопа в шагах цены.
UseTakeProfit
true
Включить блок тейк-профита.
TakeProfitPoints
60
Дистанция тейк-профита в шагах цены.
UseTrailingStop
true
Включить трейлинг-стоп.
TrailingStopPoints
30
Отступ трейлинг-стопа в шагах цены.
CandleType
Таймфрейм 15 минут
Тип свечей для расчётов.
Все числовые параметры оформлены как StrategyParam<T>, поэтому их можно оптимизировать средствами StockSharp.
Управление рисками
Для перевода параметров в пунктах используется шаг цены инструмента (Security.PriceStep).
Трейлинг-стоп активируется только тогда, когда цена ушла дальше заданного расстояния, повторяя оригинальную проверку OrderOpenPrice - Bid/Ask.
В OnStarted вызывается StartProtection(), чтобы активировать встроенные защитные механизмы StockSharp.
Рекомендации по использованию
Подписывайте стратегию на инструмент, поддерживающий выбранный тип свечей.
Заранее задайте требуемый объём сделки через свойство Volume.
Стратегия начинает торговать только после формирования всех индикаторов; первые свечи используются для прогрева расчётов.
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>
/// Suffic369 breakout strategy based on fast/slow SMA crossover.
/// Buy when fast SMA crosses above slow SMA, sell when fast crosses below slow.
/// </summary>
public class Suffic369Strategy : Strategy
{
private readonly StrategyParam<int> _fastMaLength;
private readonly StrategyParam<int> _slowMaLength;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevFast;
private decimal _prevSlow;
private bool _hasPrev;
/// <summary>
/// Fast SMA length.
/// </summary>
public int FastMaLength
{
get => _fastMaLength.Value;
set => _fastMaLength.Value = value;
}
/// <summary>
/// Slow SMA length.
/// </summary>
public int SlowMaLength
{
get => _slowMaLength.Value;
set => _slowMaLength.Value = value;
}
/// <summary>
/// Candle type used for calculations.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="Suffic369Strategy"/> class.
/// </summary>
public Suffic369Strategy()
{
_fastMaLength = Param(nameof(FastMaLength), 3)
.SetDisplay("Fast SMA Length", "Fast moving average period", "Indicators");
_slowMaLength = Param(nameof(SlowMaLength), 6)
.SetDisplay("Slow SMA Length", "Slow moving average period", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Primary candle source", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevFast = 0m;
_prevSlow = 0m;
_hasPrev = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_hasPrev = false;
var fastSma = new SimpleMovingAverage { Length = FastMaLength };
var slowSma = new SimpleMovingAverage { Length = SlowMaLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fastSma, slowSma, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)
{
if (candle.State != CandleStates.Finished)
return;
if (!_hasPrev)
{
_prevFast = fast;
_prevSlow = slow;
_hasPrev = true;
return;
}
// Buy signal: fast SMA crosses above slow SMA
var longSignal = _prevFast <= _prevSlow && fast > slow;
// Sell signal: fast SMA crosses below slow SMA
var shortSignal = _prevFast >= _prevSlow && fast < slow;
if (Position > 0 && shortSignal)
{
SellMarket();
}
else if (Position < 0 && longSignal)
{
BuyMarket();
}
else if (Position == 0)
{
if (longSignal)
BuyMarket();
else if (shortSignal)
SellMarket();
}
_prevFast = fast;
_prevSlow = slow;
}
}
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
from StockSharp.Algo.Strategies import Strategy
from datatype_extensions import *
from indicator_extensions import *
class suffic369_strategy(Strategy):
"""Fast/slow SMA crossover (3/6)."""
def __init__(self):
super(suffic369_strategy, self).__init__()
self._fast_ma_length = self.Param("FastMaLength", 3).SetDisplay("Fast SMA Length", "Fast moving average period", "Indicators")
self._slow_ma_length = self.Param("SlowMaLength", 6).SetDisplay("Slow SMA Length", "Slow moving average period", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))).SetDisplay("Candle Type", "Primary candle source", "General")
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, value): self._candle_type.Value = value
def OnReseted(self):
super(suffic369_strategy, self).OnReseted()
self._prev_fast = 0
self._prev_slow = 0
self._has_prev = False
def OnStarted2(self, time):
super(suffic369_strategy, self).OnStarted2(time)
self._prev_fast = 0
self._prev_slow = 0
self._has_prev = False
fast_sma = SimpleMovingAverage()
fast_sma.Length = self._fast_ma_length.Value
slow_sma = SimpleMovingAverage()
slow_sma.Length = self._slow_ma_length.Value
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(fast_sma, slow_sma, self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, sub)
self.DrawIndicator(area, fast_sma)
self.DrawIndicator(area, slow_sma)
self.DrawOwnTrades(area)
def OnProcess(self, candle, fast, slow):
if candle.State != CandleStates.Finished:
return
if not self._has_prev:
self._prev_fast = fast
self._prev_slow = slow
self._has_prev = True
return
long_signal = self._prev_fast <= self._prev_slow and fast > slow
short_signal = self._prev_fast >= self._prev_slow and fast < slow
if self.Position > 0 and short_signal:
self.SellMarket()
elif self.Position < 0 and long_signal:
self.BuyMarket()
elif self.Position == 0:
if long_signal:
self.BuyMarket()
elif short_signal:
self.SellMarket()
self._prev_fast = fast
self._prev_slow = slow
def CreateClone(self):
return suffic369_strategy()