MFI 确认的晨星/暮星策略
概述
该策略复刻 MetaTrader 专家顾问 Expert_AMS_ES_MFI 的核心思想,将多根 K 线反转形态与资金流量指数(MFI)动量确认结合在一起。策略在选定周期上监控三根蜡烛组成的晨星与暮星形态,并使用 MFI 阈值过滤信号,确认当前波段衰竭后再进场。MFI 的穿越信号也用于提前离场。
交易逻辑
- 数据来源:所配置周期的收盘 K 线以及对应的 MFI 数值。
- 指标:
- 资金流量指数(MFI),周期可配置(默认 49)。
- 入场规则:
- 做多:检测到晨星形态(强烈的第一根看跌蜡烛、第二根小实体蜡烛、第三根强烈看涨蜡烛且收盘价高于第一根实体中点),并且上一根 K 线的 MFI 低于做多确认阈值(默认 40)。
- 做空:检测到暮星形态(强烈的第一根看涨蜡烛、第二根小实体蜡烛、第三根强烈看跌蜡烛且收盘价低于第一根实体中点),并且上一根 K 线的 MFI 高于做空确认阈值(默认 60)。
- 当需要反向开仓时,策略会先平掉旧方向的仓位,再开立新的方向。
- 离场规则:
- 多单离场:当 MFI 上穿超买阈值(默认 70)或下穿超卖阈值(默认 30)时,视为动量过热或反转失败,立即平仓。
- 空单离场:当 MFI 上穿超卖阈值(默认 30)或继续上穿超买阈值(默认 70)时,视为多头动量增强,立即平仓。
- 委托类型:使用 StockSharp 中配置的策略默认手数以市价单执行。
参数
| 名称 | 说明 | 默认值 |
|---|---|---|
CandleType |
用于分析的 K 线周期。 | 1 小时 K 线 |
MfiPeriod |
MFI 指标周期。 | 49 |
BullishMfiThreshold |
晨星信号的 MFI 确认阈值。 | 40 |
BearishMfiThreshold |
暮星信号的 MFI 确认阈值。 | 60 |
UpperExitLevel |
用于识别超买的 MFI 离场阈值。 | 70 |
LowerExitLevel |
用于识别超卖的 MFI 离场阈值。 | 30 |
所有参数都可以在 StockSharp Designer/Optimizer 中进行优化。
使用说明
- 将策略附加到目标品种,并将
CandleType设置为与原始 MQL 专家顾问相同的时间周期。 - 在 StockSharp 平台中配置风险参数,如策略手数或经纪商要求的下单数量。
- 启动策略后,它会自动订阅 K 线、计算 MFI,并按照上述规则管理仓位。
来源
该策略直接移植自 MQL/323 目录中的 MQL5 专家顾问,保留了形态与 MFI 组合的决策逻辑,并改写为适配 StockSharp 高级 API。
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// Morning/Evening Star + MFI strategy.
/// Buys on morning star with low MFI, sells on evening star with high MFI.
/// </summary>
public class MorningEveningMfiStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _mfiPeriod;
private readonly StrategyParam<decimal> _mfiLow;
private readonly StrategyParam<decimal> _mfiHigh;
private readonly StrategyParam<int> _signalCooldownCandles;
private ICandleMessage _prevCandle;
private ICandleMessage _prevPrevCandle;
private int _candlesSinceTrade;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int MfiPeriod { get => _mfiPeriod.Value; set => _mfiPeriod.Value = value; }
public decimal MfiLow { get => _mfiLow.Value; set => _mfiLow.Value = value; }
public decimal MfiHigh { get => _mfiHigh.Value; set => _mfiHigh.Value = value; }
public int SignalCooldownCandles { get => _signalCooldownCandles.Value; set => _signalCooldownCandles.Value = value; }
public MorningEveningMfiStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_mfiPeriod = Param(nameof(MfiPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("MFI Period", "MFI period", "Indicators");
_mfiLow = Param(nameof(MfiLow), 40m)
.SetDisplay("MFI Low", "MFI oversold threshold", "Signals");
_mfiHigh = Param(nameof(MfiHigh), 60m)
.SetDisplay("MFI High", "MFI overbought threshold", "Signals");
_signalCooldownCandles = Param(nameof(SignalCooldownCandles), 6)
.SetGreaterThanZero()
.SetDisplay("Signal Cooldown", "Bars to wait between trades", "Trading");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevCandle = null;
_prevPrevCandle = null;
_candlesSinceTrade = SignalCooldownCandles;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevCandle = null;
_prevPrevCandle = null;
_candlesSinceTrade = SignalCooldownCandles;
var mfi = new MoneyFlowIndex { Length = MfiPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(mfi, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal mfiValue)
{
if (candle.State != CandleStates.Finished) return;
if (_candlesSinceTrade < SignalCooldownCandles)
_candlesSinceTrade++;
if (_prevCandle != null && _prevPrevCandle != null)
{
var prevBody = Math.Abs(_prevCandle.ClosePrice - _prevCandle.OpenPrice);
var prevRange = _prevCandle.HighPrice - _prevCandle.LowPrice;
var isSmallBody = prevRange > 0 && prevBody < prevRange * 0.3m;
var firstMidpoint = (_prevPrevCandle.OpenPrice + _prevPrevCandle.ClosePrice) / 2m;
var firstBearish = _prevPrevCandle.OpenPrice > _prevPrevCandle.ClosePrice;
var currBullish = candle.ClosePrice > candle.OpenPrice;
var isMorningStar = firstBearish && isSmallBody && currBullish && candle.ClosePrice > firstMidpoint;
var firstBullish = _prevPrevCandle.ClosePrice > _prevPrevCandle.OpenPrice;
var currBearish = candle.OpenPrice > candle.ClosePrice;
var isEveningStar = firstBullish && isSmallBody && currBearish && candle.ClosePrice < firstMidpoint;
if (isMorningStar && mfiValue < MfiLow && Position <= 0 && _candlesSinceTrade >= SignalCooldownCandles)
{
BuyMarket();
_candlesSinceTrade = 0;
}
else if (isEveningStar && mfiValue > MfiHigh && Position >= 0 && _candlesSinceTrade >= SignalCooldownCandles)
{
SellMarket();
_candlesSinceTrade = 0;
}
}
_prevPrevCandle = _prevCandle;
_prevCandle = candle;
}
}
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 MoneyFlowIndex
from StockSharp.Algo.Strategies import Strategy
class morning_evening_mfi_strategy(Strategy):
def __init__(self):
super(morning_evening_mfi_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(60)))
self._mfi_period = self.Param("MfiPeriod", 14)
self._mfi_low = self.Param("MfiLow", 40.0)
self._mfi_high = self.Param("MfiHigh", 60.0)
self._signal_cooldown_candles = self.Param("SignalCooldownCandles", 6)
self._prev_candle = None
self._prev_prev_candle = None
self._candles_since_trade = 6
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def MfiPeriod(self):
return self._mfi_period.Value
@MfiPeriod.setter
def MfiPeriod(self, value):
self._mfi_period.Value = value
@property
def MfiLow(self):
return self._mfi_low.Value
@MfiLow.setter
def MfiLow(self, value):
self._mfi_low.Value = value
@property
def MfiHigh(self):
return self._mfi_high.Value
@MfiHigh.setter
def MfiHigh(self, value):
self._mfi_high.Value = value
@property
def SignalCooldownCandles(self):
return self._signal_cooldown_candles.Value
@SignalCooldownCandles.setter
def SignalCooldownCandles(self, value):
self._signal_cooldown_candles.Value = value
def OnReseted(self):
super(morning_evening_mfi_strategy, self).OnReseted()
self._prev_candle = None
self._prev_prev_candle = None
self._candles_since_trade = self.SignalCooldownCandles
def OnStarted2(self, time):
super(morning_evening_mfi_strategy, self).OnStarted2(time)
self._prev_candle = None
self._prev_prev_candle = None
self._candles_since_trade = self.SignalCooldownCandles
mfi = MoneyFlowIndex()
mfi.Length = self.MfiPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(mfi, self._process_candle).Start()
def _process_candle(self, candle, mfi_value):
if candle.State != CandleStates.Finished:
return
if self._candles_since_trade < self.SignalCooldownCandles:
self._candles_since_trade += 1
mfi_val = float(mfi_value)
if self._prev_candle is not None and self._prev_prev_candle is not None:
prev_body = abs(float(self._prev_candle.ClosePrice) - float(self._prev_candle.OpenPrice))
prev_range = float(self._prev_candle.HighPrice) - float(self._prev_candle.LowPrice)
is_small_body = prev_range > 0 and prev_body < prev_range * 0.3
first_midpoint = (float(self._prev_prev_candle.OpenPrice) + float(self._prev_prev_candle.ClosePrice)) / 2.0
first_bearish = float(self._prev_prev_candle.OpenPrice) > float(self._prev_prev_candle.ClosePrice)
curr_bullish = float(candle.ClosePrice) > float(candle.OpenPrice)
is_morning_star = first_bearish and is_small_body and curr_bullish and float(candle.ClosePrice) > first_midpoint
first_bullish = float(self._prev_prev_candle.ClosePrice) > float(self._prev_prev_candle.OpenPrice)
curr_bearish = float(candle.OpenPrice) > float(candle.ClosePrice)
is_evening_star = first_bullish and is_small_body and curr_bearish and float(candle.ClosePrice) < first_midpoint
if is_morning_star and mfi_val < self.MfiLow and self.Position <= 0 and self._candles_since_trade >= self.SignalCooldownCandles:
self.BuyMarket()
self._candles_since_trade = 0
elif is_evening_star and mfi_val > self.MfiHigh and self.Position >= 0 and self._candles_since_trade >= self.SignalCooldownCandles:
self.SellMarket()
self._candles_since_trade = 0
self._prev_prev_candle = self._prev_candle
self._prev_candle = candle
def CreateClone(self):
return morning_evening_mfi_strategy()