Стратегия повторяет эксперта MetaTrader 5 «55 MA»: сравнивает значения 55-периодной скользящей средней на двух барах и открывает позицию, как только разница превышает заданный порог. Расчёты выполняются только по закрытым свечам в пределах пользовательского торгового окна, при необходимости направление сигналов можно инвертировать. Алгоритм полностью сохраняет исходное поведение — при отсутствии условий для покупки всегда открывается короткая позиция.
Логика торговли
Подписка на выбранный тип свечей и расчёт скользящей средней с заданными параметрами периода, метода и используемой цены.
Ведение буфера последних значений, чтобы получать MA для индексов BarA и BarB, даже если включён горизонтальный сдвиг.
Для каждой завершённой свечи внутри интервала [StartHour, EndHour):
Получить MA в точках BarA + MaShift и BarB + MaShift.
Если значение в BarA больше значения в BarB на величину, превосходящую DifferenceThreshold, открыть длинную позицию (или короткую при включённом ReverseSignals).
Если значение в BarA меньше значения в BarB на величину, превосходящую DifferenceThreshold, открыть короткую позицию (или длинную при ReverseSignals).
Если ни одно условие не выполнено, стратегия, как и оригинальный советник, инициирует продажу.
Заявки отправляются по рынку с объёмом Volume. При активном CloseOppositePositions объём увеличивается, чтобы закрыть встречную позицию перед открытием новой.
Опциональные стоп-лосс и тейк-профит подключаются через StartProtection. Дистанции задаются в пунктах (pip); для инструментов с 3 или 5 знаками после запятой один пункт равен PriceStep * 10.
Параметры
Имя
Тип
Значение по умолчанию
Описание
CandleType
DataType
Таймфрейм 1 минута
Ряд свечей, по которому ведётся расчёт и формируются сигналы.
StopLossPips
int
30
Размер стоп-лосса в пунктах. 0 — отключить.
TakeProfitPips
int
50
Размер тейк-профита в пунктах. 0 — отключить.
StartHour
int
8
Начало торгового окна (включительно, часы 0–23).
EndHour
int
21
Конец торгового окна (не включительно). Должен быть больше StartHour.
DifferenceThreshold
decimal
0.0001
Минимальная абсолютная разница между сравниваемыми MA для генерации сигнала.
BarA
int
0
Индекс первого бара для сравнения (0 — текущая свеча).
BarB
int
1
Индекс второго бара.
ReverseSignals
bool
false
Инверсия условий на покупку/продажу.
CloseOppositePositions
bool
false
При включении закрывает встречные позиции за счёт увеличения объёма сделки.
MaShift
int
0
Горизонтальный сдвиг скользящей средней; положительные значения обращаются к более ранним значениям.
MaLength
int
55
Период скользящей средней.
MaMethod
MovingAverageMethods
Exponential
Метод сглаживания (Simple, Exponential, Smoothed, Weighted).
AppliedPrice
AppliedPriceTypes
Median
Тип цены для расчёта (Close, Open, High, Low, Median, Typical, Weighted).
Управление позицией
Базовый объём задаётся свойством стратегии Volume; при активном CloseOppositePositions он автоматически дополняется величиной встречной позиции.
StartProtection вызывается только если расстояние стоп-лосса или тейк-профита больше нуля.
Примечания
Торговое окно определяется временем инструмента: сигналы вне диапазона [StartHour, EndHour) игнорируются.
При отрицательном MaShift стратегия ожидает накопления истории, аналогично тому, как исходный советник возвращает EMPTY_VALUE для смещённых буферов.
Из-за сохранения оригинальной логики при отсутствии условий на покупку выполняется продажа. При необходимости можно увеличить DifferenceThreshold, чтобы уменьшить количество таких входов.
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>
/// 55 MA bar comparison strategy. Compares candle body with MA direction.
/// </summary>
public class FiftyFiveMaBarComparisonStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _maPeriod;
private decimal? _prevMa;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int MaPeriod
{
get => _maPeriod.Value;
set => _maPeriod.Value = value;
}
public FiftyFiveMaBarComparisonStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Timeframe", "General");
_maPeriod = Param(nameof(MaPeriod), 55)
.SetGreaterThanZero()
.SetDisplay("MA Period", "Moving average period", "Indicators");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevMa = null;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevMa = null;
var sma = new SimpleMovingAverage { Length = MaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(sma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, sma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal maVal)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
{
_prevMa = maVal;
return;
}
if (_prevMa == null)
{
_prevMa = maVal;
return;
}
var close = candle.ClosePrice;
var bullishBar = candle.ClosePrice > candle.OpenPrice;
var bearishBar = candle.ClosePrice < candle.OpenPrice;
var maRising = maVal > _prevMa.Value;
var maFalling = maVal < _prevMa.Value;
_prevMa = maVal;
// Bullish bar + rising MA + close above MA → buy
if (bullishBar && maRising && close > maVal && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
}
// Bearish bar + falling MA + close below MA → sell
else if (bearishBar && maFalling && close < maVal && 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 SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class fifty_five_ma_bar_comparison_strategy(Strategy):
def __init__(self):
super(fifty_five_ma_bar_comparison_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._ma_period = self.Param("MaPeriod", 55) \
.SetDisplay("MA Period", "Moving average period", "Indicators")
self._prev_ma = None
@property
def CandleType(self):
return self._candle_type.Value
@property
def MaPeriod(self):
return self._ma_period.Value
def OnReseted(self):
super(fifty_five_ma_bar_comparison_strategy, self).OnReseted()
self._prev_ma = None
def OnStarted2(self, time):
super(fifty_five_ma_bar_comparison_strategy, self).OnStarted2(time)
self._prev_ma = None
sma = SimpleMovingAverage()
sma.Length = self.MaPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(sma, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, sma)
self.DrawOwnTrades(area)
def _on_process(self, candle, ma_value):
if candle.State != CandleStates.Finished:
return
mv = float(ma_value)
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_ma = mv
return
if self._prev_ma is None:
self._prev_ma = mv
return
close = float(candle.ClosePrice)
open_price = float(candle.OpenPrice)
bullish_bar = close > open_price
bearish_bar = close < open_price
ma_rising = mv > self._prev_ma
ma_falling = mv < self._prev_ma
self._prev_ma = mv
# Bullish bar + rising MA + close above MA
if bullish_bar and ma_rising and close > mv and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
# Bearish bar + falling MA + close below MA
elif bearish_bar and ma_falling and close < mv and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
def CreateClone(self):
return fifty_five_ma_bar_comparison_strategy()