e-TurboFx Momentum-Strategie
Überblick
Die e-TurboFx Momentum Strategy ist eine direkte Portierung des ursprünglichen MetaTrader 4 Expertenberaters „e-TurboFx“. Das System scannt die zuletzt fertiggestellten Kerzen und sucht nach Richtungsabschnitten, in denen sich die Kerzenkörper weiter ausdehnen. Aufeinanderfolgende bärische Kerzen mit wachsender Körpergröße signalisieren eine mögliche Kapitulation, die mit einem Long-Einstieg abgeschwächt werden kann, während aufeinanderfolgende bullische Kerzen mit expandierenden Körpern auf eine überzogene Rallye hinweisen, die möglicherweise leer verkauft wird. Die StockSharp-Implementierung hält die Logik ereignisgesteuert durch Kerzenabonnements und fügt automatisch optionalen Stop-Loss- und Take-Profit-Schutz hinzu.
Handelslogik
- Abonnieren Sie einen konfigurierbaren Kerzentyp (Zeitrahmen) und verarbeiten Sie nur fertige Kerzen.
- Verfolgen Sie zwei separate Sequenzen: eine für bärische Kerzen und eine für bullische Kerzen.
- Messen Sie für jede Kerze die absolute Körpergröße (
|Close - Open|).
- Setzen Sie die Gegenrichtungssequenz zurück, sobald eine Kerze in die andere Richtung schließt.
- Innerhalb jeder Sequenz sind streng expandierende Körper erforderlich – jede neue Kerze muss einen größeren Körper haben als die vorherige. Jede Kontraktion startet den Sequenzzähler von 1 neu.
- Wenn die Anzahl der Kerzen in einer Sequenz
DepthAnalysis erreicht, lösen Sie einen Markteintritt in die entgegengesetzte Richtung der letzten Sequenz aus (Kauf nach Abwärtstrends, Verkauf nach Aufwärtstrends).
- Sobald eine Position offen ist, pausieren Sie die Signalerkennung, bis die Strategie zu einer flachen Position zurückkehrt. Das integrierte
StartProtection verwaltet optionale Stop-Loss- und Take-Profit-Abstände, ausgedrückt in Preisschritten (Ticks).
Dieses Verhalten reproduziert den MQL4-Algorithmus, bei dem der Fachberater die letzten N geschlossenen Kerzen überprüfte und bestätigte, dass alle Körper in die gleiche Richtung ausgerichtet waren und dass jeder Körper größer als der Körper der nächstälteren Kerze war.
Details zur Implementierung
- Verwendet das High-Level-Kerzenabonnement API mit
SubscribeCandles und Bind, um die Projektrichtlinien einzuhalten.
- Behält nur Skalarfelder (
_bearishSequence, _bullishSequence, _previousBearishBody, _previousBullishBody), um benutzerdefinierte Sammlungen zu vermeiden und sich auf den internen Status zwischen Ereignissen zu verlassen.
- Ruft
StartProtection nur einmal in OnStarted auf, um optionale Stop-Loss- und Take-Profit-Orders in Preisschritten zu konfigurieren. Ein Wert von 0 deaktiviert jede Schutzanordnung, genau wie der ursprüngliche Experte.
- Bietet ausführliche englische Kommentare im Quellcode, einschließlich Erläuterungen zu Zurücksetzungen und Eintragsauslösern.
- Zeichnet Kerzen und eigene Trades in einem Diagrammbereich, wenn es im Designer oder auf der Benutzeroberfläche ausgeführt wird, um das Debuggen zu erleichtern.
Parameter
| Parameter |
Beschreibung |
Standard |
DepthAnalysis |
Anzahl der aufeinanderfolgenden fertigen Kerzen, die in eine Richtung mit expandierenden Körpern erforderlich sind, bevor ein Handel eröffnet wird. |
3 |
TakeProfitSteps |
Take-Profit-Distanz gemessen in Börsenpreisschritten (Ticks). Auf 0 setzen, um den Take-Profit zu deaktivieren. |
120 |
StopLossSteps |
Stop-Loss-Distanz gemessen in Börsenkursschritten (Ticks). Auf 0 setzen, um den Stop-Loss zu deaktivieren. |
70 |
TradeVolume |
Mit jeder Marktorder gesendetes Volumen. Wenn Sie diesen Parameter ändern, wird auch die Basis Strategy.Volume aktualisiert. |
0.1 |
CandleType |
Für die Analyse abonnierter Kerzendatentyp (Zeitrahmen). |
1 hour |
Alle numerischen Parameter stellen Optimierungsmetadaten bereit, sodass die Strategie bei Bedarf mit StockSharp Optimierern optimiert werden kann.
Hinweise und Empfehlungen
- Da die Strategie auf die Expansion des Kerzenkörpers reagiert, beeinflusst der gewählte Zeitrahmen die Signalfrequenz erheblich. Kürzere Intervalle führen zu mehr Trades, erfordern jedoch möglicherweise engere Schutzabstände.
- Stellen Sie sicher, dass die verbundene Sicherheit einen gültigen
PriceStep definiert. andernfalls können die stufenweisen Schutzabstände nicht in absolute Preise umgerechnet werden.
- Testen Sie den Port im StockSharp-Designer vor der Live-Bereitstellung erneut, um zu überprüfen, wie Stopp und Ziel für das ausgewählte Instrument übersetzt werden.
- Die Strategie behält jeweils eine einzelne offene Position bei. Nach jedem Beenden werden die Zähler zurückgesetzt und das Muster muss von Grund auf neu erstellt werden, um das ursprüngliche MQL4-Verhalten widerzuspiegeln.
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 ETurboFxMomentumStrategy : 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="ETurboFxMomentumStrategy" /> class.
/// </summary>
public ETurboFxMomentumStrategy()
{
_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(
takeProfit: takeProfitUnit,
stopLoss: stopLossUnit,
isStopTrailing: false,
useMarketOrders: true);
}
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;
// Convert the user-friendly tick distance into a StockSharp Unit instance.
return new Unit(steps, UnitTypes.Absolute);
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
// indicators formed check removed
if (Position != 0)
{
// Do not look for new signals while a position is active.
ResetState();
return;
}
var bodySize = Math.Abs(candle.ClosePrice - candle.OpenPrice);
// The original expert compared absolute bodies of the latest N closed candles.
// Measuring the body here reproduces that behaviour candle by candle.
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)
{
// Bearish candles reset the bullish path and allow the downside streak to continue.
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)
{
// Bullish candles reset the bearish path and allow the upside streak to continue.
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_momentum_strategy(Strategy):
"""Momentum reversal strategy that tracks consecutive candles with expanding bodies.
Uses StartProtection for SL/TP."""
def __init__(self):
super(e_turbo_fx_momentum_strategy, self).__init__()
self._depth_analysis = self.Param("DepthAnalysis", 3) \
.SetGreaterThanZero() \
.SetDisplay("Depth Analysis", "Number of finished candles used for pattern detection", "Trading Rules")
self._take_profit_steps = self.Param("TakeProfitSteps", 120.0) \
.SetDisplay("Take Profit (steps)", "Take profit distance in price steps (ticks)", "Risk Management")
self._stop_loss_steps = self.Param("StopLossSteps", 70.0) \
.SetDisplay("Stop Loss (steps)", "Stop loss distance in price steps (ticks)", "Risk Management")
self._trade_volume = self.Param("TradeVolume", 0.1) \
.SetGreaterThanZero() \
.SetDisplay("Trade Volume", "Order volume used for entries", "Trading Rules")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Timeframe of the candles analysed by the strategy", "Market Data")
self._bearish_sequence = 0
self._bullish_sequence = 0
self._previous_bearish_body = 0.0
self._previous_bullish_body = 0.0
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def DepthAnalysis(self):
return self._depth_analysis.Value
@property
def TakeProfitSteps(self):
return self._take_profit_steps.Value
@property
def StopLossSteps(self):
return self._stop_loss_steps.Value
@property
def TradeVolume(self):
return self._trade_volume.Value
def OnReseted(self):
super(e_turbo_fx_momentum_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(e_turbo_fx_momentum_strategy, self).OnStarted2(time)
self._reset_state()
self.Volume = float(self.TradeVolume)
tp_steps = float(self.TakeProfitSteps)
sl_steps = float(self.StopLossSteps)
tp_unit = Unit(tp_steps, UnitTypes.Absolute) if tp_steps > 0 else None
sl_unit = Unit(sl_steps, UnitTypes.Absolute) if sl_steps > 0 else None
if tp_unit is not None or sl_unit is not None:
self.StartProtection(tp_unit, sl_unit)
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._process_candle).Start()
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
if self.Position != 0:
self._reset_state()
return
body_size = abs(float(candle.ClosePrice) - float(candle.OpenPrice))
if float(candle.ClosePrice) < float(candle.OpenPrice):
self._handle_bearish_candle(body_size)
elif float(candle.ClosePrice) > float(candle.OpenPrice):
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:
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 >= self.DepthAnalysis:
self.BuyMarket()
self._reset_bearish_sequence()
def _handle_bullish_candle(self, body_size):
self._reset_bearish_sequence()
if body_size <= 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 >= 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 CreateClone(self):
return e_turbo_fx_momentum_strategy()