Auf GitHub ansehen

HPCS Inter5-Strategie

Überblick

Die HPCS Inter5-Strategie ist ein Single-Shot-Momentum-Skript, das vom MetaTrader 4-Experten _HPCS_Inter5_MT4_EA_V01_WE konvertiert wurde. Wenn die Strategie startet, prüft sie die letzten abgeschlossenen Kerzen und sendet einen Marktkaufauftrag, wenn der Schlusskurs von vor fünf Balken höher ist als der letzte Schlusskurs. Optionale schützende Stop-Loss- und Take-Profit-Distanzen emulieren das Pip-basierte Verhalten des ursprünglichen EA.

Handelslogik

  1. Abonnieren Sie die konfigurierte Kerzenserie und behalten Sie die letzten sechs abgeschlossenen Abschlüsse bei.
  2. Nachdem der Puffer gefüllt ist, vergleichen Sie den Schlusskurs von vor fünf Balken mit dem letzten Schlusskurs (Close[5] > Close[1] in MetaTrader-Begriffen).
  3. Wenn die Bedingung erfüllt ist und noch kein Handel getätigt wurde, senden Sie eine Marktkauforder mit dem konfigurierten Volumen.
  4. Schutzbefehle werden einmalig beim Start bis StartProtection aktiviert, wobei eine Pip-Umrechnung im MetaTrader-Stil verwendet wird: Instrumente mit 3 oder 5 Dezimalstellen multiplizieren PriceStep mit 10, um die Pip-Größe zu bestimmen, andernfalls wird die Rohgröße PriceStep verwendet.

Die Strategie eröffnet keine zusätzlichen Trades und ignoriert jedes nachfolgende Signal, sobald die erste Position besetzt ist.

Parameter

Name Standard Beschreibung
Candle Type Zeitrahmen von 1 Minute Kerzentyp, der zur Erfassung der Schlusskurse verwendet wird. Stellen Sie den Zeitrahmen ein, der Ihrem gewünschten Signalintervall entspricht.
Stop Loss (pips) 10 Abstand für den schützenden Stop-Loss in MetaTrader Pips. Ein Wert von 0 deaktiviert den Stopp.
Take Profit (pips) 10 Abstand für den schützenden Take-Profit in MetaTrader Pips. Ein Wert von 0 deaktiviert den Take-Profit.
Trade Volume 1 Volumen der Market-Order, die bei Auslösung der Eintrittsbedingung übermittelt wird.

Implementierungshinweise

  • Die Strategie erfordert einen konfigurierten Security.PriceStep (oder Security.Step), um Pip-Abstände umzuwandeln. Fehlt diese Information, bleiben die Schutzoffsets inaktiv, das Einfahrsignal funktioniert jedoch weiterhin.
  • Nur fertige Kerzen (CandleStates.Finished) werden verarbeitet, um dem MetaTrader-Verhalten zu entsprechen, das auf Close[1] und älteren Werten basiert.
  • Der interne Puffer speichert genau sechs Schließungen, ohne die Indikatorhistorie zu verwenden, wobei der minimalistische Charakter der Quelle EA berücksichtigt wird.
  • IsFormedAndOnlineAndAllowTrading() wird vor dem Senden der Bestellung überprüft, um sicherzustellen, dass die Umgebung zur Ausführung bereit ist.

Nutzungstipps

  1. Weisen Sie ein Forex-Instrument mit den richtigen Preis- und Volumeneinstellungen zu.
  2. Passen Sie Candle Type an den Zeitrahmen an, den Sie analysieren möchten.
  3. Lassen Sie den Stop-Loss oder Take-Profit bei Null, wenn Sie Exits lieber manuell verwalten möchten.
  4. Starten Sie die Strategie immer dann neu, wenn Sie die Eintrittsbedingung neu bewerten möchten, da sie nur einmal pro Sitzung ausgelöst wird.
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;
	}
}