Открыть на GitHub

Стратегия Farhad Hill Version 2 (C#)

Обзор

Стратегия представляет собой порт эксперта MetaTrader "Farhad Hill Version 2" на платформу StockSharp. Она объединяет несколько индикаторных фильтров для поиска разворотных точек на валютных парах. В переносе сохранены индикаторы оригинала (MACD, Stochastic, Parabolic SAR, Momentum и опциональный крест скользящих средних), а также логика управления капиталом и сопровождения позиции.

Стратегия работает на одной таймфреймной подписке (по умолчанию 30-минутные свечи) и держит не более одной позиции одновременно. Для защиты используются стоп-лосс, тейк-профит и три режима трейлинг-стопа, как в MQL-версии. Все комментарии в коде оставлены на английском языке в соответствии с требованиями.

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

  • Фильтр MACD – при включении лонг допускается, если основная линия MACD ниже сигнальной, шорт – если основная линия выше сигнальной.
  • Уровневой фильтр Stochastic – для покупок %K должен находиться ниже нижнего порога, для продаж – выше верхнего порога. При активном опциональном фильтре пересечения дополнительно требуется бычий крест %K/%D для входа в покупку и медвежий крест для входа в продажу.
  • Фильтр Parabolic SAR – покупки возможны, когда SAR ниже цены и делает шаг вниз, продажи – когда SAR выше цены и движется вверх. В порте используются цены закрытия свечей.
  • Фильтр Momentum – рассчитывается по ценам открытия, как в исходном советнике. Для покупок Momentum должен быть ниже нижнего порога, для продаж – выше верхнего порога.
  • Пересечение скользящих (опционально) – задаются тип, применяемая цена и периоды. Для покупок быстрая скользящая должна быть выше медленной, для продаж – ниже.

Проверка сигналов выполняется только на завершённых свечах. Новые заявки не открываются, если уже есть открытая позиция. Для входа используются рыночные ордера с расчётным объёмом.

Управление позицией

  • Стоп-лосс / Тейк-профит – задаются в пунктах. Размер пункта берётся из PriceStep инструмента, при отсутствии используется значение 0.0001.
  • Типы трейлинг-стопов
    1. Немедленный – стоп следует за ценой на заданной дистанции, как только рынок проходит стоп-дистанцию.
    2. Отложенный – сначала ждёт движения на заданное количество пунктов, затем подтягивает стоп на фиксированном расстоянии.
    3. Трёхступенчатый – воспроизводит три уровня фиксации прибыли с двумя шагами к безубытку и финальным трейлингом.
  • Защитные приказы выставляются как видимые на бирже SellStop/BuyStop (стоп-лосс) и SellLimit/BuyLimit (тейк-профит).

Управление капиталом

  • Фиксированный лот – торгует заданным объёмом. При активном флаге AccountIsMini объём переводится в мини-лоты с минимальным шагом 0.1.
  • Процент от капитала – воспроизводит формулу оригинала floor(FreeMargin * percent / 10000) / 10, ограниченную параметром MaxLots и учитывающую мини-счёт. Если значение портфеля недоступно, стратегия использует фиксированный объём.

Параметры

Все настройки вынесены в StrategyParam<T> и могут оптимизироваться или изменяться из интерфейса. Основные группы:

Группа Параметр Описание
General CandleType Таймфрейм свечей для сигналов
Money Management AccountIsMini, UseMoneyManagement, TradeSizePercent, FixedVolume, MaxLots
Risk StopLossPips, TakeProfitPips, UseTrailingStop, TrailingStopType, TrailingStopPips, FirstMovePips, TrailingStop1, SecondMovePips, TrailingStop2, ThirdMovePips, TrailingStop3
Indicators UseMacd, UseStochasticLevel, UseStochasticCross, UseParabolicSar, UseMomentum, UseMovingAverageCross, MacdFast, MacdSlow, MacdSignal, StochasticK, StochasticD, StochasticSlowing, StochasticHigh, StochasticLow, MomentumPeriod, MomentumHigh, MomentumLow, SlowMaPeriod, FastMaPeriod, MaMode, MaPrice

Примечания и допущения

  • Для Parabolic SAR используется сравнение с ценой закрытия, что делает расчёты детерминированными на истории.
  • Для расчёта объёма в режиме money management требуется подключённый портфель; без него применяется фиксированный лот.
  • Все индикаторные проверки выполняются только на закрытых свечах, чтобы исключить ложные сигналы.

Файлы

  • CS/FarhadHillVersion2Strategy.cs – реализация стратегии на C#.
  • README.md – документация на английском.
  • README_ru.md – данный файл с описанием на русском.
  • README_zh.md – описание на китайском языке.
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 Hill V2 strategy - EMA + Momentum trend follower.
/// Buys when fast EMA crosses above slow EMA with positive momentum.
/// Sells on bearish crossover with negative momentum.
/// </summary>
public class FarhadHillVersion2Strategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _momentumPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevFast;
	private decimal _prevSlow;
	private bool _hasPrev;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int MomentumPeriod { get => _momentumPeriod.Value; set => _momentumPeriod.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public FarhadHillVersion2Strategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 8)
			.SetDisplay("Fast EMA", "Fast EMA period", "Indicators");

		_slowPeriod = Param(nameof(SlowPeriod), 21)
			.SetDisplay("Slow EMA", "Slow EMA period", "Indicators");

		_momentumPeriod = Param(nameof(MomentumPeriod), 10)
			.SetDisplay("Momentum", "Momentum period", "Indicators");

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

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

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

		_hasPrev = false;

		var fast = new ExponentialMovingAverage { Length = FastPeriod };
		var slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var momentum = new Momentum { Length = MomentumPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(fast, slow, momentum, ProcessCandle)
			.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow, decimal momentum)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (!_hasPrev)
		{
			_prevFast = fast;
			_prevSlow = slow;
			_hasPrev = true;
			return;
		}

		var crossUp = _prevFast <= _prevSlow && fast > slow;
		var crossDown = _prevFast >= _prevSlow && fast < slow;

		if (crossUp && momentum > 0 && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		else if (crossDown && momentum < 0 && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}

		_prevFast = fast;
		_prevSlow = slow;
	}
}