Estratégia e-TurboFx Steps
Visão Geral
A estratégia e-TurboFx é um sistema de reversão por exaustão de momentum originalmente escrito para MetaTrader 5. Ela monitora as velas concluídas mais recentes e procura sequências onde os corpos das velas continuam se expandindo na mesma direção. Uma série crescente de velas baixistas indica capitulação e, portanto, uma possível configuração de compra, enquanto uma série crescente de velas altistas anuncia uma possível oportunidade de venda. O port do StockSharp usa a API de alto nível com assinaturas de velas e proteção automatizada de posição.
Lógica de Trading
- Inspecionar as últimas
DepthAnalysis velas concluídas do CandleType selecionado.
- Contar quantas velas consecutivas fecharam abaixo de sua abertura (baixistas) e quantas fecharam acima de sua abertura (altistas).
- Rastrear a progressão do tamanho do corpo: cada nova vela na sequência deve ter um corpo absoluto maior que a anterior. Quando essa condição falhar, a sequência é redefinida.
- Entrada comprada:
DepthAnalysis velas baixistas consecutivas com corpos estritamente em expansão acionam uma compra a mercado, desde que nenhuma posição esteja aberta no momento.
- Entrada vendida:
DepthAnalysis velas altistas consecutivas com corpos estritamente em expansão acionam uma venda a mercado, igualmente apenas quando a posição está zerada.
- Enquanto uma posição está ativa, a estratégia pausa a detecção de sinais para evitar o empilhamento de trades. O gerenciamento de risco é delegado ao bloco de proteção integrado configurado no início.
Gerenciamento de Posição
StartProtection registra automaticamente ordens de stop-loss e take-profit usando distâncias medidas em passos de preço (ticks do exchange). Definir uma distância como zero desabilita a ordem de proteção correspondente.
- A estratégia mantém apenas uma posição aberta. Quando um novo sinal aparece depois que o trade anterior é fechado, as sequências de velas são reconstruídas do zero com base em dados de mercado frescos.
- As entradas a mercado usam o parâmetro
TradeVolume. Alterar o parâmetro na UI atualiza imediatamente o volume da estratégia.
Parâmetros
| Parâmetro |
Descrição |
Padrão |
DepthAnalysis |
Número de velas concluídas recentes usadas para validar o padrão de expansão. Valores mais altos exigem sequências mais longas antes de operar. |
3 |
TakeProfitSteps |
Distância do take-profit em passos de preço do exchange (ticks). 0 desabilita o take-profit. |
120 |
StopLossSteps |
Distância do stop-loss em passos de preço do exchange (ticks). 0 desabilita o stop-loss. |
70 |
TradeVolume |
Volume de ordem enviado com cada entrada a mercado. |
0.1 |
CandleType |
Tipo de dados de vela (período) assinado para a análise. |
Período de 1 hora |
Todos os parâmetros numéricos têm metadados de otimização para que possam ser incluídos nas otimizações do StockSharp, se desejado.
Notas e Recomendações
- O consultor especializado MQL5 original recalculava os dados de velas a cada tick; a implementação do StockSharp alcança o mesmo comportamento com eventos de velas concluídas e contadores internos.
- Como a estratégia depende de comparações de corpo de velas, ela é sensível ao período selecionado. Períodos mais curtos produzirão mais sinais, mas podem exigir stops mais apertados.
- Certifique-se de que o instrumento conectado exponha um
PriceStep válido para que as distâncias de stop-loss e take-profit definidas em passos sejam traduzidas corretamente para preços.
- Antes de operar ao vivo, valide o comportamento no Designer/Backtester para confirmar que as distâncias de stop e alvo se alinhem com o instrumento escolhido.
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()