Market Slayer Strategy
This strategy uses a weighted moving average crossover with a higher timeframe SSL trend confirmation. A long position is opened when the short WMA crosses above the long WMA while the trend is bullish; a short position is opened on the opposite conditions. Optional absolute take profit and stop loss can be enabled.
Details
- Entry Criteria:
- Long: short WMA crosses above long WMA and the higher timeframe SSL is bullish.
- Short: short WMA crosses below long WMA and the higher timeframe SSL is bearish.
- Long/Short: Both sides.
- Exit Criteria:
- Trend filter turns opposite.
- Optional stop loss or take profit when enabled.
- Stops: Optional.
- Default Values:
ShortLength= 10.LongLength= 20.ConfirmationTrendValue= 2.CandleType= TimeSpan.FromMinutes(5).TimeFrame().TrendCandleType= TimeSpan.FromMinutes(240).TimeFrame().TakeProfitEnabled= false.TakeProfitValue= 20.StopLossEnabled= false.StopLossValue= 50.
- Filters:
- Category: Trend following
- Direction: Both
- Indicators: WMA, SSL
- Stops: Optional
- Complexity: Basic
- Timeframe: Intraday
- Seasonality: No
- Neural networks: No
- Divergence: No
- Risk level: Medium
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>
/// Market Slayer strategy.
/// The SSL channel on TrendCandleType is built from WMAs of highs and lows over ConfirmationTrendValue candles: a close above the
/// high WMA makes the trend bullish, a close below the low WMA makes it bearish, otherwise the previous state is kept. On CandleType
/// the short WMA crossing above the long WMA in a bullish trend goes long and crossing below in a bearish trend goes short. A position
/// closes when the trend turns opposite. Optional take profit and stop loss are distances in price steps.
/// </summary>
public class MarketSlayerStrategy : Strategy
{
private readonly StrategyParam<int> _shortLength;
private readonly StrategyParam<int> _longLength;
private readonly StrategyParam<int> _confirmationTrendValue;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<DataType> _trendCandleType;
private readonly StrategyParam<bool> _takeProfitEnabled;
private readonly StrategyParam<decimal> _takeProfitValue;
private readonly StrategyParam<bool> _stopLossEnabled;
private readonly StrategyParam<decimal> _stopLossValue;
private WeightedMovingAverage _trendHigh;
private WeightedMovingAverage _trendLow;
private int _trendHlv;
private decimal? _prevShort;
private decimal? _prevLong;
/// <summary>
/// Short WMA length.
/// </summary>
public int ShortLength
{
get => _shortLength.Value;
set => _shortLength.Value = value;
}
/// <summary>
/// Long WMA length.
/// </summary>
public int LongLength
{
get => _longLength.Value;
set => _longLength.Value = value;
}
/// <summary>
/// SSL WMA length on the trend timeframe.
/// </summary>
public int ConfirmationTrendValue
{
get => _confirmationTrendValue.Value;
set => _confirmationTrendValue.Value = value;
}
/// <summary>
/// Signal candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Trend candle type.
/// </summary>
public DataType TrendCandleType
{
get => _trendCandleType.Value;
set => _trendCandleType.Value = value;
}
/// <summary>
/// Enable take profit.
/// </summary>
public bool TakeProfitEnabled
{
get => _takeProfitEnabled.Value;
set => _takeProfitEnabled.Value = value;
}
/// <summary>
/// Take profit distance in price steps.
/// </summary>
public decimal TakeProfitValue
{
get => _takeProfitValue.Value;
set => _takeProfitValue.Value = value;
}
/// <summary>
/// Enable stop loss.
/// </summary>
public bool StopLossEnabled
{
get => _stopLossEnabled.Value;
set => _stopLossEnabled.Value = value;
}
/// <summary>
/// Stop loss distance in price steps.
/// </summary>
public decimal StopLossValue
{
get => _stopLossValue.Value;
set => _stopLossValue.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public MarketSlayerStrategy()
{
_shortLength = Param(nameof(ShortLength), 10)
.SetGreaterThanZero()
.SetDisplay("Short Length", "Short WMA length", "Indicators");
_longLength = Param(nameof(LongLength), 20)
.SetGreaterThanZero()
.SetDisplay("Long Length", "Long WMA length", "Indicators");
_confirmationTrendValue = Param(nameof(ConfirmationTrendValue), 2)
.SetGreaterThanZero()
.SetDisplay("Trend Length", "SSL WMA length on the trend timeframe", "Trend");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Signal candle type", "General");
_trendCandleType = Param(nameof(TrendCandleType), TimeSpan.FromMinutes(240).TimeFrame())
.SetDisplay("Trend Candle Type", "Higher timeframe for the SSL trend", "Trend");
_takeProfitEnabled = Param(nameof(TakeProfitEnabled), false)
.SetDisplay("Use Take Profit", "Enable take profit", "Risk");
_takeProfitValue = Param(nameof(TakeProfitValue), 20m)
.SetNotNegative()
.SetDisplay("Take Profit", "Take profit distance in price steps", "Risk");
_stopLossEnabled = Param(nameof(StopLossEnabled), false)
.SetDisplay("Use Stop Loss", "Enable stop loss", "Risk");
_stopLossValue = Param(nameof(StopLossValue), 50m)
.SetNotNegative()
.SetDisplay("Stop Loss", "Stop loss distance in price steps", "Risk");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType), (Security, TrendCandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_trendHigh = null;
_trendLow = null;
_trendHlv = 0;
_prevShort = null;
_prevLong = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_trendHlv = 0;
_prevShort = null;
_prevLong = null;
var shortWma = new WeightedMovingAverage { Length = ShortLength };
var longWma = new WeightedMovingAverage { Length = LongLength };
_trendHigh = new WeightedMovingAverage { Length = ConfirmationTrendValue };
_trendLow = new WeightedMovingAverage { Length = ConfirmationTrendValue };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(shortWma, longWma, ProcessCandle)
.Start();
SubscribeCandles(TrendCandleType)
.Bind(ProcessTrendCandle)
.Start();
if (TakeProfitEnabled || StopLossEnabled)
{
var step = Security?.PriceStep ?? 1m;
StartProtection(
TakeProfitEnabled ? new Unit(TakeProfitValue * step, UnitTypes.Absolute) : new Unit(),
StopLossEnabled ? new Unit(StopLossValue * step, UnitTypes.Absolute) : new Unit(),
useMarketOrders: true);
}
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, shortWma);
DrawIndicator(area, longWma);
DrawOwnTrades(area);
}
}
private void ProcessTrendCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var highValue = _trendHigh.Process(new DecimalIndicatorValue(_trendHigh, candle.HighPrice, candle.OpenTime) { IsFinal = true });
var lowValue = _trendLow.Process(new DecimalIndicatorValue(_trendLow, candle.LowPrice, candle.OpenTime) { IsFinal = true });
if (!_trendHigh.IsFormed || !_trendLow.IsFormed)
return;
var high = highValue.GetValue<decimal>();
var low = lowValue.GetValue<decimal>();
if (candle.ClosePrice > high)
_trendHlv = 1;
else if (candle.ClosePrice < low)
_trendHlv = -1;
}
private void ProcessCandle(ICandleMessage candle, decimal shortValue, decimal longValue)
{
if (candle.State != CandleStates.Finished)
return;
var prevShort = _prevShort;
var prevLong = _prevLong;
_prevShort = shortValue;
_prevLong = longValue;
if (prevShort is not decimal ps || prevLong is not decimal pl)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var bullish = _trendHlv > 0;
var bearish = _trendHlv < 0;
if (Position > 0 && bearish)
{
SellMarket(Position);
return;
}
if (Position < 0 && bullish)
{
BuyMarket(-Position);
return;
}
if (ps <= pl && shortValue > longValue && bullish && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
else if (ps >= pl && shortValue < longValue && bearish && 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 WeightedMovingAverage
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class market_slayer_strategy(Strategy):
"""
Market Slayer strategy.
The SSL channel on TrendCandleType is built from WMAs of highs and lows over ConfirmationTrendValue candles: a close above the
high WMA makes the trend bullish, a close below the low WMA makes it bearish, otherwise the previous state is kept. On CandleType
the short WMA crossing above the long WMA in a bullish trend goes long and crossing below in a bearish trend goes short. A position
closes when the trend turns opposite. Optional take profit and stop loss are distances in price steps.
"""
def __init__(self):
super(market_slayer_strategy, self).__init__()
self._short_length = self.Param("ShortLength", 10).SetGreaterThanZero().SetDisplay("Short Length", "Short WMA length", "Indicators")
self._long_length = self.Param("LongLength", 20).SetGreaterThanZero().SetDisplay("Long Length", "Long WMA length", "Indicators")
self._confirmation_trend_value = self.Param("ConfirmationTrendValue", 2).SetGreaterThanZero().SetDisplay("Trend Length", "SSL WMA length on the trend timeframe", "Trend")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Signal candle type", "General")
self._trend_candle_type = self.Param("TrendCandleType", DataType.TimeFrame(TimeSpan.FromMinutes(240))).SetDisplay("Trend Candle Type", "Higher timeframe for the SSL trend", "Trend")
self._take_profit_enabled = self.Param("TakeProfitEnabled", False).SetDisplay("Use Take Profit", "Enable take profit", "Risk")
self._take_profit_value = self.Param("TakeProfitValue", 20.0).SetNotNegative().SetDisplay("Take Profit", "Take profit distance in price steps", "Risk")
self._stop_loss_enabled = self.Param("StopLossEnabled", False).SetDisplay("Use Stop Loss", "Enable stop loss", "Risk")
self._stop_loss_value = self.Param("StopLossValue", 50.0).SetNotNegative().SetDisplay("Stop Loss", "Stop loss distance in price steps", "Risk")
self._trend_high = None
self._trend_low = None
self._reset_state()
@property
def candle_type(self):
return self._candle_type.Value
@property
def trend_candle_type(self):
return self._trend_candle_type.Value
def GetWorkingSecurities(self):
return [(self.Security, self.candle_type), (self.Security, self.trend_candle_type)]
def _reset_state(self):
self._trend_hlv = 0
self._prev_short = None
self._prev_long = None
def OnReseted(self):
super(market_slayer_strategy, self).OnReseted()
self._trend_high = None
self._trend_low = None
self._reset_state()
def OnStarted2(self, time):
super(market_slayer_strategy, self).OnStarted2(time)
self._reset_state()
short_wma = WeightedMovingAverage()
short_wma.Length = self._short_length.Value
long_wma = WeightedMovingAverage()
long_wma.Length = self._long_length.Value
self._trend_high = WeightedMovingAverage()
self._trend_high.Length = self._confirmation_trend_value.Value
self._trend_low = WeightedMovingAverage()
self._trend_low.Length = self._confirmation_trend_value.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(short_wma, long_wma, self._process_candle).Start()
self.SubscribeCandles(self.trend_candle_type).Bind(self._process_trend_candle).Start()
use_tp = self._take_profit_enabled.Value
use_sl = self._stop_loss_enabled.Value
if use_tp or use_sl:
step = Decimal(1)
if self.Security is not None and self.Security.PriceStep is not None:
step = self.Security.PriceStep
tp = Unit(Decimal(self._take_profit_value.Value) * step, UnitTypes.Absolute) if use_tp else Unit()
sl = Unit(Decimal(self._stop_loss_value.Value) * step, UnitTypes.Absolute) if use_sl else Unit()
self.StartProtection(tp, sl, useMarketOrders=True)
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, short_wma)
self.DrawIndicator(area, long_wma)
self.DrawOwnTrades(area)
def _process_trend_candle(self, candle):
if candle.State != CandleStates.Finished:
return
high_value = process_value(self._trend_high, candle.HighPrice, candle.OpenTime, True)
low_value = process_value(self._trend_low, candle.LowPrice, candle.OpenTime, True)
if not self._trend_high.IsFormed or not self._trend_low.IsFormed:
return
high = high_value.GetValue[Decimal](None)
low = low_value.GetValue[Decimal](None)
if candle.ClosePrice > high:
self._trend_hlv = 1
elif candle.ClosePrice < low:
self._trend_hlv = -1
def _process_candle(self, candle, short_value, long_value):
if candle.State != CandleStates.Finished:
return
ps = self._prev_short
pl = self._prev_long
self._prev_short = short_value
self._prev_long = long_value
if ps is None or pl is None:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
bullish = self._trend_hlv > 0
bearish = self._trend_hlv < 0
if self.Position > 0 and bearish:
self.SellMarket(self.Position)
return
if self.Position < 0 and bullish:
self.BuyMarket(-self.Position)
return
if ps <= pl and short_value > long_value and bullish and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
elif ps >= pl and short_value < long_value and bearish and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
def CreateClone(self):
return market_slayer_strategy()