Estrategia e-TurboFx Steps
Descripción General
La estrategia e-TurboFx es un sistema de reversión por agotamiento del impulso originalmente escrito para MetaTrader 5. Monitorea las velas terminadas más recientes y busca secuencias donde los cuerpos de las velas siguen expandiéndose en la misma dirección. Una serie creciente de velas bajistas indica capitulación y por tanto un posible setup largo, mientras que una serie creciente de velas alcistas anuncia una posible oportunidad corta. El puerto de StockSharp usa la API de alto nivel con suscripciones a velas y protección automatizada de posición.
Lógica de Trading
- Inspeccionar las últimas
DepthAnalysis velas terminadas del CandleType seleccionado.
- Contar cuántas velas consecutivas cerraron por debajo de su apertura (bajistas) y cuántas cerraron por encima de su apertura (alcistas).
- Seguir la progresión del tamaño del cuerpo: cada nueva vela en la secuencia debe tener un cuerpo absoluto más grande que la anterior. Cuando esta condición falla, la secuencia se reinicia.
- Entrada larga:
DepthAnalysis velas bajistas consecutivas con cuerpos estrictamente en expansión desencadenan una compra a mercado, siempre que no haya posición abierta actualmente.
- Entrada corta:
DepthAnalysis velas alcistas consecutivas con cuerpos estrictamente en expansión desencadenan una venta a mercado, igualmente solo cuando la posición está plana.
- Mientras una posición está activa, la estrategia pausa la detección de señales para evitar apilar operaciones. La gestión del riesgo se delega al bloque de protección integrado configurado al inicio.
Gestión de Posición
StartProtection registra automáticamente órdenes de stop-loss y take-profit usando distancias medidas en pasos de precio (ticks del exchange). Establecer una distancia en cero deshabilita la orden de protección correspondiente.
- La estrategia mantiene solo una posición abierta. Cuando aparece una nueva señal después de que la operación anterior se cierra, las secuencias de velas se reconstruyen desde cero basándose en datos de mercado frescos.
- Las entradas a mercado usan el parámetro
TradeVolume. Cambiar el parámetro en la UI actualiza inmediatamente el volumen de la estrategia.
Parámetros
| Parámetro |
Descripción |
Predeterminado |
DepthAnalysis |
Número de velas terminadas recientes usadas para validar el patrón de expansión. Valores más altos exigen rachas más largas antes de operar. |
3 |
TakeProfitSteps |
Distancia del take-profit en pasos de precio del exchange (ticks). 0 deshabilita el take-profit. |
120 |
StopLossSteps |
Distancia del stop-loss en pasos de precio del exchange (ticks). 0 deshabilita el stop-loss. |
70 |
TradeVolume |
Volumen de orden enviado con cada entrada a mercado. |
0.1 |
CandleType |
Tipo de datos de vela (marco temporal) suscrito para el análisis. |
Marco temporal de 1 hora |
Todos los parámetros numéricos tienen metadatos de optimización para que puedan incluirse en las optimizaciones de StockSharp si se desea.
Notas y Recomendaciones
- El asesor experto MQL5 original recalculaba los datos de velas en cada tick; la implementación de StockSharp logra el mismo comportamiento con eventos de velas terminadas y contadores internos.
- Debido a que la estrategia se basa en comparaciones del cuerpo de velas, es sensible al marco temporal seleccionado. Los marcos temporales más cortos producirán más señales pero pueden requerir stops más ajustados.
- Asegúrese de que el instrumento conectado exponga un
PriceStep válido para que las distancias de stop-loss y take-profit definidas en pasos se traduzcan correctamente a precios.
- Antes de operar en vivo, valide el comportamiento en el Designer/Backtester para confirmar que las distancias de stop y objetivo se alineen con el instrumento elegido.
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()