View on GitHub

HPCS Inter4 Strategy (3518)

Overview

This strategy ports the MetaTrader expert advisor "_HPCS_IntFourth_MT4_EA_V01_WE" to the StockSharp high-level API. The original script immediately opens a long position, applies protective stop-loss and take-profit levels measured in MetaTrader pips, and forcefully closes the trade after a short holding period. The C# version reproduces the same behaviour by combining the built-in protection manager with a one-second timer that monitors the elapsed time since entry.

Trading Logic

  1. Initialisation

    • When the strategy starts it computes the MetaTrader pip size from the security PriceStep and decimal precision (5- and 3-digit symbols use a 10x multiplier).
    • The high-level StartProtection helper is configured with the requested take-profit and stop-loss distances. The stop-loss distance includes the extra buffer that the original EA applies using OrderModify.
    • The volume is fixed and comes from the OrderVolume parameter.
  2. Entry

    • A single market buy order is submitted immediately after the strategy is launched. No further entries are placed.
    • Once the first fill is reported the strategy stores the execution time.
  3. Exit

    • A timer checks the open position every second.
    • When the holding period reaches CloseDelaySeconds, the strategy closes the long position with a market sell order if exposure is still positive.
    • Protective stop-loss and take-profit orders are maintained automatically by the protection manager using market exits.

The logic only trades in the long direction, mirroring the behaviour of the MetaTrader script.

Parameters

Name Description Default Optimizable
OrderVolume Fixed volume used when sending the initial market buy order. 1 No
StopLossPips Base MetaTrader pip distance applied to the initial stop-loss. 10 No
ExtraStopPips Additional MetaTrader pip buffer subtracted from the stop after entry. 10 No
TakeProfitPips MetaTrader pip distance of the profit target. 10 No
CloseDelaySeconds Time in seconds before the position is forcefully closed. 0 disables the timer exit. 30 No

Implementation Notes

  • The pip size helper multiplies the reported PriceStep by 10 for 3- and 5-decimal instruments so that parameter values keep the same scale as in MetaTrader.
  • StartProtection uses UnitTypes.Price distances so that protective orders operate with market exits, exactly like the EA did with OrderClose.
  • OnNewMyTrade records the first filled buy trade to start the holding-period countdown and resets the state when the position is fully closed.
  • The timer runs at one-second intervals to replicate the original OnTick time check while remaining insensitive to market inactivity.
  • All code comments are written in English to comply with repository guidelines.
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Port of the "_HPCS_IntFourth_MT4_EA_V01_WE" MetaTrader expert advisor.
/// Uses SMA crossover to enter positions with time-based exit.
/// </summary>
public class HpcsInter4Strategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;

	private decimal _prevFast;
	private decimal _prevSlow;
	private bool _isFirstValue = true;

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public int FastPeriod
	{
		get => _fastPeriod.Value;
		set => _fastPeriod.Value = value;
	}

	public int SlowPeriod
	{
		get => _slowPeriod.Value;
		set => _slowPeriod.Value = value;
	}

	public HpcsInter4Strategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
			.SetDisplay("Candle Type", "Time frame for calculation", "General");

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

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevFast = 0;
		_prevSlow = 0;
		_isFirstValue = true;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevFast = 0;
		_prevSlow = 0;
		_isFirstValue = true;

		var fastSma = new ExponentialMovingAverage { Length = FastPeriod };
		var slowSma = new ExponentialMovingAverage { Length = SlowPeriod };

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

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, fastSma);
			DrawIndicator(area, slowSma);
			DrawOwnTrades(area);
		}
	}

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

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		if (_isFirstValue)
		{
			_prevFast = fastValue;
			_prevSlow = slowValue;
			_isFirstValue = false;
			return;
		}

		// Fast crosses above slow - buy
		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{
			BuyMarket();
		}
		// Fast crosses below slow - sell
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{
			SellMarket();
		}

		_prevFast = fastValue;
		_prevSlow = slowValue;
	}
}