Стратегия Fractal MFI
Стратегия является переводом советника Exp_Fractal_MFI.mq5. Она использует индекс денежного потока (MFI) и формирует торговые сигналы при пересечении заданных верхнего и нижнего уровней.
Принцип работы
- Вычисляет MFI за настраиваемый период.
- Если предыдущее значение MFI было выше нижнего уровня, а текущее опустилось ниже, формируется сигнал.
- В режиме Direct открывается длинная позиция и при необходимости закрываются короткие.
- В режиме Against открывается короткая позиция и при необходимости закрываются длинные.
- Если предыдущее значение MFI было ниже верхнего уровня, а текущее поднялось выше, формируется противоположный сигнал.
- В режиме Direct открывается короткая позиция и при необходимости закрываются длинные.
- В режиме Against открывается длинная позиция и при необходимости закрываются короткие.
Обрабатываются только завершённые свечи. Можно отдельно включать или отключать открытие и закрытие длинных и коротких позиций.
Параметры
MfiPeriod– период расчёта индекса MFI.HighLevel– верхний порог индикатора.LowLevel– нижний порог индикатора.CandleType– таймфрейм свечей.Trend– режим торговли:Direct(по направлению индикатора) илиAgainst(против индикатора).BuyPosOpen/SellPosOpen– разрешить открытие длинных или коротких позиций.BuyPosClose/SellPosClose– разрешить закрытие позиций по противоположным сигналам.
Примечания
Данная C# версия использует высокоуровневый API и не реализует правила управления капиталом или стоп-уровни из оригинального 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()