SMC Strategy
SMC Strategy defines premium, equilibrium, and discount zones from recent swing highs and lows. It trades in discount or premium zones with an SMA trend filter and simple order block confirmation.
Details
- Entry Criteria: price in discount zone above SMA with order block support; price in premium zone below SMA with order block resistance
- Long/Short: Both
- Exit Criteria: opposite signal
- Stops: No
- Default Values:
SwingHighLength= 8SwingLowLength= 8SmaLength= 50OrderBlockLength= 20
- Filters:
- Category: Zone
- Direction: Both
- Indicators: Highest, Lowest, SMA
- Stops: No
- Complexity: Basic
- Timeframe: Intraday
- Seasonality: No
- Neural networks: No
- Divergence: No
- Risk level: Medium
using System;
using System.Collections.Generic;
using System.Linq;
using Ecng.Common;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Premium/discount SMC zone strategy with SMA and simple order-block confirmation.
/// </summary>
public class SmcStrategy : Strategy
{
private readonly StrategyParam<int> _swingHighLength;
private readonly StrategyParam<int> _swingLowLength;
private readonly StrategyParam<int> _smaLength;
private readonly StrategyParam<int> _orderBlockLength;
private readonly StrategyParam<DataType> _candleType;
private readonly List<ICandleMessage> _candles = [];
public int SwingHighLength { get => _swingHighLength.Value; set => _swingHighLength.Value = value; }
public int SwingLowLength { get => _swingLowLength.Value; set => _swingLowLength.Value = value; }
public int SmaLength { get => _smaLength.Value; set => _smaLength.Value = value; }
public int OrderBlockLength { get => _orderBlockLength.Value; set => _orderBlockLength.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public SmcStrategy()
{
_swingHighLength = Param(nameof(SwingHighLength), 8).SetGreaterThanZero();
_swingLowLength = Param(nameof(SwingLowLength), 8).SetGreaterThanZero();
_smaLength = Param(nameof(SmaLength), 50).SetGreaterThanZero();
_orderBlockLength = Param(nameof(OrderBlockLength), 20).SetGreaterThanZero();
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame());
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
protected override void OnReseted()
{
base.OnReseted();
_candles.Clear();
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
SubscribeCandles(CandleType).Bind(ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
_candles.Add(candle);
var required = Math.Max(Math.Max(SwingHighLength, SwingLowLength), Math.Max(SmaLength, OrderBlockLength)) + 1;
if (_candles.Count > required)
_candles.RemoveRange(0, _candles.Count - required);
if (_candles.Count < required)
return;
var previous = _candles.Take(_candles.Count - 1).ToArray();
var swingHigh = previous.Skip(previous.Length - SwingHighLength).Max(c => c.HighPrice);
var swingLow = previous.Skip(previous.Length - SwingLowLength).Min(c => c.LowPrice);
var sma = _candles.Skip(_candles.Count - SmaLength).Average(c => c.ClosePrice);
var orderBlock = previous.Skip(previous.Length - OrderBlockLength).ToArray();
var support = orderBlock.Min(c => c.LowPrice);
var resistance = orderBlock.Max(c => c.HighPrice);
var hasSupport = candle.LowPrice <= support && candle.ClosePrice >= support;
var hasResistance = candle.HighPrice >= resistance && candle.ClosePrice <= resistance;
var signal = GetSignal(
candle.ClosePrice, swingLow, swingHigh, sma, hasSupport, hasResistance);
if (signal > 0 && Position <= 0m)
BuyMarket(Volume + Math.Abs(Position));
else if (signal < 0 && Position >= 0m)
SellMarket(Volume + Math.Abs(Position));
}
internal static int GetSignal(
decimal price,
decimal swingLow,
decimal swingHigh,
decimal sma,
bool hasSupport,
bool hasResistance)
{
if (swingHigh <= swingLow)
return 0;
var equilibrium = (swingHigh + swingLow) / 2m;
if (price <= equilibrium && price > sma && hasSupport)
return 1;
if (price >= equilibrium && price < sma && hasResistance)
return -1;
return 0;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Strategies import Strategy
class smc_strategy(Strategy):
def __init__(self):
super(smc_strategy, self).__init__()
self._swing_high_length = self.Param("SwingHighLength", 8).SetGreaterThanZero()
self._swing_low_length = self.Param("SwingLowLength", 8).SetGreaterThanZero()
self._sma_length = self.Param("SmaLength", 50).SetGreaterThanZero()
self._order_block_length = self.Param("OrderBlockLength", 20).SetGreaterThanZero()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5)))
self._candles = []
def GetWorkingSecurities(self):
return [(self.Security, self._candle_type.Value)]
def OnReseted(self):
super(smc_strategy, self).OnReseted()
self._candles = []
def OnStarted2(self, time):
super(smc_strategy, self).OnStarted2(time)
self.SubscribeCandles(self._candle_type.Value).Bind(self._process_candle).Start()
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
self._candles.append(candle)
required = max(
int(self._swing_high_length.Value),
int(self._swing_low_length.Value),
int(self._sma_length.Value),
int(self._order_block_length.Value)) + 1
if len(self._candles) > required:
del self._candles[:-required]
if len(self._candles) < required:
return
previous = self._candles[:-1]
sh = int(self._swing_high_length.Value)
sl = int(self._swing_low_length.Value)
sma_len = int(self._sma_length.Value)
ob_len = int(self._order_block_length.Value)
swing_high = max(float(c.HighPrice) for c in previous[-sh:])
swing_low = min(float(c.LowPrice) for c in previous[-sl:])
sma = sum(float(c.ClosePrice) for c in self._candles[-sma_len:]) / sma_len
order_block = previous[-ob_len:]
support = min(float(c.LowPrice) for c in order_block)
resistance = max(float(c.HighPrice) for c in order_block)
close = float(candle.ClosePrice)
has_support = float(candle.LowPrice) <= support and close >= support
has_resistance = float(candle.HighPrice) >= resistance and close <= resistance
signal = self.get_signal(close, swing_low, swing_high, sma, has_support, has_resistance)
if signal > 0 and self.Position <= 0:
self.BuyMarket(self.Volume + Math.Abs(self.Position))
elif signal < 0 and self.Position >= 0:
self.SellMarket(self.Volume + Math.Abs(self.Position))
@staticmethod
def get_signal(price, swing_low, swing_high, sma, has_support, has_resistance):
if swing_high <= swing_low:
return 0
equilibrium = (swing_high + swing_low) / 2.0
if price <= equilibrium and price > sma and has_support:
return 1
if price >= equilibrium and price < sma and has_resistance:
return -1
return 0
def CreateClone(self):
return smc_strategy()