Flawless Victory 策略
Flawless Victory 是一种模块化动量系统,将振荡指标与布林带结合。根据不同版本,它可以仅依赖 RSI 信号、加入固定的止盈止损百分比,或要求资金流量指标 MFI 共同确认。目标是在波动带边缘捕捉耗竭并利用价格回归。
版本1 在 RSI 离开超卖或超买区域并接近布林带极值时入场。版本2 在此基础上增加百分比止盈止损以控制风险。版本3 需要 RSI 和 MFI 同向,过滤掉弱势反转。
该策略最适合波动区间明确的日内市场。
详情
- 入场条件:
- 多头: 见对应版本(RSI <30 且靠近下轨;版本3 还需
MFI < 20) - 空头: RSI >70 且靠近上轨(版本3 还需
MFI > 80)
- 多头: 见对应版本(RSI <30 且靠近下轨;版本3 还需
- 多空方向: 双向
- 退出条件:
- 版本1: 相反的 RSI 信号
- 版本2: 百分比止盈或止损
- 版本3: 相反的 RSI/MFI 组合
- 止损: 版本2 可选
- 默认值:
RSI_length= 14MFI_length= 14BBLength= 20BBMultiplier= 2.0TakeProfitPct= 1.5StopLossPct= 1.0
- 过滤器:
- 类型: 均值回归
- 方向: 双向
- 指标: RSI, MFI, 布林带
- 止损: 可选
- 复杂度: 中等
- 时间框架: 短期
- 季节性: 否
- 神经网络: 否
- 背离: 是
- 风险等级: 中等
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>
/// Flawless Victory strategy.
/// Goes long when the close is below the lower Bollinger band with RSI under 30 and short when it is above the upper band
/// with RSI over 70; the opposite signal reverses the position. Version 2 adds percent take-profit and stop-loss exits,
/// and Version 3 additionally requires MFI under 20 for longs and over 80 for shorts.
/// </summary>
public class FlawlessVictoryStrategy : Strategy
{
private const decimal _rsiOversold = 30m;
private const decimal _rsiOverbought = 70m;
private const decimal _mfiOversold = 20m;
private const decimal _mfiOverbought = 80m;
private readonly StrategyParam<int> _version;
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<int> _mfiLength;
private readonly StrategyParam<int> _bbLength;
private readonly StrategyParam<decimal> _bbMultiplier;
private readonly StrategyParam<decimal> _takeProfitPct;
private readonly StrategyParam<decimal> _stopLossPct;
private readonly StrategyParam<DataType> _candleType;
/// <summary>
/// Strategy version: 1, 2 or 3.
/// </summary>
public int Version
{
get => _version.Value;
set => _version.Value = value;
}
/// <summary>
/// RSI period.
/// </summary>
public int RSI_length
{
get => _rsiLength.Value;
set => _rsiLength.Value = value;
}
/// <summary>
/// MFI period.
/// </summary>
public int MFI_length
{
get => _mfiLength.Value;
set => _mfiLength.Value = value;
}
/// <summary>
/// Bollinger Bands period.
/// </summary>
public int BBLength
{
get => _bbLength.Value;
set => _bbLength.Value = value;
}
/// <summary>
/// Bollinger Bands standard deviation multiplier.
/// </summary>
public decimal BBMultiplier
{
get => _bbMultiplier.Value;
set => _bbMultiplier.Value = value;
}
/// <summary>
/// Take-profit percentage used by version 2.
/// </summary>
public decimal TakeProfitPct
{
get => _takeProfitPct.Value;
set => _takeProfitPct.Value = value;
}
/// <summary>
/// Stop-loss percentage used by version 2.
/// </summary>
public decimal StopLossPct
{
get => _stopLossPct.Value;
set => _stopLossPct.Value = value;
}
/// <summary>
/// Candle type for strategy calculation.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public FlawlessVictoryStrategy()
{
_version = Param(nameof(Version), 1)
.SetRange(1, 3)
.SetDisplay("Version", "1: RSI signals, 2: adds take-profit/stop-loss, 3: adds MFI confirmation", "General");
_rsiLength = Param(nameof(RSI_length), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Length", "RSI period", "Indicators");
_mfiLength = Param(nameof(MFI_length), 14)
.SetGreaterThanZero()
.SetDisplay("MFI Length", "MFI period", "Indicators");
_bbLength = Param(nameof(BBLength), 20)
.SetGreaterThanZero()
.SetDisplay("BB Period", "Bollinger Bands period", "Indicators");
_bbMultiplier = Param(nameof(BBMultiplier), 2.0m)
.SetGreaterThanZero()
.SetDisplay("BB Multiplier", "Bollinger Bands standard deviation multiplier", "Indicators");
_takeProfitPct = Param(nameof(TakeProfitPct), 1.5m)
.SetNotNegative()
.SetDisplay("Take Profit %", "Take-profit percentage for version 2", "Risk");
_stopLossPct = Param(nameof(StopLossPct), 1.0m)
.SetNotNegative()
.SetDisplay("Stop Loss %", "Stop-loss percentage for version 2", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
.SetDisplay("Candle type", "Candle type for strategy calculation", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var bollinger = new BollingerBands
{
Length = BBLength,
Width = BBMultiplier
};
var rsi = new RelativeStrengthIndex { Length = RSI_length };
var mfi = new MoneyFlowIndex { Length = MFI_length };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(bollinger, rsi, mfi, ProcessCandle)
.Start();
if (Version == 2)
{
StartProtection(
TakeProfitPct > 0 ? new Unit(TakeProfitPct, UnitTypes.Percent) : new Unit(),
StopLossPct > 0 ? new Unit(StopLossPct, UnitTypes.Percent) : new Unit(),
useMarketOrders: true);
}
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, bollinger);
DrawOwnTrades(area);
var oscillators = CreateChartArea();
if (oscillators != null)
{
DrawIndicator(oscillators, rsi);
DrawIndicator(oscillators, mfi);
}
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue bollingerValue, IIndicatorValue rsiValue, IIndicatorValue mfiValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!bollingerValue.IsFormed || !rsiValue.IsFormed || !mfiValue.IsFormed)
return;
var bb = (BollingerBandsValue)bollingerValue;
if (bb.UpBand is not decimal upper || bb.LowBand is not decimal lower)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var rsi = rsiValue.ToDecimal();
var mfi = mfiValue.ToDecimal();
var close = candle.ClosePrice;
var useMfi = Version == 3;
var longSignal = close < lower && rsi < _rsiOversold && (!useMfi || mfi < _mfiOversold);
var shortSignal = close > upper && rsi > _rsiOverbought && (!useMfi || mfi > _mfiOverbought);
if (longSignal && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
else if (shortSignal && Position >= 0)
SellMarket(Volume + Math.Abs(Position));
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes
from StockSharp.Algo.Indicators import BollingerBands, RelativeStrengthIndex, MoneyFlowIndex
from StockSharp.Algo.Strategies import Strategy
RSI_OVERSOLD = 30.0
RSI_OVERBOUGHT = 70.0
MFI_OVERSOLD = 20.0
MFI_OVERBOUGHT = 80.0
class flawless_victory_strategy(Strategy):
"""
Flawless Victory strategy.
Goes long when the close is below the lower Bollinger band with RSI under 30 and short when it is above the upper band
with RSI over 70; the opposite signal reverses the position. Version 2 adds percent take-profit and stop-loss exits,
and Version 3 additionally requires MFI under 20 for longs and over 80 for shorts.
"""
def __init__(self):
super(flawless_victory_strategy, self).__init__()
self._version = self.Param("Version", 1) \
.SetRange(1, 3) \
.SetDisplay("Version", "1: RSI signals, 2: adds take-profit/stop-loss, 3: adds MFI confirmation", "General")
self._rsi_length = self.Param("RSI_length", 14) \
.SetGreaterThanZero() \
.SetDisplay("RSI Length", "RSI period", "Indicators")
self._mfi_length = self.Param("MFI_length", 14) \
.SetGreaterThanZero() \
.SetDisplay("MFI Length", "MFI period", "Indicators")
self._bb_length = self.Param("BBLength", 20) \
.SetGreaterThanZero() \
.SetDisplay("BB Period", "Bollinger Bands period", "Indicators")
self._bb_multiplier = self.Param("BBMultiplier", 2.0) \
.SetGreaterThanZero() \
.SetDisplay("BB Multiplier", "Bollinger Bands standard deviation multiplier", "Indicators")
self._take_profit_pct = self.Param("TakeProfitPct", 1.5) \
.SetNotNegative() \
.SetDisplay("Take Profit %", "Take-profit percentage for version 2", "Risk")
self._stop_loss_pct = self.Param("StopLossPct", 1.0) \
.SetNotNegative() \
.SetDisplay("Stop Loss %", "Stop-loss percentage for version 2", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))) \
.SetDisplay("Candle type", "Candle type for strategy calculation", "General")
@property
def CandleType(self):
return self._candle_type.Value
def GetWorkingSecurities(self):
return [(self.Security, self.CandleType)]
def OnStarted2(self, time):
super(flawless_victory_strategy, self).OnStarted2(time)
bollinger = BollingerBands()
bollinger.Length = self._bb_length.Value
bollinger.Width = Decimal(self._bb_multiplier.Value)
rsi = RelativeStrengthIndex()
rsi.Length = self._rsi_length.Value
mfi = MoneyFlowIndex()
mfi.Length = self._mfi_length.Value
subscription = self.SubscribeCandles(self.CandleType)
subscription.BindEx(bollinger, rsi, mfi, self._process_candle).Start()
if int(self._version.Value) == 2:
tp = float(self._take_profit_pct.Value)
sl = float(self._stop_loss_pct.Value)
self.StartProtection(
Unit(Decimal(tp), UnitTypes.Percent) if tp > 0 else Unit(),
Unit(Decimal(sl), UnitTypes.Percent) if sl > 0 else Unit(),
useMarketOrders=True)
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, bollinger)
self.DrawOwnTrades(area)
oscillators = self.CreateChartArea()
if oscillators is not None:
self.DrawIndicator(oscillators, rsi)
self.DrawIndicator(oscillators, mfi)
def _process_candle(self, candle, bollinger_value, rsi_value, mfi_value):
if candle.State != CandleStates.Finished:
return
if not bollinger_value.IsFormed or not rsi_value.IsFormed or not mfi_value.IsFormed:
return
if bollinger_value.UpBand is None or bollinger_value.LowBand is None:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
upper = float(bollinger_value.UpBand)
lower = float(bollinger_value.LowBand)
rsi = float(rsi_value.GetValue[Decimal](None))
mfi = float(mfi_value.GetValue[Decimal](None))
close = float(candle.ClosePrice)
use_mfi = int(self._version.Value) == 3
long_signal = close < lower and rsi < RSI_OVERSOLD and (not use_mfi or mfi < MFI_OVERSOLD)
short_signal = close > upper and rsi > RSI_OVERBOUGHT and (not use_mfi or mfi > MFI_OVERBOUGHT)
if long_signal and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
elif short_signal and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
def CreateClone(self):
return flawless_victory_strategy()