Открыть на GitHub

Стратегия iMA iSAR EA

Обзор

Стратегия воспроизводит советник "iMA iSAR EA" из MetaTrader 5 с использованием высокоуровневого API StockSharp. Она сочетает три взвешенные скользящие средние и две кривые Parabolic SAR, чтобы отбирать импульсные прорывы. Длинная позиция открывается, когда быстрая скользящая средняя находится выше двух остальных, а обе линии SAR располагаются ниже цены закрытия. Зеркальное условие формирует короткий сигнал. Управление рисками выполняется в пунктах (pips): фиксированные стоп-лоссы, тейк-профиты и опциональный трейлинг-стоп.

Реализация использует единственный поток свечей, выбираемый параметром CandleType. Все индикаторы рассчитываются в этом таймфрейме. В оригинальном MQ5-файле индикаторы брались с разных периодов, поэтому в StockSharp добавлены отдельные сдвиги для каждой средней, что позволяет читать значения через указанное число закрытых баров.

Торговые правила

  • Индикаторы
    • Три взвешенные скользящие средние (быстрая, базовая и медленная) на выбранном свечном потоке. Параметры сдвига имитируют задержку буферов из MetaTrader.
    • Две линии Parabolic SAR (быстрая и базовая) используют тот же поток свечей, но имеют независимые шаги ускорения и максимальные значения.
  • Входы
    • Покупка: быстрая средняя выше базовой и медленной, а обе SAR ниже цены закрытия.
    • Продажа: быстрая средняя ниже базовой и медленной, а обе SAR выше цены закрытия.
    • При появлении обратного сигнала стратегия закрывает противоположную позицию и разворачивается одним рыночным ордером.
  • Риск-менеджмент
    • Стоп-лосс и тейк-профит заданы в пунктах (кратных шагу цены) и проверяются по завершённым свечам.
    • При включённом трейлинг-стопе уровень подтягивается за ценой с заданной дистанцией и шагом обновления.
    • Объём заявок автоматически приводится к ограничениям инструмента (VolumeStep, MinVolume, MaxVolume).

Параметры

Имя Тип Значение по умолчанию Описание
Volume decimal 0.1 Базовый объём заявки; при развороте добавляется объём для закрытия противоположной позиции.
StopLossPips decimal 50 Дистанция стоп-лосса в пунктах. 0 — без стопа.
TakeProfitPips decimal 50 Дистанция тейк-профита в пунктах. 0 — без цели.
UseTrailing bool true Включает сопровождение трейлинг-стопом.
TrailingStopPips decimal 25 Дистанция между ценой и трейлинг-стопом (в пунктах).
TrailingStepPips decimal 5 Минимальное благоприятное движение (в пунктах) перед подтягиванием трейлинг-стопа.
CandleType DataType Свечи 15 минут Свечной поток для всех расчётов.
FastMaPeriod int 10 Период быстрой взвешенной средней.
FastMaShift int 0 Сдвиг (количество закрытых баров) для быстрой средней.
NormalMaPeriod int 30 Период базовой взвешенной средней.
NormalMaShift int 3 Сдвиг для базовой средней.
SlowMaPeriod int 60 Период медленной взвешенной средней.
SlowMaShift int 6 Сдвиг для медленной средней.
FastSarStep decimal 0.02 Шаг ускорения для быстрой SAR.
FastSarMax decimal 0.2 Максимальное ускорение для быстрой SAR.
NormalSarStep decimal 0.02 Шаг ускорения для базовой SAR.
NormalSarMax decimal 0.2 Максимальное ускорение для базовой SAR.

Примечания

  • Проверка трейлинг-стопа выполняется на закрытии свечи; для внутридневного контроля можно подключить компонент, работающий с тиковыми данными.
  • При наличии PriceStep он используется как величина пункта, иначе предполагается стандартный шаг 0.0001 для валютных пар.
  • Как и в оригинале, сигналы формируются только на закрытых барах; отложенные заявки не используются — подаются рыночные ордера.
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 ImaIsarEaStrategy : 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 ImaIsarEaStrategy()
	{
		_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;
	}
}