Estrategia Fractal MFI
Esta estrategia es una traducción del asesor experto Exp_Fractal_MFI.mq5. Utiliza el indicador Money Flow Index (MFI) para generar señales de trading cuando el oscilador cruza niveles superiores e inferiores predefinidos.
Cómo funciona
- Calcula el MFI durante un período configurable.
- Cuando el valor anterior del MFI estaba por encima del Nivel Bajo y el valor actual cae por debajo, se genera una señal.
- En modo Direct, esto abre una posición larga y opcionalmente cierra cortos.
- En modo Against, esto abre una posición corta y opcionalmente cierra largos.
- Cuando el valor anterior del MFI estaba por debajo del Nivel Alto y el valor actual sube por encima, se genera otra señal.
- En modo Direct, esto abre una posición corta y opcionalmente cierra largos.
- En modo Against, esto abre una posición larga y opcionalmente cierra cortos.
Solo se procesan velas completadas. La estrategia puede configurarse para habilitar o deshabilitar la apertura y cierre de posiciones largas o cortas por separado.
Parámetros
MfiPeriod– período del cálculo del Money Flow Index.HighLevel– umbral superior para el MFI.LowLevel– umbral inferior para el MFI.CandleType– marco temporal de velas usado en los cálculos.Trend– elegirDirectpara operar en la dirección del indicador oAgainstpara invertir las señales.BuyPosOpen/SellPosOpen– permitir apertura de posiciones largas o cortas.BuyPosClose/SellPosClose– permitir cierre de posiciones existentes en señales opuestas.
Notas
Esta versión en C# se enfoca en el uso de la API de alto nivel y no implementa las reglas de gestión de dinero originales ni los niveles de stop del código MQL.
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 crossover strategy using upper and lower thresholds.
/// Opens or closes positions depending on MFI crossing defined levels.
/// The trading direction can follow the trend or work against it.
/// </summary>
public class FractalMfiStrategy : Strategy
{
private readonly StrategyParam<int> _mfiPeriod;
private readonly StrategyParam<decimal> _highLevel;
private readonly StrategyParam<decimal> _lowLevel;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<TrendModes> _trend;
private readonly StrategyParam<bool> _buyPosOpen;
private readonly StrategyParam<bool> _sellPosOpen;
private readonly StrategyParam<bool> _buyPosClose;
private readonly StrategyParam<bool> _sellPosClose;
private decimal _prevMfi;
private bool _isPrevSet;
/// <summary>
/// MFI calculation period.
/// </summary>
public int MfiPeriod { get => _mfiPeriod.Value; set => _mfiPeriod.Value = value; }
/// <summary>
/// Upper threshold for MFI.
/// </summary>
public decimal HighLevel { get => _highLevel.Value; set => _highLevel.Value = value; }
/// <summary>
/// Lower threshold for MFI.
/// </summary>
public decimal LowLevel { get => _lowLevel.Value; set => _lowLevel.Value = value; }
/// <summary>
/// Type of candles used by the strategy.
/// </summary>
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
/// <summary>
/// Trading direction mode.
/// </summary>
public TrendModes Trend { get => _trend.Value; set => _trend.Value = value; }
/// <summary>
/// Allow opening long positions.
/// </summary>
public bool BuyPosOpen { get => _buyPosOpen.Value; set => _buyPosOpen.Value = value; }
/// <summary>
/// Allow opening short positions.
/// </summary>
public bool SellPosOpen { get => _sellPosOpen.Value; set => _sellPosOpen.Value = value; }
/// <summary>
/// Allow closing long positions on signals.
/// </summary>
public bool BuyPosClose { get => _buyPosClose.Value; set => _buyPosClose.Value = value; }
/// <summary>
/// Allow closing short positions on signals.
/// </summary>
public bool SellPosClose { get => _sellPosClose.Value; set => _sellPosClose.Value = value; }
/// <summary>
/// Constructor.
/// </summary>
public FractalMfiStrategy()
{
_mfiPeriod = Param(nameof(MfiPeriod), 30)
.SetGreaterThanZero()
.SetDisplay("MFI Period", "Length of MFI indicator", "Indicator")
;
_highLevel = Param<decimal>(nameof(HighLevel), 70m)
.SetDisplay("High Level", "Upper MFI threshold", "Levels")
;
_lowLevel = Param<decimal>(nameof(LowLevel), 30m)
.SetDisplay("Low Level", "Lower MFI threshold", "Levels")
;
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
_trend = Param(nameof(Trend), TrendModes.Direct)
.SetDisplay("Trend Mode", "Follow or trade against the trend", "General")
;
_buyPosOpen = Param(nameof(BuyPosOpen), true)
.SetDisplay("Buy Open", "Enable long entries", "Signals");
_sellPosOpen = Param(nameof(SellPosOpen), true)
.SetDisplay("Sell Open", "Enable short entries", "Signals");
_buyPosClose = Param(nameof(BuyPosClose), true)
.SetDisplay("Buy Close", "Enable closing longs", "Signals");
_sellPosClose = Param(nameof(SellPosClose), true)
.SetDisplay("Sell Close", "Enable closing shorts", "Signals");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevMfi = 0m;
_isPrevSet = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var mfi = new MoneyFlowIndex { Length = MfiPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(mfi, (candle, currentMfi) =>
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (!mfi.IsFormed)
{
_prevMfi = currentMfi;
return;
}
if (!_isPrevSet)
{
_prevMfi = currentMfi;
_isPrevSet = true;
return;
}
ProcessSignal(candle.ClosePrice, _prevMfi, currentMfi);
_prevMfi = currentMfi;
})
.Start();
StartProtection(null, null);
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, mfi);
DrawOwnTrades(area);
}
}
private void ProcessSignal(decimal price, decimal prev, decimal current)
{
if (Trend == TrendModes.Direct)
{
if (prev > LowLevel && current <= LowLevel)
{
if (SellPosClose && Position < 0)
BuyMarket(Math.Abs(Position));
if (BuyPosOpen && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
}
if (prev < HighLevel && current >= HighLevel)
{
if (BuyPosClose && Position > 0)
SellMarket(Math.Abs(Position));
if (SellPosOpen && Position >= 0)
SellMarket(Volume + Math.Abs(Position));
}
}
else
{
if (prev > LowLevel && current <= LowLevel)
{
if (BuyPosClose && Position > 0)
SellMarket(Math.Abs(Position));
if (SellPosOpen && Position >= 0)
SellMarket(Volume + Math.Abs(Position));
}
if (prev < HighLevel && current >= HighLevel)
{
if (SellPosClose && Position < 0)
BuyMarket(Math.Abs(Position));
if (BuyPosOpen && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
}
}
}
/// <summary>
/// Trend mode enumeration.
/// </summary>
public enum TrendModes
{
/// <summary>
/// Trade in direction of indicator.
/// </summary>
Direct,
/// <summary>
/// Trade against indicator direction.
/// </summary>
Against
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import MoneyFlowIndex
from StockSharp.Algo.Strategies import Strategy
DIRECT = 0
AGAINST = 1
class fractal_mfi_strategy(Strategy):
def __init__(self):
super(fractal_mfi_strategy, self).__init__()
self._mfi_period = self.Param("MfiPeriod", 30)
self._high_level = self.Param("HighLevel", 70.0)
self._low_level = self.Param("LowLevel", 30.0)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1)))
self._trend = self.Param("Trend", DIRECT)
self._buy_pos_open = self.Param("BuyPosOpen", True)
self._sell_pos_open = self.Param("SellPosOpen", True)
self._buy_pos_close = self.Param("BuyPosClose", True)
self._sell_pos_close = self.Param("SellPosClose", True)
self._prev_mfi = 0.0
self._is_prev_set = False
@property
def MfiPeriod(self):
return self._mfi_period.Value
@MfiPeriod.setter
def MfiPeriod(self, value):
self._mfi_period.Value = value
@property
def HighLevel(self):
return self._high_level.Value
@HighLevel.setter
def HighLevel(self, value):
self._high_level.Value = value
@property
def LowLevel(self):
return self._low_level.Value
@LowLevel.setter
def LowLevel(self, value):
self._low_level.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def Trend(self):
return self._trend.Value
@Trend.setter
def Trend(self, value):
self._trend.Value = value
@property
def BuyPosOpen(self):
return self._buy_pos_open.Value
@BuyPosOpen.setter
def BuyPosOpen(self, value):
self._buy_pos_open.Value = value
@property
def SellPosOpen(self):
return self._sell_pos_open.Value
@SellPosOpen.setter
def SellPosOpen(self, value):
self._sell_pos_open.Value = value
@property
def BuyPosClose(self):
return self._buy_pos_close.Value
@BuyPosClose.setter
def BuyPosClose(self, value):
self._buy_pos_close.Value = value
@property
def SellPosClose(self):
return self._sell_pos_close.Value
@SellPosClose.setter
def SellPosClose(self, value):
self._sell_pos_close.Value = value
def OnStarted2(self, time):
super(fractal_mfi_strategy, self).OnStarted2(time)
self._prev_mfi = 0.0
self._is_prev_set = False
mfi = MoneyFlowIndex()
mfi.Length = self.MfiPeriod
self._mfi = mfi
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(mfi, self.ProcessCandle).Start()
self.StartProtection(None, None)
def ProcessCandle(self, candle, current_mfi):
if candle.State != CandleStates.Finished:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
mfi_val = float(current_mfi)
if not self._mfi.IsFormed:
self._prev_mfi = mfi_val
return
if not self._is_prev_set:
self._prev_mfi = mfi_val
self._is_prev_set = True
return
self._process_signal(float(candle.ClosePrice), self._prev_mfi, mfi_val)
self._prev_mfi = mfi_val
def _process_signal(self, price, prev, current):
high = float(self.HighLevel)
low = float(self.LowLevel)
vol = float(self.Volume)
if int(self.Trend) == DIRECT:
if prev > low and current <= low:
pos = float(self.Position)
if self.SellPosClose and pos < 0:
self.BuyMarket(abs(pos))
pos = float(self.Position)
if self.BuyPosOpen and pos <= 0:
self.BuyMarket(vol + abs(pos))
if prev < high and current >= high:
pos = float(self.Position)
if self.BuyPosClose and pos > 0:
self.SellMarket(abs(pos))
pos = float(self.Position)
if self.SellPosOpen and pos >= 0:
self.SellMarket(vol + abs(pos))
else:
if prev > low and current <= low:
pos = float(self.Position)
if self.BuyPosClose and pos > 0:
self.SellMarket(abs(pos))
pos = float(self.Position)
if self.SellPosOpen and pos >= 0:
self.SellMarket(vol + abs(pos))
if prev < high and current >= high:
pos = float(self.Position)
if self.SellPosClose and pos < 0:
self.BuyMarket(abs(pos))
pos = float(self.Position)
if self.BuyPosOpen and pos <= 0:
self.BuyMarket(vol + abs(pos))
def OnReseted(self):
super(fractal_mfi_strategy, self).OnReseted()
self._prev_mfi = 0.0
self._is_prev_set = False
def CreateClone(self):
return fractal_mfi_strategy()