A estratégia Lavika100 é uma versão fiel do expert advisor do MetaTrader 5 "Lavika cent". O sistema combina um filtro de momentum RAVI de uma hora (H1) e de quatro horas (H4) para decidir quando abrir operações. Ele mantém as escolhas originais de gestão de dinheiro (lote fixo ou percentual de risco), disciplina de uma posição, reversão opcional de sinal e gestão automática de stops. A versão StockSharp segue as diretrizes da API de alto nível: assinaturas de candles conduzem o fluxo, indicadores são acessados por binders e ordens de proteção são configuradas com StartProtection.
Fluxo de trabalho
Assinaturas de dados - a estratégia assina candles H1 para o período de execução e candles H4 para o filtro de tendência. O indicador SimpleMovingAverage é aplicado aos preços de abertura para emular as chamadas MT5 iMA(..., PRICE_OPEN).
Momentum RAVI - duas médias móveis em cada período (rápida/lenta) geram uma porcentagem "RAVI": (fast - slow) / slow * 100. O valor H1 precisa ser positivo antes que qualquer operação seja considerada.
Detecção do padrão de tendência - os quatro valores RAVI mais recentes em H4 são inspecionados:
Uma sequência ascendente (r0 > r1, r1 < r2, r2 < r3) aciona um sinal comprado.
Uma sequência descendente (r0 < r1, r1 > r2, r2 > r3) aciona um sinal vendido. Isso espelha o comportamento do código original, embora o expert só invertesse direção pelo flag Reverse.
Reversão de sinais e zeragem - dependendo dos parâmetros ReverseSignals e CloseOpposite, o algoritmo abre na direção detectada ou a reverte, fechando antes qualquer posição oposta.
Gestão de dinheiro - o volume vem de FixedVolume ou é escalado por risco via método RiskPercent (valor do portfólio * percentual / distância do stop).
Proteção - stop-loss, take-profit, trailing stop e passo trailing são ativados via StartProtection assim que a estratégia inicia e os parâmetros são diferentes de zero.
Regras de negociação
Entrada comprada - RAVI H1 é positivo e a série H4 mostra um padrão ascendente. A estratégia fecha uma posição vendida existente quando CloseOpposite=true antes de comprar.
Entrada vendida - RAVI H1 é positivo e a série H4 mostra um padrão descendente. Quando ReverseSignals=true, as direções são trocadas, correspondendo ao seletor "Reverse" do MT5.
Posição única - com OnlyOnePosition=true, qualquer exposição não zerada bloqueia entradas adicionais até que a posição seja fechada.
Dimensionamento de volume - o modo de percentual de risco usa o par PriceStep/StepPrice do instrumento para converter distância de preço em valor monetário, respeitando VolumeStep, VolumeMin e VolumeMax.
Parâmetros
Nome
Descrição
H1CandleType
Período para a lógica de execução (padrão 1 hora).
H4CandleType
Período mais alto usado pelo filtro de tendência (padrão 4 horas).
H1FastPeriod / H1SlowPeriod
Comprimentos de média móvel para o RAVI H1.
H4FastPeriod / H4SlowPeriod
Comprimentos de média móvel para o RAVI H4.
StopLossPoints
Distância de stop-loss em pontos baseados em pips.
TakeProfitPoints
Distância de take-profit em pontos baseados em pips.
TrailingStopPoints
Distância do trailing stop. Defina como zero para desabilitar trailing.
TrailingStepPoints
Passo mínimo para atualizações de trailing. Deve ser positivo quando trailing está habilitado.
FixedVolume
Tamanho do lote usado no modo fixo.
RiskPercent
Percentual do valor do portfólio a arriscar quando MoneyMode é igual a RiskPercent.
MoneyMode
Alterna entre FixedLot e RiskPercent.
OnlyOnePosition
Permite apenas uma única posição aberta.
ReverseSignals
Inverte ações compradas/vendidas (padrão true para corresponder à configuração do EA).
CloseOpposite
Fecha uma posição oposta antes de colocar uma nova ordem.
Notas de conversão
A conversão de pips imita o expert MT5: cotações de três e cinco dígitos multiplicam PriceStep por dez para obter um incremento do tamanho de um pip.
O histórico RAVI é armazenado sem coleções personalizadas - apenas quatro campos anuláveis - respeitando as restrições do repositório contra buffers manuais.
A gestão de dinheiro evita chamadas GetValue de indicadores e usa metadados de mercado do StockSharp para mapear risco percentual para volume.
StartProtection só é chamado quando pelo menos uma das distâncias de proteção é positiva, garantindo execução segura durante backtests e negociação ao vivo.
Dicas de uso
Forneça um instrumento no estilo Forex com PriceStep, StepPrice, VolumeStep, VolumeMin e VolumeMax corretamente configurados.
Ao usar dimensionamento baseado em risco, defina StopLossPoints diferente de zero; caso contrário, o volume calculado será zero.
Como o EA original continha uma peculiaridade lógica em que ambos os padrões definiam o flag de compra, mantenha ReverseSignals=true se precisar reproduzir suas operações exatas.
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;
public class Lavika100Strategy : 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 Lavika100Strategy()
{
_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 lavika100_strategy(Strategy):
def __init__(self):
super(lavika100_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(lavika100_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(lavika100_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 lavika100_strategy()