Multi-Band-Vergleich
Der Multi-Band-Vergleich verwendet SMA, Standardabweichung und Preis-Quantil-Bänder. Die Strategie geht long, wenn der Preis über dem oberen Quantil minus Standardabweichung für eine definierte Anzahl von Bars schließt, und steigt aus, wenn der Preis für eine festgelegte Anzahl von Bars unter dieses Niveau fällt.
Details
- Daten: Preiskerzen.
- Einstiegskriterien:
- Long: Schluss über (oberes Quantil - Standardabweichung) für
EntryConfirmBarsBars.
- Long: Schluss über (oberes Quantil - Standardabweichung) für
- Ausstiegskriterien: Schluss unter dieser Linie für
ExitConfirmBarsBars. - Stops: Keine.
- Standardwerte:
Length= 20BollingerMultiplier= 1UpperQuantile= 0.95EntryConfirmBars= 1ExitConfirmBars= 1
- Filter:
- Kategorie: Statistisch
- Richtung: Long
- Indikatoren: SMA, Standard Deviation
- Komplexität: Moderat
- Risikolevel: Mittel
namespace StockSharp.Samples.Strategies;
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
/// <summary>
/// Multi-band comparison strategy that enters on an upper volatility band breakout
/// and exits on a return to the middle band.
/// </summary>
public class MultiBandComparisonStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _length;
private readonly StrategyParam<decimal> _bollingerMultiplier;
private readonly StrategyParam<int> _entryConfirmBars;
private readonly StrategyParam<int> _exitConfirmBars;
private readonly StrategyParam<int> _signalCooldownBars;
private int _entryCounter;
private int _exitCounter;
private int _cooldownRemaining;
private bool _wasAboveEntryLevel;
/// <summary>
/// Candle type for strategy calculation.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// SMA period.
/// </summary>
public int Length
{
get => _length.Value;
set => _length.Value = value;
}
/// <summary>
/// Volatility multiplier used for the breakout band.
/// </summary>
public decimal BollingerMultiplier
{
get => _bollingerMultiplier.Value;
set => _bollingerMultiplier.Value = value;
}
/// <summary>
/// Bars required for entry confirmation.
/// </summary>
public int EntryConfirmBars
{
get => _entryConfirmBars.Value;
set => _entryConfirmBars.Value = value;
}
/// <summary>
/// Bars required for exit confirmation.
/// </summary>
public int ExitConfirmBars
{
get => _exitConfirmBars.Value;
set => _exitConfirmBars.Value = value;
}
/// <summary>
/// Bars to wait before accepting a new signal.
/// </summary>
public int SignalCooldownBars
{
get => _signalCooldownBars.Value;
set => _signalCooldownBars.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public MultiBandComparisonStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
_length = Param(nameof(Length), 40)
.SetDisplay("Length", "SMA period", "Bands")
.SetGreaterThanZero();
_bollingerMultiplier = Param(nameof(BollingerMultiplier), 1.1m)
.SetDisplay("BB Mult", "Volatility multiplier for the breakout band", "Bands")
.SetGreaterThanZero();
_entryConfirmBars = Param(nameof(EntryConfirmBars), 1)
.SetDisplay("Entry Confirm Bars", "Bars for entry confirmation", "Trading")
.SetGreaterThanZero();
_exitConfirmBars = Param(nameof(ExitConfirmBars), 1)
.SetDisplay("Exit Confirm Bars", "Bars for exit confirmation", "Trading")
.SetGreaterThanZero();
_signalCooldownBars = Param(nameof(SignalCooldownBars), 8)
.SetDisplay("Signal Cooldown", "Bars to wait before accepting a new signal", "Trading")
.SetGreaterThanZero();
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_entryCounter = 0;
_exitCounter = 0;
_cooldownRemaining = 0;
_wasAboveEntryLevel = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var sma = new SMA { Length = Length };
var std = new StandardDeviation { Length = Length };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(sma, std, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, sma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal smaValue, decimal stdValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (_cooldownRemaining > 0)
_cooldownRemaining--;
if (stdValue <= 0m)
return;
var entryLevel = smaValue + stdValue * BollingerMultiplier;
var exitLevel = smaValue;
var isAboveEntryLevel = candle.ClosePrice > entryLevel;
var crossedUp = !_wasAboveEntryLevel && isAboveEntryLevel;
var crossedDown = _wasAboveEntryLevel && candle.ClosePrice < exitLevel;
_entryCounter = crossedUp ? _entryCounter + 1 : 0;
_exitCounter = crossedDown ? _exitCounter + 1 : 0;
if (Position <= 0 && _cooldownRemaining == 0 && _entryCounter >= EntryConfirmBars)
{
BuyMarket();
_entryCounter = 0;
_cooldownRemaining = SignalCooldownBars;
}
else if (Position > 0 && _exitCounter >= ExitConfirmBars)
{
SellMarket(Position);
_exitCounter = 0;
_cooldownRemaining = SignalCooldownBars;
}
_wasAboveEntryLevel = isAboveEntryLevel;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import SimpleMovingAverage, StandardDeviation
from StockSharp.Algo.Strategies import Strategy
class multi_band_comparison_strategy(Strategy):
def __init__(self):
super(multi_band_comparison_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._length = self.Param("Length", 40) \
.SetDisplay("Length", "SMA period", "Bands") \
.SetGreaterThanZero()
self._bollinger_multiplier = self.Param("BollingerMultiplier", 1.1) \
.SetDisplay("BB Mult", "Volatility multiplier for the breakout band", "Bands") \
.SetGreaterThanZero()
self._entry_confirm_bars = self.Param("EntryConfirmBars", 1) \
.SetDisplay("Entry Confirm Bars", "Bars for entry confirmation", "Trading") \
.SetGreaterThanZero()
self._exit_confirm_bars = self.Param("ExitConfirmBars", 1) \
.SetDisplay("Exit Confirm Bars", "Bars for exit confirmation", "Trading") \
.SetGreaterThanZero()
self._signal_cooldown_bars = self.Param("SignalCooldownBars", 8) \
.SetDisplay("Signal Cooldown", "Bars to wait before accepting a new signal", "Trading") \
.SetGreaterThanZero()
self._entry_counter = 0
self._exit_counter = 0
self._cooldown_remaining = 0
self._was_above_entry_level = False
@property
def candle_type(self):
return self._candle_type.Value
@candle_type.setter
def candle_type(self, value):
self._candle_type.Value = value
def OnReseted(self):
super(multi_band_comparison_strategy, self).OnReseted()
self._entry_counter = 0
self._exit_counter = 0
self._cooldown_remaining = 0
self._was_above_entry_level = False
def OnStarted2(self, time):
super(multi_band_comparison_strategy, self).OnStarted2(time)
self._entry_counter = 0
self._exit_counter = 0
self._cooldown_remaining = 0
self._was_above_entry_level = False
self._sma = SimpleMovingAverage()
self._sma.Length = self._length.Value
self._std = StandardDeviation()
self._std.Length = self._length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._sma, self._std, self.OnProcess).Start()
def OnProcess(self, candle, sma_value, std_value):
if candle.State != CandleStates.Finished:
return
sv = float(sma_value)
sdv = float(std_value)
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
if sdv <= 0.0:
return
bm = float(self._bollinger_multiplier.Value)
entry_level = sv + sdv * bm
exit_level = sv
close = float(candle.ClosePrice)
is_above_entry_level = close > entry_level
crossed_up = not self._was_above_entry_level and is_above_entry_level
crossed_down = self._was_above_entry_level and close < exit_level
self._entry_counter = self._entry_counter + 1 if crossed_up else 0
self._exit_counter = self._exit_counter + 1 if crossed_down else 0
ecb = self._entry_confirm_bars.Value
xcb = self._exit_confirm_bars.Value
scb = self._signal_cooldown_bars.Value
if self.Position <= 0 and self._cooldown_remaining == 0 and self._entry_counter >= ecb:
self.BuyMarket()
self._entry_counter = 0
self._cooldown_remaining = scb
elif self.Position > 0 and self._exit_counter >= xcb:
self.SellMarket()
self._exit_counter = 0
self._cooldown_remaining = scb
self._was_above_entry_level = is_above_entry_level
def CreateClone(self):
return multi_band_comparison_strategy()