Estrategia XDerivative
La Estrategia XDerivative rastrea cambios en el momentum del precio usando una tasa de cambio suavizada. El experto MQL original combina un cálculo de tasa de cambio con suavizado Jurik para detectar puntos de giro. La versión de StockSharp reutiliza indicadores integrados para implementar el mismo concepto.
La estrategia calcula la tasa de cambio sobre RocPeriod barras y la suaviza con una Jurik Moving Average de longitud MaLength. Cuando la derivada suavizada forma un valle (el valor anterior es inferior a su predecesor y el valor actual sube por encima del anterior), la estrategia entra o cambia a una posición larga. Cuando se forma un pico (el valor anterior es superior a su predecesor y el valor actual cae por debajo de él), la estrategia entra o cambia a una posición corta. Los stops de protección gestionan las salidas.
Detalles
- Criterios de entrada:
- Largo: La derivada suavizada gira hacia arriba después de un mínimo local.
- Corto: La derivada suavizada gira hacia abajo después de un máximo local.
- Largo/Corto: Ambas direcciones.
- Criterios de salida: Giro opuesto de la derivada o stop de protección.
- Stops: Sí, take profit y stop loss en porcentaje.
- Valores predeterminados:
RocPeriod = 34
MaLength = 7
TakeProfitPercent = 2
StopLossPercent = 1
CandleType = TimeSpan.FromHours(4)
- Filtros:
- Categoría: Seguimiento de tendencia
- Dirección: Ambos
- Indicadores: RateOfChange, JurikMovingAverage
- Stops: Sí
- Complejidad: Básico
- Marco temporal: 4H
- 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>
/// XDerivative strategy based on smoothed rate of change.
/// Enters long when the smoothed derivative forms a trough and short when it forms a peak.
/// </summary>
public class XDerivativeStrategy : Strategy
{
private readonly StrategyParam<int> _rocPeriod;
private readonly StrategyParam<int> _maLength;
private readonly StrategyParam<decimal> _takeProfitPct;
private readonly StrategyParam<decimal> _stopLossPct;
private readonly StrategyParam<DataType> _candleType;
private JurikMovingAverage _jma;
private decimal? _prevValue;
private decimal? _prevPrevValue;
public int RocPeriod
{
get => _rocPeriod.Value;
set => _rocPeriod.Value = value;
}
public int MaLength
{
get => _maLength.Value;
set => _maLength.Value = value;
}
public decimal TakeProfitPct
{
get => _takeProfitPct.Value;
set => _takeProfitPct.Value = value;
}
public decimal StopLossPct
{
get => _stopLossPct.Value;
set => _stopLossPct.Value = value;
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public XDerivativeStrategy()
{
_rocPeriod = Param(nameof(RocPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("ROC Period", "Period for rate of change", "Parameters");
_maLength = Param(nameof(MaLength), 7)
.SetGreaterThanZero()
.SetDisplay("JMA Length", "Period for Jurik MA smoothing", "Parameters");
_takeProfitPct = Param(nameof(TakeProfitPct), 3m)
.SetGreaterThanZero()
.SetDisplay("Take Profit %", "Take profit percentage", "Risk");
_stopLossPct = Param(nameof(StopLossPct), 2m)
.SetGreaterThanZero()
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "Parameters");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_jma = default;
_prevValue = null;
_prevPrevValue = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_jma = new JurikMovingAverage { Length = MaLength };
var roc = new RateOfChange { Length = RocPeriod };
Indicators.Add(_jma);
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(roc, (candle, rocValue) =>
{
if (candle.State != CandleStates.Finished)
return;
var jmaResult = _jma.Process(rocValue, candle.OpenTime, true);
if (!jmaResult.IsFormed)
return;
var value = jmaResult.ToDecimal();
if (_prevValue is decimal prev && _prevPrevValue is decimal prev2)
{
var turnUp = prev < prev2 && value > prev;
var turnDown = prev > prev2 && value < prev;
if (turnUp && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
}
else if (turnDown && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
}
}
_prevPrevValue = _prevValue;
_prevValue = value;
})
.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);
DrawIndicator(area, roc);
DrawOwnTrades(area);
}
}
}
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, Unit, UnitTypes
from StockSharp.Algo.Indicators import JurikMovingAverage, RateOfChange
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class x_derivative_strategy(Strategy):
def __init__(self):
super(x_derivative_strategy, self).__init__()
self._roc_period = self.Param("RocPeriod", 14) \
.SetDisplay("ROC Period", "Period for rate of change", "Parameters")
self._ma_length = self.Param("MaLength", 7) \
.SetDisplay("JMA Length", "Period for Jurik MA smoothing", "Parameters")
self._take_profit_pct = self.Param("TakeProfitPct", 3.0) \
.SetDisplay("Take Profit %", "Take profit percentage", "Risk")
self._stop_loss_pct = self.Param("StopLossPct", 2.0) \
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles", "Parameters")
self._jma = None
self._prev_value = None
self._prev_prev_value = None
@property
def roc_period(self):
return self._roc_period.Value
@property
def ma_length(self):
return self._ma_length.Value
@property
def take_profit_pct(self):
return self._take_profit_pct.Value
@property
def stop_loss_pct(self):
return self._stop_loss_pct.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(x_derivative_strategy, self).OnReseted()
self._jma = None
self._prev_value = None
self._prev_prev_value = None
def OnStarted2(self, time):
super(x_derivative_strategy, self).OnStarted2(time)
self._jma = JurikMovingAverage()
self._jma.Length = self.ma_length
roc = RateOfChange()
roc.Length = self.roc_period
self.Indicators.Add(self._jma)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(roc, self.on_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.DrawIndicator(area, roc)
self.DrawOwnTrades(area)
def on_candle(self, candle, roc_value):
if candle.State != CandleStates.Finished:
return
jma_result = process_float(self._jma, roc_value, candle.OpenTime, True)
if not jma_result.IsFormed:
return
value = float(jma_result)
if self._prev_value is not None and self._prev_prev_value is not None:
turn_up = self._prev_value < self._prev_prev_value and value > self._prev_value
turn_down = self._prev_value > self._prev_prev_value and value < self._prev_value
if turn_up and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif turn_down and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_prev_value = self._prev_value
self._prev_value = value
def CreateClone(self):
return x_derivative_strategy()