Ver en GitHub

Estrategia de inversión de SimpleTrade

Descripción general

  • StockSharp puerto del asesor experto MetaTrader 4 SimpleTrade.mq4 (también conocido como "neroTrade").
  • Diseñado para operar con un solo símbolo en el período configurado mediante el parámetro CandleType.
  • Siempre mantiene como máximo una posición abierta y cambia de dirección al abrir cada nueva barra.

Lógica de trading

  1. Cada vez que se activa una nueva vela, la estrategia compara el precio de apertura de la vela con el precio de apertura de la vela que tiene LookbackBars períodos más antigua.
  2. Si la nueva apertura es estrictamente superior a la referencia histórica, todas las posiciones existentes se cierran y se envía una nueva orden larga de mercado con TradeVolume lotes.
  3. De lo contrario (la apertura es igual o inferior), la estrategia cierra cualquier posición existente y abre una posición corta en el mercado del mismo tamaño.
  4. El parámetro StopLossPoints refleja la configuración stop original de EA. Cuando tanto el PriceStep como el StopLossPoints del valor están disponibles, la estrategia convierte el valor en una distancia absoluta y lo reenvía a StartProtection, permitiendo que StockSharp mantenga las órdenes protectoras de stop-loss automáticamente.
  5. Las aperturas de velas se rastrean mediante la suscripción de velas de alto nivel API. Las velas terminadas llenan la lista del historial, mientras que la vela activa activa la decisión una vez por barra.

Parámetros

Parámetro Descripción Predeterminado
TradeVolume Tamaño base del pedido expresado en lotes. Debe ser positivo. 1
StopLossPoints Distancia de parada de protección en los puntos del instrumento. Establezca en 0 para desactivar el stop-loss automático. 120
LookbackBars Número de barras utilizadas para la comparación de precios abiertos. Un valor de 3 reproduce Open[0] frente a Open[3] del código original. 3
CandleType Periodo de tiempo (como DataType) a partir del cual se solicitan velas. Controla cuando aparecen nuevas señales. 1 hour timeframe

Notas de implementación

  • Utiliza el flujo de trabajo de alto nivel SubscribeCandles(...).Bind(...), por lo que la estrategia sigue siendo liviana y reacciona tanto a velas históricas como a velas activas.
  • StartProtection se invoca una vez durante OnStarted. Asegúrese de que la seguridad conectada proporcione PriceStep; de lo contrario, la distancia del stop-loss no se puede traducir a precios absolutos.
  • Debido a que todas las operaciones se ingresan con órdenes de mercado al comienzo de cada barra, el manejo del deslizamiento se delega al centro de negociación y no hay ningún parámetro slippage adicional.
  • El búfer abierto histórico mantiene solo una pequeña ventana móvil (valores LookbackBars + 5) para evitar el uso innecesario de memoria.
  • No se proporciona ningún puerto Python; el directorio CS/ contiene la única implementación.

Estructura de archivos

4002_SimpleTrade/
├── CS/
│ └── SimpleTradeFlipStrategy.cs
├── README.md
├── README_zh.md
└── README_ru.md
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>
/// StockSharp port of the MetaTrader "SimpleTrade" expert advisor.
/// Compares the opening price of the current bar with the bar from several periods ago and flips the position accordingly.
/// </summary>
public class SimpleTradeFlipStrategy : Strategy
{
	private readonly StrategyParam<decimal> _tradeVolume;
	private readonly StrategyParam<decimal> _stopLossPoints;
	private readonly StrategyParam<int> _lookbackBars;
	private readonly StrategyParam<DataType> _candleType;

	private readonly List<decimal> _openHistory = new();
	private int _cooldown;

	/// <summary>
	/// Initializes a new instance of the <see cref="SimpleTradeFlipStrategy"/> class.
	/// </summary>
	public SimpleTradeFlipStrategy()
	{
		_tradeVolume = Param(nameof(TradeVolume), 1m)
			.SetGreaterThanZero()
			.SetDisplay("Trade Volume", "Order size in lots", "Trading");

		_stopLossPoints = Param(nameof(StopLossPoints), 120m)
			.SetNotNegative()
			.SetDisplay("Stop-Loss Points", "Protective stop distance expressed in instrument points", "Risk");

		_lookbackBars = Param(nameof(LookbackBars), 10)
			.SetGreaterThanZero()
			.SetDisplay("Lookback Bars", "Number of bars used for the open price comparison", "Signals");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(8).TimeFrame())
			.SetDisplay("Candle Type", "Primary timeframe used for signal calculations", "General");
	}

	/// <summary>
	/// Order size submitted with each entry.
	/// </summary>
	public decimal TradeVolume
	{
		get => _tradeVolume.Value;
		set => _tradeVolume.Value = value;
	}

	/// <summary>
	/// Stop-loss distance in instrument points.
	/// </summary>
	public decimal StopLossPoints
	{
		get => _stopLossPoints.Value;
		set => _stopLossPoints.Value = Math.Max(0m, value);
	}

	/// <summary>
	/// Number of historical bars used for the open price comparison.
	/// </summary>
	public int LookbackBars
	{
		get => _lookbackBars.Value;
		set => _lookbackBars.Value = Math.Max(1, value);
	}

	/// <summary>
	/// Candle type that defines the working timeframe.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_openHistory.Clear();
		_cooldown = 0;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(ProcessCandle)
			.Start();

		var step = Security?.PriceStep ?? 0m;
		Unit stopLossUnit = null;

		if (StopLossPoints > 0m && step > 0m)
			stopLossUnit = new Unit(StopLossPoints * step, UnitTypes.Absolute);

		StartProtection(null, stopLossUnit);
	}

	private void ProcessCandle(ICandleMessage candle)
	{
		if (candle.State != CandleStates.Finished)
			return;

		// Store the open price for future comparisons.
		_openHistory.Add(candle.OpenPrice);

		var maxHistory = Math.Max(LookbackBars + 5, 5);
		if (_openHistory.Count > maxHistory)
			_openHistory.RemoveRange(0, _openHistory.Count - maxHistory);

		var lookback = LookbackBars;
		if (_openHistory.Count <= lookback)
			return;

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		if (_cooldown > 0)
		{
			_cooldown--;
			return;
		}

		var volume = TradeVolume;
		if (volume <= 0m)
			return;

		var currentOpen = candle.OpenPrice;
		var referenceOpen = _openHistory[^(lookback + 1)];

		// Only trade on clear directional difference
		var diff = currentOpen - referenceOpen;
		if (Math.Abs(diff) < currentOpen * 0.001m)
			return;

		if (diff > 0 && Position <= 0)
		{
			if (Position < 0m)
				BuyMarket(Math.Abs(Position));
			BuyMarket(volume);
			_cooldown = 5;
		}
		else if (diff < 0 && Position >= 0)
		{
			if (Position > 0m)
				SellMarket(Position);
			SellMarket(volume);
			_cooldown = 5;
		}
	}
}