Ver en GitHub

Estrategia de MaRobot

Resumen

  • Implementa un sistema de cruce de medias móviles basado en barras que opera en un marco temporal intradía configurable mientras usa filtros diarios de ADX y RSI.
  • Usa los bindings de alto nivel de StockSharp para calcular dos medias móviles simples junto con detectores de oscilación Lowest/Highest e indicadores diarios AverageDirectionalIndex y RelativeStrengthIndex.
  • Recrea la lógica de protección original de MT4: take-profit por porcentaje, stop-loss basado en oscilaciones, y un stop de break-even opcional una vez que se alcanza una ganancia mínima.

Indicadores

  • SimpleMovingAverage (rápida y lenta) en el marco temporal principal.
  • Lowest / Highest para capturar los precios extremos de las últimas BackClose velas para la colocación del stop.
  • Valores diarios de AverageDirectionalIndex y RelativeStrengthIndex para filtros de fuerza de tendencia e impulso.

Parámetros

  • CandleType – marco temporal principal (predeterminado: velas de 15 minutos).
  • FastPeriod, SlowPeriod – longitudes de las líneas SMA rápida y lenta.
  • AdxThreshold – valor máximo permitido del ADX diario para habilitar nuevas entradas.
  • RsiThreshold – nivel de RSI diario para entradas largas (la entrada corta usa 100 - RsiThreshold).
  • TakeProfitRatio – distancia fraccional entre el precio de entrada y el objetivo de ganancia.
  • StopLossPoints – distancia del stop de protección (en puntos de instrumento) que se activa al alcanzar ProtectThreshold.
  • ProtectThreshold – proporción mínima de ganancia abierta que activa el stop de protección.
  • BackClose – número de velas completadas usadas para el cálculo del stop de máximo/mínimo de oscilación.
  • DailyAdxPeriod, DailyRsiPeriod – longitudes de los indicadores diarios.

Reglas de trading

  1. Trabajar solo en velas terminadas para coincidir con el asesor experto de MT4.
  2. Esperar hasta que todos los indicadores estén completamente formados y los valores diarios estén disponibles.
  3. Filtros de entrada:
    • Rechazar nuevas posiciones cuando el ADX diario supera AdxThreshold.
    • La entrada larga requiere que la SMA rápida cruce por encima de la SMA lenta y el RSI diario esté por debajo de RsiThreshold.
    • La entrada corta requiere que la SMA rápida cruce por debajo de la SMA lenta y el RSI diario esté por encima de 100 - RsiThreshold.
  4. Al entrar, almacenar el extremo de oscilación (Lowest para largos, Highest para cortos) como referencia de stop manual.
  5. Lógica de salida mientras una posición está activa:
    • Cerrar con TakeProfitRatio de ganancia medido desde el precio de entrada almacenado.
    • Cerrar si el cierre de la vela rompe el nivel de stop de oscilación almacenado.
    • Cerrar en un cruce de media móvil opuesto.
    • Después de que la ganancia supere ProtectThreshold, armar un stop de estilo break-even desplazado por StopLossPoints (redondeado al tamaño de tick) y cerrar si el precio retrocede a través de él.
  6. Restablecer todo el estado interno cuando la posición neta vuelve a cero.

Notas

  • Todos los comentarios en el código C# se mantienen en inglés según las directrices del repositorio.
  • La estrategia depende únicamente de suscripciones de alto nivel de StockSharp a través de Bind, evitando búferes de indicadores manuales.
  • La traducción a Python se omite intencionalmente según las instrucciones de la tarea.
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 MaRobotStrategy : 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 MaRobotStrategy()
	{
		_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;
	}
}