GitHub で見る

HPCS Inter5 Strategy

Overview

The HPCS Inter5 Strategy is a single-shot momentum script converted from the MetaTrader 4 expert _HPCS_Inter5_MT4_EA_V01_WE. When the strategy starts it inspects the latest completed candles and, if the close price from five bars ago is higher than the most recent close, it submits one market buy order. Optional protective stop-loss and take-profit distances emulate the pip-based behaviour of the original EA.

Trading Logic

  1. Subscribe to the configured candle series and maintain the last six completed closes.
  2. After the buffer is filled, compare the close from five bars ago with the latest close (Close[5] > Close[1] in MetaTrader terms).
  3. If the condition is satisfied and no trade has been placed yet, send a market buy order with the configured volume.
  4. Protective orders are armed once at start-up through StartProtection, using MetaTrader-style pip conversion: instruments with 3 or 5 decimals multiply PriceStep by 10 to determine the pip size, otherwise the raw PriceStep is used.

The strategy does not open additional trades and ignores every subsequent signal once the first position is filled.

Parameters

Name Default Description
Candle Type 1 minute time frame Candle type used to collect the close prices. Set it to the timeframe that matches your desired signal interval.
Stop Loss (pips) 10 Distance for the protective stop-loss in MetaTrader pips. A value of 0 disables the stop.
Take Profit (pips) 10 Distance for the protective take-profit in MetaTrader pips. A value of 0 disables the take profit.
Trade Volume 1 Volume of the market order submitted when the entry condition triggers.

Implementation Notes

  • The strategy requires a configured Security.PriceStep (or Security.Step) to convert pip distances. If this information is missing the protective offsets remain inactive but the entry signal still works.
  • Only finished candles (CandleStates.Finished) are processed to match the MetaTrader behaviour that relies on Close[1] and older values.
  • The internal buffer holds exactly six closes without using indicator history, respecting the minimalistic nature of the source EA.
  • IsFormedAndOnlineAndAllowTrading() is checked before sending the order to ensure the environment is ready for execution.

Usage Tips

  1. Assign a Forex instrument with proper price and volume settings.
  2. Adjust the Candle Type to match the timeframe you want to analyse.
  3. Leave the stop-loss or take-profit at zero if you prefer to manage exits manually.
  4. Restart the strategy whenever you want to re-evaluate the entry condition because it fires only once per session.
using System;
using System.Linq;
using System.Collections.Generic;

using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;

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

using StockSharp.Algo;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Single-shot momentum strategy converted from the "_HPCS_Inter5" MetaTrader script.
/// Places one market buy when the close price from five bars ago exceeds the most recent close.
/// </summary>
public class HpcsInter5Strategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _stopLossPips;
	private readonly StrategyParam<int> _takeProfitPips;
	private readonly StrategyParam<decimal> _tradeVolume;

	private readonly decimal?[] _recentCloses = new decimal?[6];
	private decimal _pipSize;
	private bool _wasLongSignal;
	private bool _hasSignal;

	/// <summary>
	/// Candle type used to evaluate the closing prices.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Stop-loss distance expressed in MetaTrader-style pips.
	/// </summary>
	public int StopLossPips
	{
		get => _stopLossPips.Value;
		set => _stopLossPips.Value = value;
	}

	/// <summary>
	/// Take-profit distance expressed in MetaTrader-style pips.
	/// </summary>
	public int TakeProfitPips
	{
		get => _takeProfitPips.Value;
		set => _takeProfitPips.Value = value;
	}

	/// <summary>
	/// Trade volume submitted with the market entry.
	/// </summary>
	public decimal TradeVolume
	{
		get => _tradeVolume.Value;
		set => _tradeVolume.Value = value;
	}

	/// <summary>
	/// Initializes a new instance of the <see cref="HpcsInter5Strategy"/> class.
	/// </summary>
	public HpcsInter5Strategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(120).TimeFrame())
			.SetDisplay("Candle Type", "Candle type used for the close comparison", "General");

		_stopLossPips = Param(nameof(StopLossPips), 20)
			.SetDisplay("Stop Loss (pips)", "Stop-loss distance expressed in pips", "Risk Management");

		_takeProfitPips = Param(nameof(TakeProfitPips), 20)
			.SetDisplay("Take Profit (pips)", "Take-profit distance expressed in pips", "Risk Management");

		_tradeVolume = Param(nameof(TradeVolume), 1m)
			.SetGreaterThanZero()
			.SetDisplay("Trade Volume", "Volume submitted with the market entry", "Trading");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		Array.Clear(_recentCloses, 0, _recentCloses.Length);
		_pipSize = 0m;
		_wasLongSignal = false;
		_hasSignal = false;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		if (Security is null)
			throw new InvalidOperationException("Security must be assigned before starting the strategy.");

		base.OnStarted2(time);

		InitializePipSize();
		Volume = TradeVolume;
		_wasLongSignal = false;
		_hasSignal = false;

		var stopLoss = StopLossPips > 0 && _pipSize > 0m
			? new Unit(StopLossPips * _pipSize, UnitTypes.Absolute)
			: null;

		var takeProfit = TakeProfitPips > 0 && _pipSize > 0m
			? new Unit(TakeProfitPips * _pipSize, UnitTypes.Absolute)
			: null;

		if (stopLoss != null || takeProfit != null)
			StartProtection(stopLoss: stopLoss, takeProfit: takeProfit);

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(ProcessCandle)
			.Start();
	}

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

		ShiftCloses(candle.ClosePrice);

		if (_recentCloses[1] is not decimal lastClose || _recentCloses[5] is not decimal olderClose)
			return;

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		var threshold = lastClose * 0.005m;
		var longSignal = olderClose - lastClose > threshold;
		var shortSignal = lastClose - olderClose > threshold;
		var crossedLong = longSignal && (!_hasSignal || !_wasLongSignal);
		var crossedShort = shortSignal && (!_hasSignal || _wasLongSignal);

		if (crossedLong && Position <= 0)
		{
			BuyMarket();
		}
		else if (crossedShort && Position >= 0)
		{
			SellMarket();
		}

		if (longSignal || shortSignal)
		{
			_wasLongSignal = longSignal;
			_hasSignal = true;
		}
	}

	private void ShiftCloses(decimal close)
	{
		for (var i = _recentCloses.Length - 1; i > 0; i--)
			_recentCloses[i] = _recentCloses[i - 1];

		_recentCloses[0] = close;
	}

	private void InitializePipSize()
	{
		var step = Security.PriceStep ?? 0.01m;
		if (step <= 0m)
			step = 0.01m;

		var pipFactor = Security.Decimals is 3 or 5 ? 10m : 1m;
		_pipSize = step * pipFactor;
	}
}