Ver no GitHub

Estratégia Exp Skyscraper Fix + ColorAML + X2MA Candle MMRec

Visão geral

  • Conversão em C# do especialista MetaTrader Exp_Skyscraper_Fix_ColorAML_X2MACandle_MMRec.
  • Combina três filtros independentes baseados em cor (canal Skyscraper Fix, nível adaptativo ColorAML, velas de dupla suavização X2MACandle).
  • Cada filtro pode abrir ou fechar trades por conta própria enquanto compartilha o mesmo símbolo, permitindo seguimento de tendência cooperativo e reversões rápidas.
  • Inclui um módulo de gestão monetária simplificado: quando os últimos trades de uma direção perdem repetidamente, o módulo muda para o volume reduzido (SmallMM).

Lógica da estratégia

Bloco Skyscraper Fix

  1. Constrói o canal trailing Skyscraper Fix analisando o intervalo ATR e a fonte de preço escolhida (high/low ou close).
  2. Quando a cor do canal se torna altista, o bloco:
    • fecha qualquer posição vendida pendente (se Skyscraper Close Shorts estiver habilitado);
    • pode abrir uma nova posição comprada após o atraso de sinal configurado (se Skyscraper Buy estiver habilitado).
  3. Quando a cor se torna baixista, a lógica espelha os passos para trades vendidos.
  4. Os envelopes de high/low, o multiplicador ATR (Kv) e o offset percentual reproduzem o comportamento do indicador original.

Bloco ColorAML

  1. Calcula o Nível de Mercado Adaptativo (AML) medindo o intervalo de duas janelas fractais consecutivas e suavizando o preço composto.
  2. O indicador gera três cores: 2 (altista), 0 (baixista) e 1 (neutro). Velas neutras não desencadeiam nenhuma ação.
  3. Uma cor altista fecha vendidos (se permitido) e pode abrir um comprado quando a cor mudou de altista na vela anterior inspecionada.
  4. Uma cor baixista realiza as ações simétricas para trades vendidos.

Bloco X2MACandle

  1. Cascateia dois médias móveis configuráveis sobre cada componente OHLC (abertura, máxima, mínima, fechamento) para construir uma vela sintética.
  2. A cor é determinada pelo corpo da vela suavizada: altista quando fechamento > abertura, baixista quando fechamento < abertura, neutro caso contrário.
  3. Um pequeno limiar de lacuna (em passos de preço) aplaina corpos de velas muito pequenos para evitar flips rápidos de cor.
  4. Cores altistas fecham vendidos e podem abrir comprados; cores baixistas fazem o oposto.

Gestão monetária

  1. Cada bloco mantém um histórico independente de seus próprios trades para as direções longa e curta.
  2. Após o fechamento de um trade, o módulo registra se terminou com perda.
  3. Se os últimos Loss Trigger trades para uma direção foram todos perdas, a próxima ordem desse bloco muda para o volume reduzido (SmallMM).
  4. Quando um trade lucrativo ou neutro quebra a sequência de perdas, o módulo retorna automaticamente ao volume normal (MM).

Parâmetros

