Стратегия MA Price Cross — это конвертация советника MetaTrader 4 «MA Price Cross» на высокоуровневый API StockSharp. Алгоритм ожидает пересечения выбранной скользящей средней с текущей ценой в пределах заданного торгового окна. Когда средняя пробивает цену снизу вверх, стратегия открывает длинную позицию; при пробое сверху вниз — короткую. Уровни стоп-лосса и тейк-профита задаются в пунктах MetaTrader и автоматически переводятся в абсолютные ценовые расстояния через PriceStep инструмента.
Вместо обработки каждого тика, как в исходном MQL-коде, версия на StockSharp работает с завершёнными свечами и использует подписку SubscribeCandles. Это гарантирует единственное решение на свечу и полную совместимость с конвейером индикаторов. Скользящая средняя настраивается в соответствии со всеми четырьмя режимами MetaTrader и поддерживает различные источники цены (close, open, high, low, median, typical, weighted).
Логика работы
Ожидать, пока текущее время попадёт в торговое окно [StartTime, StopTime). Окна, пересекающие полночь, поддерживаются.
Обрабатывать только закрытые свечи. Передавать в индикатор выбранный тип цены.
Сохранять предыдущее значение скользящей средней, имитируя использование сдвигов iMA в MetaTrader.
Если предыдущая средняя была ниже последней цены, а новое значение стало выше, открыть (или развернуть в) длинную позицию.
Если предыдущая средняя была выше, а текущее значение стало ниже цены, открыть (или развернуть в) короткую позицию.
Перед открытием позиции противоположного направления закрыть текущую, повторяя условие OrdersTotal() == 0 из оригинала.
Запустить виртуальные стоп-уровни и тейк-профит, умножив заданные в пунктах расстояния на PriceStep инструмента.
Значения по умолчанию
Параметр
Значение
Описание
CandleType
TimeFrame(1m)
Ряд свечей, используемый в расчётах.
MaPeriod
160
Количество баров в окне скользящей средней.
MaMethod
Simple
Тип скользящей средней: Simple, Exponential, Smoothed или LinearWeighted.
PriceType
Close
Источник цены для индикатора (close/open/high/low/median/typical/weighted).
StartTime
01:00
Время начала торговли.
StopTime
22:00
Время окончания приёма новых сигналов.
StopLossPoints
200
Дистанция стоп-лосса в пунктах MetaTrader, пересчитанная в цену.
TakeProfitPoints
600
Дистанция тейк-профита в пунктах MetaTrader, пересчитанная в цену.
OrderVolume
0.1
Объём рыночных заявок.
Особенности
При равных StartTime и StopTime фильтр времени отключается.
Если StopLossPoints или TakeProfitPoints равны нулю, соответствующий защитный уровень не создаётся.
Для фильтра времени используется candle.CloseTime.TimeOfDay, поэтому стратегия учитывает часовой пояс коннектора.
При отсутствии PriceStep пункты применяются напрямую без масштабирования.
Источник оригинала
Файл: MQL/44133/MA Price Cross.mq4
Автор: JBlanked (2023)
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Simplified from "MA Price Cross" MetaTrader expert.
/// Enters when SMA crosses above/below the current close price.
/// </summary>
public class MaPriceCrossStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _maPeriod;
private ExponentialMovingAverage _sma;
private decimal? _prevAverage;
private decimal? _prevClose;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int MaPeriod
{
get => _maPeriod.Value;
set => _maPeriod.Value = value;
}
public MaPriceCrossStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for MA cross detection", "General");
_maPeriod = Param(nameof(MaPeriod), 100)
.SetGreaterThanZero()
.SetDisplay("MA Period", "SMA period", "Indicators");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_sma = new ExponentialMovingAverage { Length = MaPeriod };
_prevAverage = null;
_prevClose = null;
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 smaValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_sma.IsFormed)
{
_prevAverage = smaValue;
_prevClose = candle.ClosePrice;
return;
}
if (_prevAverage is null || _prevClose is null)
{
_prevAverage = smaValue;
_prevClose = candle.ClosePrice;
return;
}
var close = candle.ClosePrice;
var volume = Volume;
if (volume <= 0)
volume = 1;
// MA was below price, now crosses above -> sell signal (price goes under MA)
var sellSignal = _prevClose.Value >= _prevAverage.Value && close < smaValue;
// MA was above price, now crosses below -> buy signal (price goes above MA)
var buySignal = _prevClose.Value <= _prevAverage.Value && close > smaValue;
if (buySignal)
{
if (Position <= 0)
BuyMarket(Position < 0 ? Math.Abs(Position) + volume : volume);
}
else if (sellSignal)
{
if (Position >= 0)
SellMarket(Position > 0 ? Math.Abs(Position) + volume : volume);
}
_prevAverage = smaValue;
_prevClose = close;
}
/// <inheritdoc />
protected override void OnReseted()
{
_sma = null;
_prevAverage = null;
_prevClose = null;
base.OnReseted();
}
}
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 ma_price_cross_strategy(Strategy):
def __init__(self):
super(ma_price_cross_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(60)))
self._ma_period = self.Param("MaPeriod", 100)
self._prev_average = None
self._prev_close = None
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def MaPeriod(self):
return self._ma_period.Value
@MaPeriod.setter
def MaPeriod(self, value):
self._ma_period.Value = value
def OnReseted(self):
super(ma_price_cross_strategy, self).OnReseted()
self._prev_average = None
self._prev_close = None
def OnStarted2(self, time):
super(ma_price_cross_strategy, self).OnStarted2(time)
self._prev_average = None
self._prev_close = None
sma = ExponentialMovingAverage()
sma.Length = self.MaPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(sma, self._process_candle).Start()
def _process_candle(self, candle, sma_value):
if candle.State != CandleStates.Finished:
return
sma_val = float(sma_value)
close = float(candle.ClosePrice)
if self._prev_average is None or self._prev_close is None:
self._prev_average = sma_val
self._prev_close = close
return
# Price crosses above MA -> buy
buy_signal = self._prev_close <= self._prev_average and close > sma_val
# Price crosses below MA -> sell
sell_signal = self._prev_close >= self._prev_average and close < sma_val
if buy_signal:
if self.Position <= 0:
self.BuyMarket()
elif sell_signal:
if self.Position >= 0:
self.SellMarket()
self._prev_average = sma_val
self._prev_close = close
def CreateClone(self):
return ma_price_cross_strategy()