Estrategia cruzada de Cronex DeMarker
Descripción general
La estrategia Cronex DeMarker Crossover reproduce el indicador MetaTrader Cronex DeMarker y lo transforma en un sistema de comercio automatizado. El indicador original traza el oscilador DeMarker junto con dos promedios móviles ponderados lineales (LWMA). La estrategia refleja esa configuración, evalúa los cruces alcistas y bajistas entre las líneas suavizadas del oscilador y las convierte en órdenes de mercado. Esto permite que la lógica comercial reaccione inmediatamente cuando el impulso cambia de presión bajista a presión alcista (y viceversa) según el indicador.
Construcción de indicadores
- Oscilador DeMarker – Mide la relación entre la vela actual y la vela anterior:
- Si el máximo actual es más alto que el máximo anterior, la presión positiva es igual a la diferencia de los máximos; en caso contrario es cero.
- Si el mínimo actual es más bajo que el mínimo anterior, la presión negativa es igual a la distancia entre los mínimos; en caso contrario es cero.
- Las sumas de la presión positiva y negativa sobre
DeMarkerPeriod barras forman el valor del oscilador deMax / (deMax + deMin).
- LWMA rápido: se aplica una media móvil ponderada lineal con período
FastMaPeriod a los valores sin procesar de DeMarker para enfatizar los últimos cambios del oscilador.
- LWMA lento: otra media móvil ponderada lineal con período
SlowMaPeriod suaviza el mismo flujo de DeMarker para crear una línea de confirmación más lenta.
La estrategia alimenta cada vela terminada a esta pila de indicadores, coincidiendo exactamente con los cálculos del buffer del archivo MQ4 original.
Lógica comercial
- Espere hasta que el oscilador DeMarker y ambos LWMA estén completamente formados.
- Después de cada vela completa, calcule el nuevo valor de DeMarker y actualice ambos promedios móviles.
- Detecta cruces entre las series LWMA rápida y lenta:
- Cruce alcista – La LWMA rápida se mueve de abajo hacia arriba de la LWMA lenta. La estrategia cierra cualquier exposición corta y abre una posición larga en el mercado.
- Cruce bajista – La LWMA rápida se mueve desde arriba hacia debajo de la LWMA lenta. La estrategia cierra cualquier exposición larga y abre una posición corta en el mercado.
- Las órdenes se omiten mientras la estrategia aún no se haya formado, mientras esté fuera de línea o cuando el comercio esté deshabilitado.
Las posiciones se invierten inmediatamente ante señales opuestas. La exposición existente se cierra añadiendo la cantidad requerida a la nueva orden de mercado.
Parámetros
| Parámetro |
Descripción |
Predeterminado |
DeMarkerPeriod |
Número de velas utilizadas para construir el oscilador DeMarker. |
25 |
FastMaPeriod |
Período de la media móvil ponderada lineal rápida que reacciona a nuevos valores del oscilador. |
14 |
SlowMaPeriod |
Periodo de la media móvil ponderada lineal lenta que confirma la dirección. |
25 |
CandleType |
Serie de velas procesadas por la estrategia (plazo u otro DataType). |
1 Hour período de tiempo |
Detalles de implementación
- Utiliza el nivel alto
SubscribeCandles API. Los indicadores se actualizan solo cuando una vela alcanza el estado Finished para evitar que se vuelva a pintar a mitad de la barra.
- La estrategia se basa en los indicadores integrados
DeMarker y WeightedMovingAverage de StockSharp para replicar fielmente los buffers MQ4.
- Se crea automáticamente un área del gráfico, que traza las velas de precios junto con el oscilador y ambos promedios móviles para una confirmación visual.
StartProtection() se invoca durante el inicio para que la protección de posición se active exactamente una vez, como lo exigen las pautas del proyecto.
Uso
- Adjunte la estrategia al valor deseado y asigne el tipo de vela preferido (por ejemplo, velas con un marco de tiempo de 1 hora).
- Configure el DeMarker y los períodos de media móvil para que coincidan con el indicador original o ajústelos para optimizarlos.
- Ejecute la estrategia. Comenzará a operar una vez que los indicadores estén completamente formados y se permita el comercio.
- Supervise el gráfico trazado para ver el oscilador DeMarker y las señales cruzadas LWMA que impulsan las entradas.
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy that replicates the Cronex DeMarker indicator setup and trades crossovers of its smoothed values.
/// </summary>
public class CronexDeMarkerCrossoverStrategy : Strategy
{
private readonly StrategyParam<int> _deMarkerPeriod;
private readonly StrategyParam<int> _fastMaPeriod;
private readonly StrategyParam<int> _slowMaPeriod;
private readonly StrategyParam<DataType> _candleType;
private DeMarker _deMarker;
private WeightedMovingAverage _fastMa;
private WeightedMovingAverage _slowMa;
private decimal? _previousFast;
private decimal? _previousSlow;
/// <summary>
/// DeMarker indicator period.
/// </summary>
public int DeMarkerPeriod
{
get => _deMarkerPeriod.Value;
set => _deMarkerPeriod.Value = value;
}
/// <summary>
/// Fast linear weighted moving average period.
/// </summary>
public int FastMaPeriod
{
get => _fastMaPeriod.Value;
set => _fastMaPeriod.Value = value;
}
/// <summary>
/// Slow linear weighted moving average period.
/// </summary>
public int SlowMaPeriod
{
get => _slowMaPeriod.Value;
set => _slowMaPeriod.Value = value;
}
/// <summary>
/// Candle type processed by the strategy.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="CronexDeMarkerCrossoverStrategy"/>.
/// </summary>
public CronexDeMarkerCrossoverStrategy()
{
_deMarkerPeriod = Param(nameof(DeMarkerPeriod), 25)
.SetRange(2, 150)
.SetDisplay("DeMarker Period", "Length of the DeMarker oscillator", "Indicators")
;
_fastMaPeriod = Param(nameof(FastMaPeriod), 14)
.SetRange(2, 100)
.SetDisplay("Fast LWMA Period", "Length of the fast linear weighted moving average", "Indicators")
;
_slowMaPeriod = Param(nameof(SlowMaPeriod), 25)
.SetRange(2, 150)
.SetDisplay("Slow LWMA Period", "Length of the slow linear weighted moving average", "Indicators")
;
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Time frame of processed candles", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_deMarker = null;
_fastMa = null;
_slowMa = null;
_previousFast = null;
_previousSlow = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
// Instantiate indicators matching the original MetaTrader logic.
_deMarker = new DeMarker
{
Length = DeMarkerPeriod
};
_fastMa = new WeightedMovingAverage
{
Length = FastMaPeriod
};
_slowMa = new WeightedMovingAverage
{
Length = SlowMaPeriod
};
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _deMarker);
DrawIndicator(area, _fastMa);
DrawIndicator(area, _slowMa);
DrawOwnTrades(area);
}
StartProtection(null, null);
}
private void ProcessCandle(ICandleMessage candle)
{
// Only act on completed candles to avoid repainting effects.
if (candle.State != CandleStates.Finished)
return;
if (_deMarker is null || _fastMa is null || _slowMa is null)
return;
// Update the DeMarker oscillator with the full candle data.
var deMarkerResult = _deMarker.Process(new CandleIndicatorValue(_deMarker, candle));
if (deMarkerResult.IsEmpty)
{
return;
}
var deMarkerValue = deMarkerResult.GetValue<decimal>();
// Smooth the oscillator with linear weighted moving averages.
var fastResult = _fastMa.Process(new DecimalIndicatorValue(_fastMa, deMarkerValue, candle.OpenTime) { IsFinal = true });
if (fastResult.IsEmpty) return;
var fastValue = fastResult.GetValue<decimal>();
var slowResult = _slowMa.Process(new DecimalIndicatorValue(_slowMa, deMarkerValue, candle.OpenTime) { IsFinal = true });
if (slowResult.IsEmpty) return;
var slowValue = slowResult.GetValue<decimal>();
// Ensure all indicators accumulated enough samples.
if (!_deMarker.IsFormed || !_fastMa.IsFormed || !_slowMa.IsFormed)
{
_previousFast = fastValue;
_previousSlow = slowValue;
return;
}
var previousFast = _previousFast;
var previousSlow = _previousSlow;
_previousFast = fastValue;
_previousSlow = slowValue;
if (!previousFast.HasValue || !previousSlow.HasValue)
return;
// Check readiness and trading permissions before sending orders.
// indicators formed check removed
var crossUp = previousFast.Value <= previousSlow.Value && fastValue > slowValue;
var crossDown = previousFast.Value >= previousSlow.Value && fastValue < slowValue;
if (crossUp)
{
// Close short exposure and establish a long position.
if (Position < 0)
BuyMarket();
BuyMarket();
}
else if (crossDown)
{
// Close long exposure and establish a short position.
if (Position > 0)
SellMarket();
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, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import DeMarker, WeightedMovingAverage, CandleIndicatorValue
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class cronex_de_marker_crossover_strategy(Strategy):
"""Cronex DeMarker crossover strategy. Smooths the DeMarker oscillator with
fast and slow WMA and trades on their crossover."""
def __init__(self):
super(cronex_de_marker_crossover_strategy, self).__init__()
self._de_marker_period = self.Param("DeMarkerPeriod", 25) \
.SetDisplay("DeMarker Period", "Length of the DeMarker oscillator", "Indicators")
self._fast_ma_period = self.Param("FastMaPeriod", 14) \
.SetDisplay("Fast LWMA Period", "Length of the fast linear weighted moving average", "Indicators")
self._slow_ma_period = self.Param("SlowMaPeriod", 25) \
.SetDisplay("Slow LWMA Period", "Length of the slow linear weighted moving average", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Time frame of processed candles", "General")
self._de_marker = None
self._fast_ma = None
self._slow_ma = None
self._previous_fast = None
self._previous_slow = None
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def DeMarkerPeriod(self):
return self._de_marker_period.Value
@property
def FastMaPeriod(self):
return self._fast_ma_period.Value
@property
def SlowMaPeriod(self):
return self._slow_ma_period.Value
def OnReseted(self):
super(cronex_de_marker_crossover_strategy, self).OnReseted()
self._de_marker = None
self._fast_ma = None
self._slow_ma = None
self._previous_fast = None
self._previous_slow = None
def OnStarted2(self, time):
super(cronex_de_marker_crossover_strategy, self).OnStarted2(time)
self._de_marker = DeMarker()
self._de_marker.Length = self.DeMarkerPeriod
self._fast_ma = WeightedMovingAverage()
self._fast_ma.Length = self.FastMaPeriod
self._slow_ma = WeightedMovingAverage()
self._slow_ma.Length = self.SlowMaPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._process_candle).Start()
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
if self._de_marker is None or self._fast_ma is None or self._slow_ma is None:
return
dm_input = CandleIndicatorValue(self._de_marker, candle)
dm_input.IsFinal = True
dm_result = self._de_marker.Process(dm_input)
if dm_result.IsEmpty:
return
dm_value = float(dm_result)
fast_result = process_float(self._fast_ma, dm_value, candle.OpenTime, True)
if fast_result.IsEmpty:
return
fast_value = float(fast_result)
slow_result = process_float(self._slow_ma, dm_value, candle.OpenTime, True)
if slow_result.IsEmpty:
return
slow_value = float(slow_result)
if not self._de_marker.IsFormed or not self._fast_ma.IsFormed or not self._slow_ma.IsFormed:
self._previous_fast = fast_value
self._previous_slow = slow_value
return
previous_fast = self._previous_fast
previous_slow = self._previous_slow
self._previous_fast = fast_value
self._previous_slow = slow_value
if previous_fast is None or previous_slow is None:
return
cross_up = previous_fast <= previous_slow and fast_value > slow_value
cross_down = previous_fast >= previous_slow and fast_value < slow_value
if cross_up:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif cross_down:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
def CreateClone(self):
return cronex_de_marker_crossover_strategy()