Malr Kanalausbruch-Strategie
Diese Strategie handelt Ausbrüche eines benutzerdefinierten MALR (Moving Average Linear Regression)-Kanals. Der MALR-Indikator kombiniert einen einfachen gleitenden Durchschnitt und einen linear gewichteten gleitenden Durchschnitt, um eine Mittellinie zu bilden. Die Standardabweichung des Preises relativ zu dieser Linie erzeugt zwei äußere Bänder.
Eine Long-Position wird eröffnet, wenn das obere Ausbruchsband die Schlusskurslinie von oben nach unten kreuzt, was auf einen Aufwärtsausbruch hindeutet. Eine Short-Position wird eröffnet, wenn das untere Ausbruchsband den Schlusskurs von unten nach oben kreuzt, was einen Abwärtsausbruch signalisiert.
Parameter
MaPeriod – Zeitraum für die gleitenden Durchschnitte und die Standardabweichung.
ChannelReversal – Breite des inneren MALR-Kanals gemessen in Standardabweichungen.
ChannelBreakout – zusätzliche Breite für den äußeren Ausbruchskanal.
CandleType – Kerzentyp, der für Berechnungen verwendet wird.
Funktionsweise
- SMA und LWMA des Schlusskurses berechnen.
- Die MALR-Linie
FF = 3 * LWMA - 2 * SMA berechnen.
- Standardabweichung von
close - FF über denselben Zeitraum messen.
- Ausbruchsbänder ableiten:
FF ± StdDev * (ChannelReversal + ChannelBreakout).
- Long eingehen, wenn das obere Band von oben nach unten unter den Schlusskurs kreuzt.
- Short eingehen, wenn das untere Band von unten nach oben über den Schlusskurs kreuzt.
Die Strategie schließt immer die entgegengesetzte Position, bevor sie eine neue eröffnet.
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>
/// MALR channel breakout strategy.
/// Enters long when price breaks above the upper MALR band and short when breaking below the lower band.
/// </summary>
public class MalrChannelBreakoutStrategy : Strategy
{
private readonly StrategyParam<int> _maPeriod;
private readonly StrategyParam<decimal> _channelReversal;
private readonly StrategyParam<decimal> _channelBreakout;
private readonly StrategyParam<DataType> _candleType;
private SimpleMovingAverage _sma;
private WeightedMovingAverage _lwma;
private StandardDeviation _stdDev;
private decimal? _prevUpper;
private decimal? _prevLower;
private decimal? _prevClose;
public int MaPeriod { get => _maPeriod.Value; set => _maPeriod.Value = value; }
public decimal ChannelReversal { get => _channelReversal.Value; set => _channelReversal.Value = value; }
public decimal ChannelBreakout { get => _channelBreakout.Value; set => _channelBreakout.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public MalrChannelBreakoutStrategy()
{
_maPeriod = Param(nameof(MaPeriod), 120)
.SetGreaterThanZero()
.SetDisplay("MA", "Moving average period", "General")
.SetOptimize(50, 200, 10);
_channelReversal = Param(nameof(ChannelReversal), 1.1m)
.SetGreaterThanZero()
.SetDisplay("Reversal", "Channel reversal width", "General")
.SetOptimize(0.5m, 2m, 0.1m);
_channelBreakout = Param(nameof(ChannelBreakout), 1.1m)
.SetGreaterThanZero()
.SetDisplay("Breakout", "Channel breakout width", "General")
.SetOptimize(0.5m, 2m, 0.1m);
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle", "Candle type", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_sma = default;
_lwma = default;
_stdDev = default;
_prevUpper = null;
_prevLower = null;
_prevClose = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_sma = new SimpleMovingAverage { Length = MaPeriod };
_lwma = new WeightedMovingAverage { Length = MaPeriod };
_stdDev = new StandardDeviation { Length = MaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ProcessCandle).Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _sma);
DrawIndicator(area, _lwma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var smaResult = _sma.Process(candle.ClosePrice, candle.OpenTime, true);
var lwmaResult = _lwma.Process(candle.ClosePrice, candle.OpenTime, true);
if (!smaResult.IsFormed || !lwmaResult.IsFormed)
{
_prevClose = candle.ClosePrice;
return;
}
var smaVal = smaResult.ToDecimal();
var lwmaVal = lwmaResult.ToDecimal();
var ff = 3m * lwmaVal - 2m * smaVal;
var deviation = candle.ClosePrice - ff;
var stdResult = _stdDev.Process(deviation, candle.OpenTime, true);
if (!stdResult.IsFormed)
{
_prevClose = candle.ClosePrice;
_prevUpper = ff;
_prevLower = ff;
return;
}
var std = stdResult.ToDecimal();
var upper = ff + std * (ChannelReversal + ChannelBreakout);
var lower = ff - std * (ChannelReversal + ChannelBreakout);
if (_prevUpper.HasValue && _prevLower.HasValue && _prevClose.HasValue)
{
// Price breaks above upper channel
if (_prevClose.Value <= _prevUpper.Value && candle.ClosePrice > upper && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
}
// Price breaks below lower channel
else if (_prevClose.Value >= _prevLower.Value && candle.ClosePrice < lower && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
}
}
_prevUpper = upper;
_prevLower = lower;
_prevClose = candle.ClosePrice;
}
}
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 System import Decimal
from StockSharp.Algo.Indicators import SimpleMovingAverage, WeightedMovingAverage, StandardDeviation
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class malr_channel_breakout_strategy(Strategy):
def __init__(self):
super(malr_channel_breakout_strategy, self).__init__()
self._ma_period = self.Param("MaPeriod", 120) \
.SetGreaterThanZero() \
.SetDisplay("MA", "Moving average period", "General") \
.SetOptimize(50, 200, 10)
self._channel_reversal = self.Param("ChannelReversal", 1.1) \
.SetGreaterThanZero() \
.SetDisplay("Reversal", "Channel reversal width", "General") \
.SetOptimize(0.5, 2.0, 0.1)
self._channel_breakout = self.Param("ChannelBreakout", 1.1) \
.SetGreaterThanZero() \
.SetDisplay("Breakout", "Channel breakout width", "General") \
.SetOptimize(0.5, 2.0, 0.1)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle", "Candle type", "General")
self._sma = None
self._lwma = None
self._std_dev = None
self._prev_upper = None
self._prev_lower = None
self._prev_close = None
@property
def ma_period(self):
return self._ma_period.Value
@property
def channel_reversal(self):
return self._channel_reversal.Value
@property
def channel_breakout(self):
return self._channel_breakout.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(malr_channel_breakout_strategy, self).OnReseted()
self._prev_upper = None
self._prev_lower = None
self._prev_close = None
def OnStarted2(self, time):
super(malr_channel_breakout_strategy, self).OnStarted2(time)
self._sma = SimpleMovingAverage()
self._sma.Length = self.ma_period
self._lwma = WeightedMovingAverage()
self._lwma.Length = self.ma_period
self._std_dev = StandardDeviation()
self._std_dev.Length = self.ma_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, self._sma)
self.DrawIndicator(area, self._lwma)
self.DrawOwnTrades(area)
def process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
sma_result = process_float(self._sma, candle.ClosePrice, candle.OpenTime, True)
lwma_result = process_float(self._lwma, candle.ClosePrice, candle.OpenTime, True)
if not sma_result.IsFormed or not lwma_result.IsFormed:
self._prev_close = close
return
sma_val = float(sma_result)
lwma_val = float(lwma_result)
ff = 3.0 * lwma_val - 2.0 * sma_val
deviation = close - ff
std_result = process_float(self._std_dev, Decimal(deviation), candle.OpenTime, True)
if not std_result.IsFormed:
self._prev_close = close
self._prev_upper = ff
self._prev_lower = ff
return
std = float(std_result)
cr = float(self.channel_reversal)
cb = float(self.channel_breakout)
upper = ff + std * (cr + cb)
lower = ff - std * (cr + cb)
if self._prev_upper is not None and self._prev_lower is not None and self._prev_close is not None:
if self._prev_close <= self._prev_upper and close > upper and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif self._prev_close >= self._prev_lower and close < lower and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_upper = upper
self._prev_lower = lower
self._prev_close = close
def CreateClone(self):
return malr_channel_breakout_strategy()