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/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 = 14
MFI_length = 14
BBLength = 20
BBMultiplier = 2.0
TakeProfitPct = 1.5
StopLossPct = 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
namespace StockSharp.Samples.Strategies;
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
/// <summary>
/// Flawless Victory Strategy.
/// Uses Bollinger Bands and RSI for mean reversion trading.
/// Buys when price below lower BB with RSI oversold.
/// Sells when price above upper BB with RSI overbought.
/// </summary>
public class FlawlessVictoryStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleTypeParam;
private readonly StrategyParam<int> _bbLength;
private readonly StrategyParam<decimal> _bbWidth;
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<decimal> _rsiOversold;
private readonly StrategyParam<decimal> _rsiOverbought;
private readonly StrategyParam<int> _cooldownBars;
private BollingerBands _bollinger;
private RelativeStrengthIndex _rsi;
private int _cooldownRemaining;
public FlawlessVictoryStrategy()
{
_candleTypeParam = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
.SetDisplay("Candle type", "Candle type for strategy calculation.", "General");
_bbLength = Param(nameof(BBLength), 20)
.SetGreaterThanZero()
.SetDisplay("BB Period", "Bollinger Bands period", "Bollinger Bands");
_bbWidth = Param(nameof(BBWidth), 1.5m)
.SetDisplay("BB Width", "Bollinger Bands standard deviation", "Bollinger Bands");
_rsiLength = Param(nameof(RSILength), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Length", "RSI period", "RSI");
_rsiOversold = Param(nameof(RSIOversold), 42m)
.SetDisplay("RSI Oversold", "RSI oversold level", "RSI");
_rsiOverbought = Param(nameof(RSIOverbought), 70m)
.SetDisplay("RSI Overbought", "RSI overbought level", "RSI");
_cooldownBars = Param(nameof(CooldownBars), 10)
.SetDisplay("Cooldown Bars", "Bars to wait between trades", "Risk");
}
public DataType CandleType
{
get => _candleTypeParam.Value;
set => _candleTypeParam.Value = value;
}
public int BBLength
{
get => _bbLength.Value;
set => _bbLength.Value = value;
}
public decimal BBWidth
{
get => _bbWidth.Value;
set => _bbWidth.Value = value;
}
public int RSILength
{
get => _rsiLength.Value;
set => _rsiLength.Value = value;
}
public decimal RSIOversold
{
get => _rsiOversold.Value;
set => _rsiOversold.Value = value;
}
public decimal RSIOverbought
{
get => _rsiOverbought.Value;
set => _rsiOverbought.Value = value;
}
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_bollinger = null;
_rsi = null;
_cooldownRemaining = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_bollinger = new BollingerBands
{
Length = BBLength,
Width = BBWidth
};
_rsi = new RelativeStrengthIndex { Length = RSILength };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(_bollinger, _rsi, OnProcess)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _bollinger);
DrawOwnTrades(area);
}
}
private void OnProcess(ICandleMessage candle, IIndicatorValue bollingerValue, IIndicatorValue rsiValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_bollinger.IsFormed || !_rsi.IsFormed)
return;
var bb = (BollingerBandsValue)bollingerValue;
if (bb.UpBand is not decimal upper ||
bb.LowBand is not decimal lower ||
bb.MovingAverage is not decimal middle)
return;
if (rsiValue.IsEmpty)
return;
var rsi = rsiValue.ToDecimal();
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (_cooldownRemaining > 0)
{
_cooldownRemaining--;
return;
}
var close = candle.ClosePrice;
// Buy: price below lower BB with RSI oversold
if (close < lower && rsi < RSIOversold && Position <= 0)
{
if (Position < 0)
BuyMarket(Math.Abs(Position));
BuyMarket(Volume);
_cooldownRemaining = CooldownBars;
}
// Sell: price above upper BB with RSI overbought
else if (close > upper && rsi > RSIOverbought && Position >= 0)
{
if (Position > 0)
SellMarket(Math.Abs(Position));
SellMarket(Volume);
_cooldownRemaining = CooldownBars;
}
// Exit long at middle band
else if (Position > 0 && close >= middle)
{
SellMarket(Math.Abs(Position));
_cooldownRemaining = CooldownBars;
}
// Exit short at middle band
else if (Position < 0 && close <= middle)
{
BuyMarket(Math.Abs(Position));
_cooldownRemaining = CooldownBars;
}
}
}
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 BollingerBands, RelativeStrengthIndex, IndicatorHelper
from StockSharp.Algo.Strategies import Strategy
class flawless_victory_strategy(Strategy):
"""Flawless Victory Strategy. Bollinger Bands + RSI mean reversion."""
def __init__(self):
super(flawless_victory_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))) \
.SetDisplay("Candle type", "Candle type for strategy calculation.", "General")
self._bb_length = self.Param("BBLength", 20) \
.SetDisplay("BB Period", "Bollinger Bands period", "Bollinger Bands")
self._bb_width = self.Param("BBWidth", 1.5) \
.SetDisplay("BB Width", "Bollinger Bands standard deviation", "Bollinger Bands")
self._rsi_length = self.Param("RSILength", 14) \
.SetDisplay("RSI Length", "RSI period", "RSI")
self._rsi_oversold = self.Param("RSIOversold", 42.0) \
.SetDisplay("RSI Oversold", "RSI oversold level", "RSI")
self._rsi_overbought = self.Param("RSIOverbought", 70.0) \
.SetDisplay("RSI Overbought", "RSI overbought level", "RSI")
self._cooldown_bars = self.Param("CooldownBars", 10) \
.SetDisplay("Cooldown Bars", "Bars to wait between trades", "Risk")
self._bollinger = None
self._rsi = None
self._cooldown_remaining = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(flawless_victory_strategy, self).OnReseted()
self._bollinger = None
self._rsi = None
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(flawless_victory_strategy, self).OnStarted2(time)
self._bollinger = BollingerBands()
self._bollinger.Length = int(self._bb_length.Value)
self._bollinger.Width = float(self._bb_width.Value)
self._rsi = RelativeStrengthIndex()
self._rsi.Length = int(self._rsi_length.Value)
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(self._bollinger, self._rsi, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, self._bollinger)
self.DrawOwnTrades(area)
def _on_process(self, candle, bb_value, rsi_value):
if candle.State != CandleStates.Finished:
return
if not self._bollinger.IsFormed or not self._rsi.IsFormed:
return
if bb_value.UpBand is None or bb_value.LowBand is None or bb_value.MovingAverage is None:
return
if rsi_value.IsEmpty:
return
rsi = float(IndicatorHelper.ToDecimal(rsi_value))
if not self.IsFormedAndOnlineAndAllowTrading():
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
return
upper = float(bb_value.UpBand)
lower = float(bb_value.LowBand)
middle = float(bb_value.MovingAverage)
close = float(candle.ClosePrice)
cooldown = int(self._cooldown_bars.Value)
if close < lower and rsi < float(self._rsi_oversold.Value) and self.Position <= 0:
if self.Position < 0:
self.BuyMarket(Math.Abs(self.Position))
self.BuyMarket(self.Volume)
self._cooldown_remaining = cooldown
elif close > upper and rsi > float(self._rsi_overbought.Value) and self.Position >= 0:
if self.Position > 0:
self.SellMarket(Math.Abs(self.Position))
self.SellMarket(self.Volume)
self._cooldown_remaining = cooldown
elif self.Position > 0 and close >= middle:
self.SellMarket(Math.Abs(self.Position))
self._cooldown_remaining = cooldown
elif self.Position < 0 and close <= middle:
self.BuyMarket(Math.Abs(self.Position))
self._cooldown_remaining = cooldown
def CreateClone(self):
return flawless_victory_strategy()