Convertido do especialista MetaTrader 5 Exp_VortexIndicator_MMRec_Duplex.mq5 (MQL ID 23180).
Mantém dois fluxos independentes do indicador Vortex: um dedicado a operações longas e outro a operações curtas. Cada fluxo tem seu próprio período, comprimento e deslocamento de barra para que a lógica altista e baixista possa ser ajustada separadamente.
Replica o módulo de recuperação de gestão de dinheiro "MMRec" do EA original. A estratégia rastreia os últimos resultados de operações por direção e muda temporariamente para um tamanho de ordem reduzido após um número configurável de perdas.
Lógica de Sinal
Assinar o tipo de vela configurado para cada fluxo e calcular o indicador Vortex (VI+ e VI-).
Entradas longas: quando a barra anterior tinha VI+ abaixo ou igual a VI- e a barra atual fecha com VI+ acima de VI- (cruzamento altista). As entradas são permitidas apenas se AllowLongEntries estiver habilitado.
Saídas longas: quando VI- sobe acima de VI+ na barra avaliada, desde que AllowLongExits esteja habilitado.
Entradas curtas: quando a barra anterior tinha VI+ acima ou igual a VI- e a barra atual fecha com VI+ abaixo de VI- (cruzamento baixista), controlado por AllowShortEntries.
Saídas curtas: quando VI+ sobe de volta acima de VI- na barra avaliada, controlado por AllowShortExits.
Cada direção mantém seus próprios níveis de stop-loss e take-profit medidos em passos de preço. Atingir qualquer um deles fecha imediatamente a posição e registra o resultado para os contadores de recuperação.
Recuperação de Gestão de Dinheiro
O EA original inspeciona uma janela deslizante de operações passadas para decidir se a próxima ordem deve usar o volume normal ou o reduzido. Este port replica o mesmo comportamento.
Para operações longas, a fila armazena até LongTotalTrigger resultados de PnL mais recentes. Se pelo menos LongLossTrigger deles forem operações perdedoras, a próxima entrada longa usa LongSmallMoneyManagement; caso contrário usa LongMoneyManagement.
Operações curtas repetem a mesma lógica com ShortTotalTrigger, ShortLossTrigger, ShortSmallMoneyManagement e ShortMoneyManagement.
Quando os valores de gatilho são zero, as filas são esvaziadas e o volume base é sempre usado.
Modos de Margem
MarginModeOption descreve como o valor de gestão de dinheiro é convertido em um volume executável:
FreeMargin (0): tratar o valor como uma fração do capital (aproximação do modo "margem livre" original).
Balance (1): idêntico a FreeMargin neste port; usa o valor atual do portfólio.
LossFreeMargin (2): arriscar uma fração do capital usando a distância de stop-loss configurada. Recorre ao dimensionamento baseado em preço se a distância do stop for zero.
LossBalance (3): igual a LossFreeMargin nesta implementação.
Lot (4): interpretar o valor diretamente como volume de ordem.
Todos os tamanhos calculados são normalizados usando o passo de volume do instrumento, bem como as restrições de volume mínimo e máximo.
Parâmetros
Parâmetro
Padrão
Descrição
LongCandleType
H4
Período usado para o indicador Vortex do lado comprado.
ShortCandleType
H4
Período usado para o indicador Vortex do lado vendido.
LongLength
14
Período do indicador Vortex para sinais longos.
ShortLength
14
Período do indicador Vortex para sinais curtos.
LongSignalBar
1
Deslocamento de barra fechada avaliado para cruzamentos longos (0 = última barra fechada).
ShortSignalBar
1
Deslocamento de barra fechada avaliado para cruzamentos curtos.
AllowLongEntries
true
Habilitar entradas longas quando o cruzamento altista aparecer.
AllowLongExits
true
Habilitar fechamento de posições longas quando VI- domina VI+.
AllowShortEntries
true
Habilitar entradas curtas quando o cruzamento baixista aparecer.
AllowShortExits
true
Habilitar fechamento de posições curtas quando VI+ domina VI-.
LongTotalTrigger
5
Número de operações longas recentes inspecionadas pelo contador de recuperação.
LongLossTrigger
3
Operações longas perdedoras necessárias antes de mudar para o volume longo reduzido.
LongMoneyManagement
0.1
Valor base de gestão de dinheiro para operações longas.
LongSmallMoneyManagement
0.01
Valor reduzido de gestão de dinheiro após uma sequência de perdas longas.
LongMarginMode
Lot
Interpretação do valor de gestão de dinheiro longo (ver modos acima).
LongStopLossSteps
1000
Distância protetora abaixo da entrada longa expressa em passos de preço.
LongTakeProfitSteps
2000
Distância de take-profit acima da entrada longa expressa em passos de preço.
LongSlippageSteps
10
Tolerância de slippage informativa para ordens longas (não usada para dimensionamento).
ShortTotalTrigger
5
Número de operações curtas recentes inspecionadas pelo contador de recuperação.
ShortLossTrigger
3
Operações curtas perdedoras necessárias antes de mudar para o volume curto reduzido.
ShortMoneyManagement
0.1
Valor base de gestão de dinheiro para operações curtas.
ShortSmallMoneyManagement
0.01
Valor reduzido de gestão de dinheiro após uma sequência de perdas curtas.
ShortMarginMode
Lot
Interpretação do valor de gestão de dinheiro curto.
ShortStopLossSteps
1000
Distância protetora acima da entrada curta expressa em passos de preço.
ShortTakeProfitSteps
2000
Distância de take-profit abaixo da entrada curta expressa em passos de preço.
ShortSlippageSteps
10
Tolerância de slippage informativa para ordens curtas.
Notas de Implementação
Construído inteiramente na API de alto nível do StockSharp. As assinaturas de velas impulsionam os indicadores Vortex através de Bind, que entrega barras finalizadas antes de qualquer decisão ser tomada.
A lógica de recuperação de operações armazena séries de lucros por direção em filas e replica as funções BuyTradeMMRecounterS / SellTradeMMRecounterS do MetaTrader.
Os níveis de stop-loss e take-profit são recalculados em unidades de preço (passo de preço do instrumento × passos configurados) e aplicados em cada vela recebida.
Os volumes de ordem são normalizados via restrições de VolumeStep, MinVolume e MaxVolume do instrumento para evitar envios inválidos.
Os parâmetros de slippage são preservados para fins de documentação, mas não são usados diretamente pelos manipuladores de ordens do StockSharp.
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Vortex Indicator MMRec Duplex strategy using EMA crossover.
/// Buys when fast EMA crosses above slow EMA, sells on reverse.
/// </summary>
public class VortexIndicatorMmrecDuplexStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private ExponentialMovingAverage _fast;
private ExponentialMovingAverage _slow;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
private int _cooldown;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }
public VortexIndicatorMmrecDuplexStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
protected override void OnReseted()
{
base.OnReseted();
_fast = null; _slow = null;
_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fast = new ExponentialMovingAverage { Length = FastPeriod };
_slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
subscription.Bind(_fast, _slow, ProcessCandle);
subscription.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished) return;
if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }
var close = candle.ClosePrice;
var step = Security?.PriceStep ?? 1m;
if (Position > 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }
_prevFast = fastValue; _prevSlow = slowValue;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class vortex_indicator_mmrec_duplex_strategy(Strategy):
def __init__(self):
super(vortex_indicator_mmrec_duplex_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 14) \
.SetDisplay("Fast Period", "Fast MA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 50) \
.SetDisplay("Slow Period", "Slow MA period", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 200) \
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 400) \
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk")
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def stop_loss_points(self):
return self._stop_loss_points.Value
@property
def take_profit_points(self):
return self._take_profit_points.Value
def OnReseted(self):
super(vortex_indicator_mmrec_duplex_strategy, self).OnReseted()
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(vortex_indicator_mmrec_duplex_strategy, self).OnStarted2(time)
self._fast = ExponentialMovingAverage()
self._fast.Length = self.fast_period
self._slow = ExponentialMovingAverage()
self._slow.Length = self.slow_period
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(5)))
subscription.Bind(self._fast, self._slow, self._process_candle)
subscription.Start()
def _process_candle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_value)
slow_val = float(slow_value)
if not self._fast.IsFormed or not self._slow.IsFormed:
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = float(candle.ClosePrice)
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
if self.Position > 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close <= self._entry_price - self.stop_loss_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close >= self._entry_price + self.take_profit_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close >= self._entry_price + self.stop_loss_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close <= self._entry_price - self.take_profit_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._prev_fast <= self._prev_slow and fast_val > slow_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 100
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return vortex_indicator_mmrec_duplex_strategy()