Die Kerzen-Strategie (Candle) ist ein direkter Port des klassischen MT5-Experten „Candle.mq5". Sie bewertet die Farbe jeder abgeschlossenen Kerze im gewählten Zeitrahmen und hält die Position im Einklang mit dem letzten Schlusskurs. Bullische Kerzen treiben die Strategie in eine Long-Position, bärische in eine Short-Position, und flache Kerzen lassen die Position unverändert. Das Risiko wird durch Pip-basierte Take-Profit- und Trailing-Stop-Abstände kontrolliert, die über die Tick-Größe des Instruments in absolute Preise umgerechnet werden.
Die Strategie reagiert erst nach vollständiger Ausbildung einer Kerze, um Lärm innerhalb der Bar zu vermeiden. Ein obligatorischer Rückblick (MinBars * 2 abgeschlossene Kerzen) validiert, dass das Chart ausreichend Historie enthält, während eine konfigurierbare Abkühlzeit zwischen Handelsvorgängen wartet. Dies erzeugt eine getreue StockSharp-Implementierung der ursprünglichen MetaTrader-Logik ohne Abhängigkeit von Low-Level-Serienzugriff.
Handelsstrategie
Vorbereitung
Verarbeitet Kerzen, die von CandleType bereitgestellt werden; keine weiteren Datenquellen erforderlich.
Wartet, bis mindestens 2 * MinBars abgeschlossene Kerzen verarbeitet wurden, bevor Einstiege erlaubt sind.
Handelt nur, wenn die Strategie online, geformt und zur Orderausführung berechtigt ist.
Erzwingt das TradeCooldown-Intervall (Standard: 10 Sekunden) zwischen jeweils zwei Handelsvorgängen.
Einstiegs- und Umkehrregeln
Flacher Zustand:
Long einsteigen (BuyMarket), wenn eine Kerze über ihrer Eröffnung schließt.
Short einsteigen (SellMarket), wenn eine Kerze unter ihrer Eröffnung schließt.
Bestehende Position:
Bei einer Long-Position gegenüber einer bärischen Kerze |Position| + Volume verkaufen, um zu schließen und sofort zu einer Short-Position der Größe Volume umzukehren.
Bei einer Short-Position gegenüber einer bullischen Kerze |Position| + Volume kaufen, um zu schließen und sofort zu einer Long-Position der Größe Volume umzukehren.
Neutrale Kerzen:
Wenn Schluss gleich Eröffnung ist, wird keine manuelle Aktion durchgeführt; nur die Schutzorders können den Trade beenden.
Risikomanagement und Ausstiege
StartProtection legt einen in Pips gemessenen Take-Profit und Trailing Stop fest. Die Strategie multipliziert jeden Pip-Wert mit (PriceStep * 10), um die MetaTrader-Anpassung für 3- und 5-stellige Notierungen nachzubilden.
Der Trailing Stop wird nur aktiviert, wenn TrailingStopPips größer als null ist; er folgt dem Preis automatisch, sobald sich der Trade in die günstige Richtung bewegt.
Der Take-Profit schließt die Position, wenn der konfigurierte Abstand erreicht ist. Jedes Schutzniveau storniert die entgegengesetzte Order nach der Ausführung.
Manuelle Umkehrungen durch die Kerzenfarbe glätten ebenfalls das vorherige Engagement vor dem Öffnen der neuen Position.
Parameter
CandleType – Zeitrahmen der zu analysierenden Kerzenserie (Standard: 15-Minuten-Kerzen).
TakeProfitPips – Abstand zum Take-Profit-Ziel in Pips (Standard: 50).
TrailingStopPips – Trailing-Stop-Abstand in Pips (Standard: 30).
MinBars – Mindestanzahl von Bars vor dem ersten Trade (Standard: 26; Strategie wartet auf 52 abgeschlossene Kerzen).
TradeCooldown – Wartezeit nach jeder Handelsaktion (Standard: 10 Sekunden).
Setzen Sie die Volume-Eigenschaft der Strategie auf die gewünschte Ordergröße. Bei einer Marktumkehr sendet die Strategie automatisch genügend Volumen, um sowohl die vorherige Position zu beenden als auch die neue zu etablieren.
Implementierungshinweise
Nur abgeschlossene Kerzen (CandleStates.Finished) werden verarbeitet. Dies spiegelt den MetaTrader-Experten wider, der sich auf geschlossene Barwerte stützte, die über CopyOpen/CopyClose abgerufen wurden.
Der Code verwendet StockSharps High-Level-API: SubscribeCandles für Daten, Bind zur Verarbeitung eingehender Bars und BuyMarket/SellMarket zur Orderausführung.
Schutzorders werden von StartProtection verwaltet, sodass keine manuelle Stop-Limit-Orderverwaltung notwendig ist.
Die Pip-Größenberechnung PriceStep * 10 reproduziert die MQL-„Digits-Adjust"-Logik für Symbole, die mit 3 oder 5 Dezimalstellen notiert sind.
Da Einstiege durch den Körper der letzten Kerze ausgelöst werden, bleibt die Strategie tendenziell kontinuierlich im Markt und wechselt die Seite, wenn sich die Kerzenfarbe ändert.
Passen Sie die Pip-Abstände, die Abkühlzeit und den Zeitrahmen an das gehandelte Instrument an. Die Standardkonfiguration spiegelt das ursprüngliche MT5-Beispiel wider, kann aber über das Parameterframework von StockSharp optimiert werden.
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>
/// Candle color reversal strategy with pip-based protection and trade cooldown.
/// </summary>
public class CandleStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<decimal> _takeProfitPips;
private readonly StrategyParam<decimal> _trailingStopPips;
private readonly StrategyParam<int> _minBars;
private readonly StrategyParam<TimeSpan> _tradeCooldown;
private decimal _pipSize;
private int _finishedCandles;
private DateTimeOffset _nextAllowedTime;
/// <summary>
/// Initializes a new instance of the <see cref="CandleStrategy"/>.
/// </summary>
public CandleStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe used for candle evaluation", "General");
_takeProfitPips = Param(nameof(TakeProfitPips), 50m)
.SetDisplay("Take Profit (pips)", "Distance to take profit in pips", "Risk")
.SetGreaterThanZero();
_trailingStopPips = Param(nameof(TrailingStopPips), 30m)
.SetDisplay("Trailing Stop (pips)", "Trailing stop distance in pips", "Risk")
.SetGreaterThanZero();
_minBars = Param(nameof(MinBars), 26)
.SetDisplay("Minimum Bars", "History length required before trading", "General")
.SetGreaterThanZero();
_tradeCooldown = Param(nameof(TradeCooldown), TimeSpan.FromSeconds(10))
.SetDisplay("Trade Cooldown", "Waiting time after each trade", "Risk");
}
/// <summary>
/// Candle type and timeframe used by the strategy.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Take profit distance expressed in pips.
/// </summary>
public decimal TakeProfitPips
{
get => _takeProfitPips.Value;
set => _takeProfitPips.Value = value;
}
/// <summary>
/// Trailing stop distance expressed in pips.
/// </summary>
public decimal TrailingStopPips
{
get => _trailingStopPips.Value;
set => _trailingStopPips.Value = value;
}
/// <summary>
/// Minimum number of bars required on the chart.
/// </summary>
public int MinBars
{
get => _minBars.Value;
set => _minBars.Value = value;
}
/// <summary>
/// Cooldown between consecutive trading operations.
/// </summary>
public TimeSpan TradeCooldown
{
get => _tradeCooldown.Value;
set => _tradeCooldown.Value = value;
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_pipSize = 0m;
_finishedCandles = 0;
_nextAllowedTime = DateTimeOffset.MinValue;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_pipSize = (Security?.PriceStep ?? 1m) * 10m;
Unit takeProfit = TakeProfitPips > 0m && _pipSize > 0m
? new Unit(TakeProfitPips * _pipSize, UnitTypes.Absolute)
: null;
Unit trailingStop = TrailingStopPips > 0m && _pipSize > 0m
? new Unit(TrailingStopPips * _pipSize, UnitTypes.Absolute)
: null;
// Enable automatic protective orders and trailing stop handling.
if (trailingStop != null || takeProfit != null)
StartProtection(takeProfit, trailingStop);
// Subscribe to candle data for the configured timeframe.
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
// Draw candles and executions on the chart if visualization is available.
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
// Skip unfinished candles to work only with final prices.
if (candle.State != CandleStates.Finished)
return;
_finishedCandles++;
// Wait until the chart has enough historical data.
if (_finishedCandles < MinBars * 2)
return;
var time = candle.CloseTime;
// Enforce cooldown between trading operations.
if (time < _nextAllowedTime)
return;
var isBullish = candle.ClosePrice > candle.OpenPrice;
var isBearish = candle.ClosePrice < candle.OpenPrice;
var tradeExecuted = false;
if (Position > 0 && isBearish)
{
// Reverse from long to short when a bearish candle appears.
var volume = Math.Abs(Position) + Volume;
if (volume > 0m)
{
SellMarket();
tradeExecuted = true;
}
}
else if (Position < 0 && isBullish)
{
// Reverse from short to long when a bullish candle appears.
var volume = Math.Abs(Position) + Volume;
if (volume > 0m)
{
BuyMarket();
tradeExecuted = true;
}
}
else if (Position == 0)
{
if (isBullish)
{
// Enter long on bullish close.
if (Volume > 0m)
{
BuyMarket();
tradeExecuted = true;
}
}
else if (isBearish)
{
// Enter short on bearish close.
if (Volume > 0m)
{
SellMarket();
tradeExecuted = true;
}
}
}
if (tradeExecuted)
{
_nextAllowedTime = time + TradeCooldown;
}
}
}
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, DateTimeOffset
from StockSharp.Messages import DataType, CandleStates, UnitTypes, Unit
from StockSharp.Algo.Strategies import Strategy
class candle_strategy(Strategy):
"""
Candle color reversal strategy with pip-based protection and trade cooldown.
"""
def __init__(self):
super(candle_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe used for candle evaluation", "General")
self._take_profit_pips = self.Param("TakeProfitPips", 50.0) \
.SetDisplay("Take Profit (pips)", "Distance to take profit in pips", "Risk")
self._trailing_stop_pips = self.Param("TrailingStopPips", 30.0) \
.SetDisplay("Trailing Stop (pips)", "Trailing stop distance in pips", "Risk")
self._min_bars = self.Param("MinBars", 26) \
.SetDisplay("Minimum Bars", "History length required before trading", "General")
self._trade_cooldown = self.Param("TradeCooldown", TimeSpan.FromSeconds(10)) \
.SetDisplay("Trade Cooldown", "Waiting time after each trade", "Risk")
self._pip_size = 0.0
self._finished_candles = 0
self._next_allowed_time = DateTimeOffset.MinValue
@property
def candle_type(self):
return self._candle_type.Value
@candle_type.setter
def candle_type(self, value):
self._candle_type.Value = value
@property
def take_profit_pips(self):
return self._take_profit_pips.Value
@take_profit_pips.setter
def take_profit_pips(self, value):
self._take_profit_pips.Value = value
@property
def trailing_stop_pips(self):
return self._trailing_stop_pips.Value
@trailing_stop_pips.setter
def trailing_stop_pips(self, value):
self._trailing_stop_pips.Value = value
@property
def min_bars(self):
return self._min_bars.Value
@min_bars.setter
def min_bars(self, value):
self._min_bars.Value = value
@property
def trade_cooldown(self):
return self._trade_cooldown.Value
@trade_cooldown.setter
def trade_cooldown(self, value):
self._trade_cooldown.Value = value
def OnReseted(self):
super(candle_strategy, self).OnReseted()
self._pip_size = 0.0
self._finished_candles = 0
self._next_allowed_time = DateTimeOffset.MinValue
def OnStarted2(self, time):
super(candle_strategy, self).OnStarted2(time)
self._pip_size = (self.Security.PriceStep if self.Security.PriceStep is not None else 1.0) * 10.0
tp = None
trailing = None
if self.take_profit_pips > 0 and self._pip_size > 0:
tp = Unit(self.take_profit_pips * self._pip_size, UnitTypes.Absolute)
if self.trailing_stop_pips > 0 and self._pip_size > 0:
trailing = Unit(self.trailing_stop_pips * self._pip_size, UnitTypes.Absolute)
if trailing is not None or tp is not None:
self.StartProtection(tp, trailing)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self.on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def on_process(self, candle):
if candle.State != CandleStates.Finished:
return
self._finished_candles += 1
if self._finished_candles < self.min_bars * 2:
return
close_time = candle.CloseTime
if self._next_allowed_time != DateTimeOffset.MinValue:
try:
if close_time < self._next_allowed_time:
return
except:
if DateTimeOffset(close_time) < self._next_allowed_time:
return
is_bullish = candle.ClosePrice > candle.OpenPrice
is_bearish = candle.ClosePrice < candle.OpenPrice
trade_executed = False
if self.Position > 0 and is_bearish:
self.SellMarket()
trade_executed = True
elif self.Position < 0 and is_bullish:
self.BuyMarket()
trade_executed = True
elif self.Position == 0:
if is_bullish:
self.BuyMarket()
trade_executed = True
elif is_bearish:
self.SellMarket()
trade_executed = True
if trade_executed:
self._next_allowed_time = close_time + self.trade_cooldown
def CreateClone(self):
return candle_strategy()