MA Crossover Demand Supply Zones SLTP Strategy
This strategy combines a short/long simple moving average crossover with demand and supply zone detection. The system looks for crossovers occurring near recently confirmed demand or supply zones, then enters in the direction of the crossover and manages the position with fixed-percent stop loss and take profit.
Details
- Entry Criteria:
- Long: short SMA crosses above long SMA near a demand zone.
- Short: short SMA crosses below long SMA near a supply zone.
- Long/Short: Both sides.
- Exit Criteria:
- Price hits take profit or stop loss levels.
- Stops: Percent-based stop loss and take profit.
- Default Values:
ShortMaLength= 9LongMaLength= 21ZoneLookback= 50ZoneStrength= 2StopLossPercent= 1TakeProfitPercent= 2
- Filters:
- Category: Trend following
- Direction: Both
- Indicators: SMA, Highest, Lowest
- Stops: Yes
- Complexity: Basic
- Timeframe: Any
- 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>
/// MA crossover with demand/supply zone proximity and SL/TP.
/// </summary>
public class MaCrossoverDemandSupplyZonesSltpStrategy : Strategy
{
private readonly StrategyParam<int> _shortMaLength;
private readonly StrategyParam<int> _longMaLength;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<decimal> _takeProfitPercent;
private readonly StrategyParam<DataType> _candleType;
private ExponentialMovingAverage _shortMa;
private ExponentialMovingAverage _longMa;
private decimal _prevShort;
private decimal _prevLong;
private bool _initialized;
private decimal _entryPrice;
private int _cooldown;
public int ShortMaLength { get => _shortMaLength.Value; set => _shortMaLength.Value = value; }
public int LongMaLength { get => _longMaLength.Value; set => _longMaLength.Value = value; }
public decimal StopLossPercent { get => _stopLossPercent.Value; set => _stopLossPercent.Value = value; }
public decimal TakeProfitPercent { get => _takeProfitPercent.Value; set => _takeProfitPercent.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public MaCrossoverDemandSupplyZonesSltpStrategy()
{
_shortMaLength = Param(nameof(ShortMaLength), 9).SetGreaterThanZero()
.SetDisplay("Short MA", "Short MA period", "Indicators");
_longMaLength = Param(nameof(LongMaLength), 21).SetGreaterThanZero()
.SetDisplay("Long MA", "Long MA period", "Indicators");
_stopLossPercent = Param(nameof(StopLossPercent), 7m).SetGreaterThanZero()
.SetDisplay("SL %", "Stop loss percent", "Risk");
_takeProfitPercent = Param(nameof(TakeProfitPercent), 10m).SetGreaterThanZero()
.SetDisplay("TP %", "Take profit percent", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(20).TimeFrame())
.SetDisplay("Candle Type", "Candles", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevShort = default;
_prevLong = default;
_initialized = false;
_entryPrice = default;
_cooldown = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_shortMa = new ExponentialMovingAverage { Length = ShortMaLength };
_longMa = new ExponentialMovingAverage { Length = LongMaLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_shortMa, _longMa, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _shortMa);
DrawIndicator(area, _longMa);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal shortMa, decimal longMa)
{
if (candle.State != CandleStates.Finished)
return;
if (!_shortMa.IsFormed || !_longMa.IsFormed)
return;
if (!_initialized)
{
_prevShort = shortMa;
_prevLong = longMa;
_initialized = true;
return;
}
if (_cooldown > 0)
{
_cooldown--;
_prevShort = shortMa;
_prevLong = longMa;
return;
}
var crossUp = _prevShort <= _prevLong && shortMa > longMa;
var crossDown = _prevShort >= _prevLong && shortMa < longMa;
if (crossUp && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
_entryPrice = candle.ClosePrice;
_cooldown = 10;
}
else if (crossDown && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
_entryPrice = candle.ClosePrice;
_cooldown = 10;
}
// SL/TP for long
if (Position > 0 && _entryPrice > 0)
{
var sl = _entryPrice * (1m - StopLossPercent / 100m);
var tp = _entryPrice * (1m + TakeProfitPercent / 100m);
if (candle.ClosePrice <= sl || candle.ClosePrice >= tp)
{
SellMarket();
_entryPrice = 0;
_cooldown = 15;
}
}
// SL/TP for short
else if (Position < 0 && _entryPrice > 0)
{
var sl = _entryPrice * (1m + StopLossPercent / 100m);
var tp = _entryPrice * (1m - TakeProfitPercent / 100m);
if (candle.ClosePrice >= sl || candle.ClosePrice <= tp)
{
BuyMarket();
_entryPrice = 0;
_cooldown = 15;
}
}
_prevShort = shortMa;
_prevLong = longMa;
}
}
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 ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class ma_crossover_demand_supply_zones_sltp_strategy(Strategy):
def __init__(self):
super(ma_crossover_demand_supply_zones_sltp_strategy, self).__init__()
self._short_ma_length = self.Param("ShortMaLength", 9) \
.SetGreaterThanZero() \
.SetDisplay("Short MA", "Short MA period", "Indicators")
self._long_ma_length = self.Param("LongMaLength", 21) \
.SetGreaterThanZero() \
.SetDisplay("Long MA", "Long MA period", "Indicators")
self._stop_loss_percent = self.Param("StopLossPercent", 7.0) \
.SetGreaterThanZero() \
.SetDisplay("SL %", "Stop loss percent", "Risk")
self._take_profit_percent = self.Param("TakeProfitPercent", 10.0) \
.SetGreaterThanZero() \
.SetDisplay("TP %", "Take profit percent", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(20))) \
.SetDisplay("Candle Type", "Candles", "General")
self._prev_short = 0.0
self._prev_long = 0.0
self._initialized = False
self._entry_price = 0.0
self._cooldown = 0
@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(ma_crossover_demand_supply_zones_sltp_strategy, self).OnReseted()
self._prev_short = 0.0
self._prev_long = 0.0
self._initialized = False
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(ma_crossover_demand_supply_zones_sltp_strategy, self).OnStarted2(time)
self._prev_short = 0.0
self._prev_long = 0.0
self._initialized = False
self._entry_price = 0.0
self._cooldown = 0
self._short_ma = ExponentialMovingAverage()
self._short_ma.Length = self._short_ma_length.Value
self._long_ma = ExponentialMovingAverage()
self._long_ma.Length = self._long_ma_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._short_ma, self._long_ma, self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, self._short_ma)
self.DrawIndicator(area, self._long_ma)
self.DrawOwnTrades(area)
def OnProcess(self, candle, short_ma, long_ma):
if candle.State != CandleStates.Finished:
return
if not self._short_ma.IsFormed or not self._long_ma.IsFormed:
return
sv = float(short_ma)
lv = float(long_ma)
if not self._initialized:
self._prev_short = sv
self._prev_long = lv
self._initialized = True
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_short = sv
self._prev_long = lv
return
close = float(candle.ClosePrice)
cross_up = self._prev_short <= self._prev_long and sv > lv
cross_down = self._prev_short >= self._prev_long and sv < lv
if cross_up and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 10
elif cross_down and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 10
sl_pct = float(self._stop_loss_percent.Value) / 100.0
tp_pct = float(self._take_profit_percent.Value) / 100.0
if self.Position > 0 and self._entry_price > 0.0:
sl = self._entry_price * (1.0 - sl_pct)
tp = self._entry_price * (1.0 + tp_pct)
if close <= sl or close >= tp:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 15
elif self.Position < 0 and self._entry_price > 0.0:
sl = self._entry_price * (1.0 + sl_pct)
tp = self._entry_price * (1.0 - tp_pct)
if close >= sl or close <= tp:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 15
self._prev_short = sv
self._prev_long = lv
def CreateClone(self):
return ma_crossover_demand_supply_zones_sltp_strategy()