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;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import FractalAdaptiveMovingAverage, ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class fractal_ama_mbk_strategy(Strategy):
def __init__(self):
super(fractal_ama_mbk_strategy, self).__init__()
self._frama_period = self.Param("FramaPeriod", 18) \
.SetDisplay("FRAMA Period", "Period for Fractal Adaptive Moving Average", "Indicator")
self._signal_period = self.Param("SignalPeriod", 18) \
.SetDisplay("Signal EMA Period", "Period for signal EMA", "Indicator")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._prev_frama = 0.0
self._prev_signal = 0.0
self._is_first = True
@property
def FramaPeriod(self):
return self._frama_period.Value
@FramaPeriod.setter
def FramaPeriod(self, value):
self._frama_period.Value = value
@property
def SignalPeriod(self):
return self._signal_period.Value
@SignalPeriod.setter
def SignalPeriod(self, value):
self._signal_period.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnStarted2(self, time):
super(fractal_ama_mbk_strategy, self).OnStarted2(time)
self._is_first = True
frama = FractalAdaptiveMovingAverage()
frama.Length = self.FramaPeriod
signal = ExponentialMovingAverage()
signal.Length = self.SignalPeriod
self.SubscribeCandles(self.CandleType) \
.Bind(frama, signal, self.ProcessCandle) \
.Start()
def ProcessCandle(self, candle, frama_value, signal_value):
if candle.State != CandleStates.Finished:
return
frama = float(frama_value)
signal = float(signal_value)
if self._is_first:
self._prev_frama = frama
self._prev_signal = signal
self._is_first = False
return
was_above = self._prev_frama > self._prev_signal
is_above = frama > signal
if not was_above and is_above and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif was_above and not is_above and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_frama = frama
self._prev_signal = signal
def OnReseted(self):
super(fractal_ama_mbk_strategy, self).OnReseted()
self._prev_frama = 0.0
self._prev_signal = 0.0
self._is_first = True
def CreateClone(self):
return fractal_ama_mbk_strategy()