A estratégia Basket Close Utility reflete o comportamento do especialista MetaTrader "Basket Close 2". Ele monitora continuamente os lucros e perdas flutuantes de cada posição aberta na carteira conectada. 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, ele pode abrir automaticamente uma pequena posição de teste sempre que o livro estiver plano, o que é útil em backtests para validar se a lógica de proteção funciona conforme o esperado.
Parâmetros
Nome
Descrição
LossMode
Chooses whether the loss guard compares percentages or currency values.
LossPercentage
Negative percentage drawdown (expressed in absolute value) that triggers the loss exit when LossMode is Percentage.
LossCurrency
Floating loss in account currency that triggers the exit when LossMode is Currency.
ProfitMode
Escolhe se o objetivo de lucro compara porcentagens ou valores monetários.
ProfitPercentage
Percentage gain that closes all positions when ProfitMode is Percentage.
ProfitCurrency
Lucro flutuante na moeda da conta que fecha todas as posições quando ProfitMode é Currency.
CandleType
Prazo usado para acionar verificações periódicas dos lucros e perdas flutuantes.
EnableTestOrders
Quando habilitada, a estratégia envia uma única ordem de compra de mercado sempre que não houver posições abertas.
TestOrderVolume
Tamanho da negociação usado quando a ordem de teste opcional está ativa.
Lógica de negociação
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.
Agregue os lucros e perdas flutuantes de cada posição aberta. If the portfolio object exposes a combined floating profit it is used; otherwise the strategy sums the PnL of each position.
Compute the percentage change relative to the current account balance captured at start-up.
Acione a rotina de perda quando o PnL flutuante ultrapassar o limite configurado. Trigger the profit routine when the floating PnL or the percentage gain reaches the profit target.
Uma vez acionado, continue enviando ordens de mercado até que todas as posições abertas em todo o portfólio sejam fechadas. Isso inclui o título principal, bem como as posições abertas por estratégias infantis.
Opcionalmente, envie uma ordem de mercado para reabrir a exposição (para teste) depois que o livro ficar estável.
Notas
O especialista MetaTrader exibiu informações textuais no gráfico. Em StockSharp, os números importantes são registrados por meio de LogInfo.
Os ajustes de swap e comissão do script original são implicitamente incluídos no PnL flutuante reportado pela carteira ou posições individuais.
Os limites percentuais utilizam o saldo da conta capturado quando a estratégia é iniciada. Ajuste os limites ao realizar sessões longas se a base de capital mudar substancialmente.
Quando a ordem de teste opcional está habilitada, a ordem auxiliar é reemitida sempre que a exposição anterior tiver sido encerrada pela guarda de lucros ou perdas.
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()