Aggressive High IV Strategy
Aggressive High IV Strategy combines EMA crossovers with an ATR volatility filter. Trades are opened only when volatility exceeds its mean by one standard deviation and closed with ATR based targets.
Testing indicates solid returns in highly volatile markets.
The strategy enters on EMA crossovers during heightened volatility, seeking quick gains with predefined risk controls.
Positions are closed using ATR-based stop-loss and take-profit levels.
Details
- Entry Criteria: Fast EMA crosses slow EMA with ATR above its mean plus standard deviation.
- Long/Short: Both.
- Exit Criteria: ATR-based stop-loss or take-profit hit.
- Stops: Yes.
- Default Values:
FastEmaLength= 10SlowEmaLength= 30AtrLength= 14AtrMeanLength= 20AtrStdLength= 20RiskFactor= 0.01mCandleType= TimeSpan.FromMinutes(15)
- Filters:
- Category: Trend
- Direction: Both
- Indicators: EMA, ATR
- Stops: Yes
- Complexity: Intermediate
- Timeframe: Intraday
- Seasonality: No
- Neural Networks: No
- Divergence: No
- Risk Level: High
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>
/// Aggressive High IV strategy.
/// Trades fast/slow EMA crossovers only while ATR is above its mean plus one standard deviation. A bullish cross goes long and
/// a bearish cross goes short, reversing an opposite position. Each position is closed by an ATR stop-loss or take-profit
/// measured from the entry close.
/// </summary>
public class AggressiveHighIvStrategy : Strategy
{
private const decimal _stopAtrMultiple = 2m;
private const decimal _takeAtrMultiple = 4m;
private readonly StrategyParam<int> _fastEmaLength;
private readonly StrategyParam<int> _slowEmaLength;
private readonly StrategyParam<int> _atrLength;
private readonly StrategyParam<int> _atrMeanLength;
private readonly StrategyParam<int> _atrStdLength;
private readonly StrategyParam<decimal> _riskFactor;
private readonly StrategyParam<DataType> _candleType;
private SimpleMovingAverage _atrMean;
private StandardDeviation _atrStd;
private decimal? _prevFast;
private decimal? _prevSlow;
private decimal _stopPrice;
private decimal _takePrice;
/// <summary>
/// Fast EMA period.
/// </summary>
public int FastEmaLength
{
get => _fastEmaLength.Value;
set => _fastEmaLength.Value = value;
}
/// <summary>
/// Slow EMA period.
/// </summary>
public int SlowEmaLength
{
get => _slowEmaLength.Value;
set => _slowEmaLength.Value = value;
}
/// <summary>
/// ATR period.
/// </summary>
public int AtrLength
{
get => _atrLength.Value;
set => _atrLength.Value = value;
}
/// <summary>
/// Period of the ATR mean.
/// </summary>
public int AtrMeanLength
{
get => _atrMeanLength.Value;
set => _atrMeanLength.Value = value;
}
/// <summary>
/// Period of the ATR standard deviation.
/// </summary>
public int AtrStdLength
{
get => _atrStdLength.Value;
set => _atrStdLength.Value = value;
}
/// <summary>
/// Fraction of equity risked per trade (informational).
/// </summary>
public decimal RiskFactor
{
get => _riskFactor.Value;
set => _riskFactor.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public AggressiveHighIvStrategy()
{
_fastEmaLength = Param(nameof(FastEmaLength), 10)
.SetGreaterThanZero()
.SetDisplay("Fast EMA Length", "Fast EMA period", "Indicators");
_slowEmaLength = Param(nameof(SlowEmaLength), 30)
.SetGreaterThanZero()
.SetDisplay("Slow EMA Length", "Slow EMA period", "Indicators");
_atrLength = Param(nameof(AtrLength), 14)
.SetGreaterThanZero()
.SetDisplay("ATR Length", "ATR period", "Volatility");
_atrMeanLength = Param(nameof(AtrMeanLength), 20)
.SetGreaterThanZero()
.SetDisplay("ATR Mean Length", "Period of the ATR mean", "Volatility");
_atrStdLength = Param(nameof(AtrStdLength), 20)
.SetGreaterThanZero()
.SetDisplay("ATR Std Length", "Period of the ATR standard deviation", "Volatility");
_riskFactor = Param(nameof(RiskFactor), 0.01m)
.SetNotNegative()
.SetDisplay("Risk Factor", "Fraction of equity risked per trade", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_atrMean = null;
_atrStd = null;
ResetState();
}
private void ResetState()
{
_prevFast = null;
_prevSlow = null;
_stopPrice = 0m;
_takePrice = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ResetState();
var fastEma = new ExponentialMovingAverage { Length = FastEmaLength };
var slowEma = new ExponentialMovingAverage { Length = SlowEmaLength };
var atr = new AverageTrueRange { Length = AtrLength };
_atrMean = new SimpleMovingAverage { Length = AtrMeanLength };
_atrStd = new StandardDeviation { Length = AtrStdLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fastEma, slowEma, atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fastEma);
DrawIndicator(area, slowEma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow, decimal atr)
{
if (candle.State != CandleStates.Finished)
return;
var atrMean = _atrMean.Process(atr, candle.ServerTime, true).ToDecimal();
var atrStd = _atrStd.Process(atr, candle.ServerTime, true).ToDecimal();
var prevFast = _prevFast;
var prevSlow = _prevSlow;
_prevFast = fast;
_prevSlow = slow;
if (prevFast is not decimal pf || prevSlow is not decimal ps || !_atrMean.IsFormed || !_atrStd.IsFormed)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (CheckExits(candle))
return;
var highVolatility = atr > atrMean + atrStd;
var crossUp = pf <= ps && fast > slow;
var crossDown = pf >= ps && fast < slow;
var close = candle.ClosePrice;
if (highVolatility && crossUp && Position <= 0)
{
BuyMarket(Volume + Math.Abs(Position));
_stopPrice = close - _stopAtrMultiple * atr;
_takePrice = close + _takeAtrMultiple * atr;
}
else if (highVolatility && crossDown && Position >= 0)
{
SellMarket(Volume + Math.Abs(Position));
_stopPrice = close + _stopAtrMultiple * atr;
_takePrice = close - _takeAtrMultiple * atr;
}
}
private bool CheckExits(ICandleMessage candle)
{
if (Position > 0 && _stopPrice > 0m && (candle.LowPrice <= _stopPrice || candle.HighPrice >= _takePrice))
{
SellMarket(Position);
_stopPrice = 0m;
_takePrice = 0m;
return true;
}
if (Position < 0 && _stopPrice > 0m && (candle.HighPrice >= _stopPrice || candle.LowPrice <= _takePrice))
{
BuyMarket(-Position);
_stopPrice = 0m;
_takePrice = 0m;
return true;
}
return false;
}
}
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, AverageTrueRange, SimpleMovingAverage, StandardDeviation
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
STOP_ATR_MULTIPLE = 2.0
TAKE_ATR_MULTIPLE = 4.0
class aggressive_high_iv_strategy(Strategy):
"""
Aggressive High IV strategy.
Trades fast/slow EMA crossovers only while ATR is above its mean plus one standard deviation. A bullish cross goes long and
a bearish cross goes short, reversing an opposite position. Each position is closed by an ATR stop-loss or take-profit
measured from the entry close.
"""
def __init__(self):
super(aggressive_high_iv_strategy, self).__init__()
self._fast_ema_length = self.Param("FastEmaLength", 10) \
.SetGreaterThanZero() \
.SetDisplay("Fast EMA Length", "Fast EMA period", "Indicators")
self._slow_ema_length = self.Param("SlowEmaLength", 30) \
.SetGreaterThanZero() \
.SetDisplay("Slow EMA Length", "Slow EMA period", "Indicators")
self._atr_length = self.Param("AtrLength", 14) \
.SetGreaterThanZero() \
.SetDisplay("ATR Length", "ATR period", "Volatility")
self._atr_mean_length = self.Param("AtrMeanLength", 20) \
.SetGreaterThanZero() \
.SetDisplay("ATR Mean Length", "Period of the ATR mean", "Volatility")
self._atr_std_length = self.Param("AtrStdLength", 20) \
.SetGreaterThanZero() \
.SetDisplay("ATR Std Length", "Period of the ATR standard deviation", "Volatility")
self._risk_factor = self.Param("RiskFactor", 0.01) \
.SetNotNegative() \
.SetDisplay("Risk Factor", "Fraction of equity risked per trade", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._atr_mean = None
self._atr_std = None
self._reset_state()
@property
def CandleType(self):
return self._candle_type.Value
def GetWorkingSecurities(self):
return [(self.Security, self.CandleType)]
def _reset_state(self):
self._prev_fast = None
self._prev_slow = None
self._stop_price = 0.0
self._take_price = 0.0
def OnReseted(self):
super(aggressive_high_iv_strategy, self).OnReseted()
self._atr_mean = None
self._atr_std = None
self._reset_state()
def OnStarted2(self, time):
super(aggressive_high_iv_strategy, self).OnStarted2(time)
self._reset_state()
fast_ema = ExponentialMovingAverage()
fast_ema.Length = self._fast_ema_length.Value
slow_ema = ExponentialMovingAverage()
slow_ema.Length = self._slow_ema_length.Value
atr = AverageTrueRange()
atr.Length = self._atr_length.Value
self._atr_mean = SimpleMovingAverage()
self._atr_mean.Length = self._atr_mean_length.Value
self._atr_std = StandardDeviation()
self._atr_std.Length = self._atr_std_length.Value
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(fast_ema, slow_ema, atr, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast_ema)
self.DrawIndicator(area, slow_ema)
self.DrawOwnTrades(area)
def _process_candle(self, candle, fast_value, slow_value, atr_value):
if candle.State != CandleStates.Finished:
return
fast = float(fast_value)
slow = float(slow_value)
atr = float(atr_value)
atr_mean = float(process_float(self._atr_mean, atr, candle.ServerTime, True))
atr_std = float(process_float(self._atr_std, atr, candle.ServerTime, True))
prev_fast = self._prev_fast
prev_slow = self._prev_slow
self._prev_fast = fast
self._prev_slow = slow
if prev_fast is None or prev_slow is None or not self._atr_mean.IsFormed or not self._atr_std.IsFormed:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
if self._check_exits(candle):
return
high_volatility = atr > atr_mean + atr_std
cross_up = prev_fast <= prev_slow and fast > slow
cross_down = prev_fast >= prev_slow and fast < slow
close = float(candle.ClosePrice)
if high_volatility and cross_up and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
self._stop_price = close - STOP_ATR_MULTIPLE * atr
self._take_price = close + TAKE_ATR_MULTIPLE * atr
elif high_volatility and cross_down and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
self._stop_price = close + STOP_ATR_MULTIPLE * atr
self._take_price = close - TAKE_ATR_MULTIPLE * atr
def _check_exits(self, candle):
high = float(candle.HighPrice)
low = float(candle.LowPrice)
if self.Position > 0 and self._stop_price > 0 and (low <= self._stop_price or high >= self._take_price):
self.SellMarket(self.Position)
self._stop_price = 0.0
self._take_price = 0.0
return True
if self.Position < 0 and self._stop_price > 0 and (high >= self._stop_price or low <= self._take_price):
self.BuyMarket(-self.Position)
self._stop_price = 0.0
self._take_price = 0.0
return True
return False
def CreateClone(self):
return aggressive_high_iv_strategy()