Three Red / Three Green Strategy with ATR Filter
Enters long after three consecutive bearish candles if ATR is above its 30-period SMA. Exits after three bullish candles or when maximum trade duration is reached.
Parameters
- CandleType: Type of candles.
- MaxTradeDuration: Maximum number of bars to keep an open position.
- UseGreenExit: Whether to exit after three green candles.
- AtrPeriod: Period for ATR calculation (0 disables the filter).
namespace StockSharp.Samples.Strategies;
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
/// <summary>
/// Three Red / Three Green Strategy with volatility filter.
/// Buys after 3 red candles (mean reversion) with ATR > average.
/// Exits after 3 green candles or max hold period.
/// </summary>
public class ThreeRedGreenVolatilityStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _maxTradeDuration;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<int> _cooldownBars;
private AverageTrueRange _atr;
private SimpleMovingAverage _atrAvg;
private int _redCount;
private int _greenCount;
private int _barsSinceEntry;
private int _cooldownRemaining;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int MaxTradeDuration
{
get => _maxTradeDuration.Value;
set => _maxTradeDuration.Value = value;
}
public int AtrPeriod
{
get => _atrPeriod.Value;
set => _atrPeriod.Value = value;
}
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
public ThreeRedGreenVolatilityStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_maxTradeDuration = Param(nameof(MaxTradeDuration), 20)
.SetGreaterThanZero()
.SetDisplay("Max Hold Bars", "Maximum bars in position", "Trading");
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("ATR Period", "ATR period", "Indicators");
_cooldownBars = Param(nameof(CooldownBars), 12)
.SetDisplay("Cooldown Bars", "Bars between trades", "Risk");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_atr = null;
_atrAvg = null;
_redCount = 0;
_greenCount = 0;
_barsSinceEntry = 0;
_cooldownRemaining = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_atr = new AverageTrueRange { Length = AtrPeriod };
_atrAvg = new SimpleMovingAverage { Length = 30 };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_atr, OnProcess)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void OnProcess(ICandleMessage candle, decimal atrVal)
{
if (candle.State != CandleStates.Finished)
return;
if (!_atr.IsFormed)
return;
// Update ATR average manually
var atrAvgResult = _atrAvg.Process(new DecimalIndicatorValue(_atrAvg, atrVal, candle.ServerTime));
var atrAvgVal = _atrAvg.IsFormed ? atrAvgResult.ToDecimal() : atrVal;
var isRed = candle.ClosePrice < candle.OpenPrice;
var isGreen = candle.ClosePrice > candle.OpenPrice;
_redCount = isRed ? _redCount + 1 : 0;
_greenCount = isGreen ? _greenCount + 1 : 0;
if (Position != 0)
_barsSinceEntry++;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (_cooldownRemaining > 0)
{
_cooldownRemaining--;
return;
}
var highVol = atrVal > atrAvgVal * 0.8m;
// Buy after 3 red candles + volatility check
if (_redCount >= 3 && highVol && Position <= 0)
{
if (Position < 0)
BuyMarket(Math.Abs(Position));
BuyMarket(Volume);
_barsSinceEntry = 0;
_cooldownRemaining = CooldownBars;
}
// Sell after 3 green candles + high volatility
else if (_greenCount >= 3 && highVol && Position >= 0)
{
if (Position > 0)
SellMarket(Math.Abs(Position));
SellMarket(Volume);
_barsSinceEntry = 0;
_cooldownRemaining = CooldownBars;
}
// Exit long: 3 green candles or max hold
else if (Position > 0 && (_greenCount >= 3 || _barsSinceEntry >= MaxTradeDuration))
{
SellMarket(Math.Abs(Position));
_cooldownRemaining = CooldownBars;
}
// Exit short: 3 red candles or max hold
else if (Position < 0 && (_redCount >= 3 || _barsSinceEntry >= MaxTradeDuration))
{
BuyMarket(Math.Abs(Position));
_cooldownRemaining = CooldownBars;
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import AverageTrueRange, SimpleMovingAverage, IndicatorHelper
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class three_red_green_volatility_strategy(Strategy):
"""Three Red / Three Green Strategy with volatility filter."""
def __init__(self):
super(three_red_green_volatility_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._max_trade_duration = self.Param("MaxTradeDuration", 20) \
.SetDisplay("Max Hold Bars", "Maximum bars in position", "Trading")
self._atr_period = self.Param("AtrPeriod", 14) \
.SetDisplay("ATR Period", "ATR period", "Indicators")
self._cooldown_bars = self.Param("CooldownBars", 12) \
.SetDisplay("Cooldown Bars", "Bars between trades", "Risk")
self._atr = None
self._atr_avg = None
self._red_count = 0
self._green_count = 0
self._bars_since_entry = 0
self._cooldown_remaining = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(three_red_green_volatility_strategy, self).OnReseted()
self._atr = None
self._atr_avg = None
self._red_count = 0
self._green_count = 0
self._bars_since_entry = 0
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(three_red_green_volatility_strategy, self).OnStarted2(time)
self._atr = AverageTrueRange()
self._atr.Length = int(self._atr_period.Value)
self._atr_avg = SimpleMovingAverage()
self._atr_avg.Length = 30
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._atr, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _on_process(self, candle, atr_val):
if candle.State != CandleStates.Finished:
return
if not self._atr.IsFormed:
return
atr_v = float(atr_val)
atr_avg_result = process_float(self._atr_avg, atr_val, candle.ServerTime, True)
atr_avg_val = float(IndicatorHelper.ToDecimal(atr_avg_result)) if self._atr_avg.IsFormed else atr_v
is_red = candle.ClosePrice < candle.OpenPrice
is_green = candle.ClosePrice > candle.OpenPrice
self._red_count = self._red_count + 1 if is_red else 0
self._green_count = self._green_count + 1 if is_green else 0
if self.Position != 0:
self._bars_since_entry += 1
if not self.IsFormedAndOnlineAndAllowTrading():
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
return
max_hold = int(self._max_trade_duration.Value)
cooldown = int(self._cooldown_bars.Value)
high_vol = atr_v > atr_avg_val * 0.8
if self._red_count >= 3 and high_vol and self.Position <= 0:
if self.Position < 0:
self.BuyMarket(Math.Abs(self.Position))
self.BuyMarket(self.Volume)
self._bars_since_entry = 0
self._cooldown_remaining = cooldown
elif self._green_count >= 3 and high_vol and self.Position >= 0:
if self.Position > 0:
self.SellMarket(Math.Abs(self.Position))
self.SellMarket(self.Volume)
self._bars_since_entry = 0
self._cooldown_remaining = cooldown
elif self.Position > 0 and (self._green_count >= 3 or self._bars_since_entry >= max_hold):
self.SellMarket(Math.Abs(self.Position))
self._cooldown_remaining = cooldown
elif self.Position < 0 and (self._red_count >= 3 or self._bars_since_entry >= max_hold):
self.BuyMarket(Math.Abs(self.Position))
self._cooldown_remaining = cooldown
def CreateClone(self):
return three_red_green_volatility_strategy()