MFI Slowdown
The strategy monitors the Money Flow Index (MFI) on a higher timeframe and reacts when it reaches extreme zones. If SeekSlowdown is enabled, a signal is confirmed only when the MFI value changes less than one point between two consecutive bars. On an upward signal it closes short positions and optionally opens a new long position; on a downward signal it closes long positions and can open a short one. Risk management is handled by StartProtection.
Details
- Entry Criteria:
- Upward signal:
MFI >= UpperThresholdand (no slowdown check or slowdown detected). - Downward signal:
MFI <= LowerThresholdand (no slowdown check or slowdown detected).
- Upward signal:
- Long/Short: Both, depending on parameters.
- Exit Criteria:
- Opposite signal closes the position.
- Stop-loss and take-profit via
StopLossPercentandTakeProfitPercent.
- Stops: Yes, via StartProtection.
- Default Values:
MfiPeriod= 2UpperThreshold= 90LowerThreshold= 10SeekSlowdown= trueTakeProfitPercent= 2StopLossPercent= 1CandleType= 6-hour timeframeBuyPosOpen=BuyPosClose=SellPosOpen=SellPosClose= true
- Filters:
- Category: Momentum
- Direction: Both
- Indicators: MFI
- Stops: Yes
- Complexity: Low
- Timeframe: Any
- Seasonality: No
- Neural networks: No
- Divergence: Optional (slowdown check)
- Risk level: Medium
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Money Flow Index strategy that reacts to extreme values and optional slowdown.
/// Closes opposite positions and optionally opens new ones on signals.
/// Uses StartProtection for stop-loss and take-profit.
/// </summary>
public class MfiSlowdownStrategy : Strategy
{
private readonly StrategyParam<int> _mfiPeriod;
private readonly StrategyParam<decimal> _upperThreshold;
private readonly StrategyParam<decimal> _lowerThreshold;
private readonly StrategyParam<bool> _seekSlowdown;
private readonly StrategyParam<bool> _buyPosOpen;
private readonly StrategyParam<bool> _buyPosClose;
private readonly StrategyParam<bool> _sellPosOpen;
private readonly StrategyParam<bool> _sellPosClose;
private readonly StrategyParam<decimal> _takeProfitPercent;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prevMfi;
/// <summary>
/// Period for MFI calculation.
/// </summary>
public int MfiPeriod { get => _mfiPeriod.Value; set => _mfiPeriod.Value = value; }
/// <summary>
/// Upper threshold for MFI.
/// </summary>
public decimal UpperThreshold { get => _upperThreshold.Value; set => _upperThreshold.Value = value; }
/// <summary>
/// Lower threshold for MFI.
/// </summary>
public decimal LowerThreshold { get => _lowerThreshold.Value; set => _lowerThreshold.Value = value; }
/// <summary>
/// Require MFI slowdown before signaling.
/// </summary>
public bool SeekSlowdown { get => _seekSlowdown.Value; set => _seekSlowdown.Value = value; }
/// <summary>
/// Allow opening long positions.
/// </summary>
public bool BuyPosOpen { get => _buyPosOpen.Value; set => _buyPosOpen.Value = value; }
/// <summary>
/// Allow closing long positions.
/// </summary>
public bool BuyPosClose { get => _buyPosClose.Value; set => _buyPosClose.Value = value; }
/// <summary>
/// Allow opening short positions.
/// </summary>
public bool SellPosOpen { get => _sellPosOpen.Value; set => _sellPosOpen.Value = value; }
/// <summary>
/// Allow closing short positions.
/// </summary>
public bool SellPosClose { get => _sellPosClose.Value; set => _sellPosClose.Value = value; }
/// <summary>
/// Take-profit percentage.
/// </summary>
public decimal TakeProfitPercent { get => _takeProfitPercent.Value; set => _takeProfitPercent.Value = value; }
/// <summary>
/// Stop-loss percentage.
/// </summary>
public decimal StopLossPercent { get => _stopLossPercent.Value; set => _stopLossPercent.Value = value; }
/// <summary>
/// Candle type used by the strategy.
/// </summary>
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
/// <summary>
/// Initializes a new instance of the <see cref="MfiSlowdownStrategy"/> class.
/// </summary>
public MfiSlowdownStrategy()
{
_mfiPeriod = Param(nameof(MfiPeriod), 2)
.SetGreaterThanZero()
.SetDisplay("MFI Period", "Period for the MFI indicator", "Indicator");
_upperThreshold = Param(nameof(UpperThreshold), 90m)
.SetRange(0m, 100m)
.SetDisplay("Upper Threshold", "MFI upper level", "Signal");
_lowerThreshold = Param(nameof(LowerThreshold), 10m)
.SetRange(0m, 100m)
.SetDisplay("Lower Threshold", "MFI lower level", "Signal");
_seekSlowdown = Param(nameof(SeekSlowdown), true)
.SetDisplay("Seek Slowdown", "Require MFI to slow down", "Signal");
_buyPosOpen = Param(nameof(BuyPosOpen), true)
.SetDisplay("Open Long", "Allow opening long positions", "Trading");
_buyPosClose = Param(nameof(BuyPosClose), true)
.SetDisplay("Close Long", "Allow closing long positions", "Trading");
_sellPosOpen = Param(nameof(SellPosOpen), true)
.SetDisplay("Open Short", "Allow opening short positions", "Trading");
_sellPosClose = Param(nameof(SellPosClose), true)
.SetDisplay("Close Short", "Allow closing short positions", "Trading");
_takeProfitPercent = Param(nameof(TakeProfitPercent), 2m)
.SetRange(0m, 10m)
.SetDisplay("Take Profit %", "Take-profit percentage", "Risk");
_stopLossPercent = Param(nameof(StopLossPercent), 1m)
.SetRange(0m, 10m)
.SetDisplay("Stop Loss %", "Stop-loss percentage", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(6).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();
_prevMfi = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var mfi = new MoneyFlowIndex { Length = MfiPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(mfi, ProcessCandle)
.Start();
StartProtection(
new Unit(TakeProfitPercent, UnitTypes.Percent),
new Unit(StopLossPercent, UnitTypes.Percent)
);
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, mfi);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal mfiValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var slowdown = _prevMfi.HasValue && Math.Abs(mfiValue - _prevMfi.Value) < 1m;
var upSignal = mfiValue >= UpperThreshold && (!SeekSlowdown || slowdown);
var downSignal = mfiValue <= LowerThreshold && (!SeekSlowdown || slowdown);
if (upSignal)
{
if (SellPosClose && Position < 0)
BuyMarket();
if (BuyPosOpen && Position <= 0)
BuyMarket();
}
else if (downSignal)
{
if (BuyPosClose && Position > 0)
SellMarket();
if (SellPosOpen && Position >= 0)
SellMarket();
}
_prevMfi = mfiValue;
}
}
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, Unit, UnitTypes
from StockSharp.Algo.Indicators import MoneyFlowIndex
from StockSharp.Algo.Strategies import Strategy
class mfi_slowdown_strategy(Strategy):
def __init__(self):
super(mfi_slowdown_strategy, self).__init__()
self._mfi_period = self.Param("MfiPeriod", 2) \
.SetDisplay("MFI Period", "Period for the MFI indicator", "Indicator")
self._upper_threshold = self.Param("UpperThreshold", 90.0) \
.SetDisplay("Upper Threshold", "MFI upper level", "Signal")
self._lower_threshold = self.Param("LowerThreshold", 10.0) \
.SetDisplay("Lower Threshold", "MFI lower level", "Signal")
self._seek_slowdown = self.Param("SeekSlowdown", True) \
.SetDisplay("Seek Slowdown", "Require MFI to slow down", "Signal")
self._buy_pos_open = self.Param("BuyPosOpen", True) \
.SetDisplay("Open Long", "Allow opening long positions", "Trading")
self._buy_pos_close = self.Param("BuyPosClose", True) \
.SetDisplay("Close Long", "Allow closing long positions", "Trading")
self._sell_pos_open = self.Param("SellPosOpen", True) \
.SetDisplay("Open Short", "Allow opening short positions", "Trading")
self._sell_pos_close = self.Param("SellPosClose", True) \
.SetDisplay("Close Short", "Allow closing short positions", "Trading")
self._take_profit_percent = self.Param("TakeProfitPercent", 2.0) \
.SetDisplay("Take Profit %", "Take-profit percentage", "Risk")
self._stop_loss_percent = self.Param("StopLossPercent", 1.0) \
.SetDisplay("Stop Loss %", "Stop-loss percentage", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(6))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._prev_mfi = None
@property
def mfi_period(self):
return self._mfi_period.Value
@property
def upper_threshold(self):
return self._upper_threshold.Value
@property
def lower_threshold(self):
return self._lower_threshold.Value
@property
def seek_slowdown(self):
return self._seek_slowdown.Value
@property
def buy_pos_open(self):
return self._buy_pos_open.Value
@property
def buy_pos_close(self):
return self._buy_pos_close.Value
@property
def sell_pos_open(self):
return self._sell_pos_open.Value
@property
def sell_pos_close(self):
return self._sell_pos_close.Value
@property
def take_profit_percent(self):
return self._take_profit_percent.Value
@property
def stop_loss_percent(self):
return self._stop_loss_percent.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(mfi_slowdown_strategy, self).OnReseted()
self._prev_mfi = None
def OnStarted2(self, time):
super(mfi_slowdown_strategy, self).OnStarted2(time)
self._prev_mfi = None
mfi = MoneyFlowIndex()
mfi.Length = int(self.mfi_period)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(mfi, self.process_candle).Start()
self.StartProtection(
takeProfit=Unit(float(self.take_profit_percent), UnitTypes.Percent),
stopLoss=Unit(float(self.stop_loss_percent), UnitTypes.Percent))
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, mfi)
self.DrawOwnTrades(area)
def process_candle(self, candle, mfi_value):
if candle.State != CandleStates.Finished:
return
mfi_value = float(mfi_value)
slowdown = self._prev_mfi is not None and abs(mfi_value - self._prev_mfi) < 1.0
ut = float(self.upper_threshold)
lt = float(self.lower_threshold)
up_signal = mfi_value >= ut and (not self.seek_slowdown or slowdown)
down_signal = mfi_value <= lt and (not self.seek_slowdown or slowdown)
if up_signal:
if self.sell_pos_close and self.Position < 0:
self.BuyMarket()
if self.buy_pos_open and self.Position <= 0:
self.BuyMarket()
elif down_signal:
if self.buy_pos_close and self.Position > 0:
self.SellMarket()
if self.sell_pos_open and self.Position >= 0:
self.SellMarket()
self._prev_mfi = mfi_value
def CreateClone(self):
return mfi_slowdown_strategy()