Ver en GitHub

Estrategia de OneHrStocTrader

Descripción general

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

  1. 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.
  2. 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].
  3. 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.
  4. Gestión de órdenes
    • Cierra una posición opuesta antes de abrir una nueva.
    • Limita entradas consecutivas en la misma dirección mediante MaxOrdersPerDirection.
  5. 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;
	}
}