Estrategia de Movimiento de Stop Loss
Esta estrategia de utilidad monitorea una posición abierta y mueve su stop-loss al precio de entrada cuando el mercado alcanza un nivel predefinido. Se suscribe a datos de velas y verifica cada vela completada. Para posiciones largas, una vez que el máximo de la vela supera el MoveSlPrice configurado, se coloca una orden stop en el precio de entrada. Para posiciones cortas, el stop se mueve cuando el mínimo de la vela cae por debajo del nivel.
La estrategia no genera nuevas señales de trading. Abre una única posición larga al inicio con fines demostrativos y luego la protege moviendo el stop al punto de equilibrio una vez cumplidas las condiciones. Esto permite a los traders asegurar ganancias mientras dejan correr la operación.
Detalles
- Criterios de entrada: Se abre una posición larga al inicio. No se utilizan señales adicionales.
- Largo/Corto: Soporta ambos, pero el ejemplo abre una posición larga.
- Criterios de salida: La posición sale cuando se activa la orden stop en el precio de entrada.
- Stops: El stop-loss se mueve al precio de entrada cuando se alcanza
MoveSlPrice. - Valores predeterminados:
MoveSlPrice= 0 (debe ajustarse antes de ejecutar).CandleType= marco temporal de 1 minuto.
- Filtros:
- Categoría: Gestión de riesgos
- Dirección: Ambos
- Indicadores: Ninguno
- Stops: Sí
- Complejidad: Simple
- Marco temporal: Corto plazo
- Estacionalidad: No
- Redes neuronales: No
- Divergencia: No
- Nivel de riesgo: Bajo
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 that enters on EMA crossover and moves stop-loss to break-even
/// when price moves favorably by a specified StdDev multiple.
/// </summary>
public class StopLossMoverStrategy : Strategy
{
private readonly StrategyParam<int> _fastLength;
private readonly StrategyParam<int> _slowLength;
private readonly StrategyParam<decimal> _stopMult;
private readonly StrategyParam<DataType> _candleType;
private decimal _entryPrice;
private decimal _stopPrice;
private bool _isStopMoved;
private decimal _prevFast;
private decimal _prevSlow;
private bool _hasPrev;
public int FastLength { get => _fastLength.Value; set => _fastLength.Value = value; }
public int SlowLength { get => _slowLength.Value; set => _slowLength.Value = value; }
public decimal StopMult { get => _stopMult.Value; set => _stopMult.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public StopLossMoverStrategy()
{
_fastLength = Param(nameof(FastLength), 10)
.SetGreaterThanZero()
.SetDisplay("Fast EMA", "Fast EMA period", "Indicators");
_slowLength = Param(nameof(SlowLength), 30)
.SetGreaterThanZero()
.SetDisplay("Slow EMA", "Slow EMA period", "Indicators");
_stopMult = Param(nameof(StopMult), 1.5m)
.SetDisplay("Stop Mult", "StdDev multiplier for initial stop", "Risk");
_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();
_entryPrice = 0;
_stopPrice = 0;
_isStopMoved = false;
_prevFast = 0;
_prevSlow = 0;
_hasPrev = false;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var fastEma = new ExponentialMovingAverage { Length = FastLength };
var slowEma = new ExponentialMovingAverage { Length = SlowLength };
var stdDev = new StandardDeviation { Length = 14 };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fastEma, slowEma, stdDev, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fastEma);
DrawIndicator(area, slowEma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow, decimal stdVal)
{
if (candle.State != CandleStates.Finished)
return;
if (stdVal <= 0)
return;
var close = candle.ClosePrice;
// Check stop-loss hit
if (Position > 0 && _stopPrice > 0 && close <= _stopPrice)
{
SellMarket();
_entryPrice = 0;
_stopPrice = 0;
_isStopMoved = false;
_prevFast = fast;
_prevSlow = slow;
_hasPrev = true;
return;
}
else if (Position < 0 && _stopPrice > 0 && close >= _stopPrice)
{
BuyMarket();
_entryPrice = 0;
_stopPrice = 0;
_isStopMoved = false;
_prevFast = fast;
_prevSlow = slow;
_hasPrev = true;
return;
}
// Move stop to break-even when price moves favorably by 2*stdDev
if (Position > 0 && !_isStopMoved && _entryPrice > 0)
{
if (close >= _entryPrice + 2 * stdVal)
{
_stopPrice = _entryPrice;
_isStopMoved = true;
}
}
else if (Position < 0 && !_isStopMoved && _entryPrice > 0)
{
if (close <= _entryPrice - 2 * stdVal)
{
_stopPrice = _entryPrice;
_isStopMoved = true;
}
}
if (!_hasPrev)
{
_prevFast = fast;
_prevSlow = slow;
_hasPrev = true;
return;
}
// Entry signals: EMA crossover
var crossUp = _prevFast <= _prevSlow && fast > slow;
var crossDown = _prevFast >= _prevSlow && fast < slow;
if (crossUp && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
_entryPrice = close;
_stopPrice = close - StopMult * stdVal;
_isStopMoved = false;
}
else if (crossDown && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
_entryPrice = close;
_stopPrice = close + StopMult * stdVal;
_isStopMoved = false;
}
_prevFast = fast;
_prevSlow = slow;
}
}
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, StandardDeviation
from StockSharp.Algo.Strategies import Strategy
class stop_loss_mover_strategy(Strategy):
def __init__(self):
super(stop_loss_mover_strategy, self).__init__()
self._fast_length = self.Param("FastLength", 10) \
.SetDisplay("Fast EMA", "Fast EMA period", "Indicators")
self._slow_length = self.Param("SlowLength", 30) \
.SetDisplay("Slow EMA", "Slow EMA period", "Indicators")
self._stop_mult = self.Param("StopMult", 1.5) \
.SetDisplay("Stop Mult", "StdDev multiplier for initial stop", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._entry_price = 0.0
self._stop_price = 0.0
self._is_stop_moved = False
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
@property
def fast_length(self):
return self._fast_length.Value
@property
def slow_length(self):
return self._slow_length.Value
@property
def stop_mult(self):
return self._stop_mult.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(stop_loss_mover_strategy, self).OnReseted()
self._entry_price = 0.0
self._stop_price = 0.0
self._is_stop_moved = False
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(stop_loss_mover_strategy, self).OnStarted2(time)
fast_ema = ExponentialMovingAverage()
fast_ema.Length = self.fast_length
slow_ema = ExponentialMovingAverage()
slow_ema.Length = self.slow_length
std_dev = StandardDeviation()
std_dev.Length = 14
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(fast_ema, slow_ema, std_dev, self.on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast_ema)
self.DrawIndicator(area, slow_ema)
self.DrawOwnTrades(area)
def on_process(self, candle, fast, slow, std_val):
if candle.State != CandleStates.Finished:
return
if std_val <= 0:
return
close = candle.ClosePrice
# Check stop-loss hit
if self.Position > 0 and self._stop_price > 0 and close <= self._stop_price:
self.SellMarket()
self._entry_price = 0
self._stop_price = 0
self._is_stop_moved = False
self._prev_fast = fast
self._prev_slow = slow
self._has_prev = True
return
elif self.Position < 0 and self._stop_price > 0 and close >= self._stop_price:
self.BuyMarket()
self._entry_price = 0
self._stop_price = 0
self._is_stop_moved = False
self._prev_fast = fast
self._prev_slow = slow
self._has_prev = True
return
# Move stop to break-even when price moves favorably by 2*stdDev
if self.Position > 0 and not self._is_stop_moved and self._entry_price > 0:
if close >= self._entry_price + 2 * std_val:
self._stop_price = self._entry_price
self._is_stop_moved = True
elif self.Position < 0 and not self._is_stop_moved and self._entry_price > 0:
if close <= self._entry_price - 2 * std_val:
self._stop_price = self._entry_price
self._is_stop_moved = True
if not self._has_prev:
self._prev_fast = fast
self._prev_slow = slow
self._has_prev = True
return
# Entry signals: EMA crossover
cross_up = self._prev_fast <= self._prev_slow and fast > slow
cross_down = self._prev_fast >= self._prev_slow and fast < slow
if cross_up and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._stop_price = close - self.stop_mult * std_val
self._is_stop_moved = False
elif cross_down and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._stop_price = close + self.stop_mult * std_val
self._is_stop_moved = False
self._prev_fast = fast
self._prev_slow = slow
def CreateClone(self):
return stop_loss_mover_strategy()