Открыть на GitHub

Стратегия Exp Cronex MFI

Обзор

Стратегия воспроизводит советник Exp_CronexMFI. Денежный поток (MFI) сглаживается дважды, после чего сделки открываются против пересечения полученных линий. В порте сохранена контртрендовая логика и все настройки вынесены в параметры StockSharp.

Принцип работы

  1. Подписка на выбранные свечи (по умолчанию таймфрейм 4 часа).
  2. Расчёт индикатора Money Flow Index с заданным периодом.
  3. Двухступенчатое сглаживание выбранным методом: первый проход формирует быструю линию Cronex, второй — сглаживает её ещё раз для медленной линии.
  4. Сохранение пар значений с учётом задержки SignalShift, аналогичной параметру SignalBar в MQL.
  5. Когда быстрая линия пересекает медленную сверху вниз, стратегия закрывает короткие позиции (если разрешено) и открывает/увеличивает длинные. При пересечении снизу вверх закрываются лонги и открываются шорты.
  6. Заявки отправляются по рынку с объёмом Volume; возможность входа и выхода отдельно настраивается для длинной и короткой стороны.

Расчёт ведётся только по завершённым свечам, как и в версии для MetaTrader.

Параметры

Имя Тип Значение по умолчанию Описание
MfiPeriod int 25 Период Money Flow Index.
FastPeriod int 14 Период первого сглаживания (быстрая линия Cronex).
SlowPeriod int 25 Период второго сглаживания (медленная линия Cronex).
SignalShift int 1 Количество завершённых свечей для задержки сигнала.
Smoothing SmoothingMethod Simple Алгоритм сглаживания для обеих линий.
EnableLongEntries bool true Разрешает открытие/увеличение длинных позиций.
EnableShortEntries bool true Разрешает открытие/увеличение коротких позиций.
EnableLongExits bool true Разрешает закрытие длинных позиций по сигналу.
EnableShortExits bool true Разрешает закрытие коротких позиций по сигналу.
CandleType DataType TimeFrame(4h) Серия свечей для расчётов.
Volume decimal 1 Объём заявок при открытии позиции.

Варианты сглаживания

В оригинальном индикаторе доступны дополнительные режимы. В StockSharp они сопоставлены встроенным средним:

Концепция MQL Значение SmoothingMethod Примечание
SMA Simple Простое скользящее среднее.
EMA Exponential Экспоненциальное скользящее среднее.
SMMA Smoothed Сглаженное среднее (по Уайлдеру).
LWMA Weighted Линейно-взвешенное среднее.
JJMA / JurX / ParMA / T3 / VIDYA / AMA DoubleExponential, TripleExponential, Hull, ZeroLagExponential, ArnaudLegoux, KaufmanAdaptive Подберите ближайший аналог адаптивного сглаживания.

Отличия от версии MQL

  • Выбор между тиковым и реальным объёмом отсутствует — используются объёмы свечей StockSharp.
  • Управление позицией выполнено рыночными заявками; задержка исполнения реализована параметром SignalShift.
  • Стоп-лосс и тейк-профит необходимо настраивать отдельно (например, через StartProtection или правила риска).

Рекомендации

  • Подбирайте таймфрейм под ликвидность инструмента; значение 4 часа соответствует исходному советнику.
  • Увеличивайте SignalShift, если требуется дополнительное подтверждение сигнала.
  • Используйте защитные правила и контроль капитала для ограничения убытков.
using System;
using System.Collections.Generic;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

public class ExpCronexMfiStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;

	private ExponentialMovingAverage _fast;
	private ExponentialMovingAverage _slow;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;
	private int _cooldown;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
	public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }

	public ExpCronexMfiStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
		_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
		_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
		_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
	}

	protected override void OnReseted()
	{
		base.OnReseted();
		_fast = null; _slow = null;
		_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_fast = new ExponentialMovingAverage { Length = FastPeriod };
		_slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
		subscription.Bind(_fast, _slow, ProcessCandle);
		subscription.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
		if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }

		var close = candle.ClosePrice;
		var step = Security?.PriceStep ?? 1m;

		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}
		else if (Position < 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}

		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }

		_prevFast = fastValue; _prevSlow = slowValue;
	}
}