Esta estratégia reproduz o consultor especialista MetaTrader Close_on_PROFIT_or_LOSS_inAccont_Currency. Ele monitora continuamente o patrimônio do portfólio ao qual a estratégia está vinculada e, uma vez atingida uma meta de lucro configurada ou piso de rebaixamento, liquida todas as posições abertas e cancela todas as ordens pendentes gerenciadas pela estratégia. A classe depende do alto nível de StockSharp API: uma assinatura de vela fornece o batimento cardíaco, CancelActiveOrders() remove ordens de trabalho e ClosePosition() nivela a exposição por meio de ordens de mercado.
Como funciona
A estratégia continua pesquisando o patrimônio atual (Portfolio.CurrentValue) sempre que uma vela de pulsação fecha.
Se o patrimônio for maior ou igual a Fecho Positivo, a estratégia envia uma solicitação de fechamento completo.
Se o patrimônio for menor ou igual a Fecho Negativo, a mesma rotina de liquidação é executada para limitar as perdas.
Durante a liquidação, a estratégia cancela todas as ordens pendentes, envia ordens de mercado para fechar todas as posições ativas e, finalmente, para (espelhando a chamada ExpertRemove() do EA original).
Importante: defina os limites na moeda da conta. Para emular o comportamento original, escolha um valor de Fecho Positivo acima do patrimônio atual e um valor de Fecho Negativo abaixo dele; caso contrário, a saída será acionada imediatamente na partida.
Parâmetros
Nome
Descrição
Padrão
PositiveClosureInAccountCurrency
Nível de patrimônio que desencadeia uma liquidação total quando excedido.
0
NegativeClosureInAccountCurrency
Piso patrimonial que força a liquidação quando atingido.
0
CandleType
Período usado para as velas de pulsação que orientam as verificações de patrimônio. Reduza-o para reações mais rápidas.
1 minute
Notas
StartProtection() é ativado na inicialização para copiar o comportamento de segurança original.
A estratégia interage apenas com posições e ordens que administra; anexe-o à carteira que contém as negociações que você deseja proteger.
Não há entrada separada de spread/slippage porque StockSharp ordens de mercado já contabilizam custos de execução específicos do conector.
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
using StockSharp.Algo.Candles;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Closes all positions when portfolio equity reaches configured profit or loss thresholds.
/// Pending orders are cancelled before liquidating the exposure.
/// </summary>
public class CloseOnProfitOrLossInAccountCurrencyStrategy : Strategy
{
private readonly StrategyParam<decimal> _positiveClosure;
private readonly StrategyParam<decimal> _negativeClosure;
private readonly StrategyParam<DataType> _candleType;
private bool _closeRequested;
private SimpleMovingAverage _smaFast;
private SimpleMovingAverage _smaSlow;
/// <summary>
/// Equity level in account currency that triggers closing all positions when exceeded.
/// </summary>
public decimal PositiveClosureInAccountCurrency
{
get => _positiveClosure.Value;
set => _positiveClosure.Value = value;
}
/// <summary>
/// Equity level in account currency that triggers closing all positions when reached on drawdown.
/// </summary>
public decimal NegativeClosureInAccountCurrency
{
get => _negativeClosure.Value;
set => _negativeClosure.Value = value;
}
/// <summary>
/// Candle type used as a heartbeat to evaluate portfolio equity.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="CloseOnProfitOrLossInAccountCurrencyStrategy"/> class.
/// </summary>
public CloseOnProfitOrLossInAccountCurrencyStrategy()
{
_positiveClosure = Param(nameof(PositiveClosureInAccountCurrency), 0m)
.SetDisplay("Positive Closure", "Equity level that triggers full liquidation", "Risk");
_negativeClosure = Param(nameof(NegativeClosureInAccountCurrency), 0m)
.SetDisplay("Negative Closure", "Equity floor that forces liquidation", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Heartbeat Candle", "Candle type that triggers equity checks", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
if (Security == null)
return Array.Empty<(Security, DataType)>();
return new (Security, DataType)[] { (Security, CandleType) };
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
if (Portfolio == null)
throw new InvalidOperationException("Portfolio cannot be null.");
if (Security == null)
throw new InvalidOperationException("Security must be set to subscribe for candles.");
_smaFast = new SimpleMovingAverage { Length = 10 };
_smaSlow = new SimpleMovingAverage { Length = 30 };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_smaFast, _smaSlow, ProcessCandleWithIndicators)
.Start();
}
private void ProcessCandleWithIndicators(ICandleMessage candle, decimal fast, decimal slow)
{
if (candle.State != CandleStates.Finished)
return;
if (fast > slow && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
}
else if (fast < slow && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
}
}
private void RequestCloseAll(string reason)
{
if (_closeRequested)
return;
_closeRequested = true;
LogInfo(reason);
// Cancel any pending orders to avoid unexpected executions during liquidation.
CancelActiveOrders();
foreach (var position in Positions.ToArray())
{
var value = GetPositionValue(position.Security, Portfolio) ?? 0m;
if (value == 0m)
continue;
// Submit a market order opposite to the current exposure.
ClosePosition(position.Security);
}
// Stop the strategy after sending exit orders, mirroring ExpertRemove behavior.
Stop();
}
}
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 SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class close_on_profit_or_loss_in_account_currency_strategy(Strategy):
"""
SMA crossover strategy with equity-based closure.
Uses fast/slow SMA crossover for entries.
"""
def __init__(self):
super(close_on_profit_or_loss_in_account_currency_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Heartbeat Candle", "Candle type that triggers equity checks", "General")
@property
def candle_type(self):
return self._candle_type.Value
def OnStarted2(self, time):
super(close_on_profit_or_loss_in_account_currency_strategy, self).OnStarted2(time)
sma_fast = SimpleMovingAverage()
sma_fast.Length = 10
sma_slow = SimpleMovingAverage()
sma_slow.Length = 30
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(sma_fast, sma_slow, self.on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, sma_fast)
self.DrawIndicator(area, sma_slow)
self.DrawOwnTrades(area)
def on_process(self, candle, fast_val, slow_val):
if candle.State != CandleStates.Finished:
return
if fast_val > slow_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif fast_val < slow_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
def CreateClone(self):
return close_on_profit_or_loss_in_account_currency_strategy()