Flawless Victory戦略
Flawless Victoryは、オシレーターとボリンジャーバンドを組み合わせたモジュール式モメンタムシステムです。選択したバージョンによって、シンプルなRSIシグナルのみで動作したり、固定のテイクプロフィットとストップロスを適用したり、Money Flow Indexによる確認を要求したりすることができます。目標は、ボラティリティバンドの端での過熱を利用し、平均回帰のスイングに乗ることです。
バージョン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, Bollinger Bands
- ストップ: オプション
- 複雑さ: 中級
- 時間軸: 短期
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: はい
- リスクレベル: 中
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()