Volatile Action Strategy
This strategy combines a short-term volatility breakout with Bill Williams' Alligator trend filter calculated on the 4-hour timeframe.
Trading Rules
- Long entry when:
- The 1-period ATR is greater than Volatility Coef times the ATR with period ATR Period.
- The candle is bullish and sets a new 24-bar high.
- Alligator lines are aligned up (Lips > Teeth > Jaw) and both the open and close are above the Teeth line.
- Short entry when the above conditions are mirrored in the opposite direction.
On entry the strategy sets stop-loss and take-profit levels at multiples of the 1-period ATR:
- Stop-loss = entry price ± Stop Coef × ATR(1)
- Take-profit = entry price ± Profit Coef × ATR(1)
Parameters
- Volatility Coef – multiplier comparing fast ATR to slow ATR.
- ATR Period – period of the slow ATR.
- Stop Coef – ATR multiplier for stop-loss.
- Profit Coef – ATR multiplier for take-profit.
- Candle Type – timeframe for the main analysis (Alligator uses 4H candles).
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>
/// Volatile Action strategy.
/// Combines volatility breakout (ATR) with Alligator trend filter.
/// </summary>
public class VolatileActionStrategy : Strategy
{
private readonly StrategyParam<decimal> _volatilityCoef;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<decimal> _stopLossPct;
private readonly StrategyParam<decimal> _takeProfitPct;
private readonly StrategyParam<DataType> _candleType;
private AverageTrueRange _atr1;
private AverageTrueRange _atrBase;
private SmoothedMovingAverage _jaw;
private SmoothedMovingAverage _teeth;
private SmoothedMovingAverage _lips;
public decimal VolatilityCoef { get => _volatilityCoef.Value; set => _volatilityCoef.Value = value; }
public int AtrPeriod { get => _atrPeriod.Value; set => _atrPeriod.Value = value; }
public decimal StopLossPct { get => _stopLossPct.Value; set => _stopLossPct.Value = value; }
public decimal TakeProfitPct { get => _takeProfitPct.Value; set => _takeProfitPct.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public VolatileActionStrategy()
{
_volatilityCoef = Param(nameof(VolatilityCoef), 1m)
.SetGreaterThanZero()
.SetDisplay("Volatility Coef", "ATR1 multiplier against base ATR", "General");
_atrPeriod = Param(nameof(AtrPeriod), 23)
.SetGreaterThanZero()
.SetDisplay("ATR Period", "Base ATR period", "General");
_stopLossPct = Param(nameof(StopLossPct), 2m)
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk");
_takeProfitPct = Param(nameof(TakeProfitPct), 3m)
.SetDisplay("Take Profit %", "Take profit percentage", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for main calculation", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_atr1 = default;
_atrBase = default;
_jaw = default;
_teeth = default;
_lips = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_atr1 = new AverageTrueRange { Length = 1 };
_atrBase = new AverageTrueRange { Length = AtrPeriod };
_jaw = new SmoothedMovingAverage { Length = 13 };
_teeth = new SmoothedMovingAverage { Length = 8 };
_lips = new SmoothedMovingAverage { Length = 5 };
Indicators.Add(_atr1);
Indicators.Add(_atrBase);
Indicators.Add(_jaw);
Indicators.Add(_teeth);
Indicators.Add(_lips);
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ProcessCandle).Start();
StartProtection(
takeProfit: new Unit(TakeProfitPct, UnitTypes.Percent),
stopLoss: new Unit(StopLossPct, UnitTypes.Percent),
useMarketOrders: true);
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var atr1Val = _atr1.Process(candle);
var atrBaseVal = _atrBase.Process(candle);
var median = (candle.HighPrice + candle.LowPrice) / 2m;
var jawVal = _jaw.Process(median, candle.OpenTime, true);
var teethVal = _teeth.Process(median, candle.OpenTime, true);
var lipsVal = _lips.Process(median, candle.OpenTime, true);
if (!atr1Val.IsFormed || !atrBaseVal.IsFormed || !jawVal.IsFormed || !teethVal.IsFormed || !lipsVal.IsFormed)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var atr1 = atr1Val.ToDecimal();
var atrBase = atrBaseVal.ToDecimal();
var jaw = jawVal.ToDecimal();
var teeth = teethVal.ToDecimal();
var lips = lipsVal.ToDecimal();
var hl = candle.HighPrice - candle.LowPrice;
if (hl == 0) return;
var hc = Math.Abs(candle.HighPrice - candle.ClosePrice);
var lc = Math.Abs(candle.LowPrice - candle.ClosePrice);
// Alligator bullish: lips > teeth > jaw
var bullGator = lips > teeth && teeth > jaw;
// Alligator bearish: lips < teeth < jaw
var bearGator = lips < teeth && teeth < jaw;
// Volatility breakout
var volBreakout = atrBase > 0 && atr1 > VolatilityCoef * atrBase;
if (Position == 0)
{
if (volBreakout && bullGator && candle.ClosePrice > candle.OpenPrice)
{
BuyMarket();
}
else if (volBreakout && bearGator && candle.ClosePrice < candle.OpenPrice)
{
SellMarket();
}
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
clr.AddReference("StockSharp.BusinessEntities")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes
from StockSharp.Algo.Indicators import AverageTrueRange, SmoothedMovingAverage, CandleIndicatorValue
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class volatile_action_strategy(Strategy):
def __init__(self):
super(volatile_action_strategy, self).__init__()
self._volatility_coef = self.Param("VolatilityCoef", 1.0) \
.SetDisplay("Volatility Coef", "ATR1 multiplier against base ATR", "General")
self._atr_period = self.Param("AtrPeriod", 23) \
.SetDisplay("ATR Period", "Base ATR period", "General")
self._stop_loss_pct = self.Param("StopLossPct", 2.0) \
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk")
self._take_profit_pct = self.Param("TakeProfitPct", 3.0) \
.SetDisplay("Take Profit %", "Take profit percentage", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Timeframe for main calculation", "General")
self._atr1 = None
self._atr_base = None
self._jaw = None
self._teeth = None
self._lips = None
@property
def volatility_coef(self):
return self._volatility_coef.Value
@property
def atr_period(self):
return self._atr_period.Value
@property
def stop_loss_pct(self):
return self._stop_loss_pct.Value
@property
def take_profit_pct(self):
return self._take_profit_pct.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(volatile_action_strategy, self).OnReseted()
self._atr1 = None
self._atr_base = None
self._jaw = None
self._teeth = None
self._lips = None
def OnStarted2(self, time):
super(volatile_action_strategy, self).OnStarted2(time)
self._atr1 = AverageTrueRange()
self._atr1.Length = 1
self._atr_base = AverageTrueRange()
self._atr_base.Length = self.atr_period
self._jaw = SmoothedMovingAverage()
self._jaw.Length = 13
self._teeth = SmoothedMovingAverage()
self._teeth.Length = 8
self._lips = SmoothedMovingAverage()
self._lips.Length = 5
self.Indicators.Add(self._atr1)
self.Indicators.Add(self._atr_base)
self.Indicators.Add(self._jaw)
self.Indicators.Add(self._teeth)
self.Indicators.Add(self._lips)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self.process_candle).Start()
self.StartProtection(
takeProfit=Unit(self.take_profit_pct, UnitTypes.Percent),
stopLoss=Unit(self.stop_loss_pct, UnitTypes.Percent),
useMarketOrders=True)
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
cv1 = CandleIndicatorValue(self._atr1, candle)
atr1_val = self._atr1.Process(cv1)
cv2 = CandleIndicatorValue(self._atr_base, candle)
atr_base_val = self._atr_base.Process(cv2)
median = (candle.HighPrice + candle.LowPrice) / 2
t = candle.OpenTime
jaw_val = process_float(self._jaw, median, t, True)
teeth_val = process_float(self._teeth, median, t, True)
lips_val = process_float(self._lips, median, t, True)
if (not atr1_val.IsFormed or not atr_base_val.IsFormed or
not jaw_val.IsFormed or not teeth_val.IsFormed or not lips_val.IsFormed):
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
atr1 = float(atr1_val)
atr_base = float(atr_base_val)
jaw = float(jaw_val)
teeth = float(teeth_val)
lips = float(lips_val)
bull_gator = lips > teeth and teeth > jaw
bear_gator = lips < teeth and teeth < jaw
vol_breakout = atr_base > 0 and atr1 > float(self.volatility_coef) * atr_base
if self.Position == 0:
if vol_breakout and bull_gator and float(candle.ClosePrice) > float(candle.OpenPrice):
self.BuyMarket()
elif vol_breakout and bear_gator and float(candle.ClosePrice) < float(candle.OpenPrice):
self.SellMarket()
def CreateClone(self):
return volatile_action_strategy()