Ver no GitHub

Estratégia IU de Cruzamento de MA em Período Superior

A estratégia IU Higher Timeframe MA Cross opera quando uma média móvel rápida calculada em um período selecionado pelo usuário cruza uma média móvel mais lenta de possivelmente outro período. Uma posição comprada é aberta em um cruzamento de alta e uma posição vendida em um cruzamento de baixa. O stop-loss é colocado no extremo da vela anterior e o take profit utiliza uma relação risco/recompensa configurável.

Detalhes

  • Dados: Velas de períodos especificados.
  • Critérios de entrada:
    • Comprado: MA1 cruza acima de MA2.
    • Vendido: MA1 cruza abaixo de MA2.
  • Critérios de saída: Stop-loss ou take profit atingido.
  • Stops: Máximo/mínimo da vela anterior com multiplicador RiskToReward.
  • Valores padrão:
    • Ma1CandleType = 60m
    • Ma1Length = 20
    • Ma1Type = MovingAverageTypeEnum.Exponential
    • Ma2CandleType = 60m
    • Ma2Length = 50
    • Ma2Type = MovingAverageTypeEnum.Exponential
    • RiskToReward = 2
  • Filtros:
    • Categoria: Tendência
    • Direção: Comprado & Vendido
    • Indicadores: Média Móvel
    • Complexidade: Baixo
    • Nível de risco: Médio
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;
using StockSharp.Algo;

namespace StockSharp.Samples.Strategies;



public class IUHigherTimeframeMACrossStrategy : Strategy
{
	private readonly StrategyParam<decimal> _riskToReward;
	private readonly StrategyParam<DataType> _ma1CandleType;
	private readonly StrategyParam<int> _ma1Length;
	private readonly StrategyParam<MovingAverageTypes> _ma1Type;
	private readonly StrategyParam<DataType> _ma2CandleType;
	private readonly StrategyParam<int> _ma2Length;
	private readonly StrategyParam<MovingAverageTypes> _ma2Type;

	private decimal? _ma1;
	private decimal? _ma2;
	private decimal? _prevMa1;
	private decimal? _prevMa2;

	private decimal? _entryPrice;
	private decimal? _stopPrice;
	private decimal? _takePrice;

	private decimal? _prevLow;
	private decimal? _prevHigh;

	private ICandleMessage _lastMa1Candle;

	private DecimalLengthIndicator _ma1Indicator;
	private DecimalLengthIndicator _ma2Indicator;

	public IUHigherTimeframeMACrossStrategy()
	{
		_riskToReward = Param(nameof(RiskToReward), 2m)
		.SetGreaterThanZero()
		.SetDisplay("RTR", "Risk to reward ratio", "Protection")
		
		.SetOptimize(1m, 5m, 0.5m);

		_ma1CandleType = Param(nameof(Ma1CandleType), TimeSpan.FromMinutes(60).TimeFrame())
		.SetDisplay("MA1 Timeframe", "Timeframe for first MA", "Moving Averages");

		_ma1Length = Param(nameof(Ma1Length), 20)
		.SetGreaterThanZero()
		.SetDisplay("MA1 Length", "Period for first MA", "Moving Averages")
		
		.SetOptimize(5, 100, 5);

		_ma1Type = Param(nameof(Ma1Type), MovingAverageTypes.Exponential)
		.SetDisplay("MA1 Type", "Type of first MA", "Moving Averages");

		_ma2CandleType = Param(nameof(Ma2CandleType), TimeSpan.FromMinutes(60).TimeFrame())
		.SetDisplay("MA2 Timeframe", "Timeframe for second MA", "Moving Averages");

		_ma2Length = Param(nameof(Ma2Length), 50)
		.SetGreaterThanZero()
		.SetDisplay("MA2 Length", "Period for second MA", "Moving Averages")
		
		.SetOptimize(10, 200, 5);

		_ma2Type = Param(nameof(Ma2Type), MovingAverageTypes.Exponential)
		.SetDisplay("MA2 Type", "Type of second MA", "Moving Averages");
	}

