Estrategia Flawless Victory
Flawless Victory es un sistema de momentum modular que combina osciladores con Bandas de Bollinger. Dependiendo de la versión seleccionada, puede operar con señales simples de RSI, aplicar objetivos fijos de take-profit y stop-loss, o exigir confirmación del Money Flow Index. El objetivo es explotar el agotamiento en los extremos de las bandas de volatilidad y aprovechar las oscilaciones de reversión a la media.
La versión 1 entra cuando el RSI sale de zonas de sobreventa o sobrecompra cerca de los extremos de Bollinger. La versión 2 añade control explícito del riesgo mediante objetivos basados en porcentajes. La versión 3 requiere que tanto el RSI como el MFI estén de acuerdo, filtrando reversiones débiles.
La estrategia funciona mejor en mercados intradía con límites de volatilidad claros.
Detalles
- Criterios de entrada:
- Largo: ver reglas de versión (RSI <30 cerca de la banda inferior; versión 3 también
MFI < 20) - Corto: RSI >70 cerca de la banda superior (versión 3 también
MFI > 80)
- Largo: ver reglas de versión (RSI <30 cerca de la banda inferior; versión 3 también
- Largo/Corto: Ambos
- Criterios de salida:
- Versión 1: señal RSI opuesta
- Versión 2: porcentajes de take-profit o stop-loss
- Versión 3: combinación opuesta RSI/MFI
- Stops: Opcional en la versión 2
- Valores predeterminados:
RSI_length= 14MFI_length= 14BBLength= 20BBMultiplier= 2.0TakeProfitPct= 1.5StopLossPct= 1.0
- Filtros:
- Categoría: Reversión a la media
- Dirección: Ambos
- Indicadores: RSI, MFI, Bollinger Bands
- Stops: Opcional
- Complejidad: Intermedio
- Marco temporal: Corto plazo
- Estacionalidad: No
- Redes neuronales: No
- Divergencia: Sí
- Nivel de riesgo: Medio
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()