Estrategia Color Step Xccx
Estrategia basada en el indicador Color Step XCCX. El indicador mide la desviación del precio respecto a una media suavizada y traza dos líneas escalonadas. Se abre una operación larga cuando la línea rápida cae por debajo de la línea lenta. Se abre una operación corta cuando la línea rápida sube por encima de la línea lenta.
Detalles
- Criterios de entrada:
- Largo: la línea rápida cruza por debajo de la línea lenta
- Corto: la línea rápida cruza por encima de la línea lenta
- Largo/Corto: Ambos
- Criterios de salida:
- Largo: la línea rápida cruza por encima de la línea lenta
- Corto: la línea rápida cruza por debajo de la línea lenta
- Stops: Ninguno
- Valores predeterminados:
DPeriod= 30MPeriod= 7StepSizeFast= 5StepSizeSlow= 30CandleType= TimeSpan.FromHours(4).TimeFrame()
- Filtros:
- Categoría: Tendencia
- Dirección: Ambos
- Indicadores: Custom, EMA
- Stops: No
- Complejidad: Intermedio
- Marco temporal: Medio plazo
- Estacionalidad: No
- Redes neuronales: No
- Divergencia: No
- Nivel de riesgo: Medio
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>
/// Normalized ColorStepXCCX step channel with JMA price smoothing and T3 deviation
/// smoothing. Long when fast steps below slow, short when fast steps above slow.
/// Unlike a conventional EMA crossover, the step parameters affect BOTH lines.
/// </summary>
public class ColorStepXccxStrategy : Strategy
{
private readonly StrategyParam<int> _dPeriod;
private readonly StrategyParam<int> _mPeriod;
private readonly StrategyParam<decimal> _stepSizeFast;
private readonly StrategyParam<decimal> _stepSizeSlow;
private readonly StrategyParam<DataType> _candleType;
private JurikMovingAverage _priceAverage;
private T3MovingAverage _signedDeviationAverage;
private T3MovingAverage _absoluteDeviationAverage;
private decimal? _previousFast;
private decimal? _previousSlow;
private decimal _fastMin;
private decimal _fastMax;
private decimal _slowMin;
private decimal _slowMax;
private int _fastTrend;
private int _slowTrend;
private bool _hasStepBaseline;
public int DPeriod { get => _dPeriod.Value; set => _dPeriod.Value = value; }
public int MPeriod { get => _mPeriod.Value; set => _mPeriod.Value = value; }
public decimal StepSizeFast { get => _stepSizeFast.Value; set => _stepSizeFast.Value = value; }
public decimal StepSizeSlow { get => _stepSizeSlow.Value; set => _stepSizeSlow.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public ColorStepXccxStrategy()
{
_dPeriod = Param(nameof(DPeriod), 30).SetGreaterThanZero()
.SetDisplay("D Period", "JMA price smoothing period", "Indicators");
_mPeriod = Param(nameof(MPeriod), 7).SetGreaterThanZero()
.SetDisplay("M Period", "T3 normalized deviation smoothing period", "Indicators");
_stepSizeFast = Param(nameof(StepSizeFast), 5m).SetGreaterThanZero()
.SetDisplay("Fast Step", "Normalized oscillator fast step", "Indicators");
_stepSizeSlow = Param(nameof(StepSizeSlow), 30m).SetGreaterThanZero()
.SetDisplay("Slow Step", "Normalized oscillator slow step", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
protected override void OnReseted()
{
base.OnReseted();
_priceAverage = null;
_signedDeviationAverage = null;
_absoluteDeviationAverage = null;
_previousFast = _previousSlow = null;
_hasStepBaseline = false;
_fastTrend = _slowTrend = 0;
_fastMin = _fastMax = _slowMin = _slowMax = 0m;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_previousFast = _previousSlow = null;
_hasStepBaseline = false;
_fastTrend = _slowTrend = 0;
_priceAverage = new JurikMovingAverage { Length = DPeriod, Phase = 100 };
// The original T3 smoothing phase (15) represents a factor of 0.15.
_signedDeviationAverage = new T3MovingAverage { Length = MPeriod, VolumeFactor = 0.15m };
_absoluteDeviationAverage = new T3MovingAverage { Length = MPeriod, VolumeFactor = 0.15m };
SubscribeCandles(CandleType).Bind(ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
// MQL applied-price default is PRICE_TYPICAL, not closing price.
var typical = (candle.HighPrice + candle.LowPrice + candle.ClosePrice) / 3m;
var baseValue = _priceAverage.Process(new DecimalIndicatorValue(_priceAverage, typical, candle.OpenTime) { IsFinal = true });
if (!_priceAverage.IsFormed)
return;
var deviation = typical - baseValue.GetValue<decimal>();
var up = _signedDeviationAverage.Process(new DecimalIndicatorValue(_signedDeviationAverage, deviation, candle.OpenTime) { IsFinal = true });
var down = _absoluteDeviationAverage.Process(new DecimalIndicatorValue(_absoluteDeviationAverage, Math.Abs(deviation), candle.OpenTime) { IsFinal = true });
if (!_signedDeviationAverage.IsFormed || !_absoluteDeviationAverage.IsFormed)
return;
var numerator = up.GetValue<decimal>();
var denominator = down.GetValue<decimal>();
var oscillator = numerator != 0m && denominator != 0m ? 100m * numerator / denominator : 0m;
ProcessNormalized(oscillator);
}
private void ProcessNormalized(decimal oscillator)
{
var fastStep = StepSizeFast;
var slowStep = StepSizeSlow;
var fastMax = oscillator + 2m * fastStep;
var fastMin = oscillator - 2m * fastStep;
var slowMax = oscillator + 2m * slowStep;
var slowMin = oscillator - 2m * slowStep;
if (!_hasStepBaseline)
{
_fastMin = fastMin;
_fastMax = fastMax;
_slowMin = slowMin;
_slowMax = slowMax;
_previousFast = _previousSlow = oscillator;
_hasStepBaseline = true;
return;
}
if (oscillator > _fastMax) _fastTrend = 1;
else if (oscillator < _fastMin) _fastTrend = -1;
if (_fastTrend > 0 && fastMin < _fastMin) fastMin = _fastMin;
if (_fastTrend < 0 && fastMax > _fastMax) fastMax = _fastMax;
if (oscillator > _slowMax) _slowTrend = 1;
else if (oscillator < _slowMin) _slowTrend = -1;
if (_slowTrend > 0 && slowMin < _slowMin) slowMin = _slowMin;
if (_slowTrend < 0 && slowMax > _slowMax) slowMax = _slowMax;
var fast = _fastTrend > 0 ? fastMin + fastStep : fastMax - fastStep;
var slow = _slowTrend > 0 ? slowMin + slowStep : slowMax - slowStep;
var prevFast = _previousFast.Value;
var prevSlow = _previousSlow.Value;
_fastMin = fastMin; _fastMax = fastMax;
_slowMin = slowMin; _slowMax = slowMax;
_previousFast = fast; _previousSlow = slow;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var position = Position;
if (prevFast > prevSlow && fast < slow && position <= 0m)
{
if (position < 0m) BuyMarket(-position);
BuyMarket(Volume);
}
else if (prevFast < prevSlow && fast > slow && position >= 0m)
{
if (position > 0m) SellMarket(position);
SellMarket(Volume);
}
}
}
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 JurikMovingAverage, T3MovingAverage
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import process_float
class color_step_xccx_strategy(Strategy):
"""Normalized stepped XCCX oscillator, never substituted by EMA crossover."""
def __init__(self):
super(color_step_xccx_strategy, self).__init__()
self._d_period = self.Param("DPeriod", 30).SetDisplay("D Period", "JMA price smoothing period", "Indicators")
self._m_period = self.Param("MPeriod", 7).SetDisplay("M Period", "T3 deviation smoothing period", "Indicators")
self._step_fast = self.Param("StepSizeFast", 5).SetDisplay("Fast Step", "Normalized oscillator fast step", "Indicators")
self._step_slow = self.Param("StepSizeSlow", 30).SetDisplay("Slow Step", "Normalized oscillator slow step", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))).SetDisplay("Candle Type", "Type of candles", "General")
self._reset_signals()
@property
def DPeriod(self):
return self._d_period.Value
@DPeriod.setter
def DPeriod(self, value):
self._d_period.Value = value
@property
def MPeriod(self):
return self._m_period.Value
@MPeriod.setter
def MPeriod(self, value):
self._m_period.Value = value
@property
def StepSizeFast(self):
return self._step_fast.Value
@StepSizeFast.setter
def StepSizeFast(self, value):
self._step_fast.Value = value
@property
def StepSizeSlow(self):
return self._step_slow.Value
@StepSizeSlow.setter
def StepSizeSlow(self, value):
self._step_slow.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def _reset_signals(self):
self._previous_fast = None
self._previous_slow = None
self._fast_min = self._fast_max = 0.0
self._slow_min = self._slow_max = 0.0
self._fast_trend = self._slow_trend = 0
self._has_step_baseline = False
def OnReseted(self):
super(color_step_xccx_strategy, self).OnReseted()
self._reset_signals()
def OnStarted2(self, time):
super(color_step_xccx_strategy, self).OnStarted2(time)
self._reset_signals()
self._price_average = JurikMovingAverage()
self._price_average.Length = self.DPeriod
self._price_average.Phase = 100
self._signed_average = T3MovingAverage()
self._signed_average.Length = self.MPeriod
self._signed_average.VolumeFactor = 0.15
self._absolute_average = T3MovingAverage()
self._absolute_average.Length = self.MPeriod
self._absolute_average.VolumeFactor = 0.15
self.SubscribeCandles(self.CandleType).Bind(self.ProcessCandle).Start()
def ProcessCandle(self, candle):
if candle.State != CandleStates.Finished:
return
typical = (float(candle.HighPrice) + float(candle.LowPrice) + float(candle.ClosePrice)) / 3.0
baseline = process_float(self._price_average, typical, candle.OpenTime, True)
if not self._price_average.IsFormed:
return
deviation = typical - float(baseline)
up = process_float(self._signed_average, deviation, candle.OpenTime, True)
down = process_float(self._absolute_average, abs(deviation), candle.OpenTime, True)
if not self._signed_average.IsFormed or not self._absolute_average.IsFormed:
return
numerator, denominator = float(up), float(down)
oscillator = 100.0 * numerator / denominator if numerator != 0 and denominator != 0 else 0.0
self._process_normalized(oscillator)
def _process_normalized(self, oscillator):
fast_step = float(self.StepSizeFast)
slow_step = float(self.StepSizeSlow)
if fast_step <= 0 or slow_step <= 0:
raise ValueError('StepSizeFast and StepSizeSlow must be positive')
fast_max, fast_min = oscillator + 2.0 * fast_step, oscillator - 2.0 * fast_step
slow_max, slow_min = oscillator + 2.0 * slow_step, oscillator - 2.0 * slow_step
if not self._has_step_baseline:
self._fast_min, self._fast_max = fast_min, fast_max
self._slow_min, self._slow_max = slow_min, slow_max
self._previous_fast = self._previous_slow = oscillator
self._has_step_baseline = True
return
if oscillator > self._fast_max:
self._fast_trend = 1
elif oscillator < self._fast_min:
self._fast_trend = -1
if self._fast_trend > 0 and fast_min < self._fast_min:
fast_min = self._fast_min
if self._fast_trend < 0 and fast_max > self._fast_max:
fast_max = self._fast_max
if oscillator > self._slow_max:
self._slow_trend = 1
elif oscillator < self._slow_min:
self._slow_trend = -1
if self._slow_trend > 0 and slow_min < self._slow_min:
slow_min = self._slow_min
if self._slow_trend < 0 and slow_max > self._slow_max:
slow_max = self._slow_max
fast = fast_min + fast_step if self._fast_trend > 0 else fast_max - fast_step
slow = slow_min + slow_step if self._slow_trend > 0 else slow_max - slow_step
previous_fast, previous_slow = self._previous_fast, self._previous_slow
self._fast_min, self._fast_max = fast_min, fast_max
self._slow_min, self._slow_max = slow_min, slow_max
self._previous_fast, self._previous_slow = fast, slow
if not self.IsFormedAndOnlineAndAllowTrading():
return
position = self.Position
if previous_fast > previous_slow and fast < slow and position <= 0:
if position < 0:
self.BuyMarket(-position)
self.BuyMarket(self.Volume)
elif previous_fast < previous_slow and fast > slow and position >= 0:
if position > 0:
self.SellMarket(position)
self.SellMarket(self.Volume)
def CreateClone(self):
return color_step_xccx_strategy()