Открыть на GitHub

Стратегия Forex Sky

Обзор

Forex Sky — это порт советника MetaTrader Forex_SKY.mq4. Стратегия торгует импульсами MACD и строго придерживается исходных ограничений: не больше одной сделки на свечу и не больше одного входа в сутки. Реализация на StockSharp полностью повторяет числовые пороги и переносит логику проверки из оригинального MQL-кода.

Данные берутся из таймфрейма CandleType (по умолчанию 15-минутные свечи). На закрытии каждой завершённой свечи вычисляется классический MACD (периоды 12/26/9).

Торговая логика

  • Покупка выполняется рыночным ордером, когда выполняются условия:
    • Текущая линия MACD выше нуля;
    • Значение также превышает +0.00009, подтверждая наличие импульса;
    • Хотя бы одно из трёх предыдущих значений MACD было меньше либо равно нулю, что фиксирует переход индикатора из отрицательной области.
  • Продажа происходит рыночным ордером при выполнении одного из условий:
    • MACD ниже нуля, опускается ниже -0.0004, хотя бы одно из трёх предыдущих значений было неотрицательным, а значение четыре свечи назад не меньше +0.001;
    • Или значение MACD четыре свечи назад достигло ≥ +0.003, что в оригинальном советнике немедленно открывает короткую позицию.
  • Управление позицией: стратегия не повторяет сделки в рамках одной свечи и ограничивает себя одной сделкой в сутки — это прямой аналог проверок Time0 и CheckTodaysOrders() в MQL. Защитные ордера создаются через StartProtection, поэтому их объём автоматически синхронизируется с текущей позицией.

Закрытие позиции осуществляется защитными ордерами или вручную, как и в MetaTrader-версии.

Параметры

Имя Значение по умолчанию Описание
FastPeriod 12 Период быстрой EMA в индикаторе MACD.
SlowPeriod 26 Период медленной EMA в MACD.
SignalPeriod 9 Период сигнальной EMA в MACD.
TakeProfitPoints 100 Дистанция до тейк-профита в пунктах инструмента. Цена рассчитывается как значение × Security.PriceStep.
StopLossPoints 3000 Дистанция до стоп-лосса в пунктах инструмента.
TradeVolume 0.1 Базовый объём рыночных ордеров (в лотах).
CandleType 15-минутный таймфрейм Таймфрейм, по которому строятся свечи и считается MACD.

Перевод пунктов в цену

Параметры TakeProfitPoints и StopLossPoints полностью соответствуют переменной Point из MQL4. Для пятизнакового форекс-инструмента (PriceStep = 0.00001):

  • Тейк-профит: 100 × 0.00001 = 0.001 цены;
  • Стоп-лосс: 3000 × 0.00001 = 0.03 цены.

Управление рисками

После открытия позиции функция StartProtection автоматически выставляет тейк-профит и стоп-лосс. При срабатывании используются рыночные ордера, что полностью соответствует поведению MetaTrader. Чтобы отключить конкретный защитный ордер, установите соответствующий параметр в 0.

Особенности миграции

  • Значения MACD сдвигаются и хранятся в полях класса, поэтому нет необходимости обращаться к индикатору с индексами, как это делалось через iMACD(..., shift).
  • Дневной лимит и запрет повторного входа на одной свече реализованы напрямую, что сохраняет дисциплину оригинального советника.
  • Комментарии переведены на английский язык, индикатор обрабатывается через высокоуровневый Bind, без низкоуровневого доступа к значениям.

Рекомендации по применению

  • Подберите CandleType под таймфрейм вашей торговой системы — MetaTrader-скрипт работал на периоде активного графика.
  • При торговле волатильными инструментами можно увеличить пороги MACD, чтобы сохранить число сигналов на приемлемом уровне.
  • Сброс дневного лимита привязан к дате открытия свечи, поэтому убедитесь, что серверное время соответствует вашей торговой сессии.
using System;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Forex Sky: MACD zero-line cross with EMA trend filter.
/// </summary>
public class ForexSkyStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastLength;
	private readonly StrategyParam<int> _slowLength;
	private readonly StrategyParam<int> _emaLength;
	private readonly StrategyParam<int> _atrLength;

	private decimal _prevMacd;
	private decimal _entryPrice;

	public ForexSkyStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe.", "General");

		_fastLength = Param(nameof(FastLength), 12)
			.SetDisplay("Fast EMA", "MACD fast period.", "Indicators");

		_slowLength = Param(nameof(SlowLength), 26)
			.SetDisplay("Slow EMA", "MACD slow period.", "Indicators");

		_emaLength = Param(nameof(EmaLength), 50)
			.SetDisplay("EMA Length", "Trend filter.", "Indicators");

		_atrLength = Param(nameof(AtrLength), 14)
			.SetDisplay("ATR Length", "ATR period.", "Indicators");
	}

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public int FastLength
	{
		get => _fastLength.Value;
		set => _fastLength.Value = value;
	}

	public int SlowLength
	{
		get => _slowLength.Value;
		set => _slowLength.Value = value;
	}

	public int EmaLength
	{
		get => _emaLength.Value;
		set => _emaLength.Value = value;
	}

	public int AtrLength
	{
		get => _atrLength.Value;
		set => _atrLength.Value = value;
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_prevMacd = 0;
		_entryPrice = 0;
	}

		protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_prevMacd = 0;
		_entryPrice = 0;

		var fastEma = new ExponentialMovingAverage { Length = FastLength };
		var slowEma = new ExponentialMovingAverage { Length = SlowLength };
		var ema = new ExponentialMovingAverage { Length = EmaLength };
		var atr = new AverageTrueRange { Length = AtrLength };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(fastEma, slowEma, ema, atr, ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, ema);
			DrawOwnTrades(area);
		}
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastVal, decimal slowVal, decimal emaVal, decimal atrVal)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (atrVal <= 0)
			return;

		var macd = fastVal - slowVal;
		var close = candle.ClosePrice;

		if (_prevMacd == 0)
		{
			_prevMacd = macd;
			return;
		}

		if (Position > 0)
		{
			if (close >= _entryPrice + atrVal * 3m || close <= _entryPrice - atrVal * 2m || (macd < 0 && _prevMacd >= 0))
			{
				SellMarket();
				_entryPrice = 0;
			}
		}
		else if (Position < 0)
		{
			if (close <= _entryPrice - atrVal * 3m || close >= _entryPrice + atrVal * 2m || (macd > 0 && _prevMacd <= 0))
			{
				BuyMarket();
				_entryPrice = 0;
			}
		}

		if (Position == 0)
		{
			if (macd > 0 && _prevMacd <= 0 && close > emaVal)
			{
				_entryPrice = close;
				BuyMarket();
			}
			else if (macd < 0 && _prevMacd >= 0 && close < emaVal)
			{
				_entryPrice = close;
				SellMarket();
			}
		}

		_prevMacd = macd;
	}
}