Trend RDS sucht nach klaren direktionalen Sequenzen in der Kursbewegung. Wenn drei abgeschlossene Kerzen strikt höhere Tiefs bilden, behandelt die Strategie die Struktur als bullisches Trendsegment. Drei strikt niedrigere Hochs markieren eine bärische Konfiguration. Eine Schutzregel blockiert Einstiege, wenn dieselben drei Bars gleichzeitig sowohl höhere Tiefs als auch niedrigere Hochs erzeugen, was üblicherweise ein sich zusammenziehendes Dreieck anstatt einer direktionalen Bewegung anzeigt. Die Strategie kann optional die Richtung über den Reverse-Parameter umkehren.
Der Handel ist auf ein konfigurierbares Zeitfenster begrenzt (Standard 09:00–12:00). Wenn das Fenster offen ist und ein gültiges Muster erscheint, schließt die Strategie jede entgegengesetzte Exposure, eröffnet eine neue Marktposition beim Kerzenschluss und platziert Stop-Loss- und Take-Profit-Orders gemessen in Pips. Der Pip-Abstand wird aus dem Preisschritt des Instruments abgeleitet und spiegelt die ursprüngliche MetaTrader-Logik wider. Ein optionaler Trailing-Stop bewegt den Schutz-Stop vorwärts, sobald der Preis um den Trailing-Abstand plus den Trailing-Schritt vorgerückt ist. Trailing-Anpassungen werden nur ausgewertet, während das Sitzungsfenster aktiv ist.
Die Positionsgröße wird bei jedem Einstieg neu berechnet. Die Strategie weist einen Bruchteil des Portfolio-Eigenkapitals zu, der durch RiskPercent definiert wird, und dividiert ihn durch das monetäre Risiko, das durch den gewählten Stop-Abstand dargestellt wird. Dies erzeugt eine dynamische Größenbestimmung, die sowohl mit der Kontogröße als auch mit der Stop-Breite skaliert und dabei den Mindestwert Volume respektiert. Das Setzen eines risikobezogenen Parameters auf null deaktiviert diese Funktion und ermöglicht bei Bedarf Einstiege mit fester Größe oder ohne Schutz.
Details
Einstiegskriterien: Drei aufeinanderfolgende Kerzen mit höheren Tiefs lösen Longs aus (oder Shorts, wenn Reverse wahr ist). Drei aufeinanderfolgende niedrigere Hochs lösen Shorts aus (oder Longs im Umkehrmodus). Signale werden ignoriert, wenn dieselben drei Bars beide Bedingungen gleichzeitig erfüllen.
Long/Short: Beide Richtungen mit einem optionalen Umkehrschalter.
Ausstiegskriterien: Marktausstiege, wenn die verfolgten Stop-Loss-, Take-Profit- oder Trailing-Stop-Niveaus durchbrochen werden.
Stops: Fester Stop-Loss und Take-Profit in Pips mit einem inkrementellen Trailing-Stop (erfordert, dass beide Trailing-Parameter positiv sind).
Zeitfenster: Handelt nur zwischen StartTime und EndTime (Standard 09:00–12:00 Börsenzeit).
Positionsgrößenbestimmung: Risikobasierte Größenbestimmung unter Verwendung von RiskPercent des Portfolio-Eigenkapitals relativ zum aktuellen Stop-Abstand (greift auf Volume zurück, wenn die Größenbestimmung nicht berechnet werden kann).
Standardwerte:
StopLossPips = 30
TakeProfitPips = 65
TrailingStopPips = 0
TrailingStepPips = 5
RiskPercent = 3
StartTime = 09:00
EndTime = 12:00
Reverse = false
Filter:
Kategorie: Trend
Richtung: Beide
Indikatoren: Kursbewegung (Hochs/Tiefs)
Stops: Ja
Komplexität: Mittel
Zeitrahmen: Intraday
Saisonalität: Nein
Neuronale Netze: Nein
Divergenz: Nein
Risikolevel: Mittel
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;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Trend recognition strategy based on three consecutive candles.
/// </summary>
public class TrendRdsStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _stopLossPips;
private readonly StrategyParam<int> _takeProfitPips;
private readonly StrategyParam<int> _trailingStopPips;
private readonly StrategyParam<int> _trailingStepPips;
private readonly StrategyParam<decimal> _riskPercent;
private readonly StrategyParam<TimeSpan> _startTime;
private readonly StrategyParam<TimeSpan> _endTime;
private readonly StrategyParam<bool> _reverse;
private decimal _prevHigh1;
private decimal _prevHigh2;
private decimal _prevHigh3;
private decimal _prevLow1;
private decimal _prevLow2;
private decimal _prevLow3;
private int _historyCount;
private decimal _entryPrice;
private decimal _stopLossPrice;
private decimal _takeProfitPrice;
/// <summary>
/// Type of candles to analyze.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Stop loss distance measured in pips.
/// </summary>
public int StopLossPips
{
get => _stopLossPips.Value;
set => _stopLossPips.Value = value;
}
/// <summary>
/// Take profit distance measured in pips.
/// </summary>
public int TakeProfitPips
{
get => _takeProfitPips.Value;
set => _takeProfitPips.Value = value;
}
/// <summary>
/// Trailing stop distance measured in pips.
/// </summary>
public int TrailingStopPips
{
get => _trailingStopPips.Value;
set => _trailingStopPips.Value = value;
}
/// <summary>
/// Trailing step measured in pips.
/// </summary>
public int TrailingStepPips
{
get => _trailingStepPips.Value;
set => _trailingStepPips.Value = value;
}
/// <summary>
/// Percent of account equity to risk per trade.
/// </summary>
public decimal RiskPercent
{
get => _riskPercent.Value;
set => _riskPercent.Value = value;
}
/// <summary>
/// Trading session start time (inclusive).
/// </summary>
public TimeSpan StartTime
{
get => _startTime.Value;
set => _startTime.Value = value;
}
/// <summary>
/// Trading session end time (exclusive).
/// </summary>
public TimeSpan EndTime
{
get => _endTime.Value;
set => _endTime.Value = value;
}
/// <summary>
/// Reverse the trade direction when enabled.
/// </summary>
public bool Reverse
{
get => _reverse.Value;
set => _reverse.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="TrendRdsStrategy"/> class.
/// </summary>
public TrendRdsStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_stopLossPips = Param(nameof(StopLossPips), 30)
.SetDisplay("Stop Loss (pips)", "Stop loss distance in pips", "Risk")
;
_takeProfitPips = Param(nameof(TakeProfitPips), 65)
.SetDisplay("Take Profit (pips)", "Take profit distance in pips", "Risk")
;
_trailingStopPips = Param(nameof(TrailingStopPips), 0)
.SetDisplay("Trailing Stop (pips)", "Trailing stop distance in pips", "Risk")
;
_trailingStepPips = Param(nameof(TrailingStepPips), 5)
.SetDisplay("Trailing Step (pips)", "Trailing step increment", "Risk")
;
_riskPercent = Param(nameof(RiskPercent), 3m)
.SetDisplay("Risk %", "Percent of equity to risk", "Risk")
.SetRange(0m, 100m);
_startTime = Param(nameof(StartTime), new TimeSpan(0, 0, 0))
.SetDisplay("Session Start", "Trading session start time", "Session");
_endTime = Param(nameof(EndTime), new TimeSpan(23, 59, 0))
.SetDisplay("Session End", "Trading session end time", "Session");
_reverse = Param(nameof(Reverse), false)
.SetDisplay("Reverse", "Trade in the opposite direction", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevHigh1 = 0m;
_prevHigh2 = 0m;
_prevHigh3 = 0m;
_prevLow1 = 0m;
_prevLow2 = 0m;
_prevLow3 = 0m;
_historyCount = 0;
_entryPrice = 0m;
_stopLossPrice = 0m;
_takeProfitPrice = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
if (TrailingStopPips > 0 && TrailingStepPips <= 0)
{
throw new InvalidOperationException("Trailing step must be greater than zero when trailing stop is enabled.");
}
if (StartTime >= EndTime)
{
throw new InvalidOperationException("Session start time must be earlier than end time.");
}
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
// Skip unfinished candles to work on closed bars only.
if (candle.State != CandleStates.Finished)
return;
var pip = GetPipSize();
// Handle protective exits even outside of the session window.
if (HandleActivePositionExits(candle))
{
UpdateHistory(candle);
return;
}
var candleTime = candle.OpenTime.TimeOfDay;
var inSession = candleTime >= StartTime && candleTime < EndTime;
if (inSession && _historyCount >= 3)
{
var higherLows = _prevLow1 > _prevLow2 && _prevLow2 > _prevLow3;
var lowerHighs = _prevHigh1 < _prevHigh2 && _prevHigh2 < _prevHigh3;
var conflict = higherLows && lowerHighs;
var longSignal = false;
var shortSignal = false;
if (!conflict)
{
if (higherLows)
{
if (Reverse)
shortSignal = true;
else
longSignal = true;
}
if (lowerHighs)
{
if (Reverse)
longSignal = true;
else
shortSignal = true;
}
}
if (longSignal && Position <= 0)
{
EnterLong(candle, pip);
}
else if (shortSignal && Position >= 0)
{
EnterShort(candle, pip);
}
// Update trailing logic after potential entries.
ApplyTrailing(candle, pip);
}
UpdateHistory(candle);
}
private void EnterLong(ICandleMessage candle, decimal pip)
{
var stopOffset = StopLossPips > 0 ? StopLossPips * pip : 0m;
var takeOffset = TakeProfitPips > 0 ? TakeProfitPips * pip : 0m;
var volume = CalculateVolume(stopOffset);
if (Position < 0)
{
volume += Math.Abs(Position);
}
BuyMarket(volume);
_entryPrice = candle.ClosePrice;
_stopLossPrice = stopOffset > 0m ? _entryPrice - stopOffset : 0m;
_takeProfitPrice = takeOffset > 0m ? _entryPrice + takeOffset : 0m;
}
private void EnterShort(ICandleMessage candle, decimal pip)
{
var stopOffset = StopLossPips > 0 ? StopLossPips * pip : 0m;
var takeOffset = TakeProfitPips > 0 ? TakeProfitPips * pip : 0m;
var volume = CalculateVolume(stopOffset);
if (Position > 0)
{
volume += Math.Abs(Position);
}
SellMarket(volume);
_entryPrice = candle.ClosePrice;
_stopLossPrice = stopOffset > 0m ? _entryPrice + stopOffset : 0m;
_takeProfitPrice = takeOffset > 0m ? _entryPrice - takeOffset : 0m;
}
private void ApplyTrailing(ICandleMessage candle, decimal pip)
{
if (TrailingStopPips <= 0)
return;
var trailingStop = TrailingStopPips * pip;
var trailingStep = TrailingStepPips * pip;
if (trailingStop <= 0m || trailingStep <= 0m || _entryPrice == 0m)
return;
var price = candle.ClosePrice;
if (Position > 0)
{
var profit = price - _entryPrice;
if (profit > trailingStop + trailingStep)
{
var threshold = price - (trailingStop + trailingStep);
if (_stopLossPrice == 0m || _stopLossPrice < threshold)
{
_stopLossPrice = price - trailingStop;
}
}
}
else if (Position < 0)
{
var profit = _entryPrice - price;
if (profit > trailingStop + trailingStep)
{
var threshold = price + (trailingStop + trailingStep);
if (_stopLossPrice == 0m || _stopLossPrice > threshold)
{
_stopLossPrice = price + trailingStop;
}
}
}
}
private bool HandleActivePositionExits(ICandleMessage candle)
{
var positionVolume = Math.Abs(Position);
if (positionVolume == 0m)
return false;
if (Position > 0)
{
if (_stopLossPrice > 0m && candle.LowPrice <= _stopLossPrice)
{
SellMarket(positionVolume);
ResetPositionState();
return true;
}
if (_takeProfitPrice > 0m && candle.HighPrice >= _takeProfitPrice)
{
SellMarket(positionVolume);
ResetPositionState();
return true;
}
}
else
{
if (_stopLossPrice > 0m && candle.HighPrice >= _stopLossPrice)
{
BuyMarket(positionVolume);
ResetPositionState();
return true;
}
if (_takeProfitPrice > 0m && candle.LowPrice <= _takeProfitPrice)
{
BuyMarket(positionVolume);
ResetPositionState();
return true;
}
}
return false;
}
private decimal CalculateVolume(decimal stopOffset)
{
var baseVolume = Volume > 0m ? Volume : 1m;
var equity = Portfolio?.CurrentValue ?? 0m;
if (stopOffset <= 0m || equity <= 0m)
return baseVolume;
var step = Security?.PriceStep ?? 0m;
var stepPrice = GetSecurityValue<decimal?>(Level1Fields.StepPrice) ?? 1m;
if (step <= 0m)
return baseVolume;
var stepsToStop = stopOffset / step;
if (stepsToStop <= 0m)
return baseVolume;
var riskAmount = equity * RiskPercent / 100m;
if (riskAmount <= 0m)
return baseVolume;
var riskPerUnit = stepsToStop * stepPrice;
if (riskPerUnit <= 0m)
return baseVolume;
var quantity = riskAmount / riskPerUnit;
if (quantity <= 0m)
return baseVolume;
return Math.Max(quantity, baseVolume);
}
private void UpdateHistory(ICandleMessage candle)
{
_prevHigh3 = _prevHigh2;
_prevHigh2 = _prevHigh1;
_prevHigh1 = candle.HighPrice;
_prevLow3 = _prevLow2;
_prevLow2 = _prevLow1;
_prevLow1 = candle.LowPrice;
if (_historyCount < 3)
{
_historyCount++;
}
}
private void ResetPositionState()
{
_entryPrice = 0m;
_stopLossPrice = 0m;
_takeProfitPrice = 0m;
}
private decimal GetPipSize()
{
var step = Security?.PriceStep ?? 0m;
if (step <= 0m)
return 1m;
var pip = step;
var decimals = GetScale(step);
if (decimals == 3 || decimals == 5)
{
pip *= 10m;
}
return pip;
}
private static int GetScale(decimal value)
{
var bits = decimal.GetBits(value);
return (bits[3] >> 16) & 0xFF;
}
}
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.Strategies import Strategy
from datatype_extensions import *
from indicator_extensions import *
class trend_rds_strategy(Strategy):
"""Trend recognition via three consecutive candle highs/lows with SL/TP management."""
def __init__(self):
super(trend_rds_strategy, self).__init__()
self._sl_points = self.Param("StopLossPoints", 30).SetDisplay("Stop Loss", "Stop loss distance", "Risk")
self._tp_points = self.Param("TakeProfitPoints", 65).SetDisplay("Take Profit", "Take profit distance", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))).SetDisplay("Candle Type", "Timeframe", "General")
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, value): self._candle_type.Value = value
def OnReseted(self):
super(trend_rds_strategy, self).OnReseted()
self._prev_high1 = 0
self._prev_high2 = 0
self._prev_high3 = 0
self._prev_low1 = 0
self._prev_low2 = 0
self._prev_low3 = 0
self._history_count = 0
self._entry_price = 0
self._stop_price = 0
self._take_price = 0
def OnStarted2(self, time):
super(trend_rds_strategy, self).OnStarted2(time)
self._prev_high1 = 0
self._prev_high2 = 0
self._prev_high3 = 0
self._prev_low1 = 0
self._prev_low2 = 0
self._prev_low3 = 0
self._history_count = 0
self._entry_price = 0
self._stop_price = 0
self._take_price = 0
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, sub)
self.DrawOwnTrades(area)
def OnProcess(self, candle):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
high = float(candle.HighPrice)
low = float(candle.LowPrice)
sl = self._sl_points.Value
tp = self._tp_points.Value
# Handle exits
if self.Position > 0:
if self._stop_price > 0 and low <= self._stop_price:
self.SellMarket()
self._entry_price = 0
self._stop_price = 0
self._take_price = 0
self._update_history(candle)
return
if self._take_price > 0 and high >= self._take_price:
self.SellMarket()
self._entry_price = 0
self._stop_price = 0
self._take_price = 0
self._update_history(candle)
return
elif self.Position < 0:
if self._stop_price > 0 and high >= self._stop_price:
self.BuyMarket()
self._entry_price = 0
self._stop_price = 0
self._take_price = 0
self._update_history(candle)
return
if self._take_price > 0 and low <= self._take_price:
self.BuyMarket()
self._entry_price = 0
self._stop_price = 0
self._take_price = 0
self._update_history(candle)
return
if self._history_count >= 3:
higher_lows = self._prev_low1 > self._prev_low2 and self._prev_low2 > self._prev_low3
lower_highs = self._prev_high1 < self._prev_high2 and self._prev_high2 < self._prev_high3
conflict = higher_lows and lower_highs
if not conflict:
if higher_lows and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._stop_price = close - sl if sl > 0 else 0
self._take_price = close + tp if tp > 0 else 0
elif lower_highs and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._stop_price = close + sl if sl > 0 else 0
self._take_price = close - tp if tp > 0 else 0
self._update_history(candle)
def _update_history(self, candle):
self._prev_high3 = self._prev_high2
self._prev_high2 = self._prev_high1
self._prev_high1 = float(candle.HighPrice)
self._prev_low3 = self._prev_low2
self._prev_low2 = self._prev_low1
self._prev_low1 = float(candle.LowPrice)
if self._history_count < 3:
self._history_count += 1
def CreateClone(self):
return trend_rds_strategy()