Auf GitHub ansehen

Cheduecoglioni Wechselnde Strategie

Überblick

Diese Strategie ist ein StockSharp-Port des MQL5 Expert Advisors "cheduecoglioni". Sie hält den Trader immer im Markt, indem sie zwischen Short- und Long-Positionen wechselt. Jeder Einstieg wird mit festen Take-Profit- und Stop-Loss-Niveaus geschützt, die in Pips definiert und gemäß der Instrumentenpräzision in Preisabstände umgerechnet werden.

Handelsregeln

  • Die Strategie hört auf die konfigurierte Kerzenserie (standardmäßig 1 Minute) und reagiert nur, wenn eine Kerze vollständig geschlossen ist. Dieses Ereignis ersetzt die tick-basierte Schleife des ursprünglichen Expert Advisors.
  • Wenn keine offene Position vorhanden ist und keine Marktorder auf Ausführung wartet, sendet die Strategie eine Marktorder in der im _nextSide-Zustand gespeicherten Richtung. Der allererste Trade nach dem Start ist ein Verkauf, entsprechend der MQL5-Implementierung.
  • Sobald eine Position aktiv wird, wartet der Algorithmus darauf, dass sie durch die Schutzorders oder manuelle Intervention geschlossen wird. Sobald die Position auf null zurückkehrt, wechselt die nächste Richtung, sodass der folgende Trade in der entgegengesetzten Richtung erfolgt.
  • Stop-Loss- und Take-Profit-Abstände werden automatisch von StartProtection angewendet, sodass jeder Trade die konfigurierten Risiko-Rendite-Abstände trägt.

Parameter

  • Trade Volume – Volumen für jeden Markteinstieg. Dies spiegelt den InpLots-Input wider.
  • Take Profit (pips) – Abstand in Pips für die Take-Profit-Order. Die Strategie konvertiert ihn in einen absoluten Preisabstand unter Verwendung der erkannten Pip-Größe.
  • Stop Loss (pips) – Abstand in Pips für den Schutz-Stop-Loss, mit derselben Pip-Größen-Logik umgerechnet.
  • Candle Type – Zeitrahmen der Kerzen, die den Entscheidungszyklus antreiben. Jeder unterstützte DataType kann angegeben werden.

Implementierungsdetails

  • Die Pip-Größe wird aus Security.PriceStep abgeleitet. Für 3- oder 5-stellige FX-Symbole wird der Wert mit 10 multipliziert, um vom Bruchteil-Pip zum Standard-Pip zu wechseln, was die MQL-Anpassung repliziert.
  • Ein Wartezeichen verhindert doppelte Marktorders, während eine vorherige Order auf Ausführung wartet. Wenn der Broker die Order ablehnt, löscht OnOrderFailed das Zeichen, sodass die nächste Kerze einen neuen Versuch starten kann.
  • OnPositionChanged verfolgt die Seite der aktiven Position und schaltet _nextSide nach jedem Flachzustand um. Dies spiegelt die MQL-Logik wider, die die entgegengesetzte Seite nach jedem Ausstieg öffnete.
  • Schutzorders werden von StartProtection mit Marktausstiegen verwaltet, was der sofortigen Stop-Loss- und Take-Profit-Zuweisung entspricht, die der Expert Advisor bei der Orderplatzierung vornahm.

Hinweise

  • Die Python-Version wurde absichtlich noch nicht erstellt.
  • Die Strategie ändert keine Unit-Tests.
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>
/// Alternates buy and sell market orders with fixed stop loss and take profit distances.
/// </summary>
public class CheduecoglioniAlternatingStrategy : Strategy
{
	private readonly StrategyParam<decimal> _tradeVolume;
	private readonly StrategyParam<decimal> _takeProfitPips;
	private readonly StrategyParam<decimal> _stopLossPips;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _pipSize;
	private Sides _nextSide;
	private Sides? _activeSide;

	/// <summary>
	/// Initializes a new instance of the <see cref="CheduecoglioniAlternatingStrategy"/> class.
	/// </summary>
	public CheduecoglioniAlternatingStrategy()
	{
		_tradeVolume = Param(nameof(TradeVolume), 1m)
			.SetDisplay("Trade Volume", "Volume per trade", "General")
			.SetGreaterThanZero();

		_takeProfitPips = Param(nameof(TakeProfitPips), 10m)
			.SetDisplay("Take Profit (pips)", "Distance to take profit", "Risk")
			.SetGreaterThanZero();

		_stopLossPips = Param(nameof(StopLossPips), 10m)
			.SetDisplay("Stop Loss (pips)", "Distance to stop loss", "Risk")
			.SetGreaterThanZero();

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Source candles for timing", "General");
	}

	/// <summary>
	/// Volume used for each market order.
	/// </summary>
	public decimal TradeVolume
	{
		get => _tradeVolume.Value;
		set => _tradeVolume.Value = value;
	}

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

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

	/// <summary>
	/// Candle type that triggers trading decisions.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

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

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

		Volume = TradeVolume;
		_nextSide = Sides.Sell;
		_activeSide = null;
		_pipSize = 0m;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		Volume = TradeVolume; // Align the base volume with the strategy parameter.

		var priceStep = Security?.PriceStep ?? 0m;
		if (priceStep <= 0m)
		{
			var decimals = Security?.Decimals ?? 4;
			priceStep = (decimal)Math.Pow(10, -decimals);
		}

		_pipSize = priceStep;

		var secDecimals = Security?.Decimals;
		if (secDecimals is int digits && (digits == 3 || digits == 5))
		{
			_pipSize *= 10m; // Convert from fractional pip to full pip for FX symbols.
		}

		if (_pipSize <= 0m)
		{
			_pipSize = 1m; // Fallback to a neutral value if the instrument metadata is missing.
		}

		StartProtection(
			takeProfit: new Unit(TakeProfitPips * _pipSize, UnitTypes.Absolute),
			stopLoss: new Unit(StopLossPips * _pipSize, UnitTypes.Absolute),
			useMarketOrders: true);

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

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

		// Strategy has no bound indicators, always allow trading.

		if (Position != 0)
			return; // Skip if a position exists.

		var volume = TradeVolume;
		if (volume <= 0m)
			return;

		if (_nextSide == Sides.Buy)
			BuyMarket();
		else
			SellMarket();

	}

	/// <inheritdoc />
	protected override void OnPositionReceived(Position position)
	{
		base.OnPositionReceived(position);

		if (Position > 0)
		{
			_activeSide = Sides.Buy;
			return;
		}

		if (Position < 0)
		{
			_activeSide = Sides.Sell;
			return;
		}

		if (_activeSide.HasValue)
		{
			_nextSide = _activeSide == Sides.Buy ? Sides.Sell : Sides.Buy; // Alternate direction after a flat position.
			_activeSide = null;
		}

	}

}