La estrategia OneHrStocTrader replica el asesor experto de MetaTrader 4 OneHrStocTrader.mq4 dentro de la API de alto nivel de StockSharp. Opera un único instrumento en velas horarias y combina el oscilador estocástico con un filtro de ancho de Bandas de Bollinger. Una operación se abre solo cuando la volatilidad (medida por la distancia entre las Bandas de Bollinger) se encuentra dentro del rango configurado y el oscilador estocástico abandona una zona extrema exactamente a la hora configurada.
Lógica de trading
Datos
Trabaja con velas horarias por defecto (configurable).
Utiliza los valores de la vela completada más reciente para coincidir con el comportamiento de MetaTrader.
Filtro de Bandas de Bollinger
Calcula la diferencia entre las bandas superior e inferior en pips.
Las señales de trading se ignoran cuando la diferencia cae fuera del rango [BollingerSpreadLower, BollingerSpreadUpper].
Disparador del oscilador estocástico
Referencia las dos últimas velas completadas de la línea %K estocástica.
Compra: %K actual por debajo de StochasticLower, %K anterior subiendo (prev < current) y la nueva barra comienza en BuyHourStart.
Venta: %K actual por encima de StochasticUpper, %K anterior bajando (prev > current) y la nueva barra comienza en SellHourStart.
Gestión de órdenes
Cierra una posición opuesta antes de abrir una nueva.
Limita entradas consecutivas en la misma dirección mediante MaxOrdersPerDirection.
Gestión de riesgo
Distancias fijas de take-profit y stop-loss definidas en pips.
Trailing stop opcional que se mueve en incrementos de pip una vez que el precio avanza más allá de la distancia configurada.
Los niveles de protección internos se monitorean en cada vela completada; cuando se alcanzan, la estrategia cierra la posición a mercado.
Parámetros
Nombre
Descripción
Valor predeterminado
TradeVolume
Tamaño de orden en lotes.
0.01
CandleType
Marco temporal utilizado para todos los cálculos.
1h
BollingerPeriod
Período de retroceso de las Bandas de Bollinger.
20
BollingerSigma
Multiplicador sigma de las Bandas de Bollinger.
2.0
BollingerSpreadLower
Diferencia mínima de banda en pips requerida para operar.
56
BollingerSpreadUpper
Diferencia máxima de banda en pips permitida para operar.
158
BuyHourStart
Hora (0-23) cuando se evalúan las entradas largas.
4
SellHourStart
Hora (0-23) cuando se evalúan las entradas cortas.
0
StochasticKPeriod
Período %K estocástico.
5
StochasticDPeriod
Período %D estocástico.
3
StochasticSlowing
Factor de ralentización estocástico.
5
StochasticLower
Umbral de sobreventa.
36
StochasticUpper
Umbral de sobrecompra.
70
TakeProfitPips
Distancia de take-profit en pips.
200
StopLossPips
Distancia de stop-loss en pips.
95
TrailingStopPips
Distancia de trailing stop en pips (0 = desactivado).
40
MaxOrdersPerDirection
Máximo de entradas consecutivas por dirección.
1
Gráficos
Cuando hay una superficie de gráfico disponible, la estrategia dibuja:
Velas de precio.
Bandas de Bollinger.
Oscilador estocástico en un panel separado.
Operaciones ejecutadas para validación visual rápida.
Notas
El tamaño del pip se deriva del paso de precio del instrumento y la precisión decimal, reflejando la lógica de multiplicador de MetaTrader.
Los niveles de protección se recalculan usando Security.ShrinkPrice para asegurar el redondeo de precios conforme al exchange.
Los ajustes del trailing stop imitan el EA original ajustando el stop solo cuando el precio avanza al menos un pip más allá del stop anterior.
La implementación no crea órdenes pendientes; todas las entradas y salidas usan órdenes a mercado exactamente como el asesor experto fuente.
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 OneHrStocTraderStrategy : 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 OneHrStocTraderStrategy()
{
_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 one_hr_stoc_trader_strategy(Strategy):
def __init__(self):
super(one_hr_stoc_trader_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(one_hr_stoc_trader_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(one_hr_stoc_trader_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 one_hr_stoc_trader_strategy()