EM VOL Strategy
This strategy trades breakouts around pivot-based support and resistance levels. It calculates yesterday's high and low plus an ATR buffer to form entry triggers. Trades are only placed when the ADX indicator signals a low volatility environment.
Logic
- Compute the previous bar's pivot and add/subtract ATR to obtain resistance and support.
- If ADX is below the threshold and price closes above resistance, enter a long position.
- If price closes below support, enter a short position.
- After entry, protective stop and take-profit orders are placed.
- A trailing stop can tighten the protective stop once profit reaches the specified level.
Parameters
TakeProfit— take-profit distance in price steps.StopLoss— stop-loss distance in price steps.AtrPeriod— ATR indicator period.AdxPeriod— ADX indicator period.AdxThreshold— maximum ADX value to allow trading.TrailStart— profit required before trailing stop begins.TrailStep— distance of the trailing stop.CandleType— timeframe used for calculations.
Indicators Used
- Average True Range
- Average Directional Index
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>
/// Pivot breakout strategy with StdDev volatility filter.
/// Enters when price breaks previous high/low + volatility band.
/// </summary>
public class EmVolStrategy : Strategy
{
private readonly StrategyParam<decimal> _takeProfit;
private readonly StrategyParam<decimal> _stopLoss;
private readonly StrategyParam<int> _stdevPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevHigh;
private decimal _prevLow;
private decimal _prevStdev;
private bool _hasPrev;
private decimal _entryPrice;
private decimal _stopPrice;
public decimal TakeProfit { get => _takeProfit.Value; set => _takeProfit.Value = value; }
public decimal StopLoss { get => _stopLoss.Value; set => _stopLoss.Value = value; }
public int StdevPeriod { get => _stdevPeriod.Value; set => _stdevPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public EmVolStrategy()
{
_takeProfit = Param(nameof(TakeProfit), 1000m)
.SetDisplay("Take Profit", "Take profit distance", "Risk");
_stopLoss = Param(nameof(StopLoss), 500m)
.SetDisplay("Stop Loss", "Stop loss distance", "Risk");
_stdevPeriod = Param(nameof(StdevPeriod), 14)
.SetDisplay("StdDev Period", "Volatility period", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Working candle timeframe", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
protected override void OnReseted()
{
base.OnReseted();
_prevHigh = 0;
_prevLow = 0;
_prevStdev = 0;
_hasPrev = false;
_entryPrice = 0;
_stopPrice = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var stdev = new StandardDeviation { Length = StdevPeriod };
SubscribeCandles(CandleType)
.Bind(stdev, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal stdevValue)
{
if (candle.State != CandleStates.Finished) return;
if (!_hasPrev)
{
_prevHigh = candle.HighPrice;
_prevLow = candle.LowPrice;
_prevStdev = stdevValue;
_hasPrev = true;
return;
}
var price = candle.ClosePrice;
var res1 = _prevHigh + _prevStdev;
var sup1 = _prevLow - _prevStdev;
if (Position == 0)
{
if (price > res1)
{
BuyMarket();
_entryPrice = price;
_stopPrice = price - StopLoss;
}
else if (price < sup1)
{
SellMarket();
_entryPrice = price;
_stopPrice = price + StopLoss;
}
}
else if (Position > 0)
{
if (price - _entryPrice >= TakeProfit || price <= _stopPrice)
SellMarket();
}
else if (Position < 0)
{
if (_entryPrice - price >= TakeProfit || price >= _stopPrice)
BuyMarket();
}
_prevHigh = candle.HighPrice;
_prevLow = candle.LowPrice;
_prevStdev = stdevValue;
}
}
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 StandardDeviation
from StockSharp.Algo.Strategies import Strategy
class em_vol_strategy(Strategy):
"""
Pivot breakout strategy with StdDev volatility filter.
Enters when price breaks previous high/low + volatility band.
"""
def __init__(self):
super(em_vol_strategy, self).__init__()
self._take_profit = self.Param("TakeProfit", 1000.0) \
.SetDisplay("Take Profit", "Take profit distance", "Risk")
self._stop_loss = self.Param("StopLoss", 500.0) \
.SetDisplay("Stop Loss", "Stop loss distance", "Risk")
self._stdev_period = self.Param("StdevPeriod", 14) \
.SetDisplay("StdDev Period", "Volatility period", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Working candle timeframe", "General")
self._prev_high = 0.0
self._prev_low = 0.0
self._prev_stdev = 0.0
self._has_prev = False
self._entry_price = 0.0
self._stop_price = 0.0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(em_vol_strategy, self).OnReseted()
self._prev_high = 0.0
self._prev_low = 0.0
self._prev_stdev = 0.0
self._has_prev = False
self._entry_price = 0.0
self._stop_price = 0.0
def OnStarted2(self, time):
super(em_vol_strategy, self).OnStarted2(time)
stdev = StandardDeviation()
stdev.Length = self._stdev_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(stdev, self._process_candle).Start()
def _process_candle(self, candle, stdev_val):
if candle.State != CandleStates.Finished:
return
stdev_val = float(stdev_val)
if not self._has_prev:
self._prev_high = float(candle.HighPrice)
self._prev_low = float(candle.LowPrice)
self._prev_stdev = stdev_val
self._has_prev = True
return
price = float(candle.ClosePrice)
res1 = self._prev_high + self._prev_stdev
sup1 = self._prev_low - self._prev_stdev
if self.Position == 0:
if price > res1:
self.BuyMarket()
self._entry_price = price
self._stop_price = price - self._stop_loss.Value
elif price < sup1:
self.SellMarket()
self._entry_price = price
self._stop_price = price + self._stop_loss.Value
elif self.Position > 0:
if price - self._entry_price >= self._take_profit.Value or price <= self._stop_price:
self.SellMarket()
elif self.Position < 0:
if self._entry_price - price >= self._take_profit.Value or price >= self._stop_price:
self.BuyMarket()
self._prev_high = float(candle.HighPrice)
self._prev_low = float(candle.LowPrice)
self._prev_stdev = stdev_val
def CreateClone(self):
return em_vol_strategy()