Открыть на GitHub

Стратегия Farhad Crab

Обзор

Farhad Crab — это порт оригинального советника MetaTrader FarhadCrab1.mq4 на высокоуровневый API StockSharp. Изначально алгоритм предназначался для минутных графиков пар GBP/JPY, GBP/USD и EUR/USD и комбинировал агрессивные входы по движению с динамическим сопровождением позиций. В новой реализации вся логика переписана на C#, а обмен данными происходит исключительно через подписки StockSharp.

В рабочем таймфрейме стратегия рассчитывает две скользящие средние: 9-периодную EMA от типичной цены и 9-периодную SMA от цен открытия свечи. Дополнительно ведётся дневная 55-периодная сглаженная средняя (SMMA), служащая «страховкой» от глобального разворота. Если минимум текущей свечи держится выше EMA и позиция отсутствует, открывается покупка. Если максимум остаётся ниже SMA от открытий, открывается продажа. Пересечение дневной SMMA с ценой закрытия в противоположную сторону немедленно закрывает позицию.

Система управления выходами повторяет настройки советника: отдельные значения тейк-профита (в пунктах) для длинных и коротких сделок, а также независимые параметры трейлинг-стопа. Трейлинг активируется только после движения цены на заданную величину, как и в MetaTrader. Закрытия выполняются рыночными заявками, что соответствует событиям высокоуровневого API.

Ключевые особенности

  • Полное соответствие индикаторной базе — EMA(9) по типичной цене, SMA(9) по ценам открытия и дневная SMMA(55).
  • Работа с несколькими таймфреймами — одновременная подписка на торговый и дневной таймфреймы без ручного накопления данных.
  • Настраиваемые выходы — тейк-профиты и трейлинг-стопы в пунктах, доступные для оптимизации.
  • Дневной предохранитель — строгая реализация условия закрытия при пересечении дневной SMMA и цены закрытия.
  • Защита позиций — однократный вызов StartProtection() в момент запуска стратегии.

Параметры

Параметр Описание Значение по умолчанию
OrderVolume Объём сделки при открытии рынка. 0.1
LongTakeProfitPips Дистанция тейк-профита для покупок в пунктах. 10
ShortTakeProfitPips Дистанция тейк-профита для продаж в пунктах. 10
LongTrailingStopPips Дистанция трейлинг-стопа для покупок (0 отключает). 8
ShortTrailingStopPips Дистанция трейлинг-стопа для продаж (0 отключает). 8
DailyMaPeriod Период дневной сглаженной средней. 55
CandleType Основной таймфрейм стратегии (по умолчанию 1 минута). 1m

Все параметры объявлены через StrategyParam<T> и помечены для оптимизации там, где это имеет смысл, поэтому стратегию легко адаптировать под конкретный инструмент.

Правила торговли

  1. Открытие лонга: минимум свечи находится выше EMA(9) по типичной цене, позиция отсутствует.
  2. Открытие шорта: максимум свечи находится ниже SMA(9) по ценам открытия, позиция отсутствует.
  3. Защитное закрытие лонга: дневная SMMA закрывается выше цены закрытия при условии, что ранее была ниже.
  4. Защитное закрытие шорта: дневная SMMA закрывается ниже цены закрытия при условии, что ранее была выше.
  5. Тейк-профит: достижение целевого количества пунктов закрывает позицию.
  6. Трейлинг-стоп: после движения в прибыль на заданное расстояние отслеживается обратный ход и при откате позиция закрывается.

Особенности реализации

  • Вся обработка данных выполняется через SubscribeCandles().Bind(...), что исключает прямой доступ к буферам индикаторов и соответствует внутренним правилам репозитория.
  • Размер пункта вычисляется на основании PriceStep с учётом пятого и третьего знаков, как в MetaTrader, поэтому параметры в пунктах работают идентично.
  • Стоп-приказы не выставляются, вместо этого стратегия контролирует цену и закрывает позиции рыночными ордерами при наступлении условий.
  • Метод OnReseted очищает все поля, благодаря чему повторные запуски и оптимизации выполняются из чистого состояния.

Рекомендации по использованию

  • Для повторения исходных результатов используйте минутные свечи по основным валютным парам. Тем не менее параметры позволяют адаптироваться к другим инструментам и волатильности.
  • Стратегия всегда держит только одну позицию, что упрощает мониторинг и анализ сделок.
  • В условиях бокового рынка можно сократить расстояние трейлинг-стопа или сосредоточиться на тейк-профите, чтобы уменьшить число ложных выходов.
  • Увеличение DailyMaPeriod сделает фильтр по дневной средне менее чувствительным, что подойдёт для более долгосрочных сценариев.
using System;
using System.Collections.Generic;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Farhad Crab strategy - EMA and SMA crossover with trend filter.
/// Buys when EMA crosses above SMA.
/// Sells when EMA crosses below SMA.
/// </summary>
public class FarhadCrabStrategy : Strategy
{
	private readonly StrategyParam<int> _emaPeriod;
	private readonly StrategyParam<int> _smaPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevEma;
	private decimal _prevSma;
	private bool _hasPrev;

	public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
	public int SmaPeriod { get => _smaPeriod.Value; set => _smaPeriod.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public FarhadCrabStrategy()
	{
		_emaPeriod = Param(nameof(EmaPeriod), 10)
			.SetDisplay("EMA Period", "EMA lookback", "Indicators");

		_smaPeriod = Param(nameof(SmaPeriod), 20)
			.SetDisplay("SMA Period", "SMA lookback", "Indicators");

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(10).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
	protected override void OnReseted() { base.OnReseted(); _prevEma = 0m; _prevSma = 0m; _hasPrev = false; }

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

		_hasPrev = false;

		var ema = new ExponentialMovingAverage { Length = EmaPeriod };
		var sma = new SimpleMovingAverage { Length = SmaPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(ema, sma, ProcessCandle)
			.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal ema, decimal sma)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (!_hasPrev)
		{
			_prevEma = ema;
			_prevSma = sma;
			_hasPrev = true;
			return;
		}

		if (_prevEma <= _prevSma && ema > sma && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		else if (_prevEma >= _prevSma && ema < sma && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}

		_prevEma = ema;
		_prevSma = sma;
	}
}