Ver no GitHub

Estratégia de alerta cruzado TenKijun (ID 3562)

Visão geral

Esta estratégia é uma versão StockSharp de alto nível API do consultor especialista MetaTrader TenKijun.mq4. O EA original apenas observa o indicador Ichimoku e envia notificações push quando o Tenkan-sen (linha de conversão) cruza o Kijun-sen (linha de base). A versão C# mantém a natureza apenas de alerta, mas atualiza a implementação com infraestrutura StockSharp, ligações de gráficos, parametrização e manipulação segura de sessões.

A lógica funciona em velas concluídas de um período configurável. Quando uma nova vela fecha dentro do horário de negociação ativo, a estratégia avalia o indicador Ichimoku calculado com os períodos clássicos de 26/09/52 e registra os últimos valores Tenkan/Kijun. Se o Tenkan cruzar acima do Kijun, uma mensagem informativa será registrada indicando uma linha de alta; se Tenkan cruzar abaixo de Kijun, um alerta de baixa será registrado. Nenhuma negociação é executada – a estratégia destina-se à geração de sinais ou a ser combinada com automação externa.

Indicador e Fluxo de Dados

  • Indicador – indicador StockSharp Ichimoku com comprimentos Tenkan, Kijun e Senkou Span B parametrizados separadamente. Apenas as linhas Tenkan e Kijun são usadas para tomada de decisão, espelhando o EA original.
  • Assinatura de dados – Usa SubscribeCandles com um CandleType configurável. Por padrão, são solicitadas velas de período de 30 minutos.
  • BindingBindEx é empregado para que o IchimokuValue digitado seja entregue ao manipulador sem chamadas manuais para GetValue.
  • Gráficos – Velas e o indicador Ichimoku são anexados automaticamente ao gráfico de estratégia para rápida validação visual de alertas.

Filtro de Sessão de Negociação

O script MetaTrader restringiu alertas a uma janela de sessão definida pelo usuário. A porta expõe o mesmo recurso por meio de dois parâmetros:

  • StartHour – início inclusivo da janela ativa (padrão 0). Aceita 0-23.
  • LastHour – fim inclusivo da janela ativa (padrão 20). Aceita 0-23.

Se StartHour for menor ou igual a LastHour, os alertas serão produzidos entre essas duas horas do dia. Se o início for maior que o fim, a janela é tratada como noturna (por exemplo, 20 → 6 abrange a sessão do final da noite até o início da manhã).

Parâmetros

Parâmetro Descrição Padrão Notas
StartHour Hora em que os alertas podem começar. 0 Inclusive, faixa de 0 a 23.
LastHour Hora em que os alertas param. 20 Inclusive, faixa de 0 a 23.
TenkanPeriod Lookback da linha de conversão. 9 Otimizável.
KijunPeriod Lookback da linha base. 26 Otimizável.
SenkouSpanBPeriod Lookback do período B inicial. 52 Fornecido para ser completo, mesmo que os alertas não dependam da nuvem.
CandleType Série de velas usada para o indicador. Prazo de 30 minutos Escolha qualquer período baseado em TimeSpan.

Lógica de Alerta

  1. Aguarde a primeira vela finalizada para inicializar o histórico de Tenkan e Kijun.
  2. Em cada vela finalizada subsequente dentro da janela de negociação:
    • Extraia os valores Tenkan e Kijun do indicador Ichimoku.
    • Detecte uma linha de alta quando o Tenkan anterior for menor ou igual ao Kijun anterior e o Tenkan atual for maior que o Kijun atual.
    • Detecte uma linha de baixa quando o Tenkan anterior for maior ou igual ao Kijun anterior e o Tenkan atual for menor que o Kijun atual.
    • Emita uma entrada de registro informativa descrevendo a direção, o preço e o carimbo de data/hora da cruz.

Dicas de uso

  • Combine esta estratégia com adaptadores de notificação StockSharp (e-mail, telegrama, som) inscrevendo-se no registro de estratégia ou estendendo o método ProcessCandle com código de notificação personalizado.
  • Para impulsionar a negociação automatizada, herde de TenKijunCrossStrategy e substitua ProcessCandle para fazer pedidos em vez de - ou além de - registrar mensagens.
  • Ajuste o período da vela para corresponder ao gráfico MetaTrader original usado pelo EA para manter os alertas alinhados.

Diferenças do original EA

  • Usa registro StockSharp em vez de MetaTrader SendNotification. O comportamento permanece apenas de alerta, mas depende do pipeline de mensagens da plataforma.
  • Adiciona metadados de parâmetros completos (SetDisplay, intervalos, sinalizadores de otimização), tornando a estratégia pronta para ferramentas Designer/Optimizer.
  • Desenha automaticamente velas e o indicador Ichimoku na janela do gráfico StockSharp quando disponível.

