Auf GitHub ansehen

Myfriend Forex Instruments Strategy

The Myfriend Forex Instruments Strategy reproduces the 2006 "MyFriend" MetaTrader expert. It trades EUR/USD on 30-minute candles by combining daily pivot levels, Donchian channel expansions and a short-vs-long momentum spread measured from closing prices. The system looks for candles that pierce the daily pivot with a wide real body or for abrupt Donchian width expansions. When one of these impulses aligns with the intraday momentum bias, the strategy opens a single position with pre-defined protective levels.

Trading logic

  1. Daily pivot map – The previous day's high, low and close build the classical pivot ladder (Pivot, R1, S1). These levels remain unchanged for the entire trading session and define the expected trading range.
  2. Momentum pulse – Two simple moving averages on the closing price (3 and 9 periods) form a short/long momentum spread. The spread is multiplied by 1000 to mimic the MetaTrader "MP" calculation and determines whether bullish or bearish pressure dominates.
  3. Breakout filters
    • Pivot thrust: after a candle closes across the pivot with a body larger than 12 points and the next candle closes in the same direction, the strategy flags a potential trade.
    • Donchian expansion: when the 16-period Donchian channel widens beyond the R1 - S1 range and its direction agrees with price action, the signal is also triggered.
  4. Order management – Only one position is allowed at a time. Long entries use the previous candle low minus a buffer as the stop and a fixed 70-point take profit. Short entries mirror this logic with the previous high plus a buffer.
  5. Exit tactics
    • Time-based exit: between the 3rd and 4th candle after entry, if the last closed bar moves 3 points against the position, the trade is closed early.
    • Trailing stop: once open profit exceeds 5 points and the Donchian boundary continues to move in the trade's favor, the stop is trailed along the channel plus/minus a 1-point buffer.
    • Hard targets: price touching the calculated stop or take-profit immediately closes the position.

Parameters

Name Description Default
BaseVolume Order volume used for each new trade. 1
TakeProfitPoints Distance of the take-profit from the entry in MetaTrader points. 70
StopLossBufferPoints Additional buffer added beyond the previous candle extremum for the protective stop. 13
ChannelPeriod Donchian channel period used for width expansion tests and trailing. 16
UseTrailingStop Enables or disables the Donchian-based trailing stop. true
TrailingStartPoints Required open profit (points) before the trailing stop can tighten. 5
TrailingBufferPoints Buffer (points) applied to the Donchian boundary when trailing. 1
UseTimeClose Enables the 3–4 candle rejection exit. true
CandleType Primary candle type (default 30-minute time frame). M30
DailyCandleType Daily candle type used to rebuild pivot levels. D1

Notes

  • The strategy is designed for EUR/USD and 30-minute candles, mirroring the original expert. Different instruments or time frames may require parameter adjustments.
  • Point-based parameters rely on the instrument's PriceStep. If it is not provided by the market data, the strategy falls back to a unit price increment.
  • Only completed candles are processed, matching the MetaTrader behaviour of the source algorithm.
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>
/// MyFriend Forex strategy - Donchian channel breakout with SMA momentum filter.
/// Buys when close breaks above upper Donchian and fast SMA > slow SMA.
/// Sells when close breaks below lower Donchian and fast SMA less than slow SMA.
/// </summary>
public class MyfriendForexInstrumentsStrategy : Strategy
{
	private readonly StrategyParam<int> _channelPeriod;
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevClose;
	private decimal _prevUpper;
	private decimal _prevLower;
	private bool _hasPrev;

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

	public MyfriendForexInstrumentsStrategy()
	{
		_channelPeriod = Param(nameof(ChannelPeriod), 16)
			.SetDisplay("Channel Period", "Donchian channel period", "Indicators");

		_fastPeriod = Param(nameof(FastPeriod), 3)
			.SetDisplay("Fast SMA", "Fast SMA period", "Indicators");

		_slowPeriod = Param(nameof(SlowPeriod), 9)
			.SetDisplay("Slow SMA", "Slow SMA period", "Indicators");

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

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevClose = 0m;
		_prevUpper = 0m;
		_prevLower = 0m;
		_hasPrev = false;
	}

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

		_hasPrev = false;

		var highest = new Highest { Length = ChannelPeriod };
		var lowest = new Lowest { Length = ChannelPeriod };
		var fastSma = new SimpleMovingAverage { Length = FastPeriod };
		var slowSma = new SimpleMovingAverage { Length = SlowPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(highest, lowest, fastSma, slowSma, ProcessCandle)
			.Start();
	}

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

		var close = candle.ClosePrice;

		if (!_hasPrev)
		{
			_prevClose = close;
			_prevUpper = high;
			_prevLower = low;
			_hasPrev = true;
			return;
		}

		// Breakout above channel with bullish momentum
		if (_prevClose <= _prevUpper && close > high && fast > slow && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		// Breakout below channel with bearish momentum
		else if (_prevClose >= _prevLower && close < low && fast < slow && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}
		// Mean reversion: close crosses midpoint
		else
		{
			var mid = (high + low) / 2m;
			var prevMid = (_prevUpper + _prevLower) / 2m;

			if (_prevClose <= prevMid && close > mid && fast > slow && Position <= 0)
			{
				if (Position < 0)
					BuyMarket();
				BuyMarket();
			}
			else if (_prevClose >= prevMid && close < mid && fast < slow && Position >= 0)
			{
				if (Position > 0)
					SellMarket();
				SellMarket();
			}
		}

		_prevClose = close;
		_prevUpper = high;
		_prevLower = low;
	}
}