Seção Nome Descrição Padrão
Skyscraper Skyscraper Candle Período para amostrar velas para o indicador Skyscraper Fix. 4h
Skyscraper Skyscraper Length Janela de média ATR (número de velas). 10
Skyscraper Skyscraper Kv Multiplicador de sensibilidade aplicado ao passo ATR. 0.9
Skyscraper Skyscraper Percentage Percentual adicional adicionado/removido da linha média. 0
Skyscraper Skyscraper Mode Fonte de preço (High/Low ou Close) usada para envelopes. High/Low
Skyscraper Skyscraper Signal Bar Número de velas já fechadas a aguardar antes de agir sobre uma cor. 1
Skyscraper Skyscraper Buy / Skyscraper Sell Permitir abertura de trades longos / curtos. true
Skyscraper Skyscraper Close Long / Skyscraper Close Short Permitir que este bloco saia de trades longos / curtos. true
Skyscraper Skyscraper Normal Volume Volume base da ordem (MM no EA). 0.1
Skyscraper Skyscraper Reduced Volume Volume reduzido da ordem usado após uma sequência de perdas (SmallMM). 0.01
Skyscraper Skyscraper Buy Loss Trigger / Skyscraper Sell Loss Trigger Número de perdas consecutivas que mudam para o volume reduzido. 2
ColorAML ColorAML Candle Tipo de vela usado pelo indicador ColorAML. 4h
ColorAML ColorAML Fractal Janela fractal (em barras) usada para o cálculo de intervalo. 6
ColorAML ColorAML Lag Parâmetro de lag que controla a suavização adaptativa. 7
ColorAML ColorAML Signal Bar Offset de vela aplicado antes de avaliar cores. 1
ColorAML ColorAML Buy / ColorAML Sell Habilitar entradas longas / curtas geradas pelo ColorAML. true
ColorAML ColorAML Close Long / ColorAML Close Short Permitir ao ColorAML fechar posições longas / curtas. true
ColorAML ColorAML Normal Volume / ColorAML Reduced Volume Volumes base e reduzido para este bloco. 0.1 / 0.01
ColorAML ColorAML Buy Loss Trigger / ColorAML Sell Loss Trigger Perdas consecutivas que ativam o volume reduzido. 2
X2MA X2MA Candle Período usado para a reconstrução de velas X2MACandle. 4h
X2MA First Method / Second Method Métodos de suavização para a primeira e segunda média móvil. SMA / JJMA
X2MA First Length / Second Length Períodos das duas etapas de suavização. 12 / 5
X2MA First Phase / Second Phase Fases de compatibilidade usadas pelas médias móveis Jurik. 15
X2MA Gap Points Limiar de lacuna (em passos de preço) que aplaina corpos de velas pequenos. 10
X2MA X2MA Signal Bar Offset de vela aplicado antes de reagir às cores. 1
X2MA X2MA Buy / X2MA Sell Permitir abertura de trades longos / curtos do bloco X2MACandle. true
X2MA X2MA Close Long / X2MA Close Short Permitir ao bloco sair de posições longas / curtas. true
X2MA X2MA Normal Volume / X2MA Reduced Volume Volumes base e reduzido para trades X2MACandle. 0.1 / 0.01
X2MA X2MA Buy Loss Trigger / X2MA Sell Loss Trigger Número de perdas consecutivas antes de mudar para o volume reduzido. 2

Dicas de uso

  1. Ajuste os tipos de velas para corresponder à volatilidade do mercado (por exemplo, 1h para trading intradiário, 4h para swing trading).
  2. Os três módulos podem ser ajustados independentemente — desabilitar um bloco ainda deixa os outros ativos.
  3. Os limiares de gestão monetária são intencionalmente conservadores. Aumente os gatilhos se o instrumento apresentar tendências fortes e você quiser manter o volume base por mais tempo.
  4. Como a estratégia depende de velas concluídas, sempre alimente-a com dados de velas que correspondam aos períodos configurados.
namespace StockSharp.Samples.Strategies;

using System;
using System.Collections.Generic;

using Ecng.Common;

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

