Auf GitHub ansehen

Fractal AMA MBK Crossover Strategy

Overview

The Fractal AMA MBK Crossover strategy uses the Fractal Adaptive Moving Average (FRAMA) together with an Exponential Moving Average (EMA) trigger line. Trading signals are generated when the FRAMA line crosses the EMA line.

How It Works

  • FRAMA adapts its smoothing factor based on the fractal dimension of recent price movement.
  • The EMA acts as a trigger line that smooths price data.
  • Long entry: when FRAMA crosses above the EMA and no long position is open.
  • Short entry: when FRAMA crosses below the EMA and no short position is open.
  • Existing positions can be protected with optional stop-loss and take-profit levels.

Parameters

Name Description
CandleType Candle type and timeframe used for calculations (default: 4-hour candles).
FramaPeriod Period length for the FRAMA indicator.
SignalPeriod Period length for the EMA trigger line.
StopLoss Stop-loss distance from entry price in absolute price units (0 disables).
TakeProfit Take-profit distance from entry price in absolute price units (0 disables).
Volume Trade volume in lots.

Notes

  • Only completed candles are processed.
  • Trades are executed using market orders (BuyMarket/SellMarket).
  • FramaPeriod and SignalPeriod parameters support optimization.
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;
	}
}