Ver en GitHub

Estrategia universal de inversores

Esta estrategia es una adaptación directa del asesor experto Universal Investor MetaTrader 4. Combina una media móvil exponencial (EMA) y una media móvil ponderada lineal (LWMA) para confirmar la dirección de la tendencia a corto plazo y realiza operaciones de una posición con un tamaño de posición adaptable.

Lógica comercial

  1. Suscríbase al CandleType configurado y calcule tanto EMA como LWMA con el período definido por MovingPeriod.
  2. Almacene los dos valores más recientes de cada promedio móvil para que la lógica imite las llamadas iMA(..., shift = 1/2) del EA original.
  3. Genere una señal de compra cuando el LWMA anterior esté por encima del EMA anterior, ambos promedios estuvieran subiendo y no haya una señal opuesta en la misma vela.
  4. Genere una señal de venta cuando el LWMA anterior esté por debajo del EMA anterior, ambos promedios estuvieran cayendo y no haya una señal opuesta en la misma vela.
  5. Cierre una posición larga abierta tan pronto como la LWMA caiga por debajo del EMA (lógica espejo para cortos).
  6. Calcule el volumen de operaciones a partir del parámetro de la estrategia Volume, increméntelo para satisfacer el requisito MaximumRisk cuando el valor de la cartera sea lo suficientemente grande y redúzcalo después de operaciones perdedoras consecutivas de acuerdo con DecreaseFactor.
  7. Envíe órdenes de mercado con BuyMarket/SellMarket y realice un seguimiento del precio de entrada para detectar salidas ganadoras o perdedoras.

La estrategia mantiene solo una posición abierta a la vez e inmediatamente se revierte solo después de un cierre completo, reproduciendo el comportamiento del script original MetaTrader.

Parámetros

Nombre Descripción
CandleType Serie de velas utilizadas para los cálculos.
MovingPeriod Período tanto para EMA como para LWMA.
MaximumRisk Fracción del capital (0,05 = 5%) utilizada para calcular el volumen mínimo de la posición.
DecreaseFactor Reduce el volumen después de operaciones perdedoras consecutivas (0 desactiva la función).
Volume Volumen de contrato base transferido a BuyMarket/SellMarket.

Indicadores

  • ExponentialMovingAverage
  • LinearWeightedMovingAverage

Notas

  • Los pedidos se realizan únicamente en velas cerradas, que coinciden con el EA que depende de los cheques Time[0].
  • La lógica del tamaño de la posición refleja la función MetaTrader LotsOptimized, incluido el componente basado en el riesgo y el multiplicador de la racha de pérdidas.
using System;

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

namespace StockSharp.Samples.Strategies;

public class UniversalInvestor3920Strategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevFast;
	private decimal _prevSlow;
	private bool _hasPrev;
	private int _cooldown;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public UniversalInvestor3920Strategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 12).SetDisplay("Fast EMA", "Fast EMA period", "Indicators");
		_slowPeriod = Param(nameof(SlowPeriod), 26).SetDisplay("Slow EMA", "Slow EMA period", "Indicators");
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame()).SetDisplay("Candle Type", "Candle timeframe", "General");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevFast = default;
		_prevSlow = default;
		_hasPrev = default;
		_cooldown = default;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_hasPrev = false;
		var fast = new ExponentialMovingAverage { Length = FastPeriod };
		var slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(fast, slow, ProcessCandle).Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!IsFormedAndOnlineAndAllowTrading()) return;

		if (!_hasPrev) { _prevFast = fast; _prevSlow = slow; _hasPrev = true; return; }
		if (_cooldown > 0)
		{
			_cooldown--;
			_prevFast = fast;
			_prevSlow = slow;
			return;
		}

		if (_prevFast <= _prevSlow && fast > slow && Position <= 0)
		{
			var volume = Volume + Math.Abs(Position);
			BuyMarket(volume);
			_cooldown = 2;
		}
		else if (_prevFast >= _prevSlow && fast < slow && Position >= 0)
		{
			var volume = Volume + Math.Abs(Position);
			SellMarket(volume);
			_cooldown = 2;
		}

		_prevFast = fast; _prevSlow = slow;
	}
}