/// <summary>
/// Combines Skyscraper Fix, ColorAML and X2MA-style filters into a single consensus strategy.
/// </summary>
public class ExpSkyscraperFixColorAmlX2MaCandleMmRecStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _channelLength;
	private readonly StrategyParam<decimal> _channelFactor;
	private readonly StrategyParam<int> _amlLength;
	private readonly StrategyParam<int> _x2FastLength;
	private readonly StrategyParam<int> _x2SlowLength;
	private readonly StrategyParam<int> _cooldownBars;
	private readonly StrategyParam<int> _stopLossPips;
	private readonly StrategyParam<int> _takeProfitPips;

	private readonly List<decimal> _highs = new();
	private readonly List<decimal> _lows = new();
	private readonly List<decimal> _closes = new();
	private readonly List<decimal> _weightedPrices = new();
	private readonly List<decimal> _amlSeries = new();
	private readonly List<decimal> _fastSeries = new();
	private readonly List<decimal> _slowSeries = new();

	private decimal? _previousAml;
	private int _previousConsensus;
	private decimal? _entryPrice;
	private int _cooldownLeft;

	public ExpSkyscraperFixColorAmlX2MaCandleMmRecStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame()).SetDisplay("Candle Type", "Timeframe", "General");
		_channelLength = Param(nameof(ChannelLength), 10).SetGreaterThanZero().SetDisplay("Channel Length", "ATR channel length", "Skyscraper");
		_channelFactor = Param(nameof(ChannelFactor), 0.9m).SetGreaterThanZero().SetDisplay("Channel Factor", "ATR multiplier", "Skyscraper");
		_amlLength = Param(nameof(AmlLength), 7).SetGreaterThanZero().SetDisplay("AML Length", "Adaptive smoothing length", "ColorAML");
		_x2FastLength = Param(nameof(X2FastLength), 12).SetGreaterThanZero().SetDisplay("X2 Fast", "Fast smoothing length", "X2MA");
		_x2SlowLength = Param(nameof(X2SlowLength), 5).SetGreaterThanZero().SetDisplay("X2 Slow", "Slow smoothing length", "X2MA");
		_cooldownBars = Param(nameof(CooldownBars), 2).SetNotNegative().SetDisplay("Cooldown Bars", "Bars between flips", "Trading");
		_stopLossPips = Param(nameof(StopLossPips), 500).SetNotNegative().SetDisplay("Stop Loss", "Stop distance in pips", "Risk");
		_takeProfitPips = Param(nameof(TakeProfitPips), 900).SetNotNegative().SetDisplay("Take Profit", "Take-profit distance in pips", "Risk");
	}

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int ChannelLength { get => _channelLength.Value; set => _channelLength.Value = value; }
	public decimal ChannelFactor { get => _channelFactor.Value; set => _channelFactor.Value = value; }
	public int AmlLength { get => _amlLength.Value; set => _amlLength.Value = value; }
	public int X2FastLength { get => _x2FastLength.Value; set => _x2FastLength.Value = value; }
	public int X2SlowLength { get => _x2SlowLength.Value; set => _x2SlowLength.Value = value; }
	public int CooldownBars { get => _cooldownBars.Value; set => _cooldownBars.Value = value; }
	public int StopLossPips { get => _stopLossPips.Value; set => _stopLossPips.Value = value; }
	public int TakeProfitPips { get => _takeProfitPips.Value; set => _takeProfitPips.Value = value; }

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

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

		_highs.Clear();
		_lows.Clear();
		_closes.Clear();
		_weightedPrices.Clear();
		_amlSeries.Clear();
		_fastSeries.Clear();
		_slowSeries.Clear();
		_previousAml = null;
		_previousConsensus = 0;
		_entryPrice = null;
		_cooldownLeft = 0;
	}

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

		OnReseted();

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

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

		if (_cooldownLeft > 0)
			_cooldownLeft--;

		_highs.Add(candle.HighPrice);
		_lows.Add(candle.LowPrice);
		_closes.Add(candle.ClosePrice);

		var weightedPrice = (candle.OpenPrice + candle.HighPrice + candle.LowPrice + 2m * candle.ClosePrice) / 5m;
		_weightedPrices.Add(weightedPrice);

		var fast = CalculateEma(_closes, X2FastLength, _fastSeries);
		var slow = CalculateEma(_fastSeries, X2SlowLength, _slowSeries);
		var aml = CalculateEma(_weightedPrices, AmlLength, _amlSeries);

		if (Position != 0 && _entryPrice is null)
			_entryPrice = candle.ClosePrice;

		if (TryExitByRisk(candle))
			return;

		if (_highs.Count <= Math.Max(ChannelLength, Math.Max(AmlLength, X2FastLength + X2SlowLength)))
		{
			_previousAml = aml;
			return;
		}

		var skyscraperSignal = GetSkyscraperSignal();
		var colorAmlSignal = _previousAml is decimal previousAml
			? aml > previousAml ? 1 : aml < previousAml ? -1 : 0
			: 0;
		var x2MaSignal = fast > slow && candle.ClosePrice >= candle.OpenPrice
			? 1
			: fast < slow && candle.ClosePrice <= candle.OpenPrice
				? -1
				: 0;

		_previousAml = aml;

		var score = skyscraperSignal + colorAmlSignal + x2MaSignal;
		var consensus = score >= 2 ? 1 : score <= -2 ? -1 : 0;

		if (!IsFormedAndOnlineAndAllowTrading())
		{
			_previousConsensus = consensus;
			return;
		}

		if (consensus == _previousConsensus || consensus == 0 || _cooldownLeft > 0)
		{
			_previousConsensus = consensus;
			return;
		}

		if (consensus > 0 && Position <= 0)
		{
			if (Position < 0)
			{
				BuyMarket(Math.Abs(Position));
				_entryPrice = null;
			}
			else
			{
				BuyMarket();
				_entryPrice = candle.ClosePrice;
			}

			_cooldownLeft = CooldownBars;
		}
		else if (consensus < 0 && Position >= 0)
		{
			if (Position > 0)
			{
				SellMarket(Position);
				_entryPrice = null;
			}
			else
			{
				SellMarket();
				_entryPrice = candle.ClosePrice;
			}

			_cooldownLeft = CooldownBars;
		}

		_previousConsensus = consensus;
	}

	private int GetSkyscraperSignal()
	{
		var length = ChannelLength;
		if (_closes.Count < length || _highs.Count < length || _lows.Count < length)
			return 0;

		var start = _closes.Count - length;
		decimal atrSum = 0m;

		for (var i = start; i < _closes.Count; i++)
		{
			var high = _highs[i];
			var low = _lows[i];
			var previousClose = i > 0 ? _closes[i - 1] : _closes[i];
			var trueRange = Math.Max(high - low, Math.Max(Math.Abs(high - previousClose), Math.Abs(low - previousClose)));
			atrSum += trueRange;
		}

		var atr = atrSum / length;
		var middle = (_highs[^1] + _lows[^1]) / 2m;
		var upper = middle + atr * ChannelFactor;
		var lower = middle - atr * ChannelFactor;
		var close = _closes[^1];

		if (close > upper)
			return 1;

		if (close < lower)
			return -1;

		return 0;
	}

	private bool TryExitByRisk(ICandleMessage candle)
	{
		if (_entryPrice is not decimal entryPrice || Position == 0)
			return false;

		var step = Security?.PriceStep ?? 1m;
		if (step <= 0)
			step = 1m;

		var stopDistance = StopLossPips * step;
		var takeDistance = TakeProfitPips * step;

		if (Position > 0)
		{
			if ((stopDistance > 0 && candle.LowPrice <= entryPrice - stopDistance) ||
				(takeDistance > 0 && candle.HighPrice >= entryPrice + takeDistance))
			{
				SellMarket(Position);
				_entryPrice = null;
				_cooldownLeft = CooldownBars;
				return true;
			}
		}
		else if (Position < 0)
		{
			var volume = Math.Abs(Position);

			if ((stopDistance > 0 && candle.HighPrice >= entryPrice + stopDistance) ||
				(takeDistance > 0 && candle.LowPrice <= entryPrice - takeDistance))
			{
				BuyMarket(volume);
				_entryPrice = null;
				_cooldownLeft = CooldownBars;
				return true;
			}
		}

		return false;
	}

	private static decimal CalculateEma(IReadOnlyList<decimal> source, int length, List<decimal> target)
	{
		var multiplier = 2m / (length + 1m);
		var value = target is { Count: > 0 }
			? source[^1] * multiplier + target[^1] * (1m - multiplier)
			: source[^1];

		target?.Add(value);
		return value;
	}
}