MaDelta Strategy
The MaDelta strategy measures the difference between a fast and a slow moving average. The difference is scaled by a multiplier and raised to the third power, producing an oscillating value px. Two dynamic thresholds separated by Delta (in pips) track the recent high and low of this value. When px breaks above the upper threshold, the strategy switches to a long bias; when px falls below the lower threshold, it switches to a short bias. Existing positions opposite to the new bias are closed and a new trade is opened in the direction of the signal.
The approach effectively captures momentum bursts when the distance between the two moving averages expands rapidly. Cubing the difference exaggerates strong moves while filtering small fluctuations. The Delta parameter defines how far px must travel before a reversal is recognized, preventing whipsaw in flat markets.
Details
- Entry Criteria:
- Long:
px > hi sets trade = 1 and opens a long when no position exists.
- Short:
px < lo sets trade = -1 and opens a short when flat.
- Reverse Logic:
- Long signal while short closes the short with a market buy before entering long.
- Short signal while long closes the long with a market sell before entering short.
- Indicators:
- Fast moving average (SMA) with period
FastMaPeriod.
- Slow moving average (EMA) with period
SlowMaPeriod.
- Oscillator:
px = ((Multiplier * 0.1) * (FastMA - SlowMA))^3.
- Parameters:
Delta – size of the high/low channel in pips.
Multiplier – scales the MA difference before cubing.
FastMaPeriod – length of the fast SMA.
SlowMaPeriod – length of the slow EMA.
Volume – order volume for entries.
CandleType – timeframe of processed candles.
- Other Notes:
- Works only with finished candles.
- No explicit stop-loss or take-profit; positions reverse on opposite signals.
- Uses high-level API with indicator binding and automatic charting.
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>
/// Moving Average Delta strategy. Trades when the cubed amplified difference
/// between fast and slow moving averages crosses dynamic thresholds.
/// </summary>
public class MaDeltaStrategy : Strategy
{
private readonly StrategyParam<int> _delta;
private readonly StrategyParam<int> _multiplier;
private readonly StrategyParam<int> _fastMaPeriod;
private readonly StrategyParam<int> _slowMaPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _hi;
private decimal _lo;
private bool _isInit;
private int _trade;
private decimal _deltaStep;
private decimal _multiplierFactor;
public int Delta { get => _delta.Value; set => _delta.Value = value; }
public int Multiplier { get => _multiplier.Value; set => _multiplier.Value = value; }
public int FastMaPeriod { get => _fastMaPeriod.Value; set => _fastMaPeriod.Value = value; }
public int SlowMaPeriod { get => _slowMaPeriod.Value; set => _slowMaPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public MaDeltaStrategy()
{
_delta = Param(nameof(Delta), 195)
.SetDisplay("Delta (pips)", "Hi-Lo threshold in pips", "General")
.SetOptimize(50, 300, 5);
_multiplier = Param(nameof(Multiplier), 392)
.SetDisplay("Multiplier", "Amplifier for MA difference", "General")
.SetOptimize(100, 500, 10);
_fastMaPeriod = Param(nameof(FastMaPeriod), 26)
.SetDisplay("Fast MA Period", "Period for fast moving average", "Indicators")
.SetOptimize(5, 50, 1);
_slowMaPeriod = Param(nameof(SlowMaPeriod), 51)
.SetDisplay("Slow MA Period", "Period for slow moving average", "Indicators")
.SetOptimize(10, 100, 1);
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_hi = 0m;
_lo = 0m;
_isInit = false;
_trade = 0;
_deltaStep = 0m;
_multiplierFactor = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_hi = 0m;
_lo = 0m;
_isInit = false;
_trade = 0;
_deltaStep = Delta * 0.00001m;
_multiplierFactor = Multiplier * 0.1m;
var fastMa = new ExponentialMovingAverage { Length = FastMaPeriod };
var slowMa = new ExponentialMovingAverage { Length = SlowMaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fastMa, slowMa, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fastMa);
DrawIndicator(area, slowMa);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal fastMaValue, decimal slowMaValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var diff = _multiplierFactor * (fastMaValue - slowMaValue);
var px = (decimal)Math.Pow((double)diff, 3);
if (!_isInit)
{
_hi = 0m;
_lo = 0m;
_trade = 0;
_isInit = true;
}
if (px > _hi)
{
_hi = px;
_lo = _hi - _deltaStep;
_trade = 1;
}
else if (px < _lo)
{
_lo = px;
_hi = _lo + _deltaStep;
_trade = -1;
}
if (_trade == 1 && Position <= 0)
BuyMarket();
else if (_trade == -1 && Position >= 0)
SellMarket();
}
}
import clr
import math
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 ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class ma_delta_strategy(Strategy):
def __init__(self):
super(ma_delta_strategy, self).__init__()
self._delta = self.Param("Delta", 195) \
.SetDisplay("Delta (pips)", "Hi-Lo threshold in pips", "General")
self._multiplier = self.Param("Multiplier", 392) \
.SetDisplay("Multiplier", "Amplifier for MA difference", "General")
self._fast_ma_period = self.Param("FastMaPeriod", 26) \
.SetDisplay("Fast MA Period", "Period for fast moving average", "Indicators")
self._slow_ma_period = self.Param("SlowMaPeriod", 51) \
.SetDisplay("Slow MA Period", "Period for slow moving average", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._hi = 0.0
self._lo = 0.0
self._is_init = False
self._trade = 0
self._delta_step = 0.0
self._multiplier_factor = 0.0
@property
def delta(self):
return self._delta.Value
@property
def multiplier(self):
return self._multiplier.Value
@property
def fast_ma_period(self):
return self._fast_ma_period.Value
@property
def slow_ma_period(self):
return self._slow_ma_period.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(ma_delta_strategy, self).OnReseted()
self._hi = 0.0
self._lo = 0.0
self._is_init = False
self._trade = 0
self._delta_step = 0.0
self._multiplier_factor = 0.0
def OnStarted2(self, time):
super(ma_delta_strategy, self).OnStarted2(time)
self._hi = 0.0
self._lo = 0.0
self._is_init = False
self._trade = 0
self._delta_step = float(self.delta) * 0.00001
self._multiplier_factor = float(self.multiplier) * 0.1
fast_ma = ExponentialMovingAverage()
fast_ma.Length = self.fast_ma_period
slow_ma = ExponentialMovingAverage()
slow_ma.Length = self.slow_ma_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(fast_ma, slow_ma, self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast_ma)
self.DrawIndicator(area, slow_ma)
self.DrawOwnTrades(area)
def process_candle(self, candle, fast_ma_value, slow_ma_value):
if candle.State != CandleStates.Finished:
return
fast_ma_value = float(fast_ma_value)
slow_ma_value = float(slow_ma_value)
diff = self._multiplier_factor * (fast_ma_value - slow_ma_value)
px = math.pow(diff, 3)
if not self._is_init:
self._hi = 0.0
self._lo = 0.0
self._trade = 0
self._is_init = True
if px > self._hi:
self._hi = px
self._lo = self._hi - self._delta_step
self._trade = 1
elif px < self._lo:
self._lo = px
self._hi = self._lo + self._delta_step
self._trade = -1
if self._trade == 1 and self.Position <= 0:
self.BuyMarket()
elif self._trade == -1 and self.Position >= 0:
self.SellMarket()
def CreateClone(self):
return ma_delta_strategy()