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;
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes, Sides
from StockSharp.Algo.Strategies import Strategy
class cheduecoglioni_alternating_strategy(Strategy):
"""
Alternates buy and sell market orders with fixed stop loss and take profit distances.
"""
def __init__(self):
super(cheduecoglioni_alternating_strategy, self).__init__()
self._take_profit_pips = self.Param("TakeProfitPips", 10.0) \
.SetDisplay("Take Profit (pips)", "Distance to take profit", "Risk")
self._stop_loss_pips = self.Param("StopLossPips", 10.0) \
.SetDisplay("Stop Loss (pips)", "Distance to stop loss", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Source candles for timing", "General")
self._pip_size = 0.0
self._next_side = Sides.Sell
self._active_side = None
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(cheduecoglioni_alternating_strategy, self).OnReseted()
self._next_side = Sides.Sell
self._active_side = None
self._pip_size = 0.0
def OnStarted2(self, time):
super(cheduecoglioni_alternating_strategy, self).OnStarted2(time)
price_step = 1.0
if self.Security is not None and self.Security.PriceStep is not None and float(self.Security.PriceStep) > 0:
price_step = float(self.Security.PriceStep)
else:
decimals = 4
if self.Security is not None and self.Security.Decimals is not None:
decimals = int(self.Security.Decimals)
price_step = 10.0 ** (-decimals)
self._pip_size = price_step
if self.Security is not None and self.Security.Decimals is not None:
digits = int(self.Security.Decimals)
if digits == 3 or digits == 5:
self._pip_size *= 10.0
if self._pip_size <= 0:
self._pip_size = 1.0
self.StartProtection(
takeProfit=Unit(self._take_profit_pips.Value * self._pip_size, UnitTypes.Absolute),
stopLoss=Unit(self._stop_loss_pips.Value * self._pip_size, UnitTypes.Absolute),
useMarketOrders=True)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self.on_process).Start()
def on_process(self, candle):
if candle.State != CandleStates.Finished:
return
if self.Position != 0:
return
if self._next_side == Sides.Buy:
self.BuyMarket()
else:
self.SellMarket()
def OnPositionReceived(self, position):
super(cheduecoglioni_alternating_strategy, self).OnPositionReceived(position)
if self.Position > 0:
self._active_side = Sides.Buy
return
if self.Position < 0:
self._active_side = Sides.Sell
return
if self._active_side is not None:
if self._active_side == Sides.Buy:
self._next_side = Sides.Sell
else:
self._next_side = Sides.Buy
self._active_side = None
def CreateClone(self):
return cheduecoglioni_alternating_strategy()