Estrategia Tres Rojas / Tres Verdes con Filtro ATR
Entra en largo después de tres velas bajistas consecutivas si el ATR está por encima de su SMA de 30 períodos. Sale después de tres velas alcistas o cuando se alcanza la duración máxima de la operación.
Parámetros
- CandleType: Tipo de velas.
- MaxTradeDuration: Número máximo de barras para mantener una posición abierta.
- UseGreenExit: Si se debe salir después de tres velas verdes.
- AtrPeriod: Período para el cálculo del ATR (0 desactiva el filtro).
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()