Diese Strategie scannt die zuletzt abgeschlossenen Kerzen und sucht nach einer Sequenz, bei der die letzten N Bars dieselbe Richtung teilen (alle bullisch oder alle bärisch). Wenn eine solche Strähne erscheint, geht sie in Richtung der Sequenz vor, wobei eine Obergrenze für die Anzahl gleichzeitig eröffneter Positionen eingehalten wird. Die Implementierung migriert den ursprünglichen MetaTrader 5 Expert Advisor auf die StockSharp High-Level-API.
Handelslogik
Die Engine abonniert den konfigurierten Kerzentyp und verarbeitet nur abgeschlossene Bars.
Für jede abgeschlossene Kerze wird die Körperrichtung bewertet: bullisch, bärisch oder neutral (Doji).
Doji-Kerzen setzen den internen Zähler zurück. Andernfalls erhöht sich der Zähler, wenn die aktuelle Kerze dieselbe Richtung wie die vorherigen hat. Sobald der Zähler den Parameter Identical Candles erreicht, gibt die Strategie eine neue Order aus.
Long-Signale schließen zunächst jedes bestehende Short-Exposure und fügen dann eine Long-Einheit hinzu, solange das gesamte gekaufte Volumen unter Max Positions * Volume bleibt.
Short-Signale funktionieren symmetrisch für bärische Strähnen.
Risikomanagement
Nach jedem ausgeführten Trade platziert die Strategie neue Schutz-Stop-Loss- und Take-Profit-Orders basierend auf dem durchschnittlichen Einstiegspreis der aktiven Position.
Abstände werden in Preisschritten des Instruments gemessen: Take Profit Points multipliziert den Schritt zur Berechnung des Ziels über (Long) oder unter (Short) dem Einstieg; Stop Loss Points verwendet dieselbe Idee für den Schutz-Stop.
Ein gestufter Trailing Stop kann den anfänglichen Stop ersetzen, sobald sich der Preis um Trailing Stop Points zugunsten der Position bewegt. Der Stop wird nur verschoben, wenn der Preis mindestens Trailing Step Points über das vorherige Trailing-Niveau hinaus vorangeschritten ist.
Parameter
Candle Type – Zeitrahmen oder Kerzenquelle zur Analyse.
Identical Candles – Erforderliche Anzahl aufeinanderfolgender Kerzen mit derselben Richtung, um einen Einstieg auszulösen.
Volume – Ordergröße für jeden neuen Einstieg in Instrumenteneinheiten.
Max Positions – Maximale Anzahl an Einstiegseinheiten, die gleichzeitig in dieselbe Richtung offen sein können.
Take Profit Points – Take-Profit-Abstand in Vielfachen des Instrument-Preisschritts.
Stop Loss Points – Stop-Loss-Abstand in Vielfachen des Instrument-Preisschritts.
Trailing Stop Points – Abstand vom aktuellen Preis zur Aktivierung und Aufrechterhaltung des Trailing Stops. Auf null setzen, um Trailing zu deaktivieren.
Trailing Step Points – Zusätzliche Distanz in Preisschritten, die zurückgelegt werden muss, bevor der Trailing Stop erneut verschoben wird.
Zusätzliche Hinweise
Die Strategie arbeitet im Netting-Modus: Wenn ein Signal in der entgegengesetzten Richtung erscheint, wird jede bestehende Exposure auf der anderen Seite geschlossen, bevor eine neue Position hinzugefügt wird.
Alle Schutzorders werden nach jeder Füllung neu erstellt, um ihr Volumen mit der offenen Positionsgröße synchronisiert zu halten.
Sicherstellen, dass das Instrument einen nicht-null PriceStep bereitstellt; andernfalls wird der Standard-Schrittwert von 1 verwendet.
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy that opens positions after detecting a sequence of candles with the same direction.
/// Uses StartProtection for take profit and stop loss management.
/// </summary>
public class NCandlesV3Strategy : Strategy
{
private readonly StrategyParam<int> _identicalCandles;
private readonly StrategyParam<decimal> _takeProfitPoints;
private readonly StrategyParam<decimal> _stopLossPoints;
private readonly StrategyParam<DataType> _candleType;
private int _sequenceDirection;
private int _sequenceCount;
/// <summary>
/// Candle type used for analysis.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Number of consecutive candles required before entering a trade.
/// </summary>
public int IdenticalCandles
{
get => _identicalCandles.Value;
set => _identicalCandles.Value = value;
}
/// <summary>
/// Take profit distance expressed in price steps.
/// </summary>
public decimal TakeProfitPoints
{
get => _takeProfitPoints.Value;
set => _takeProfitPoints.Value = value;
}
/// <summary>
/// Stop loss distance expressed in price steps.
/// </summary>
public decimal StopLossPoints
{
get => _stopLossPoints.Value;
set => _stopLossPoints.Value = value;
}
/// <summary>
/// Initializes a new instance of the strategy.
/// </summary>
public NCandlesV3Strategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles analysed by the strategy", "General");
_identicalCandles = Param(nameof(IdenticalCandles), 3)
.SetRange(1, 10)
.SetDisplay("Identical Candles", "Required number of equal candles", "Pattern");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 50m)
.SetRange(0m, 500m)
.SetDisplay("Take Profit Points", "Take profit distance in price steps", "Risk Management");
_stopLossPoints = Param(nameof(StopLossPoints), 50m)
.SetRange(0m, 500m)
.SetDisplay("Stop Loss Points", "Stop loss distance in price steps", "Risk Management");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_sequenceDirection = 0;
_sequenceCount = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_sequenceDirection = 0;
_sequenceCount = 0;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
var step = Security?.PriceStep ?? 1m;
var takeProfit = TakeProfitPoints > 0 ? TakeProfitPoints * step : (decimal?)null;
var stopLoss = StopLossPoints > 0 ? StopLossPoints * step : (decimal?)null;
if (takeProfit != null || stopLoss != null)
StartProtection(takeProfit ?? 0, stopLoss ?? 0);
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var direction = GetDirection(candle);
UpdateSequence(direction);
if (_sequenceCount < IdenticalCandles)
return;
if (_sequenceDirection > 0 && Position <= 0)
{
BuyMarket();
}
else if (_sequenceDirection < 0 && Position >= 0)
{
SellMarket();
}
}
private static int GetDirection(ICandleMessage candle)
{
if (candle.ClosePrice > candle.OpenPrice)
return 1;
if (candle.ClosePrice < candle.OpenPrice)
return -1;
return 0;
}
private void UpdateSequence(int direction)
{
if (direction == 0)
{
_sequenceDirection = 0;
_sequenceCount = 0;
return;
}
if (_sequenceDirection == direction)
{
_sequenceCount++;
}
else
{
_sequenceDirection = direction;
_sequenceCount = 1;
}
}
}
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, Unit, UnitTypes
from StockSharp.Algo.Strategies import Strategy
class n_candles_v3_strategy(Strategy):
"""
N Candles v3: consecutive same-direction candles entry with StartProtection SL/TP.
"""
def __init__(self):
super(n_candles_v3_strategy, self).__init__()
self._identical = self.Param("IdenticalCandles", 3).SetDisplay("Identical", "Required consecutive candles", "Pattern")
self._tp_points = self.Param("TakeProfitPoints", 50.0).SetDisplay("TP Points", "Take profit steps", "Risk")
self._sl_points = self.Param("StopLossPoints", 50.0).SetDisplay("SL Points", "Stop loss steps", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))).SetDisplay("Candle Type", "Candles", "General")
self._seq_dir = 0
self._seq_count = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(n_candles_v3_strategy, self).OnReseted()
self._seq_dir = 0
self._seq_count = 0
def OnStarted2(self, time):
super(n_candles_v3_strategy, self).OnStarted2(time)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._process_candle).Start()
tp = float(self._tp_points.Value)
sl = float(self._sl_points.Value)
if tp > 0 or sl > 0:
self.StartProtection(
Unit(tp, UnitTypes.Absolute) if tp > 0 else Unit(0, UnitTypes.Absolute),
Unit(sl, UnitTypes.Absolute) if sl > 0 else Unit(0, UnitTypes.Absolute)
)
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
open_p = float(candle.OpenPrice)
direction = 1 if close > open_p else (-1 if close < open_p else 0)
if direction == 0:
self._seq_dir = 0
self._seq_count = 0
return
if self._seq_dir == direction:
self._seq_count += 1
else:
self._seq_dir = direction
self._seq_count = 1
if self._seq_count < self._identical.Value:
return
if self._seq_dir > 0 and self.Position <= 0:
self.BuyMarket()
elif self._seq_dir < 0 and self.Position >= 0:
self.SellMarket()
def CreateClone(self):
return n_candles_v3_strategy()