Diese Strategie ist ein StockSharp-Port des MetaTrader-Expertenberaters "Secwenta" (MQL-ID 22977). Der Algorithmus scannt abgeschlossene Kerzen und zählt, wie viele davon bullisch (Schluss > Eröffnung) oder bärisch (Schluss < Eröffnung) innerhalb eines kurzen rollenden Verlaufs geschlossen haben. Je nach Konfiguration kann er im Nur-Kauf-, Nur-Verkauf- oder bidirektionalen Umkehrmodus betrieben werden. Wenn die erforderliche Anzahl bullischer oder bärischer Balken erscheint, öffnet oder schließt die Strategie Marktpositionen mit einem festen Volumen, das die ursprüngliche Lot-Einstellung widerspiegelt.
Signalauswertung
Nur abgeschlossene Kerzen des ausgewählten CandleType werden über die High-Level-Abonnement-API verarbeitet.
Für jede Kerze erfasst die Strategie, ob sie bullisch, bärisch oder neutral (Doji) war. Der interne Puffer hält die letzten N Richtungen, wobei N der größere Wert von BullishBarCount und BearishBarCount unter den aktivierten Seiten (Kauf und/oder Verkauf) ist.
Der bullische Zähler erhöht sich, wenn eine Kerze über ihrer Eröffnung schließt, während der bärische Zähler bei Schlüssen unter der Eröffnung erhöht wird. Neutrale Kerzen beeinflussen die Zähler nicht.
Ein Signal wird ausgelöst, wenn der entsprechende Zähler seinen konfigurierten Schwellenwert innerhalb des rollenden Fensters erreicht. Dies reproduziert die ursprüngliche MQL-Logik, die durch die jüngsten Balken iterierte, bis die angeforderte Anzahl bullischer oder bärischer Kerzen gefunden wurde.
Wenn der bullische Zähler BullishBarCount erreicht, wird eine offene Short-Position mit einer Market-Buy-Order gedeckt.
Wenn der bärische Zähler BearishBarCount erreicht und die Strategie flat ist, wird eine neue Short-Position mit OrderVolume eröffnet.
Umkehrmodus (UseBuySignals = true und UseSellSignals = true):
Ein bullischer Auslöser schließt jedes Short-Engagement und öffnet, falls die Strategie nicht bereits long ist, eine neue Long-Position durch den Kauf von OrderVolume plus dem absoluten Betrag der Short-Position. Dies ahmt die ursprüngliche Sequenz des Schließens von Verkäufen vor dem Öffnen von Käufen nach.
Ein bärischer Auslöser schließt jedes Long-Engagement und öffnet, falls die Strategie nicht bereits short ist, eine neue Short-Position durch den Verkauf von OrderVolume plus dem absoluten Betrag der Long-Position.
Alle Marktoperationen verwenden StockSharp's BuyMarket- und SellMarket-Helfer, und die Strategie ruft StartProtection() auf, damit bei Bedarf kontoseitige Schutzmaßnahmen darüber gelegt werden können.
Parameter
Parameter
Beschreibung
Standard
Hinweise
CandleType
Kerzendatentyp (Zeitrahmen) zur Auswertung von Sequenzen.
1-Stunden-Zeitrahmen
Jeder von StockSharp unterstützte Kerzentyp kann ausgewählt werden.
OrderVolume
Basis-Market-Order-Volumen, das die MQL-Lot-Größe widerspiegelt.
1
Wird beim Umkehren einer Position zum Schließvolumen addiert.
UseBuySignals
Aktiviert die Verarbeitung bullischer Signale.
true
Wenn deaktiviert, werden keine neuen Long-Trades eröffnet.
BullishBarCount
Anzahl bullischer Kerzen, die zur Auslösung eines bullischen Ereignisses erforderlich sind.
2
Sollte mit dem Schließschwellenwert konsistent bleiben, wenn im Nur-Kauf-Modus betrieben wird.
UseSellSignals
Aktiviert die Verarbeitung bärischer Signale.
false
Wenn deaktiviert, werden keine neuen Short-Trades eröffnet.
BearishBarCount
Anzahl bärischer Kerzen, die zur Auslösung eines bärischen Ereignisses erforderlich sind.
1
Dient sowohl als Öffnungsschwellenwert für Shorts als auch als Ausstiegsschwellenwert für Longs.
Implementierungshinweise
Das rollende Fenster verwendet eine Warteschlange, um die letzten Kerzenrichtungen zu halten, und stellt sicher, dass die Zähler auch nach Parameteränderungen der Fenstergröße entsprechen.
Nur abgeschlossene Kerzen werden verarbeitet, um dem ursprünglichen "neue Kerze"-Ereignis-Handling treu zu bleiben.
Neutrale (Doji-)Kerzen lassen die Zähler unverändert, genau wie im MQL-Code.
Umkehrungen werden mit einer einzigen Market-Order ausgeführt, die das Schließ- und Eröffnungsvolumen kombiniert und deterministische Expositionsänderungen beibehält.
Die Pufferlänge entspricht dem größten aktiven Schwellenwert; wenn eine Seite deaktiviert ist, trägt nur der entsprechende Schwellenwert zur Lookback-Länge bei und entspricht damit dem Verhalten von CopyRates in der MQL-Version.
Dateien
CS/SecwentaMultiBarSignalsStrategy.cs – Haupt-C#-Implementierung basierend auf der StockSharp High-Level-Strategie-API.
Hinweis: Für diese ID wird keine Python-Übersetzung bereitgestellt; nur die angeforderte C#-Version ist vorhanden.
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Secwenta MultiBar Signals strategy using SmoothedMA crossover.
/// Buys when fast SmoothedMA crosses above slow SmoothedMA, sells on reverse.
/// </summary>
public class SecwentaMultiBarSignalsStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private SmoothedMovingAverage _fast;
private SmoothedMovingAverage _slow;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
private int _cooldown;
/// <summary>
/// Fast SmoothedMA period.
/// </summary>
public int FastPeriod
{
get => _fastPeriod.Value;
set => _fastPeriod.Value = value;
}
/// <summary>
/// Slow SmoothedMA period.
/// </summary>
public int SlowPeriod
{
get => _slowPeriod.Value;
set => _slowPeriod.Value = value;
}
/// <summary>
/// Stop-loss distance in price steps.
/// </summary>
public int StopLossPoints
{
get => _stopLossPoints.Value;
set => _stopLossPoints.Value = value;
}
/// <summary>
/// Take-profit distance in price steps.
/// </summary>
public int TakeProfitPoints
{
get => _takeProfitPoints.Value;
set => _takeProfitPoints.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="SecwentaMultiBarSignalsStrategy"/> class.
/// </summary>
public SecwentaMultiBarSignalsStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 15)
.SetGreaterThanZero()
.SetDisplay("Fast Period", "Fast SmoothedMA period", "Indicator");
_slowPeriod = Param(nameof(SlowPeriod), 60)
.SetGreaterThanZero()
.SetDisplay("Slow Period", "Slow SmoothedMA period", "Indicator");
_stopLossPoints = Param(nameof(StopLossPoints), 200)
.SetNotNegative()
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 400)
.SetNotNegative()
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_fast = null;
_slow = null;
_prevFast = 0;
_prevSlow = 0;
_entryPrice = 0;
_cooldown = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fast = new SmoothedMovingAverage { Length = FastPeriod };
_slow = new SmoothedMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
subscription.Bind(_fast, _slow, ProcessCandle);
subscription.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_fast.IsFormed || !_slow.IsFormed)
{
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
if (_cooldown > 0)
{
_cooldown--;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
var close = candle.ClosePrice;
var step = Security?.PriceStep ?? 1m;
// Check SL/TP
if (Position > 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step)
{
SellMarket();
_entryPrice = 0;
_cooldown = 80;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step)
{
SellMarket();
_entryPrice = 0;
_cooldown = 80;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step)
{
BuyMarket();
_entryPrice = 0;
_cooldown = 80;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step)
{
BuyMarket();
_entryPrice = 0;
_cooldown = 80;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
}
// SmoothedMA crossover
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
_entryPrice = close;
_cooldown = 80;
}
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
_entryPrice = close;
_cooldown = 80;
}
_prevFast = fastValue;
_prevSlow = slowValue;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import SmoothedMovingAverage
from StockSharp.Algo.Strategies import Strategy
class secwenta_multi_bar_signals_strategy(Strategy):
def __init__(self):
super(secwenta_multi_bar_signals_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 15) \
.SetDisplay("Fast Period", "Fast MA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 60) \
.SetDisplay("Slow Period", "Slow MA period", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 200) \
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 400) \
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk")
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def stop_loss_points(self):
return self._stop_loss_points.Value
@property
def take_profit_points(self):
return self._take_profit_points.Value
def OnReseted(self):
super(secwenta_multi_bar_signals_strategy, self).OnReseted()
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(secwenta_multi_bar_signals_strategy, self).OnStarted2(time)
self._fast = SmoothedMovingAverage()
self._fast.Length = self.fast_period
self._slow = SmoothedMovingAverage()
self._slow.Length = self.slow_period
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(5)))
subscription.Bind(self._fast, self._slow, self._process_candle)
subscription.Start()
def _process_candle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_value)
slow_val = float(slow_value)
if not self._fast.IsFormed or not self._slow.IsFormed:
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = float(candle.ClosePrice)
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
if self.Position > 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close <= self._entry_price - self.stop_loss_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 80
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close >= self._entry_price + self.take_profit_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 80
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close >= self._entry_price + self.stop_loss_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 80
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close <= self._entry_price - self.take_profit_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 80
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._prev_fast <= self._prev_slow and fast_val > slow_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 80
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 80
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return secwenta_multi_bar_signals_strategy()