Auf GitHub ansehen

Farhad Hill Version 2 Strategy (C#)

Overview

This strategy is a StockSharp port of the MetaTrader expert advisor “Farhad Hill Version 2”. It combines multiple indicator filters to trade trend reversals on forex symbols. The converted logic retains the original indicator stack (MACD, Stochastic, Parabolic SAR, Momentum, and optional moving-average cross) and its money-management plus trailing behaviour.

The strategy works on a single timeframe (default 30-minute candles) and opens only one position at a time. Protective stop-loss, take-profit, and three trailing-stop styles are supported to mirror the MQL version. All comments in the code are provided in English as requested.

Trading Logic

  • MACD filter – when enabled, longs require MACD main line below the signal line; shorts require MACD main above the signal line.
  • Stochastic level filter – longs demand %K below the lower threshold, shorts demand %K above the upper threshold. When the optional cross filter is enabled, a bullish %K/%D cross (from below to above) is required for longs and a bearish cross for shorts.
  • Parabolic SAR filter – longs require SAR below price with a downward step (previous SAR higher than current); shorts require SAR above price with an upward step. The conversion uses closed candle prices as the reference.
  • Momentum filter – calculated on candle open prices, matching the MQL settings. Longs need momentum below the lower threshold, shorts need momentum above the upper threshold.
  • Moving-average cross (optional) – configurable MA type, applied price and periods. Longs need the fast MA above the slow MA; shorts need the inverse relationship.

The strategy only evaluates signals on finished candles and skips new entries when an open position exists. Entries are placed with market orders using the calculated lot size.

Position Management

  • Stop-loss / Take-profit – specified in pips. A pip is derived from the instrument’s PriceStep, falling back to 0.0001 if unavailable.
  • Trailing stop types
    1. Immediate – once price moves beyond the stop distance, the stop follows the price.
    2. Delayed – waits for price to move by the trailing distance from the entry before trailing at a fixed offset.
    3. Three-stage – reproduces the original three-level logic with two break-even steps and a final trailing distance.
  • Protective orders are placed with SellStop/BuyStop (for stop-loss) and SellLimit/BuyLimit (for take-profit) so they remain visible on the exchange.

Money Management

  • Fixed lot – trades the configured fixed volume. If AccountIsMini is enabled, lots are converted to mini-lot sizing with a minimum of 0.1.
  • Percentage risk – replicates the original formula floor(FreeMargin * percent / 10000) / 10, clamped by the MaxLots limit and adjusted for mini accounts when required. If the portfolio value is unavailable, the strategy falls back to the fixed lot.

Parameters

All parameters are exposed through StrategyParam<T> objects and can be optimised or changed from the UI. Key groups:

Group Parameter Description
General CandleType Timeframe of candles used for signals
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

Notes and Assumptions

  • Parabolic SAR comparisons use the candle close price to approximate bid/ask checks from MT4. This keeps the logic deterministic on historical data.
  • Money management requires a connected portfolio to obtain current equity; otherwise the fixed volume is used.
  • Indicator combinations are processed on completed candles only, avoiding premature signals on partial data.

Files

  • CS/FarhadHillVersion2Strategy.cs – C# implementation of the strategy.
  • README.md – This document.
  • README_ru.md – Russian translation.
  • README_zh.md – Chinese translation.
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;
	}
}