La Estrategia de Three Typical Candles recrea el Asesor Experto de MetaTrader "Three Typical Candles" dentro de la API de alto nivel de StockSharp. El sistema observa el precio típico de las tres últimas velas completadas y opera cuando detecta una secuencia estrictamente monótona. El precio típico se define como la media aritmética del máximo, mínimo y cierre de una vela. Cuando las tres velas finalizadas más recientes forman una secuencia creciente de precios típicos, la estrategia entra en largo. A la inversa, una secuencia decreciente desencadena una entrada en corto.
El port sigue de cerca la lógica MQL5 original:
Las señales se evalúan solo una vez por vela finalizada para evitar ruido intrabarra.
Una ventana de trading configurable puede deshabilitar el trading fuera de las horas seleccionadas y fuerza la estrategia a posición plana cuando el filtro está activo.
Las posiciones opuestas se cierran antes de abrir una nueva, por lo que la estrategia nunca mantiene ambas direcciones al mismo tiempo.
El volumen de las órdenes espeja el EA fuente usando un tamaño de lote fijo, respetando el paso de volumen de la bolsa así como las restricciones de volumen mínimo y máximo informadas por el instrumento.
Reglas de trading
Detección de señal
Calcular el precio típico Tp = (High + Low + Close) / 3 para cada vela finalizada.
Rastrear los dos valores típicos anteriores. Una vez disponibles tres valores, verificar una secuencia estrictamente creciente o estrictamente decreciente.
Entrada en largo
Si Tp[-2] < Tp[-1] < Tp[0] (tres precios típicos crecientes) y la posición actual no es larga, la estrategia cierra cualquier exposición corta y envía una orden de compra a mercado.
Entrada en corto
Si Tp[-2] > Tp[-1] > Tp[0] (tres precios típicos decrecientes) y la posición actual no es corta, la estrategia cierra cualquier exposición larga y envía una orden de venta a mercado.
Control de tiempo
Cuando el filtro de tiempo opcional está habilitado, la estrategia evalúa la señal solo cuando el tiempo de apertura de la vela cae dentro de la sesión de trading configurada. Fuera de esa ventana, cualquier posición abierta se liquida inmediatamente y no se colocan nuevas operaciones.
Gestión de posiciones
La estrategia no tiene niveles explícitos de stop-loss o take-profit. La gestión de riesgos debe manejarse externamente (p. ej., mediante estrategias protectoras o supervisión manual).
Parámetros
Nombre
Tipo
Predeterminado
Descripción
Volume
decimal
1
Volumen de orden fijo (lotes o contratos). La estrategia redondea automáticamente el valor al paso de volumen válido más cercano y aplica los límites mínimo/máximo del instrumento.
UseTimeControl
bool
true
Habilita el filtro de ventana de trading intradía. Cuando está deshabilitado, las señales se evalúan las 24 horas.
StartHour
int
11
Hora de inicio inclusiva (0-23) de la ventana de trading cuando UseTimeControl es verdadero.
EndHour
int
17
Hora de fin exclusiva (0-23) de la ventana de trading cuando UseTimeControl es verdadero. Si la hora de fin es menor que la de inicio, la ventana cruza medianoche.
CandleType
DataType
TimeFrame(1h)
Tipo de vela usado para el análisis. Seleccione un marco temporal compatible con su fuente de datos.
Notas de implementación
La clase base Strategy de StockSharp maneja las suscripciones y el enrutamiento de órdenes. Las señales se evalúan en ProcessCandle, que recibe velas completadas a través de la API de enlace de alto nivel.
Las órdenes de mercado se emiten a través de BuyMarket y SellMarket. Cuando ocurre una reversión, la estrategia primero cierra la exposición existente usando una orden de mercado opuesta antes de enviar la nueva entrada.
StartProtection() se llama durante la inicialización para permitir adjuntar mecanismos de protección opcionales si se desea.
El helper GetTradeVolume replica la normalización de lotes de MetaTrader ajustando el volumen configurado a las restricciones de la bolsa (paso de volumen, mínimo y máximo).
La estrategia almacena solo dos precios típicos históricos, suficientes para evaluar el patrón de tres velas sin mantener grandes colecciones.
Consejos de uso
Adjunte la estrategia a un instrumento con liquidez suficiente. El EA original usaba datos Forex intradía, pero cualquier mercado que proporcione velas OHLC puede usarse.
Elija un marco temporal de velas que se adapte a su horizonte de trading. Las velas de una hora predeterminadas replican el comportamiento del EA fuente, aunque intervalos más cortos o más largos pueden explorarse mediante optimización de parámetros.
Considere combinar la estrategia con controles de riesgo como límites de drawdown máximo o stop loss a nivel de cartera a través del framework de estrategias protectoras de StockSharp.
Realice backtests en múltiples instrumentos y sesiones de trading para confirmar que el patrón estrictamente monótono produce señales accionables bajo sus condiciones 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()