Estrategia Color J2JMA StdDev
Esta estrategia calcula la pendiente de una Media Móvil Jurik (JMA) y la compara con la desviación estándar de las pendientes recientes. La idea es capturar movimientos direccionales fuertes cuando la pendiente supera un múltiplo de su volatilidad reciente.
Se abre una nueva posición larga cuando la pendiente del JMA sube por encima del umbral alto (K2 × desviación estándar). Se abre una nueva posición corta cuando la pendiente cae por debajo del umbral alto negativo. Las posiciones existentes se cierran cuando la pendiente cruza el umbral bajo opuesto (K1 × desviación estándar). Los niveles de stop loss y take profit se aplican en puntos desde el precio de entrada.
Parámetros:
- JMA Length – período de la media móvil Jurik.
- StdDev Period – número de pendientes recientes utilizadas para la desviación estándar.
- K1 – multiplicador para el umbral bajo usado para cerrar posiciones.
- K2 – multiplicador para el umbral alto usado para abrir posiciones.
- Candle Type – marco temporal de velas para los cálculos.
- Stop Loss – stop de protección en puntos.
- Take Profit – objetivo de ganancias en puntos.
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>
/// Strategy based on the slope of Jurik moving average and its standard deviation.
/// Opens a long position when the JMA slope rises above the high threshold.
/// Opens a short position when the JMA slope falls below the negative high threshold.
/// Existing positions are closed when the slope crosses the opposite low threshold.
/// </summary>
public class ColorJ2JmaStdDevStrategy : Strategy
{
private readonly StrategyParam<int> _jmaLength;
private readonly StrategyParam<int> _stdDevPeriod;
private readonly StrategyParam<decimal> _k1;
private readonly StrategyParam<decimal> _k2;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prevJma;
private StandardDeviation _stdDev;
public int JmaLength
{
get => _jmaLength.Value;
set => _jmaLength.Value = value;
}
public int StdDevPeriod
{
get => _stdDevPeriod.Value;
set => _stdDevPeriod.Value = value;
}
public decimal K1
{
get => _k1.Value;
set => _k1.Value = value;
}
public decimal K2
{
get => _k2.Value;
set => _k2.Value = value;
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public ColorJ2JmaStdDevStrategy()
{
_jmaLength = Param(nameof(JmaLength), 5)
.SetDisplay("JMA Length", "Period of JMA", "Parameters")
.SetOptimize(3, 20, 1);
_stdDevPeriod = Param(nameof(StdDevPeriod), 9)
.SetDisplay("StdDev Period", "Period of standard deviation", "Parameters")
.SetOptimize(5, 20, 1);
_k1 = Param(nameof(K1), 0.5m)
.SetDisplay("K1", "First threshold multiplier (close)", "Parameters")
.SetOptimize(0.3m, 2m, 0.3m);
_k2 = Param(nameof(K2), 1.0m)
.SetDisplay("K2", "Second threshold multiplier (entry)", "Parameters")
.SetOptimize(0.5m, 3m, 0.5m);
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe", "Parameters");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevJma = null;
_stdDev = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevJma = null;
var jma = new JurikMovingAverage { Length = JmaLength };
_stdDev = new StandardDeviation { Length = StdDevPeriod };
Indicators.Add(_stdDev);
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(jma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, jma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal jmaValue)
{
if (candle.State != CandleStates.Finished)
return;
if (_prevJma is not decimal prev)
{
_prevJma = jmaValue;
return;
}
var diff = jmaValue - prev;
_prevJma = jmaValue;
// Process diff through StdDev manually with IsFinal = true
var stdResult = _stdDev.Process(new DecimalIndicatorValue(_stdDev, diff, candle.ServerTime) { IsFinal = true });
if (!_stdDev.IsFormed)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var stDev = stdResult.GetValue<decimal>();
if (stDev == 0)
return;
var lowThreshold = K1 * stDev;
var highThreshold = K2 * stDev;
// Close existing long when slope turns strongly down
if (Position > 0 && diff < -lowThreshold)
{
SellMarket();
return;
}
// Close existing short when slope turns strongly up
if (Position < 0 && diff > lowThreshold)
{
BuyMarket();
return;
}
// Open new long on strong positive slope
if (Position <= 0 && diff > highThreshold)
{
BuyMarket();
}
// Open new short on strong negative slope
else if (Position >= 0 && diff < -highThreshold)
{
SellMarket();
}
}
}
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, StandardDeviation
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class color_j2_jma_std_dev_strategy(Strategy):
def __init__(self):
super(color_j2_jma_std_dev_strategy, self).__init__()
self._jma_length = self.Param("JmaLength", 5) \
.SetDisplay("JMA Length", "Period of JMA", "Parameters")
self._std_dev_period = self.Param("StdDevPeriod", 9) \
.SetDisplay("StdDev Period", "Period of standard deviation", "Parameters")
self._k1 = self.Param("K1", 0.5) \
.SetDisplay("K1", "First threshold multiplier (close)", "Parameters")
self._k2 = self.Param("K2", 1.0) \
.SetDisplay("K2", "Second threshold multiplier (entry)", "Parameters")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe", "Parameters")
self._prev_jma = None
self._std_dev = None
@property
def jma_length(self):
return self._jma_length.Value
@property
def std_dev_period(self):
return self._std_dev_period.Value
@property
def k1(self):
return self._k1.Value
@property
def k2(self):
return self._k2.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(color_j2_jma_std_dev_strategy, self).OnReseted()
self._prev_jma = None
self._std_dev = None
def OnStarted2(self, time):
super(color_j2_jma_std_dev_strategy, self).OnStarted2(time)
self._prev_jma = None
jma = JurikMovingAverage()
jma.Length = self.jma_length
self._std_dev = StandardDeviation()
self._std_dev.Length = self.std_dev_period
self.Indicators.Add(self._std_dev)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(jma, self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, jma)
self.DrawOwnTrades(area)
def process_candle(self, candle, jma_value):
if candle.State != CandleStates.Finished:
return
jma_value = float(jma_value)
if self._prev_jma is None:
self._prev_jma = jma_value
return
diff = jma_value - self._prev_jma
self._prev_jma = jma_value
std_result = process_float(self._std_dev, diff, candle.ServerTime, True)
if not self._std_dev.IsFormed:
return
st_dev = float(std_result)
if st_dev == 0:
return
k1_val = float(self.k1)
k2_val = float(self.k2)
low_threshold = k1_val * st_dev
high_threshold = k2_val * st_dev
if self.Position > 0 and diff < -low_threshold:
self.SellMarket()
return
if self.Position < 0 and diff > low_threshold:
self.BuyMarket()
return
if self.Position <= 0 and diff > high_threshold:
self.BuyMarket()
elif self.Position >= 0 and diff < -high_threshold:
self.SellMarket()
def CreateClone(self):
return color_j2_jma_std_dev_strategy()