Ver no GitHub

Estratégia Diff TF MA

Visão geral

  • Esta estratégia é um port para StockSharp do consultor especialista do MetaTrader "Diff_TF_MA_EA".
  • Os sinais de trading vêm da comparação de uma média móvel simples calculada em um período superior com outra média móvel redimensionada para o período de trading.
  • O código mantém apenas velas finalizadas, espelha as regras de cruzamento originais e fecha qualquer exposição oposta antes de abrir uma nova posição.

Parâmetros

Nome Descrição
MaPeriod Comprimento da média móvel simples calculada no período superior.
CandleType Período de trading usado para geração de ordens.
HigherCandleType Período superior que fornece a média móvel de referência.
ReverseSignals Inverte as regras de cruzamento (comprar no cruzamento de baixa e vender no cruzamento de alta).
Volume Volume da estratégia usado pelas chamadas BuyMarket/SellMarket (definido através da propriedade base Strategy.Volume).

Lógica de trading

  1. Assinar tanto o período de trading (CandleType) quanto o período superior (HigherCandleType).
  2. Construir uma média móvel simples com comprimento MaPeriod no período superior.
  3. Converter o comprimento do período superior no período de trading multiplicando pela relação de durações de períodos e executar outra média móvel nas velas de trading.
  4. Armazenar os dois últimos valores completados para ambas as médias móveis e verificar cruzamentos em cada vela de trading finalizada.
  5. Abrir ou reverter para uma posição comprada quando a MA do período superior cruza acima da MA de trading (a menos que ReverseSignals seja true).
  6. Abrir ou reverter para uma posição vendida quando a MA do período superior cruza abaixo da MA de trading (a menos que ReverseSignals seja true).
  7. As posições são niveladas e invertidas enviando volume suficiente para compensar qualquer exposição existente.

Notas de uso

  • Escolher períodos compatíveis: o período superior geralmente deve ser maior que o período de trading para que o comprimento redimensionado seja significativo.
  • O volume padrão é 1. Ajustar Strategy.Volume antes de iniciar a estratégia se outro tamanho for necessário.
  • Os stops e take-profits da versão do MetaTrader não são reproduzidos; o gerenciamento de risco pode ser anexado através das proteções do StockSharp se necessário.
  • Quando ReverseSignals está habilitado, as ações de alta e baixa são trocadas enquanto o restante da lógica permanece inalterado.
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>
/// Two timeframe moving average crossover strategy.
/// </summary>
public class DiffTfMaStrategy : Strategy
{
	private readonly StrategyParam<int> _maPeriod;
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<DataType> _higherCandleType;
	private readonly StrategyParam<bool> _reverseSignals;

	private SimpleMovingAverage _baseMa;
	private SimpleMovingAverage _higherMa;

	private decimal? _higherMaLast;
	private decimal? _higherMaPrev;
	private decimal? _baseMaLast;
	private decimal? _baseMaPrev;

	public int MaPeriod
	{
		get => _maPeriod.Value;
		set => _maPeriod.Value = value;
	}

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

	public DataType HigherCandleType
	{
		get => _higherCandleType.Value;
		set => _higherCandleType.Value = value;
	}

	public bool ReverseSignals
	{
		get => _reverseSignals.Value;
		set => _reverseSignals.Value = value;
	}

	public DiffTfMaStrategy()
	{
		_maPeriod = Param(nameof(MaPeriod), 10)
			.SetGreaterThanZero()
			.SetDisplay("MA Period", "Moving average length on the higher timeframe", "General")
			
			.SetOptimize(5, 30, 5);

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Base Candle", "Trading timeframe", "General");

		_higherCandleType = Param(nameof(HigherCandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Higher Candle", "Higher timeframe for confirmation", "General");

		_reverseSignals = Param(nameof(ReverseSignals), false)
			.SetDisplay("Reverse Signals", "Invert the crossover logic", "General");

		Volume = 0.1m;
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType), (Security, HigherCandleType)];
	}

	protected override void OnReseted()
	{
		base.OnReseted();

		_higherMaLast = null;
		_higherMaPrev = null;
		_baseMaLast = null;
		_baseMaPrev = null;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		if (CandleType.Arg is not TimeSpan baseSpan || baseSpan <= TimeSpan.Zero)
			throw new InvalidOperationException("CandleType must contain a positive TimeSpan argument.");

		if (HigherCandleType.Arg is not TimeSpan higherSpan || higherSpan <= TimeSpan.Zero)
			throw new InvalidOperationException("HigherCandleType must contain a positive TimeSpan argument.");

		var ratio = higherSpan.TotalMinutes / baseSpan.TotalMinutes;
		var baseLength = Math.Max(1, (int)(MaPeriod * ratio));

		_baseMa = new SimpleMovingAverage { Length = baseLength };
		_higherMa = new SimpleMovingAverage { Length = MaPeriod };

		var higherSubscription = SubscribeCandles(HigherCandleType);
		higherSubscription.Bind(_higherMa, ProcessHigher).Start();

		var baseSubscription = SubscribeCandles(CandleType);
		baseSubscription.Bind(_baseMa, ProcessBase).Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, baseSubscription);
			DrawOwnTrades(area);
		}
	}

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

		if (!_higherMa.IsFormed)
			return;

		// Store the last two higher timeframe MA values for crossover comparison.
		_higherMaPrev = _higherMaLast;
		_higherMaLast = higherMaValue;
	}

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

		if (!_baseMa.IsFormed)
			return;

		// Track the last two base timeframe MA values.
		_baseMaPrev = _baseMaLast;
		_baseMaLast = baseMaValue;

		if (_higherMaPrev is not decimal higherPrev || _higherMaLast is not decimal higherLast)
			return;

		if (_baseMaPrev is not decimal basePrev || _baseMaLast is not decimal baseLast)
			return;

		var crossUp = higherPrev < basePrev && higherLast > baseLast;
		var crossDown = higherPrev > basePrev && higherLast < baseLast;

		if (ReverseSignals)
			(crossUp, crossDown) = (crossDown, crossUp);

		// Execute orders according to the detected crossover direction.
		if (crossUp && Position <= 0)
		{
			BuyMarket(Volume + Math.Abs(Position));
		}
		else if (crossDown && Position >= 0)
		{
			SellMarket(Volume + Math.Abs(Position));
		}
	}
}