La estrategia Crypto SR porta el asesor experto de MetaTrader 4 "Crypto S&R" a la API de alto nivel de StockSharp. La implementación conserva la lógica de confirmación por capas del sistema original: un filtro de tendencia basado en medias móviles ponderadas lineales (LWMA), una comprobación de impulso en un marco temporal superior, un filtro de tendencia MACD de largo plazo y niveles de soporte/resistencia derivados de fractales. Las órdenes se envían con ejecución de mercado y la posición se gestiona mediante stop-loss/take-profit fijos, ajustes a break-even y un trailing stop medido en pips.
Lógica de negociación
Análisis del marco principal: la estrategia se suscribe a la serie de velas configurada y alimenta dos LWMA con el precio típico de la vela (high + low + close) / 3. La LWMA rápida debe permanecer por encima (por debajo) de la lenta para habilitar largos (cortos).
Impulso en marco superior: un indicador Momentum se evalúa en una segunda serie de velas. La distancia absoluta de las tres últimas lecturas frente al valor neutral (100) debe superar los umbrales de compra/venta.
Filtro MACD de largo plazo: la estrategia escucha otra serie de velas donde se calcula un MACD (12, 26, 9). Las posiciones largas requieren que la línea MACD permanezca por encima de su señal; las cortas, por debajo. El marco predeterminado de largo plazo es diario para aproximar la serie mensual usada por el EA; puede ajustarse si hay velas mensuales reales disponibles.
Soporte/resistencia fractal: las velas terminadas se guardan en un búfer móvil. Cuando aparece el patrón fractal clásico de Bill Williams (dos vecinos a cada lado), el máximo/mínimo correspondiente se convierte en la resistencia o soporte activo. Se aplica un búfer configurable en pips alrededor del nivel para emular las líneas horizontales del experto original.
Reglas de entrada:
Compra: sin posición larga abierta, LWMA rápida sobre la lenta, desviación de momentum >= umbral de compra, MACD alcista, la vela actual prueba el soporte con búfer y cierra por encima del cierre anterior.
Venta: condiciones espejo con la resistencia, el umbral de venta de momentum y confirmación MACD bajista.
Gestión de riesgo: cada nueva posición recibe un stop-loss y take-profit iniciales en pips. La lógica de break-even puede desplazar el stop al alcanzar la distancia de activación, mientras que un trailing stop opcional sigue al precio usando máximos/mínimos de velas. La exposición larga/corta se cierra si el filtro MACD gira contra la operación.
Notas de implementación
El filtro MACD mensual de la versión MetaTrader se aproxima por defecto con una serie diaria porque StockSharp no proporciona velas de mes calendario de forma inmediata. Los usuarios pueden cambiar a un agregador mensual personalizado si su fuente de datos lo admite.
Las órdenes se cierran con solicitudes de mercado cuando se vulneran los niveles de protección. Esto replica las llamadas OrderClose de MQL y evita depender de órdenes stop del lado de la bolsa.
Todos los enlaces de indicadores se realizan mediante la API de suscripción de alto nivel y no se requieren llamadas directas a GetValue.
Parámetros
Nombre
Descripción
Predeterminado
FastMaPeriod
Longitud de la LWMA rápida en el marco principal.
6
SlowMaPeriod
Longitud de la LWMA lenta en el marco principal.
85
MomentumPeriod
Periodo de momentum en el marco superior.
14
MomentumBuyThreshold
Desviación absoluta mínima de momentum frente a 100 para habilitar entradas largas.
0.3
MomentumSellThreshold
Desviación absoluta mínima de momentum frente a 100 para habilitar entradas cortas.
0.3
MacdFastPeriod
Longitud de la EMA rápida para el filtro MACD de largo plazo.
12
MacdSlowPeriod
Longitud de la EMA lenta para el filtro MACD de largo plazo.
26
MacdSignalPeriod
Longitud de la EMA de señal para el filtro MACD de largo plazo.
9
StopLossPips
Distancia de stop-loss duro expresada en pips.
20
TakeProfitPips
Distancia fija de take-profit expresada en pips.
50
TrailingStopPips
Distancia del trailing stop en pips (0 desactiva el trailing).
40
UseBreakEven
Indica si se mueve el stop a break-even tras una activación de beneficio.
true
BreakEvenTriggerPips
Beneficio en pips necesario antes de aplicar ajustes de break-even.
30
BreakEvenOffsetPips
Desplazamiento añadido al mover el stop a break-even.
30
FractalWindowLength
Número de velas terminadas retenidas para confirmar máximos y mínimos fractales.
7
FractalBufferPips
Búfer adicional alrededor de niveles fractales en pips.
10
TradeVolume
Volumen enviado con cada orden de mercado.
1
CandleType
Serie principal de velas para LWMA y lógica fractal.
Marco temporal 15m
HigherCandleType
Marco superior para el filtro de momentum.
Marco temporal 1h
LongTermCandleType
Marco temporal para el filtro de tendencia MACD.
Marco temporal 1d
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;
public class CryptoSrStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private ExponentialMovingAverage _fast;
private ExponentialMovingAverage _slow;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
private int _cooldown;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }
public CryptoSrStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
protected override void OnReseted()
{
base.OnReseted();
_fast = null; _slow = null;
_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fast = new ExponentialMovingAverage { Length = FastPeriod };
_slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
subscription.Bind(_fast, _slow, ProcessCandle);
subscription.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished) return;
if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }
var close = candle.ClosePrice;
var step = Security?.PriceStep ?? 1m;
if (Position > 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }
_prevFast = fastValue; _prevSlow = slowValue;
}
}
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 ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class crypto_sr_strategy(Strategy):
def __init__(self):
super(crypto_sr_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 14) \
.SetDisplay("Fast Period", "Fast MA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 50) \
.SetDisplay("Slow Period", "Slow MA period", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 200) \
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 400) \
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk")
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def stop_loss_points(self):
return self._stop_loss_points.Value
@property
def take_profit_points(self):
return self._take_profit_points.Value
def OnReseted(self):
super(crypto_sr_strategy, self).OnReseted()
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(crypto_sr_strategy, self).OnStarted2(time)
self._fast = ExponentialMovingAverage()
self._fast.Length = self.fast_period
self._slow = ExponentialMovingAverage()
self._slow.Length = self.slow_period
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(5)))
subscription.Bind(self._fast, self._slow, self._process_candle)
subscription.Start()
def _process_candle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_value)
slow_val = float(slow_value)
if not self._fast.IsFormed or not self._slow.IsFormed:
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = float(candle.ClosePrice)
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
if self.Position > 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close <= self._entry_price - self.stop_loss_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close >= self._entry_price + self.take_profit_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close >= self._entry_price + self.stop_loss_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close <= self._entry_price - self.take_profit_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._prev_fast <= self._prev_slow and fast_val > slow_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 100
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return crypto_sr_strategy()