Ver en GitHub

Estrategia de Cruce Fractal AMA MBK

Descripción general

La estrategia de cruce Fractal AMA MBK utiliza la Media Móvil Adaptativa Fractal (FRAMA) junto con una línea de activación de Media Móvil Exponencial (EMA). Las señales de operación se generan cuando la línea FRAMA cruza la línea EMA.

Cómo funciona

  • FRAMA adapta su factor de suavizado basándose en la dimensión fractal del movimiento reciente del precio.
  • La EMA actúa como línea de activación que suaviza los datos del precio.
  • Entrada larga: cuando FRAMA cruza hacia arriba la EMA y no hay posición larga abierta.
  • Entrada corta: cuando FRAMA cruza hacia abajo la EMA y no hay posición corta abierta.
  • Las posiciones existentes pueden protegerse con niveles opcionales de stop-loss y take-profit.

Parámetros

Nombre Descripción
CandleType Tipo y marco temporal de las velas usadas para los cálculos (predeterminado: velas de 4 horas).
FramaPeriod Período del indicador FRAMA.
SignalPeriod Período de la línea EMA de activación.
StopLoss Distancia del stop-loss desde el precio de entrada en unidades de precio absolutas (0 lo deshabilita).
TakeProfit Distancia del take-profit desde el precio de entrada en unidades de precio absolutas (0 lo deshabilita).
Volume Volumen de operación en lotes.

Notas

  • Solo se procesan las velas completadas.
  • Las operaciones se ejecutan con órdenes de mercado (BuyMarket/SellMarket).
  • Los parámetros FramaPeriod y SignalPeriod admiten optimización.
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>
/// Fractal AMA MBK crossover strategy.
/// Uses FRAMA and a signal EMA to generate trade signals on crossover.
/// </summary>
public class FractalAmaMbkStrategy : Strategy
{
	private readonly StrategyParam<int> _framaPeriod;
	private readonly StrategyParam<int> _signalPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevFrama;
	private decimal _prevSignal;
	private bool _isFirst = true;

	public int FramaPeriod { get => _framaPeriod.Value; set => _framaPeriod.Value = value; }
	public int SignalPeriod { get => _signalPeriod.Value; set => _signalPeriod.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public FractalAmaMbkStrategy()
	{
		_framaPeriod = Param(nameof(FramaPeriod), 18)
			.SetGreaterThanZero()
			.SetDisplay("FRAMA Period", "Period for Fractal Adaptive Moving Average", "Indicator");

		_signalPeriod = Param(nameof(SignalPeriod), 18)
			.SetGreaterThanZero()
			.SetDisplay("Signal EMA Period", "Period for signal EMA", "Indicator");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Type of candles to use", "General");
	}

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevFrama = default;
		_prevSignal = default;
		_isFirst = true;
	}

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

		_isFirst = true;

		var frama = new FractalAdaptiveMovingAverage { Length = FramaPeriod };
		var signal = new ExponentialMovingAverage { Length = SignalPeriod };

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

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

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

		if (_isFirst)
		{
			_prevFrama = framaValue;
			_prevSignal = signalValue;
			_isFirst = false;
			return;
		}

		// Detect crossover
		var wasAbove = _prevFrama > _prevSignal;
		var isAbove = framaValue > signalValue;

		if (!wasAbove && isAbove && Position <= 0)
		{
			// FRAMA crossed above signal -> buy
			if (Position < 0) BuyMarket();
			BuyMarket();
		}
		else if (wasAbove && !isAbove && Position >= 0)
		{
			// FRAMA crossed below signal -> sell
			if (Position > 0) SellMarket();
			SellMarket();
		}

		_prevFrama = framaValue;
		_prevSignal = signalValue;
	}
}