	public decimal RiskToReward { get => _riskToReward.Value; set => _riskToReward.Value = value; }
	public DataType Ma1CandleType { get => _ma1CandleType.Value; set => _ma1CandleType.Value = value; }
	public int Ma1Length { get => _ma1Length.Value; set => _ma1Length.Value = value; }
	public MovingAverageTypes Ma1Type { get => _ma1Type.Value; set => _ma1Type.Value = value; }
	public DataType Ma2CandleType { get => _ma2CandleType.Value; set => _ma2CandleType.Value = value; }
	public int Ma2Length { get => _ma2Length.Value; set => _ma2Length.Value = value; }
	public MovingAverageTypes Ma2Type { get => _ma2Type.Value; set => _ma2Type.Value = value; }

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	=> [(Security, Ma1CandleType), (Security, Ma2CandleType)];

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

		_ma1 = null;
		_ma2 = null;
		_prevMa1 = null;
		_prevMa2 = null;
		_entryPrice = null;
		_stopPrice = null;
		_takePrice = null;
		_prevLow = null;
		_prevHigh = null;
		_lastMa1Candle = null;
		_ma1Indicator = null;
		_ma2Indicator = null;
	}

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

		_ma1Indicator = CreateMa(Ma1Type, Ma1Length);
		_ma2Indicator = CreateMa(Ma2Type, Ma2Length);

		var ma1Sub = SubscribeCandles(Ma1CandleType);
		ma1Sub.Bind(_ma1Indicator, ProcessMa1).Start();

		var ma2Sub = SubscribeCandles(Ma2CandleType);
		ma2Sub.Bind(_ma2Indicator, ProcessMa2).Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, ma1Sub);
			DrawIndicator(area, _ma1Indicator);
			DrawIndicator(area, _ma2Indicator);
			DrawOwnTrades(area);
		}
	}

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

		if (_lastMa1Candle != null)
		{
			_prevLow = _lastMa1Candle.LowPrice;
			_prevHigh = _lastMa1Candle.HighPrice;
		}
		_lastMa1Candle = candle;

		_prevMa1 = _ma1;
		_ma1 = value;

		if (Position > 0)
		{
			if (_stopPrice.HasValue && candle.LowPrice <= _stopPrice)
				CloseLong();
			else if (_takePrice.HasValue && candle.HighPrice >= _takePrice)
				CloseLong();
		}
		else if (Position < 0)
		{
			if (_stopPrice.HasValue && candle.HighPrice >= _stopPrice)
				CloseShort();
			else if (_takePrice.HasValue && candle.LowPrice <= _takePrice)
				CloseShort();
		}

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		if (_prevMa1.HasValue && _prevMa2.HasValue && _ma1.HasValue && _ma2.HasValue && Position == 0)
		{
			var crossUp = _prevMa1 < _prevMa2 && _ma1 > _ma2;
			var crossDown = _prevMa1 > _prevMa2 && _ma1 < _ma2;

			if (crossUp)
			{
				BuyMarket();
				_entryPrice = candle.ClosePrice;
				var sl = _prevLow ?? candle.LowPrice;
				_stopPrice = sl;
				_takePrice = (_entryPrice - sl) * RiskToReward + _entryPrice;
			}
			else if (crossDown)
			{
				SellMarket();
				_entryPrice = candle.ClosePrice;
				var sl = _prevHigh ?? candle.HighPrice;
				_stopPrice = sl;
				_takePrice = _entryPrice - (sl - _entryPrice) * RiskToReward;
			}
		}
	}

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

		_prevMa2 = _ma2;
		_ma2 = value;
	}

	private void CloseLong()
	{
		SellMarket(Position);
		ResetProtection();
	}

	private void CloseShort()
	{
		BuyMarket(Math.Abs(Position));
		ResetProtection();
	}

	private void ResetProtection()
	{
		_entryPrice = null;
		_stopPrice = null;
		_takePrice = null;
	}

	private static DecimalLengthIndicator CreateMa(MovingAverageTypes type, int length)
	{
		return type switch
		{
			MovingAverageTypes.Simple => new SMA { Length = length },
			MovingAverageTypes.Exponential => new EMA { Length = length },
			MovingAverageTypes.Smoothed => new SmoothedMovingAverage { Length = length },
			MovingAverageTypes.Weighted => new WeightedMovingAverage { Length = length },
			MovingAverageTypes.VolumeWeighted => new VolumeWeightedMovingAverage { Length = length },
			_ => new SMA { Length = length },
		};
	}

	public enum MovingAverageTypes
	{
		Simple,
		Exponential,
		Smoothed,
		Weighted,
		VolumeWeighted
	}
}