Открыть на GitHub

Стратегия Parabolic SAR Flip Alert (4164)

Обзор

Стратегия переносит эксперта MetaTrader pSAR_alert2 на платформу StockSharp. Она отслеживает индикатор Parabolic SAR для выбранного инструмента и таймфрейма. Когда значение SAR переходит из области выше цены закрытия в область ниже (или наоборот), стратегия фиксирует подробное уведомление. По желанию можно автоматически отправлять рыночные заявки в сторону выявленного сигнала и тем самым превратить уведомление в торговый вход.

Логика работы

  1. Подписаться на выбранную серию свечей и рассчитать индикатор Parabolic SAR с заданными ускорениями.
  2. Обрабатывать только полностью сформированные свечи, чтобы повторить логику исходного эксперта.
  3. Сравнивать положение индикатора и цену закрытия:
    • Ранее SAR находился выше цены, а теперь опустился ниже → бычий разворот.
    • Ранее SAR находился ниже цены, а теперь поднялся выше → медвежий разворот.
  4. Записывать информационное сообщение для каждого разворота. При включенном автотрейдинге закрывать противоположные позиции и открывать новую позицию по рынку в сторону сигнала.

Параметры

Параметр Описание
Candle Type Таймфрейм свечей, используемый для расчёта Parabolic SAR.
SAR Step Начальный коэффициент ускорения Parabolic SAR.
SAR Max Максимальный коэффициент ускорения Parabolic SAR.
Enable Auto Trading При значении true выполняются рыночные сделки, при false формируются только уведомления.
Trade Volume Объём заявок при включённом автотрейдинге.

Особенности конверсии

  • В исходном скрипте MetaTrader использовался Sleep для циклического опроса. В StockSharp стратегия реагирует на события формирования свечей и не требует ручных задержек.
  • Уведомления создаются через AddInfoLog, что сохраняет поведение исходного эксперта без дополнительного пользовательского интерфейса.
  • Добавлен переключатель автоторговли. Отключите параметр Enable Auto Trading, если нужна точная копия поведения MetaTrader.
  • По запросу Python-версия не предоставляется.
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Parabolic SAR Flip: EMA trend following with ATR stops.
/// </summary>
public class ParabolicSarFlipAlertStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _emaLength;
	private readonly StrategyParam<int> _atrLength;

	private decimal _prevClose;
	private decimal _entryPrice;

	public ParabolicSarFlipAlertStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe.", "General");
		_emaLength = Param(nameof(EmaLength), 20)
			.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 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();

		_prevClose = 0; _entryPrice = 0;
	}

		protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevClose = 0; _entryPrice = 0;
		var ema = new ExponentialMovingAverage { Length = EmaLength };
		var atr = new AverageTrueRange { Length = AtrLength };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(ema, atr, ProcessCandle).Start();
		var area = CreateChartArea();
		if (area != null) { DrawCandles(area, subscription); DrawIndicator(area, ema); DrawOwnTrades(area); }
	}

	private void ProcessCandle(ICandleMessage candle, decimal emaVal, decimal atrVal)
	{
		if (candle.State != CandleStates.Finished) return;
		var close = candle.ClosePrice;
		if (_prevClose == 0 || atrVal <= 0) { _prevClose = close; return; }

		if (Position > 0)
		{
			if (close >= _entryPrice + atrVal * 2.5m || close <= _entryPrice - atrVal * 1.5m || close < emaVal) { SellMarket(); _entryPrice = 0; }
		}
		else if (Position < 0)
		{
			if (close <= _entryPrice - atrVal * 2.5m || close >= _entryPrice + atrVal * 1.5m || close > emaVal) { BuyMarket(); _entryPrice = 0; }
		}

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

		_prevClose = close;
	}
}