View on GitHub

E-Friday Strategy

Overview

  • Converts the original MetaTrader expert advisor E-Friday.mq5 to the StockSharp high-level API.
  • Trades only when the chart time frame is H1 or lower; otherwise the strategy logs a warning and stays flat.
  • Enters positions in a contrarian fashion: a bearish candle opens a long position and a bullish candle opens a short position.
  • Completely disables trading every Friday to match the original weekend-protection behaviour.
  • Restricts trading to a configurable time window and can force positions to be closed after the session end.

Trading Logic

  1. At each finished candle the strategy checks the current exchange time:
    • if the day is Friday it skips any action;
    • if the hour is before the configured start hour it waits;
    • if the closing window is enabled and the hour is above the end hour it flattens every position and skips new entries.
  2. When trading is allowed the last completed candle drives the signal:
    • if Open > Close (bearish body) the strategy prepares a long entry;
    • if Open < Close (bullish body) the strategy prepares a short entry;
    • equal open and close prices cancel every pending action.
  3. Before entering a new position the current exposure is flattened, so there is never more than one net position.

Position Management

  • Lot size – taken from TradeVolume and sent to BuyMarket / SellMarket orders.
  • Stop loss & take profit – measured in pips. Pips are calculated from Security.PriceStep and multiplied by 10 when the instrument has 3 or 5 decimal places, exactly as in the MQL version.
  • Trailing stop – activates once price moves by TrailingStopPips + TrailingStepPips in favour of the position. The stop is tightened to current price - trailing stop (long) or current price + trailing stop (short).
  • Exits are evaluated using candle extrema:
    • a long position closes if the candle low touches the stop or the high reaches the take profit;
    • a short position closes if the candle high touches the stop or the low reaches the take profit.
  • After the session end hour (when UseCloseHour = true) every open position is closed via market orders.

Parameters

Name Description
CandleType Time frame of the processed candles. Must define a positive TimeSpan and should not exceed one hour.
TradeVolume Order volume in lots. Must be positive.
StopLossPips Distance from the entry price to the protective stop, expressed in pips. Set to zero to disable the initial stop.
TakeProfitPips Distance from the entry price to the profit target in pips. Set to zero to disable the target.
TrailingStopPips Trailing stop distance in pips. Works together with TrailingStepPips.
TrailingStepPips Minimal additional progress (in pips) required before the trailing stop is tightened. Must be positive when the trailing stop is enabled.
StartHour Hour (exchange time) when the strategy may start opening positions.
UseCloseHour Enables or disables forced closing after the end hour.
EndHour Hour (exchange time) after which the strategy stops trading and closes existing positions.

Implementation Notes

  • Uses SubscribeCandles and the high-level Bind API so that indicators can be added later if necessary.
  • Validates the trailing configuration at start-up: when a trailing stop is requested the trailing step must be strictly positive.
  • Pip conversion mirrors the original EA logic (PriceStep * 10 for 3/5-digit symbols) to keep stop-loss distances consistent.
  • The StockSharp version evaluates stops and targets once per finished candle. The original EA ran on every tick, therefore the StockSharp port may exit a few ticks later but the logic remains equivalent.
  • The strategy explicitly calls CloseActivePosition when the session window ends. The MQL script contained the same idea but returned before reaching the closing routine; the C# version implements the intended behaviour.
  • Informational logs (AddInfoLog / AddWarningLog) are used to surface skipped trading periods to the user interface.
using System;
using System.Collections.Generic;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// E-Friday strategy using EMA crossover.
/// Buys when fast EMA crosses above slow EMA, sells on reverse.
/// </summary>
public class EFridayStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;

	private ExponentialMovingAverage _fast;
	private ExponentialMovingAverage _slow;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;
	private int _cooldown;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
	public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }

	public EFridayStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
		_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
		_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
		_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
	}

	protected override void OnReseted()
	{
		base.OnReseted();
		_fast = null; _slow = null;
		_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_fast = new ExponentialMovingAverage { Length = FastPeriod };
		_slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
		subscription.Bind(_fast, _slow, ProcessCandle);
		subscription.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
		if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }

		var close = candle.ClosePrice;
		var step = Security?.PriceStep ?? 1m;

		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}
		else if (Position < 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}

		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }

		_prevFast = fastValue; _prevSlow = slowValue;
	}
}