Arquivos

  • CS/TenKijunCrossStrategy.cs – implementação principal em C# da lógica de alerta.
using System;
using System.Collections.Generic;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Tenkan/Kijun cross strategy based on Ichimoku indicator.
/// Buys when Tenkan crosses above Kijun, sells when Tenkan crosses below Kijun.
/// Uses SMA proxies for Tenkan (short) and Kijun (long) since Ichimoku complex type
/// requires BindEx and special value handling.
/// </summary>
public class TenKijunCrossStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _tenkanPeriod;
	private readonly StrategyParam<int> _kijunPeriod;

	private readonly Queue<decimal> _highsTenkan = new();
	private readonly Queue<decimal> _lowsTenkan = new();
	private readonly Queue<decimal> _highsKijun = new();
	private readonly Queue<decimal> _lowsKijun = new();
	private decimal? _prevTenkan;
	private decimal? _prevKijun;

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

	public int TenkanPeriod
	{
		get => _tenkanPeriod.Value;
		set => _tenkanPeriod.Value = value;
	}

	public int KijunPeriod
	{
		get => _kijunPeriod.Value;
		set => _kijunPeriod.Value = value;
	}

	public TenKijunCrossStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe for Ichimoku calculations", "General");

		_tenkanPeriod = Param(nameof(TenkanPeriod), 12)
			.SetGreaterThanZero()
			.SetDisplay("Tenkan Period", "Tenkan-sen conversion line period", "Indicators");

		_kijunPeriod = Param(nameof(KijunPeriod), 34)
			.SetGreaterThanZero()
			.SetDisplay("Kijun Period", "Kijun-sen base line period", "Indicators");
	}

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

		_prevTenkan = null;
		_prevKijun = null;
		_highsTenkan.Clear();
		_lowsTenkan.Clear();
		_highsKijun.Clear();
		_lowsKijun.Clear();

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

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

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

		// Compute Tenkan-sen = (highest high + lowest low) / 2 over TenkanPeriod
		_highsTenkan.Enqueue(candle.HighPrice);
		_lowsTenkan.Enqueue(candle.LowPrice);
		if (_highsTenkan.Count > TenkanPeriod)
		{
			_highsTenkan.Dequeue();
			_lowsTenkan.Dequeue();
		}

		// Compute Kijun-sen = (highest high + lowest low) / 2 over KijunPeriod
		_highsKijun.Enqueue(candle.HighPrice);
		_lowsKijun.Enqueue(candle.LowPrice);
		if (_highsKijun.Count > KijunPeriod)
		{
			_highsKijun.Dequeue();
			_lowsKijun.Dequeue();
		}

		if (_highsTenkan.Count < TenkanPeriod || _highsKijun.Count < KijunPeriod)
			return;

		var highsTenkan = _highsTenkan.ToArray();
		var lowsTenkan = _lowsTenkan.ToArray();
		var highsKijun = _highsKijun.ToArray();
		var lowsKijun = _lowsKijun.ToArray();
		var tenkan = (Max(highsTenkan) + Min(lowsTenkan)) / 2;
		var kijun = (Max(highsKijun) + Min(lowsKijun)) / 2;

		if (_prevTenkan is null || _prevKijun is null)
		{
			_prevTenkan = tenkan;
			_prevKijun = kijun;
			return;
		}

		var volume = Volume;
		if (volume <= 0)
			volume = 1;

		var crossUp = _prevTenkan.Value <= _prevKijun.Value && tenkan > kijun;
		var crossDown = _prevTenkan.Value >= _prevKijun.Value && tenkan < kijun;

		if (crossUp)
		{
			if (Position <= 0)
				BuyMarket(Position < 0 ? Math.Abs(Position) + volume : volume);
		}
		else if (crossDown)
		{
			if (Position >= 0)
				SellMarket(Position > 0 ? Math.Abs(Position) + volume : volume);
		}

		_prevTenkan = tenkan;
		_prevKijun = kijun;
	}

	private static decimal Max(IEnumerable<decimal> values)
	{
		decimal max = decimal.MinValue;

		foreach (var v in values)
			if (v > max) max = v;

		return max;
	}

	private static decimal Min(IEnumerable<decimal> values)
	{
		decimal min = decimal.MaxValue;

		foreach (var v in values)
			if (v < min) min = v;

		return min;
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		_prevTenkan = null;
		_prevKijun = null;
		_highsTenkan.Clear();
		_lowsTenkan.Clear();
		_highsKijun.Clear();
		_lowsKijun.Clear();

		base.OnReseted();
	}
}