Estratégia de impulso e-TurboFx
Visão geral
A e-TurboFx Momentum Strategy é uma versão direta do MetaTrader consultor especialista original "e-TurboFx". O sistema verifica as velas finalizadas mais recentes e procura trechos direcionais onde os corpos das velas continuam se expandindo. Velas de baixa consecutivas com tamanho de corpo crescente sinalizam uma capitulação potencial que pode ser atenuada com uma entrada longa, enquanto velas de alta consecutivas com corpos em expansão sugerem uma alta prolongada que pode ser vendida a descoberto. A implementação StockSharp mantém a lógica orientada a eventos por meio de assinaturas de velas e anexa automaticamente proteção opcional de stop-loss e take-profit.
Lógica de negociação
- Assine um tipo de vela configurável (período de tempo) e processe apenas velas finalizadas.
- Acompanhe duas sequências separadas: uma para velas de baixa e outra para velas de alta.
- Para cada vela, meça o tamanho absoluto do corpo (
|Close - Open|).
- Redefina a sequência de direção oposta assim que uma vela fechar na outra direção.
- Dentro de cada sequência são necessários corpos estritamente em expansão – cada nova vela deve ter um corpo maior que a anterior. Qualquer contração reinicia o contador de sequência a partir de 1.
- Quando o número de velas em uma sequência atingir
DepthAnalysis, acione uma entrada no mercado na direção oposta da última sequência (compre após sequências de baixa, venda após sequências de alta).
- Assim que uma posição estiver aberta, pause a detecção do sinal até que a estratégia retorne a uma posição plana. O
StartProtection integrado gerencia distâncias opcionais de stop-loss e take-profit expressas em etapas de preço (ticks).
Este comportamento reproduz o algoritmo MQL4 onde o consultor especialista verificou as últimas N velas fechadas e confirmou que todos os corpos estavam alinhados na mesma direção e que cada corpo era maior que o corpo da próxima vela mais antiga.
Detalhes de implementação
- Usa a assinatura de velas de alto nível API com
SubscribeCandles e Bind para permanecer em conformidade com as diretrizes do projeto.
- Mantém apenas campos escalares (
_bearishSequence, _bullishSequence, _previousBearishBody, _previousBullishBody) para evitar coleções personalizadas e confiar no estado interno entre eventos.
- Chama
StartProtection apenas uma vez em OnStarted para configurar ordens opcionais de stop-loss e take-profit em etapas de preço. Um valor de 0 desativa cada ordem de proteção, assim como o especialista original.
- Fornece extensos comentários em inglês no código-fonte, incluindo explicações para redefinições e gatilhos de entrada.
- Desenha velas e negociações próprias em uma área do gráfico ao executar dentro do Designer ou na interface do usuário para facilitar a depuração.
Parâmetros
| Parâmetro |
Descrição |
Padrão |
DepthAnalysis |
Número de velas concluídas consecutivas necessárias em uma direção com corpos em expansão antes de abrir uma negociação. |
3 |
TakeProfitSteps |
Distância de take-profit medida em etapas de preços de câmbio (ticks). Defina como 0 para desativar o lucro. |
120 |
StopLossSteps |
Distância de stop-loss medida em etapas de preços de câmbio (ticks). Defina como 0 para desativar o stop loss. |
70 |
TradeVolume |
Volume enviado com cada ordem de mercado. A alteração deste parâmetro também atualiza a base Strategy.Volume. |
0.1 |
CandleType |
Tipo de dados da vela (período de tempo) inscrito para a análise. |
1 hour |
Todos os parâmetros numéricos expõem metadados de otimização para que a estratégia possa ser ajustada em StockSharp otimizadores, se desejado.
Notas e recomendações
- Como a estratégia reage à expansão do corpo da vela, o período escolhido afeta significativamente a frequência do sinal. Intervalos mais curtos produzem mais negociações, mas podem exigir distâncias de proteção mais estreitas.
- Certifique-se de que a segurança conectada defina um
PriceStep válido; caso contrário, as distâncias de proteção baseadas em degraus não poderão ser convertidas em preços absolutos.
- Faça backtest da porta dentro do StockSharp Designer antes da implantação ao vivo para validar como a parada e o destino são convertidos para o instrumento selecionado.
- A estratégia mantém uma única posição aberta por vez. Após cada saída, os contadores são redefinidos e o padrão deve ser reconstruído do zero, espelhando o comportamento original MQL4.
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()