e-TurboFx Steps-Strategie
Überblick
Die e-TurboFx-Strategie ist ein Momentum-Erschöpfungs-Umkehrsystem, das ursprünglich für MetaTrader 5 geschrieben wurde. Es überwacht die zuletzt abgeschlossenen Kerzen und sucht nach Sequenzen, bei denen die Kerzenkörper immer mehr in dieselbe Richtung expandieren. Eine wachsende Serie bearischer Kerzen zeigt Kapitulation an und stellt damit ein potenzielles Long-Setup dar, während eine wachsende Serie bullischer Kerzen eine mögliche Short-Gelegenheit ankündigt. Der StockSharp-Port verwendet die High-Level-API mit Kerzenabonnements und automatisiertem Positionsschutz.
Handelslogik
- Inspizieren Sie die letzten
DepthAnalysis abgeschlossenen Kerzen des ausgewählten CandleType.
- Zählen Sie, wie viele aufeinanderfolgende Kerzen unter ihrer Eröffnung schlossen (bearisch) und wie viele über ihrer Eröffnung schlossen (bullisch).
- Verfolgen Sie die Körpergrößenprogression: jede neue Kerze in der Sequenz muss einen größeren absoluten Körper als die vorherige haben. Wenn diese Bedingung nicht erfüllt ist, wird die Sequenz zurückgesetzt.
- Long-Einstieg:
DepthAnalysis aufeinanderfolgende bearische Kerzen mit streng expandierenden Körpern lösen einen Markt-Kauf aus, sofern derzeit keine Position offen ist.
- Short-Einstieg:
DepthAnalysis aufeinanderfolgende bullische Kerzen mit streng expandierenden Körpern lösen einen Markt-Verkauf aus, ebenfalls nur wenn die Position flach ist.
- Während eine Position aktiv ist, pausiert die Strategie die Signalerkennung, um das Stapeln von Trades zu vermeiden. Das Risikomanagement wird an den beim Start konfigurierten eingebauten Schutzblock delegiert.
Positionsverwaltung
StartProtection registriert automatisch Stop-Loss- und Take-Profit-Orders mit Distanzen, die in Preisschritten (Exchange-Ticks) gemessen werden. Das Setzen einer Distanz auf null deaktiviert den entsprechenden Schutzauftrag.
- Die Strategie hält immer nur eine offene Position. Wenn nach dem Schließen des vorherigen Trades ein neues Signal erscheint, werden die Kerzensequenzen von Grund auf neu aufgebaut, basierend auf frischen Marktdaten.
- Markteinstiege verwenden den
TradeVolume-Parameter. Das Ändern des Parameters in der UI aktualisiert sofort das Strategie-Volumen.
Parameter
| Parameter |
Beschreibung |
Standard |
DepthAnalysis |
Anzahl der zuletzt abgeschlossenen Kerzen, die zur Validierung des Expansionsmusters verwendet werden. Höhere Werte verlangen längere Strähnen vor dem Handel. |
3 |
TakeProfitSteps |
Take-Profit-Distanz in Exchange-Preisschritten (Ticks). 0 deaktiviert den Take-Profit. |
120 |
StopLossSteps |
Stop-Loss-Distanz in Exchange-Preisschritten (Ticks). 0 deaktiviert den Stop-Loss. |
70 |
TradeVolume |
Ordervolumen, das mit jedem Markteinstieg gesendet wird. |
0.1 |
CandleType |
Kerzendatentyp (Zeitrahmen), der für die Analyse abonniert wird. |
1 Stunde Zeitrahmen |
Alle numerischen Parameter haben Optimierungsmetadaten, sodass sie bei Bedarf in StockSharp-Optimierungen einbezogen werden können.
Hinweise und Empfehlungen
- Der ursprüngliche MQL5 Expert Adviser berechnete Kerzendaten bei jedem Tick neu; die StockSharp-Implementierung erreicht dasselbe Verhalten mit abgeschlossenen Kerzenereignissen und internen Zählern.
- Da die Strategie auf Kerzenkörpervergleichen basiert, ist sie sensibel gegenüber dem ausgewählten Zeitrahmen. Kürzere Zeitrahmen erzeugen mehr Signale, können aber engere Stops erfordern.
- Stellen Sie sicher, dass das verbundene Instrument einen gültigen
PriceStep bereitstellt, damit Stop-Loss- und Take-Profit-Distanzen, die in Schritten definiert sind, korrekt in Preise übersetzt werden.
- Validieren Sie das Verhalten vor dem Live-Handel im Designer/Backtester, um zu bestätigen, dass die Stop- und Zieldistanzen mit dem gewählten Instrument übereinstimmen.
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>
/// Momentum reversal strategy that tracks consecutive candles with expanding bodies.
/// </summary>
public class ETurboFxStepsStrategy : Strategy
{
private readonly StrategyParam<int> _depthAnalysis;
private readonly StrategyParam<decimal> _takeProfitSteps;
private readonly StrategyParam<decimal> _stopLossSteps;
private readonly StrategyParam<decimal> _tradeVolume;
private readonly StrategyParam<DataType> _candleType;
private int _bearishSequence;
private int _bullishSequence;
private decimal _previousBearishBody;
private decimal _previousBullishBody;
/// <summary>
/// Number of recent candles analysed for momentum confirmation.
/// </summary>
public int DepthAnalysis
{
get => _depthAnalysis.Value;
set => _depthAnalysis.Value = value;
}
/// <summary>
/// Take profit distance measured in price steps (ticks).
/// A value of zero disables the take profit order.
/// </summary>
public decimal TakeProfitSteps
{
get => _takeProfitSteps.Value;
set => _takeProfitSteps.Value = value;
}
/// <summary>
/// Stop loss distance measured in price steps (ticks).
/// A value of zero disables the protective stop.
/// </summary>
public decimal StopLossSteps
{
get => _stopLossSteps.Value;
set => _stopLossSteps.Value = value;
}
/// <summary>
/// Volume used when sending market orders.
/// </summary>
public decimal TradeVolume
{
get => _tradeVolume.Value;
set
{
_tradeVolume.Value = value;
Volume = value;
}
}
/// <summary>
/// Candle type analysed by the strategy.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="ETurboFxStepsStrategy" /> class.
/// </summary>
public ETurboFxStepsStrategy()
{
_depthAnalysis = Param(nameof(DepthAnalysis), 3)
.SetGreaterThanZero()
.SetDisplay("Depth Analysis", "Number of finished candles used for pattern detection", "Trading Rules")
.SetOptimize(2, 6, 1);
_takeProfitSteps = Param(nameof(TakeProfitSteps), 120m)
.SetNotNegative()
.SetDisplay("Take Profit (steps)", "Take profit distance in price steps (ticks)", "Risk Management")
.SetOptimize(60m, 180m, 20m);
_stopLossSteps = Param(nameof(StopLossSteps), 70m)
.SetNotNegative()
.SetDisplay("Stop Loss (steps)", "Stop loss distance in price steps (ticks)", "Risk Management")
.SetOptimize(40m, 120m, 10m);
_tradeVolume = Param(nameof(TradeVolume), 0.1m)
.SetGreaterThanZero()
.SetDisplay("Trade Volume", "Order volume used for entries", "Trading Rules")
.SetOptimize(0.1m, 0.5m, 0.1m);
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Timeframe of the candles analysed by the strategy", "Market Data");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
ResetState();
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ResetState();
Volume = TradeVolume;
var takeProfitUnit = CreateStepUnit(TakeProfitSteps);
var stopLossUnit = CreateStepUnit(StopLossSteps);
if (takeProfitUnit != null || stopLossUnit != null)
{
// Configure protective orders once the strategy starts.
StartProtection(stopLossUnit, takeProfitUnit);
}
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private Unit CreateStepUnit(decimal steps)
{
if (steps <= 0)
return null;
return new Unit(steps, UnitTypes.Absolute);
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (Position != 0)
{
// Do not look for new signals while a position is active.
ResetState();
return;
}
var bodySize = Math.Abs(candle.ClosePrice - candle.OpenPrice);
if (candle.ClosePrice < candle.OpenPrice)
{
HandleBearishCandle(bodySize);
}
else if (candle.ClosePrice > candle.OpenPrice)
{
HandleBullishCandle(bodySize);
}
else
{
// Neutral candle breaks both sequences.
ResetState();
}
}
private void HandleBearishCandle(decimal bodySize)
{
ResetBullishSequence();
if (bodySize <= 0)
{
ResetBearishSequence();
return;
}
if (_bearishSequence == 0 || bodySize > _previousBearishBody)
{
// Body is larger than the previous bearish candle, extend the sequence.
_bearishSequence++;
}
else
{
// Sequence restarts because body did not expand.
_bearishSequence = 1;
}
_previousBearishBody = bodySize;
if (_bearishSequence >= DepthAnalysis)
{
// Expanding bearish bodies suggest exhaustion that can trigger a long entry.
BuyMarket();
ResetBearishSequence();
}
}
private void HandleBullishCandle(decimal bodySize)
{
ResetBearishSequence();
if (bodySize <= 0)
{
ResetBullishSequence();
return;
}
if (_bullishSequence == 0 || bodySize > _previousBullishBody)
{
// Body is larger than the previous bullish candle, extend the sequence.
_bullishSequence++;
}
else
{
// Sequence restarts because body did not expand.
_bullishSequence = 1;
}
_previousBullishBody = bodySize;
if (_bullishSequence >= DepthAnalysis)
{
// Expanding bullish bodies suggest potential reversal to the downside.
SellMarket();
ResetBullishSequence();
}
}
private void ResetBearishSequence()
{
_bearishSequence = 0;
_previousBearishBody = 0m;
}
private void ResetBullishSequence()
{
_bullishSequence = 0;
_previousBullishBody = 0m;
}
private void ResetState()
{
ResetBearishSequence();
ResetBullishSequence();
}
}
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
from StockSharp.Algo.Strategies import Strategy
class e_turbo_fx_steps_strategy(Strategy):
def __init__(self):
super(e_turbo_fx_steps_strategy, self).__init__()
self._depth_analysis = self.Param("DepthAnalysis", 3)
self._take_profit_steps = self.Param("TakeProfitSteps", 120.0)
self._stop_loss_steps = self.Param("StopLossSteps", 70.0)
self._trade_volume = self.Param("TradeVolume", 0.1)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1)))
self._bearish_sequence = 0
self._bullish_sequence = 0
self._previous_bearish_body = 0.0
self._previous_bullish_body = 0.0
@property
def DepthAnalysis(self):
return self._depth_analysis.Value
@DepthAnalysis.setter
def DepthAnalysis(self, value):
self._depth_analysis.Value = value
@property
def TakeProfitSteps(self):
return self._take_profit_steps.Value
@TakeProfitSteps.setter
def TakeProfitSteps(self, value):
self._take_profit_steps.Value = value
@property
def StopLossSteps(self):
return self._stop_loss_steps.Value
@StopLossSteps.setter
def StopLossSteps(self, value):
self._stop_loss_steps.Value = value
@property
def TradeVolume(self):
return self._trade_volume.Value
@TradeVolume.setter
def TradeVolume(self, value):
self._trade_volume.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnStarted2(self, time):
super(e_turbo_fx_steps_strategy, self).OnStarted2(time)
self._bearish_sequence = 0
self._bullish_sequence = 0
self._previous_bearish_body = 0.0
self._previous_bullish_body = 0.0
tp_steps = float(self.TakeProfitSteps)
sl_steps = float(self.StopLossSteps)
if sl_steps > 0.0 or tp_steps > 0.0:
sl_unit = Unit(sl_steps, UnitTypes.Absolute) if sl_steps > 0.0 else None
tp_unit = Unit(tp_steps, UnitTypes.Absolute) if tp_steps > 0.0 else None
self.StartProtection(sl_unit, tp_unit)
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self.ProcessCandle).Start()
def ProcessCandle(self, candle):
if candle.State != CandleStates.Finished:
return
if self.Position != 0:
self._reset_state()
return
close = float(candle.ClosePrice)
open_price = float(candle.OpenPrice)
body_size = abs(close - open_price)
if close < open_price:
self._handle_bearish_candle(body_size)
elif close > open_price:
self._handle_bullish_candle(body_size)
else:
self._reset_state()
def _handle_bearish_candle(self, body_size):
self._reset_bullish_sequence()
if body_size <= 0.0:
self._reset_bearish_sequence()
return
if self._bearish_sequence == 0 or body_size > self._previous_bearish_body:
self._bearish_sequence += 1
else:
self._bearish_sequence = 1
self._previous_bearish_body = body_size
if self._bearish_sequence >= int(self.DepthAnalysis):
self.BuyMarket()
self._reset_bearish_sequence()
def _handle_bullish_candle(self, body_size):
self._reset_bearish_sequence()
if body_size <= 0.0:
self._reset_bullish_sequence()
return
if self._bullish_sequence == 0 or body_size > self._previous_bullish_body:
self._bullish_sequence += 1
else:
self._bullish_sequence = 1
self._previous_bullish_body = body_size
if self._bullish_sequence >= int(self.DepthAnalysis):
self.SellMarket()
self._reset_bullish_sequence()
def _reset_bearish_sequence(self):
self._bearish_sequence = 0
self._previous_bearish_body = 0.0
def _reset_bullish_sequence(self):
self._bullish_sequence = 0
self._previous_bullish_body = 0.0
def _reset_state(self):
self._reset_bearish_sequence()
self._reset_bullish_sequence()
def OnReseted(self):
super(e_turbo_fx_steps_strategy, self).OnReseted()
self._bearish_sequence = 0
self._bullish_sequence = 0
self._previous_bearish_body = 0.0
self._previous_bullish_body = 0.0
def CreateClone(self):
return e_turbo_fx_steps_strategy()