Esta estrategia monitorea la ganancia y pérdida realizada de todas las operaciones ejecutadas por la estrategia. Cuando el beneficio acumulado supera un umbral definido por el usuario, cierra inmediatamente cualquier posición abierta y opcionalmente cancela las órdenes activas. El mismo comportamiento puede activarse para la caída estableciendo un límite de pérdida.
La estrategia no analiza indicadores ni movimientos de precio. En cambio, actúa como una capa protectora que sale del mercado una vez que se alcanza un objetivo monetario o un nivel de stop. Una suscripción simple a velas se usa únicamente para proporcionar comprobaciones periódicas del valor PnL actual.
Parámetros
UseProfitToClose – habilitar o deshabilitar el cierre por objetivo de beneficio. Por defecto: true.
ProfitToClose – valor de beneficio en unidades de moneda que activa una salida completa. Por defecto: 20.
UseLossToClose – habilitar o deshabilitar el cierre por límite de pérdida. Por defecto: false.
LossToClose – valor de pérdida en unidades de moneda que activa una salida completa cuando se supera. Por defecto: 100.
ClosePendingOrders – cancelar todas las órdenes activas al cerrar posiciones. Por defecto: true.
CandleType – tipo de velas utilizadas para activar comprobaciones periódicas. Por defecto: marco temporal de 1 minuto.
Lógica de Trading
Suscribirse a las velas del marco temporal seleccionado.
En cada vela terminada, calcular el PnL realizado actual.
Si el beneficio es mayor o igual a ProfitToClose, cerrar toda la posición y opcionalmente cancelar órdenes pendientes.
Si el monitoreo de pérdidas está habilitado y el PnL actual es menor o igual a -LossToClose, cerrar toda la posición y opcionalmente cancelar órdenes pendientes.
Notas Adicionales
La estrategia cierra solo la posición del valor al que está vinculada.
Las órdenes pendientes se cancelan usando el método integrado CancelActiveOrders.
La lógica puede combinarse con otras estrategias de entrada para implementar toma de ganancias o protección de cartera.
Filtros
Categoría: Gestión de riesgo
Dirección: Ambos
Indicadores: Ninguno
Stops: Sí
Complejidad: Básico
Marco temporal: Cualquiera
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>
/// EMA crossover strategy with profit/loss exit targets.
/// </summary>
public class CloseAtProfitStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevFast;
private decimal _prevSlow;
private bool _hasPrev;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public CloseAtProfitStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 12)
.SetGreaterThanZero()
.SetDisplay("Fast EMA", "Fast EMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 26)
.SetGreaterThanZero()
.SetDisplay("Slow EMA", "Slow EMA period", "Indicators");
_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();
_prevFast = 0;
_prevSlow = 0;
_hasPrev = false;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var fast = new ExponentialMovingAverage { Length = FastPeriod };
var slow = new ExponentialMovingAverage { Length = SlowPeriod };
SubscribeCandles(CandleType).Bind(fast, slow, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)
{
if (candle.State != CandleStates.Finished) return;
if (!_hasPrev)
{
_prevFast = fast;
_prevSlow = slow;
_hasPrev = true;
return;
}
// Fast crosses above slow => buy
if (_prevFast <= _prevSlow && fast > slow && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
}
// Fast crosses below slow => sell
else if (_prevFast >= _prevSlow && fast < slow && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
}
_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
from StockSharp.Algo.Strategies import Strategy
class close_at_profit_strategy(Strategy):
def __init__(self):
super(close_at_profit_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 12) \
.SetDisplay("Fast EMA", "Fast EMA period", "Indicators")
self._slow_period = self.Param("SlowPeriod", 26) \
.SetDisplay("Slow EMA", "Slow EMA period", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(close_at_profit_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(close_at_profit_strategy, self).OnStarted2(time)
fast = ExponentialMovingAverage()
fast.Length = self.fast_period
slow = ExponentialMovingAverage()
slow.Length = self.slow_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(fast, slow, self.on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def on_process(self, candle, fast, slow):
if candle.State != CandleStates.Finished:
return
if not self._has_prev:
self._prev_fast = fast
self._prev_slow = slow
self._has_prev = True
return
# Fast crosses above slow => buy
if self._prev_fast <= self._prev_slow and fast > slow and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
# Fast crosses below slow => sell
elif self._prev_fast >= self._prev_slow and fast < slow and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_fast = fast
self._prev_slow = slow
def CreateClone(self):
return close_at_profit_strategy()