La Estrategia de Vela (Candle) es un puerto directo del clásico experto MT5 "Candle.mq5". Evalúa el color de cada vela completada en el marco temporal seleccionado y mantiene la posición alineada con el cierre más reciente. Las velas alcistas llevan la estrategia al largo, las bajistas al corto, y las planas dejan la posición sin cambios. El riesgo se controla mediante distancias de take-profit y trailing stop en pips que se convierten a precios absolutos a través del tamaño de tick del instrumento.
La estrategia solo reacciona después de que una vela se ha formado completamente para evitar el ruido dentro de la barra. Una retrospectiva obligatoria (MinBars * 2 velas completadas) valida que el gráfico contiene suficiente historial, mientras que un período de enfriamiento configurable espera entre operaciones. Esto produce una implementación fiel en StockSharp de la lógica MetaTrader original sin depender del acceso a series de bajo nivel.
Lógica de trading
Preparación
Procesa velas proporcionadas por CandleType; no se requieren otras fuentes de datos.
Espera hasta que se hayan procesado al menos 2 * MinBars velas completadas antes de permitir entradas.
Opera solo cuando la estrategia está en línea, formada y tiene permitido ejecutar órdenes.
Aplica el intervalo TradeCooldown (por defecto 10 segundos) entre cualquier dos operaciones.
Reglas de entrada y reversión
Estado plano:
Entrar largo (BuyMarket) cuando una vela cierra por encima de su apertura.
Entrar corto (SellMarket) cuando una vela cierra por debajo de su apertura.
Posición existente:
Si una posición larga enfrenta una vela bajista, vender |Position| + Volume para cerrar e inmediatamente revertir a una posición corta de tamaño Volume.
Si una posición corta enfrenta una vela alcista, comprar |Position| + Volume para cerrar e inmediatamente revertir a una posición larga de tamaño Volume.
Velas neutrales:
Cuando el cierre es igual a la apertura, no se toma acción manual; solo las órdenes protectoras pueden salir de la operación.
Gestión de riesgos y salidas
StartProtection adjunta un take-profit y un trailing stop medidos en pips. La estrategia multiplica cada valor de pip por (PriceStep * 10) para igualar el ajuste de MetaTrader para cotizaciones de 3 y 5 dígitos.
El trailing stop se activa solo cuando TrailingStopPips es mayor que cero; sigue el precio automáticamente una vez que la operación se mueve en la dirección favorable.
El take-profit cierra la posición cuando se alcanza la distancia configurada. Cualquier nivel protector cancela la orden opuesta tras su ejecución.
Las reversiones manuales causadas por el color de la vela también aplanan la exposición anterior antes de abrir la nueva posición.
Parámetros
CandleType – marco temporal de la serie de velas a analizar (por defecto: velas de 15 minutos).
TakeProfitPips – distancia al objetivo de take-profit en pips (por defecto: 50).
TrailingStopPips – distancia del trailing stop en pips (por defecto: 30).
MinBars – recuento mínimo de barras requerido antes de la primera operación (por defecto: 26; la estrategia espera 52 velas completadas).
TradeCooldown – período de espera después de cualquier acción de trading (por defecto: 10 segundos).
Establezca la propiedad Volume de la estrategia al tamaño de orden deseado. Cuando el mercado se revierte, la estrategia envía automáticamente suficiente volumen tanto para salir de la posición anterior como para establecer la nueva.
Notas de implementación
Solo se procesan las velas completadas (CandleStates.Finished). Esto refleja el experto MetaTrader, que dependía de los valores de barra cerrada obtenidos mediante CopyOpen/CopyClose.
El código utiliza la API de alto nivel de StockSharp: SubscribeCandles para los datos, Bind para procesar las barras entrantes, y BuyMarket/SellMarket para la ejecución de órdenes.
Las órdenes protectoras son gestionadas por StartProtection, por lo que no es necesario llevar el registro manual de las órdenes stop-limit.
El cálculo del tamaño de pip PriceStep * 10 reproduce la lógica de "ajuste de dígitos" de MQL para símbolos cotizados con 3 o 5 decimales.
Dado que las entradas son activadas por el cuerpo de la vela más reciente, la estrategia tiende a permanecer en el mercado de forma continua, alternando lados cada vez que cambia el color de la vela.
Ajuste las distancias en pips, el período de enfriamiento y el marco temporal para adaptarlos al instrumento que se opera. La configuración predeterminada refleja la muestra MT5 original pero puede optimizarse a través del marco de parámetros de StockSharp.
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>
/// Candle color reversal strategy with pip-based protection and trade cooldown.
/// </summary>
public class CandleStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<decimal> _takeProfitPips;
private readonly StrategyParam<decimal> _trailingStopPips;
private readonly StrategyParam<int> _minBars;
private readonly StrategyParam<TimeSpan> _tradeCooldown;
private decimal _pipSize;
private int _finishedCandles;
private DateTimeOffset _nextAllowedTime;
/// <summary>
/// Initializes a new instance of the <see cref="CandleStrategy"/>.
/// </summary>
public CandleStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe used for candle evaluation", "General");
_takeProfitPips = Param(nameof(TakeProfitPips), 50m)
.SetDisplay("Take Profit (pips)", "Distance to take profit in pips", "Risk")
.SetGreaterThanZero();
_trailingStopPips = Param(nameof(TrailingStopPips), 30m)
.SetDisplay("Trailing Stop (pips)", "Trailing stop distance in pips", "Risk")
.SetGreaterThanZero();
_minBars = Param(nameof(MinBars), 26)
.SetDisplay("Minimum Bars", "History length required before trading", "General")
.SetGreaterThanZero();
_tradeCooldown = Param(nameof(TradeCooldown), TimeSpan.FromSeconds(10))
.SetDisplay("Trade Cooldown", "Waiting time after each trade", "Risk");
}
/// <summary>
/// Candle type and timeframe used by the strategy.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Take profit distance expressed in pips.
/// </summary>
public decimal TakeProfitPips
{
get => _takeProfitPips.Value;
set => _takeProfitPips.Value = value;
}
/// <summary>
/// Trailing stop distance expressed in pips.
/// </summary>
public decimal TrailingStopPips
{
get => _trailingStopPips.Value;
set => _trailingStopPips.Value = value;
}
/// <summary>
/// Minimum number of bars required on the chart.
/// </summary>
public int MinBars
{
get => _minBars.Value;
set => _minBars.Value = value;
}
/// <summary>
/// Cooldown between consecutive trading operations.
/// </summary>
public TimeSpan TradeCooldown
{
get => _tradeCooldown.Value;
set => _tradeCooldown.Value = value;
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_pipSize = 0m;
_finishedCandles = 0;
_nextAllowedTime = DateTimeOffset.MinValue;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_pipSize = (Security?.PriceStep ?? 1m) * 10m;
Unit takeProfit = TakeProfitPips > 0m && _pipSize > 0m
? new Unit(TakeProfitPips * _pipSize, UnitTypes.Absolute)
: null;
Unit trailingStop = TrailingStopPips > 0m && _pipSize > 0m
? new Unit(TrailingStopPips * _pipSize, UnitTypes.Absolute)
: null;
// Enable automatic protective orders and trailing stop handling.
if (trailingStop != null || takeProfit != null)
StartProtection(takeProfit, trailingStop);
// Subscribe to candle data for the configured timeframe.
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
// Draw candles and executions on the chart if visualization is available.
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
// Skip unfinished candles to work only with final prices.
if (candle.State != CandleStates.Finished)
return;
_finishedCandles++;
// Wait until the chart has enough historical data.
if (_finishedCandles < MinBars * 2)
return;
var time = candle.CloseTime;
// Enforce cooldown between trading operations.
if (time < _nextAllowedTime)
return;
var isBullish = candle.ClosePrice > candle.OpenPrice;
var isBearish = candle.ClosePrice < candle.OpenPrice;
var tradeExecuted = false;
if (Position > 0 && isBearish)
{
// Reverse from long to short when a bearish candle appears.
var volume = Math.Abs(Position) + Volume;
if (volume > 0m)
{
SellMarket();
tradeExecuted = true;
}
}
else if (Position < 0 && isBullish)
{
// Reverse from short to long when a bullish candle appears.
var volume = Math.Abs(Position) + Volume;
if (volume > 0m)
{
BuyMarket();
tradeExecuted = true;
}
}
else if (Position == 0)
{
if (isBullish)
{
// Enter long on bullish close.
if (Volume > 0m)
{
BuyMarket();
tradeExecuted = true;
}
}
else if (isBearish)
{
// Enter short on bearish close.
if (Volume > 0m)
{
SellMarket();
tradeExecuted = true;
}
}
}
if (tradeExecuted)
{
_nextAllowedTime = time + TradeCooldown;
}
}
}
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, DateTimeOffset
from StockSharp.Messages import DataType, CandleStates, UnitTypes, Unit
from StockSharp.Algo.Strategies import Strategy
class candle_strategy(Strategy):
"""
Candle color reversal strategy with pip-based protection and trade cooldown.
"""
def __init__(self):
super(candle_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe used for candle evaluation", "General")
self._take_profit_pips = self.Param("TakeProfitPips", 50.0) \
.SetDisplay("Take Profit (pips)", "Distance to take profit in pips", "Risk")
self._trailing_stop_pips = self.Param("TrailingStopPips", 30.0) \
.SetDisplay("Trailing Stop (pips)", "Trailing stop distance in pips", "Risk")
self._min_bars = self.Param("MinBars", 26) \
.SetDisplay("Minimum Bars", "History length required before trading", "General")
self._trade_cooldown = self.Param("TradeCooldown", TimeSpan.FromSeconds(10)) \
.SetDisplay("Trade Cooldown", "Waiting time after each trade", "Risk")
self._pip_size = 0.0
self._finished_candles = 0
self._next_allowed_time = DateTimeOffset.MinValue
@property
def candle_type(self):
return self._candle_type.Value
@candle_type.setter
def candle_type(self, value):
self._candle_type.Value = value
@property
def take_profit_pips(self):
return self._take_profit_pips.Value
@take_profit_pips.setter
def take_profit_pips(self, value):
self._take_profit_pips.Value = value
@property
def trailing_stop_pips(self):
return self._trailing_stop_pips.Value
@trailing_stop_pips.setter
def trailing_stop_pips(self, value):
self._trailing_stop_pips.Value = value
@property
def min_bars(self):
return self._min_bars.Value
@min_bars.setter
def min_bars(self, value):
self._min_bars.Value = value
@property
def trade_cooldown(self):
return self._trade_cooldown.Value
@trade_cooldown.setter
def trade_cooldown(self, value):
self._trade_cooldown.Value = value
def OnReseted(self):
super(candle_strategy, self).OnReseted()
self._pip_size = 0.0
self._finished_candles = 0
self._next_allowed_time = DateTimeOffset.MinValue
def OnStarted2(self, time):
super(candle_strategy, self).OnStarted2(time)
self._pip_size = (self.Security.PriceStep if self.Security.PriceStep is not None else 1.0) * 10.0
tp = None
trailing = None
if self.take_profit_pips > 0 and self._pip_size > 0:
tp = Unit(self.take_profit_pips * self._pip_size, UnitTypes.Absolute)
if self.trailing_stop_pips > 0 and self._pip_size > 0:
trailing = Unit(self.trailing_stop_pips * self._pip_size, UnitTypes.Absolute)
if trailing is not None or tp is not None:
self.StartProtection(tp, trailing)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self.on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def on_process(self, candle):
if candle.State != CandleStates.Finished:
return
self._finished_candles += 1
if self._finished_candles < self.min_bars * 2:
return
close_time = candle.CloseTime
if self._next_allowed_time != DateTimeOffset.MinValue:
try:
if close_time < self._next_allowed_time:
return
except:
if DateTimeOffset(close_time) < self._next_allowed_time:
return
is_bullish = candle.ClosePrice > candle.OpenPrice
is_bearish = candle.ClosePrice < candle.OpenPrice
trade_executed = False
if self.Position > 0 and is_bearish:
self.SellMarket()
trade_executed = True
elif self.Position < 0 and is_bullish:
self.BuyMarket()
trade_executed = True
elif self.Position == 0:
if is_bullish:
self.BuyMarket()
trade_executed = True
elif is_bearish:
self.SellMarket()
trade_executed = True
if trade_executed:
self._next_allowed_time = close_time + self.trade_cooldown
def CreateClone(self):
return candle_strategy()