Estratégia Malr de Rompimento de Canal
Esta estratégia negocia rompimentos de um canal MALR (Moving Average Linear Regression) personalizado. O indicador MALR combina uma média móvel simples e uma média móvel linear ponderada para formar uma linha central. O desvio padrão do preço em relação a essa linha cria duas bandas externas.
Uma posição comprada é aberta quando a banda superior de rompimento cruza abaixo do preço de fechamento, indicando um rompimento para cima. Uma posição vendida é aberta quando a banda inferior de rompimento cruza acima do preço de fechamento, sinalizando um rompimento para baixo.
Parâmetros
MaPeriod– período para as médias móveis e o desvio padrão.ChannelReversal– largura do canal MALR interno medida em desvios padrão.ChannelBreakout– largura adicional para o canal externo de rompimento.CandleType– tipo de velas utilizado para os cálculos.
Como funciona
- Calcular SMA e LWMA do preço de fechamento.
- Calcular a linha MALR
FF = 3 * LWMA - 2 * SMA. - Medir o desvio padrão de
close - FFsobre o mesmo período. - Derivar bandas de rompimento:
FF ± StdDev * (ChannelReversal + ChannelBreakout). - Entrar comprado quando a banda superior cruza de cima para baixo do fechamento.
- Entrar vendido quando a banda inferior cruza de baixo para cima do fechamento.
A estratégia sempre fecha a posição oposta antes de abrir uma nova.
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()