Стратегия 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
- Стопы: опционально во второй версии
- Значения по умолчанию:
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()