Ver en GitHub

Plantilla Base de Estrategia

Esta carpeta proporciona un andamio mínimo para construir ideas de trading personalizadas. La estrategia solo calcula una media móvil exponencial y expone una amplia gama de parámetros comunes: habilitación de operaciones largas o cortas, take profit y stop loss opcionales, y rangos de optimización. Los desarrolladores pueden insertar su propia lógica de entrada y salida dentro de los marcadores de posición para prototipar rápidamente nuevos sistemas.

La plantilla también demuestra cómo iniciar el módulo de protección integrado con objetivos basados en porcentajes, facilitando la experimentación con diferentes configuraciones de riesgo. Dado que no se incluyen señales reales, este script no está destinado a ser operado tal como está, sino más bien a servir como punto de partida para investigación adicional.

Detalles

  • Criterios de entrada: No implementados – reemplazar con reglas personalizadas.
  • Largo/Corto: Configurable mediante parámetros.
  • Criterios de salida: No implementados – reemplazar con reglas personalizadas.
  • Stops: Take profit y stop loss porcentuales opcionales gestionados por el módulo de protección.
  • Valores predeterminados:
    • Longitud EMA = 10.
    • Take profit = 1.2%, Stop loss = 1.8% (deshabilitado por defecto).
  • Filtros:
    • Categoría: Plantilla
    • Dirección: Configurable
    • Indicadores: EMA
    • Stops: Opcional
    • Complejidad: Bajo
    • Marco temporal: Cualquiera
    • Estacionalidad: No
    • Redes neuronales: No
    • Divergencia: No
    • Nivel de riesgo: Definido por el usuario
namespace StockSharp.Samples.Strategies;

using System;
using System.Collections.Generic;

using Ecng.Common;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

/// <summary>
/// Strategy Base - EMA trend following strategy.
/// Buys when price crosses above EMA, sells when price crosses below EMA.
/// </summary>
public class StratBaseStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _emaLength;
	private readonly StrategyParam<int> _cooldownBars;

	private ExponentialMovingAverage _ema;

	private decimal _prevClose;
	private decimal _prevEma;
	private int _cooldownRemaining;

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public int EmaLength
	{
		get => _emaLength.Value;
		set => _emaLength.Value = value;
	}

	public int CooldownBars
	{
		get => _cooldownBars.Value;
		set => _cooldownBars.Value = value;
	}

	public StratBaseStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
			.SetDisplay("Candle Type", "Type of candles to use", "General");

		_emaLength = Param(nameof(EmaLength), 20)
			.SetGreaterThanZero()
			.SetDisplay("EMA Length", "EMA period", "Moving Averages");

		_cooldownBars = Param(nameof(CooldownBars), 10)
			.SetDisplay("Cooldown Bars", "Bars to wait between trades", "Risk");
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
		=> [(Security, CandleType)];

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

		_ema = null;
		_prevClose = 0;
		_prevEma = 0;
		_cooldownRemaining = 0;
	}

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

		_ema = new ExponentialMovingAverage { Length = EmaLength };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(_ema, OnProcess)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, _ema);
			DrawOwnTrades(area);
		}
	}

	private void OnProcess(ICandleMessage candle, decimal emaVal)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (!_ema.IsFormed)
		{
			_prevClose = candle.ClosePrice;
			_prevEma = emaVal;
			return;
		}

		if (!IsFormedAndOnlineAndAllowTrading())
		{
			_prevClose = candle.ClosePrice;
			_prevEma = emaVal;
			return;
		}

		if (_cooldownRemaining > 0)
		{
			_cooldownRemaining--;
			_prevClose = candle.ClosePrice;
			_prevEma = emaVal;
			return;
		}

		if (_prevClose == 0 || _prevEma == 0)
		{
			_prevClose = candle.ClosePrice;
			_prevEma = emaVal;
			return;
		}

		// Price crosses above EMA
		var crossUp = candle.ClosePrice > emaVal && _prevClose <= _prevEma;
		// Price crosses below EMA
		var crossDown = candle.ClosePrice < emaVal && _prevClose >= _prevEma;

		if (crossUp && Position <= 0)
		{
			if (Position < 0)
				BuyMarket(Math.Abs(Position));
			BuyMarket(Volume);
			_cooldownRemaining = CooldownBars;
		}
		else if (crossDown && Position >= 0)
		{
			if (Position > 0)
				SellMarket(Math.Abs(Position));
			SellMarket(Volume);
			_cooldownRemaining = CooldownBars;
		}

		_prevClose = candle.ClosePrice;
		_prevEma = emaVal;
	}
}