Данная стратегия — это порт экспертного советника MetaTrader 5 3sma.mq5 (id 21495) на платформу StockSharp. Логика полностью сохраняет идею оригинала: сделки открываются, когда три скользящие средние расходятся друг от друга не менее чем на заданный спред. Реализация использует высокоуровневый API StockSharp с подпиской на свечи и автоматическим связыванием индикаторов, поэтому вручную управлять временными рядами не требуется.
Поведение оригинального советника MT5
Изначальная версия опирается на три простые скользящие средние с разными периодами и смещениями. Быстрая средняя рассчитывается по текущему бару, вторая и третья смещены на один и два бара назад. На каждом тике советник выполняет следующие действия:
Переводит пользовательский спред из пунктов (pips) в ценовые единицы, учитывая точность котировок инструмента.
Закрывает длинные позиции, когда быстрая SMA опускается ниже средней SMA минимум на половину спреда, и закрывает короткие позиции, когда быстрая SMA поднимается выше средней SMA на половину спреда.
Открывает новую длинную позицию, если одновременно выполняются условия MA1 > MA2 + spread и MA2 > MA3 + spread, при этом ранее открытые длинные сделки советника отсутствуют. Аналогично, короткая позиция открывается при зеркальном расположении средних.
Работает только рыночными ордерами, использует фиксированный лот и не ставит стоп-лосс/тейк-профит.
Особенности реализации на StockSharp
Индикаторы. Используются три экземпляра SimpleMovingAverage, подписанные на один и тот же источник свечей. Небольшие буферы истории воспроизводят MT5-параметр «смещение», чтобы сравнения выполнялись по значениям закрытых баров с нужным лагом.
Работа со спредом. Параметр спреда задаётся в пунктах. Стратегия вычисляет размер пункта из Security.PriceStep (или Security.Step) и умножает его на десять для инструментов с трёх- и пятизнакными котировками, повторяя MT5-подстройку под дробные пункты.
Ордерный поток. Заявки отправляются методами BuyMarket/SellMarket. При смене направления объём автоматически увеличивается на величину текущей нетто-позиции, что позволяет одной сделкой закрыть противоположное плечо и открыть новую позицию нужного направления.
Визуализация. При наличии графика стратегия отображает свечи, три скользящие средние и совершённые сделки.
Параметры
Имя
Описание
Значение по умолчанию
Volume
Объём рыночного ордера.
0.1
FastMaPeriod
Период быстрой SMA (MA1).
9
FastMaShift
Смещение быстрой SMA по числу закрытых баров.
0
MiddleMaPeriod
Период средней SMA (MA2).
14
MiddleMaShift
Смещение средней SMA по числу закрытых баров.
1
SlowMaPeriod
Период медленной SMA (MA3).
29
SlowMaShift
Смещение медленной SMA по числу закрытых баров.
2
MaSpreadPips
Минимальный спред между соседними SMA в пунктах.
10
CandleType
Тип свечей для расчётов.
Таймфрейм 1 минута
Логика торговли
Дождаться формирования всех трёх скользящих средних и заполнения буферов истории значениями с требуемыми смещениями.
Перевести спред из пунктов в денежные единицы и вычислить половину спреда для фильтра выхода.
Выходы.
Закрыть длинную позицию, если смещённая быстрая SMA опустилась ниже смещённой средней SMA минимум на половину спреда.
Закрыть короткую позицию, если смещённая быстрая SMA поднялась выше смещённой средней SMA минимум на половину спреда.
Входы.
Открыть (или развернуть в) длинную позицию, когда быстрая SMA превышает среднюю SMA на полный спред и средняя SMA превышает медленную SMA на тот же спред.
Открыть (или развернуть в) короткую позицию при зеркальном расположении — обе разницы меньше отрицательного спреда.
Отличия от версии MT5
StockSharp оперирует единой нетто-позицией. При развороте отправляется один рыночный ордер, который сначала закрывает противоположную позицию, а затем открывает новую. В MT5 эксперт мог одновременно держать независимые buy- и sell-ордера.
Конвертация пунктов использует доступные свойства Security. Если брокер не предоставляет шаг цены, стратегия применяет значение 1 как резервный вариант.
Расчёты выполняются на закрытии свечей, а не на каждом тике, поскольку высокоуровневый API работает со свечными подписками.
Подробные сообщения о результатах сделок из оригинального кода не переносятся — при необходимости можно задействовать стандартный логгер StockSharp.
Рекомендации по использованию
Подбирайте таймфрейм свечей в соответствии с настройками, использованными в MT5.
Корректируйте значение спреда при торговле инструментами с нестандартным размером пункта.
Помните, что сигналы появляются только после завершения текущей свечи.
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>
/// Triple SMA strategy that trades when three moving averages are properly aligned.
/// Enters long when fast > middle > slow, enters short when fast < middle < slow.
/// </summary>
public class TripleSmaSpreadStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _middlePeriod;
private readonly StrategyParam<int> _slowPeriod;
private int _prevSignal;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int FastPeriod
{
get => _fastPeriod.Value;
set => _fastPeriod.Value = value;
}
public int MiddlePeriod
{
get => _middlePeriod.Value;
set => _middlePeriod.Value = value;
}
public int SlowPeriod
{
get => _slowPeriod.Value;
set => _slowPeriod.Value = value;
}
public TripleSmaSpreadStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Timeframe", "General");
_fastPeriod = Param(nameof(FastPeriod), 9)
.SetGreaterThanZero()
.SetDisplay("Fast Period", "Fast SMA period", "Indicators");
_middlePeriod = Param(nameof(MiddlePeriod), 14)
.SetGreaterThanZero()
.SetDisplay("Middle Period", "Middle SMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 29)
.SetGreaterThanZero()
.SetDisplay("Slow Period", "Slow SMA period", "Indicators");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevSignal = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevSignal = 0;
var fastSma = new ExponentialMovingAverage { Length = FastPeriod };
var slowSma = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fastSma, slowSma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fastSma);
DrawIndicator(area, slowSma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var close = candle.ClosePrice;
var signal = 0;
if (fast > slow && close > fast)
signal = 1;
else if (fast < slow && close < fast)
signal = -1;
if (signal == _prevSignal)
return;
var oldSignal = _prevSignal;
_prevSignal = signal;
if (signal == 1 && oldSignal <= 0)
{
if (Position < 0)
BuyMarket();
if (Position <= 0)
BuyMarket();
}
else if (signal == -1 && oldSignal >= 0)
{
if (Position > 0)
SellMarket();
if (Position >= 0)
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 triple_sma_spread_strategy(Strategy):
def __init__(self):
super(triple_sma_spread_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", 9) \
.SetDisplay("Fast Period", "Fast SMA period", "Indicators")
self._middle_period = self.Param("MiddlePeriod", 14) \
.SetDisplay("Middle Period", "Middle SMA period", "Indicators")
self._slow_period = self.Param("SlowPeriod", 29) \
.SetDisplay("Slow Period", "Slow SMA period", "Indicators")
self._prev_signal = 0
@property
def CandleType(self):
return self._candle_type.Value
@property
def FastPeriod(self):
return self._fast_period.Value
@property
def MiddlePeriod(self):
return self._middle_period.Value
@property
def SlowPeriod(self):
return self._slow_period.Value
def OnReseted(self):
super(triple_sma_spread_strategy, self).OnReseted()
self._prev_signal = 0
def OnStarted2(self, time):
super(triple_sma_spread_strategy, self).OnStarted2(time)
self._prev_signal = 0
fast_ema = ExponentialMovingAverage()
fast_ema.Length = self.FastPeriod
slow_ema = ExponentialMovingAverage()
slow_ema.Length = self.SlowPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(fast_ema, slow_ema, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast_ema)
self.DrawIndicator(area, slow_ema)
self.DrawOwnTrades(area)
def _on_process(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
fv = float(fast_value)
sv = float(slow_value)
signal = 0
if fv > sv and close > fv:
signal = 1
elif fv < sv and close < fv:
signal = -1
if signal == self._prev_signal:
return
old_signal = self._prev_signal
self._prev_signal = signal
if signal == 1 and old_signal <= 0:
if self.Position < 0:
self.BuyMarket()
if self.Position <= 0:
self.BuyMarket()
elif signal == -1 and old_signal >= 0:
if self.Position > 0:
self.SellMarket()
if self.Position >= 0:
self.SellMarket()
def CreateClone(self):
return triple_sma_spread_strategy()