The Basket Close Utility strategy mirrors the behaviour of the MetaTrader expert "Basket Close 2". It continuously monitors the floating profit and loss of every open position in the connected portfolio. When either a configurable profit objective or a loss limit is reached, the strategy sends market orders to flatten all exposures in every instrument involved. Opcionalmente, puede abrir automáticamente una pequeña posición de prueba siempre que el libro esté plano, lo cual es útil dentro de las pruebas retrospectivas para validar que la lógica de protección funciona como se espera.
Parámetros
Nombre
Descripción
LossMode
Elige si el protector contra pérdidas compara porcentajes o valores de moneda.
LossPercentage
Negative percentage drawdown (expressed in absolute value) that triggers the loss exit when LossMode is Percentage.
LossCurrency
Pérdida flotante en la moneda de la cuenta que desencadena la salida cuando LossMode es Currency.
ProfitMode
Elige si el objetivo de ganancias compara porcentajes o valores de moneda.
ProfitPercentage
Percentage gain that closes all positions when ProfitMode is Percentage.
ProfitCurrency
Beneficio flotante en la moneda de la cuenta que cierra todas las posiciones cuando ProfitMode es Currency.
CandleType
Timeframe used to trigger periodic checks of the floating profit and loss.
EnableTestOrders
Cuando está habilitada, la estrategia envía una orden de compra de mercado única siempre que no haya posiciones abiertas.
TestOrderVolume
Tamaño de la operación utilizado cuando la orden de prueba opcional está activa.
Lógica de trading
Subscribe to the configured candle series and run the evaluation only when a candle is fully finished, matching the behaviour of the original EA that works on closed bars.
Aggregate the floating profit and loss of every open position. Si el objeto de la cartera expone un beneficio flotante combinado, se utiliza; de lo contrario, la estrategia suma el PnL de cada posición.
Compute the percentage change relative to the current account balance captured at start-up.
Trigger the loss routine when the floating PnL breaches the configured limit. Trigger the profit routine when the floating PnL or the percentage gain reaches the profit target.
Una vez activado, siga enviando órdenes de mercado hasta que se cierren todas las posiciones abiertas en toda la cartera. Esto incluye la seguridad principal, así como las posiciones abiertas por las estrategias infantiles.
Opcionalmente, envíe una orden de mercado para reabrir la exposición (para realizar pruebas) después de que el libro se estabilice.
Notas
El experto MetaTrader mostró información textual en el gráfico. En StockSharp las cifras importantes se registran a través de LogInfo.
Los ajustes de swaps y comisiones del guión original se incluyen implícitamente dentro del PnL flotante informado por la cartera o las posiciones individuales.
The percentage thresholds use the account balance captured when the strategy starts. Ajuste los límites cuando realice sesiones largas si la base de capital cambia sustancialmente.
Cuando la orden de prueba opcional está habilitada, la orden auxiliar se vuelve a emitir siempre que la guardia de pérdidas o ganancias haya cerrado la exposición anterior.
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// Basket Close strategy: EMA trend following with profit/loss close thresholds.
/// Enters on EMA direction, closes when accumulated P&L hits target or stop.
/// </summary>
public class BasketCloseStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _emaPeriod;
private decimal _entryPrice;
private bool _wasBullish;
private bool _hasPrevSignal;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
public BasketCloseStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_emaPeriod = Param(nameof(EmaPeriod), 50)
.SetGreaterThanZero()
.SetDisplay("EMA Period", "EMA period", "Indicators");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_entryPrice = 0m;
_wasBullish = false;
_hasPrevSignal = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_entryPrice = 0;
_hasPrevSignal = false;
var ema = new ExponentialMovingAverage { Length = EmaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ema, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal emaValue)
{
if (candle.State != CandleStates.Finished) return;
var close = candle.ClosePrice;
var isBullish = close > emaValue;
if (_hasPrevSignal && isBullish != _wasBullish)
{
if (isBullish && Position <= 0)
{
BuyMarket();
_entryPrice = close;
}
else if (!isBullish && Position >= 0)
{
SellMarket();
_entryPrice = close;
}
}
_wasBullish = isBullish;
_hasPrevSignal = true;
}
}
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 basket_close_strategy(Strategy):
def __init__(self):
super(basket_close_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
self._ema_period = self.Param("EmaPeriod", 50) \
.SetDisplay("EMA Period", "EMA period", "Indicators")
self._entry_price = 0.0
self._was_bullish = False
self._has_prev_signal = False
@property
def candle_type(self):
return self._candle_type.Value
@candle_type.setter
def candle_type(self, value):
self._candle_type.Value = value
@property
def ema_period(self):
return self._ema_period.Value
@ema_period.setter
def ema_period(self, value):
self._ema_period.Value = value
def OnReseted(self):
super(basket_close_strategy, self).OnReseted()
self._entry_price = 0.0
self._was_bullish = False
self._has_prev_signal = False
def OnStarted2(self, time):
super(basket_close_strategy, self).OnStarted2(time)
self._entry_price = 0.0
self._has_prev_signal = False
ema = ExponentialMovingAverage()
ema.Length = self.ema_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(ema, self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ema)
self.DrawOwnTrades(area)
def OnProcess(self, candle, ema_value):
if candle.State != CandleStates.Finished:
return
close = candle.ClosePrice
is_bullish = close > ema_value
if self._has_prev_signal and is_bullish != self._was_bullish:
if is_bullish and self.Position <= 0:
self.BuyMarket()
self._entry_price = float(close)
elif not is_bullish and self.Position >= 0:
self.SellMarket()
self._entry_price = float(close)
self._was_bullish = is_bullish
self._has_prev_signal = True
def CreateClone(self):
return basket_close_strategy()