A Estratégia de Three Typical Candles recria o Expert Advisor do MetaTrader "Three Typical Candles" dentro da API de alto nível do StockSharp. O sistema observa o preço típico das três últimas velas concluídas e opera quando detecta uma sequência estritamente monótona. O preço típico é definido como a média aritmética da máxima, mínima e fechamento de uma vela. Quando as três velas finalizadas mais recentes formam uma sequência crescente de preços típicos, a estratégia entra comprada. Uma sequência decrescente dispara uma entrada vendida.
O port segue de perto a lógica MQL5 original:
Os sinais são avaliados apenas uma vez por vela finalizada para evitar ruído intrabarra.
Uma janela de trading configurável pode desabilitar o trading fora dos horários selecionados e força a estratégia para posição zerada quando o filtro está ativo.
Posições opostas são fechadas antes de abrir uma nova, então a estratégia nunca mantém ambas as direções ao mesmo tempo.
O volume das ordens espelha o EA fonte usando um tamanho de lote fixo, respeitando o passo de volume da bolsa e as restrições de volume mínimo e máximo informadas pelo instrumento.
Regras de trading
Detecção de sinal
Calcular o preço típico Tp = (High + Low + Close) / 3 para cada vela finalizada.
Rastrear os dois valores típicos anteriores. Com três valores disponíveis, verificar uma sequência estritamente crescente ou decrescente.
Entrada comprada
Se Tp[-2] < Tp[-1] < Tp[0] (três preços típicos crescentes) e a posição atual não é comprada, a estratégia fecha qualquer exposição vendida e envia uma ordem de compra a mercado.
Entrada vendida
Se Tp[-2] > Tp[-1] > Tp[0] (três preços típicos decrescentes) e a posição atual não é vendida, a estratégia fecha qualquer exposição comprada e envia uma ordem de venda a mercado.
Controle de tempo
Quando o filtro de tempo opcional está habilitado, a estratégia avalia o sinal apenas quando o horário de abertura da vela cai dentro da sessão de trading configurada. Fora dessa janela, qualquer posição aberta é liquidada imediatamente e nenhum novo trade é realizado.
Gestão de posições
A estratégia não tem níveis explícitos de stop-loss ou take-profit. O gerenciamento de risco deve ser tratado externamente (p. ex., via estratégias protetoras ou supervisão manual).
Parâmetros
Nome
Tipo
Padrão
Descrição
Volume
decimal
1
Volume de ordem fixo (lotes ou contratos). A estratégia arredonda automaticamente o valor para o passo de volume válido mais próximo e aplica os limites mínimo/máximo do instrumento.
UseTimeControl
bool
true
Habilita o filtro de janela de trading intradiário. Quando desabilitado, os sinais são avaliados 24 horas por dia.
StartHour
int
11
Hora de início inclusiva (0-23) da janela de trading quando UseTimeControl é verdadeiro.
EndHour
int
17
Hora de fim exclusiva (0-23) da janela de trading quando UseTimeControl é verdadeiro. Se a hora de fim for menor que a de início, a janela abrange meia-noite.
CandleType
DataType
TimeFrame(1h)
Tipo de vela usado para análise. Selecione um período compatível com seu feed de dados.
Notas de implementação
A classe base Strategy do StockSharp gerencia assinaturas e roteamento de ordens. Os sinais são avaliados em ProcessCandle, que recebe velas concluídas através da API de binding de alto nível.
As ordens a mercado são emitidas através de BuyMarket e SellMarket. Quando ocorre uma reversão, a estratégia primeiro fecha a exposição existente usando uma ordem de mercado oposta antes de enviar a nova entrada.
StartProtection() é chamado durante a inicialização para permitir o anexo de mecanismos de proteção opcionais se desejado.
O helper GetTradeVolume replica a normalização de lotes do MetaTrader ajustando o volume configurado às restrições da bolsa (passo de volume, mínimo e máximo).
A estratégia armazena apenas dois preços típicos históricos, suficientes para avaliar o padrão de três velas sem manter grandes coleções.
Dicas de uso
Anexe a estratégia a um instrumento com liquidez suficiente. O EA original usava dados Forex intradiários, mas qualquer mercado que forneça velas OHLC pode ser usado.
Escolha um período de velas que se adapte ao seu horizonte de trading. As velas de uma hora padrão replicam o comportamento do EA fonte; intervalos mais curtos ou mais longos podem ser explorados através da otimização de parâmetros.
Considere combinar a estratégia com controles de risco como limites de drawdown máximo ou stop loss no nível do portfólio via o framework de estratégias protetoras do StockSharp.
Faça backtests em múltiplos instrumentos e sessões de trading para confirmar que o padrão estritamente monótono produz sinais acionáveis sob suas condições de mercado.
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 ThreeTypicalCandlesStrategy : 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 ThreeTypicalCandlesStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 15).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
_slowPeriod = Param(nameof(SlowPeriod), 60).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 three_typical_candles_strategy(Strategy):
def __init__(self):
super(three_typical_candles_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 15) \
.SetDisplay("Fast Period", "Fast MA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 60) \
.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(three_typical_candles_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(three_typical_candles_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 three_typical_candles_strategy()