Converte o utilitário MQL5 Close all positions em uma estratégia de alto nível do StockSharp.
Observa velas terminadas do período configurado e acumula o lucro flutuante de cada posição aberta no portfólio atribuído.
Quando o lucro flutuante é igual ou superior ao limiar, ordens a mercado são enviadas para zerar todos os ativos gerenciados pela estratégia (incluindo estratégias filhas) até que o livro esteja completamente fechado.
O sinalizador _closeAllRequested espelha a variável MQL m_close_all para que as ordens de saída continuem sendo emitidas até que não restem posições.
Parâmetros
Nome
Tipo
Padrão
Descrição
ProfitThreshold
decimal
10
Lucro flutuante (em moeda da conta) necessário antes de a estratégia zerar cada posição aberta. Espelha InpProfit do EA.
CandleType
DataType
Período 1m
Série de velas que define os momentos de "nova barra". A verificação de lucro é executada apenas quando uma vela termina, emulando a lógica PrevBars original.
Lógica de trading
A estratégia assina velas de CandleType e processa apenas barras terminadas, assim como o EA avaliava o lucro apenas em uma nova barra.
A cada barra terminada o helper CalculateTotalProfit recupera Portfolio.CurrentProfit (PnL flutuante incluindo comissão e swap). Se o adaptador não puder fornecer este valor ele recorre à soma dos valores individuais de PnL de posição.
Se o lucro flutuante calculado estiver abaixo de ProfitThreshold, nada acontece.
Assim que o lucro atinge o limiar, _closeAllRequested é definido como true e CloseAllPositions() é executado imediatamente.
CloseAllPositions() coleta cada ativo que tem uma exposição no portfólio ou em estratégias aninhadas e envia ordens a mercado na direção oposta ao volume atual (comprado → venda, vendido → compra).
O sinalizador _closeAllRequested permanece definido até que HasAnyOpenPosition() detecte que o portfólio está zerado, correspondendo ao comportamento MQL onde m_close_all permanecia verdadeiro até que todos os tickets estivessem fechados.
Notas adicionais
Apenas a implementação em C# é fornecida; a pasta Python é intencionalmente deixada vazia conforme os requisitos da tarefa.
A estratégia não cancela ordens pendentes porque o script original apenas fechava posições de mercado.
Usar SetOptimize em ProfitThreshold para explorar alvos de lucro alternativos através do otimizador do Designer se necessário.
Arquivos
CS/CloseAllPositionsStrategy.cs
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>
/// Opens positions based on SMA trend and closes when floating PnL reaches a profit threshold.
/// Simplified from the "Close all positions" utility expert.
/// </summary>
public class CloseAllPositionsStrategy : Strategy
{
private readonly StrategyParam<int> _smaPeriod;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private SimpleMovingAverage _sma;
private decimal _entryPrice;
private decimal _prevSma;
private int _cooldown;
/// <summary>
/// SMA period for entry signals.
/// </summary>
public int SmaPeriod
{
get => _smaPeriod.Value;
set => _smaPeriod.Value = value;
}
/// <summary>
/// Stop-loss distance in price steps.
/// </summary>
public int StopLossPoints
{
get => _stopLossPoints.Value;
set => _stopLossPoints.Value = value;
}
/// <summary>
/// Take-profit distance in price steps.
/// </summary>
public int TakeProfitPoints
{
get => _takeProfitPoints.Value;
set => _takeProfitPoints.Value = value;
}
/// <summary>
/// Initializes strategy parameters.
/// </summary>
public CloseAllPositionsStrategy()
{
_smaPeriod = Param(nameof(SmaPeriod), 100)
.SetGreaterThanZero()
.SetDisplay("SMA Period", "Moving average period for entry signals", "Indicators");
_stopLossPoints = Param(nameof(StopLossPoints), 200)
.SetNotNegative()
.SetDisplay("Stop Loss", "Stop-loss distance in price steps", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 300)
.SetNotNegative()
.SetDisplay("Take Profit", "Take-profit distance in price steps", "Risk");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_sma = null;
_entryPrice = 0;
_prevSma = 0;
_cooldown = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_sma = new SimpleMovingAverage { Length = SmaPeriod };
var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
subscription.Bind(_sma, ProcessCandle);
subscription.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal smaValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_sma.IsFormed)
{
_prevSma = smaValue;
return;
}
if (_cooldown > 0)
{
_cooldown--;
_prevSma = smaValue;
return;
}
var close = candle.ClosePrice;
var step = Security?.PriceStep ?? 1m;
// Check SL/TP
if (Position > 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step)
{
SellMarket();
_entryPrice = 0;
_cooldown = 60;
_prevSma = smaValue;
return;
}
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step)
{
SellMarket();
_entryPrice = 0;
_cooldown = 60;
_prevSma = smaValue;
return;
}
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step)
{
BuyMarket();
_entryPrice = 0;
_cooldown = 60;
_prevSma = smaValue;
return;
}
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step)
{
BuyMarket();
_entryPrice = 0;
_cooldown = 60;
_prevSma = smaValue;
return;
}
}
// Crossover entry: price crosses above SMA -> buy
if (close > smaValue && candle.OpenPrice <= _prevSma && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
_entryPrice = close;
_cooldown = 60;
}
// Price crosses below SMA -> sell
else if (close < smaValue && candle.OpenPrice >= _prevSma && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
_entryPrice = close;
_cooldown = 60;
}
_prevSma = smaValue;
}
}
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_all_positions_strategy(Strategy):
"""
Opens positions based on SMA trend and closes when SL/TP reached.
"""
def __init__(self):
super(close_all_positions_strategy, self).__init__()
self._sma_period = self.Param("SmaPeriod", 100) \
.SetDisplay("SMA Period", "Moving average period for entry signals", "Indicators")
self._stop_loss_points = self.Param("StopLossPoints", 200) \
.SetDisplay("Stop Loss", "Stop-loss distance in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 300) \
.SetDisplay("Take Profit", "Take-profit distance in price steps", "Risk")
self._entry_price = 0.0
self._prev_sma = 0.0
self._cooldown = 0
@property
def candle_type(self):
return DataType.TimeFrame(TimeSpan.FromMinutes(5))
def OnReseted(self):
super(close_all_positions_strategy, self).OnReseted()
self._entry_price = 0.0
self._prev_sma = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(close_all_positions_strategy, self).OnStarted2(time)
sma = SimpleMovingAverage()
sma.Length = self._sma_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(sma, self.on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, sma)
self.DrawOwnTrades(area)
def on_process(self, candle, sma_val):
if candle.State != CandleStates.Finished:
return
if self._prev_sma == 0.0:
self._prev_sma = sma_val
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_sma = sma_val
return
close = float(candle.ClosePrice)
open_price = float(candle.OpenPrice)
step = 1.0
if self.Security is not None and self.Security.PriceStep is not None and float(self.Security.PriceStep) > 0:
step = float(self.Security.PriceStep)
if self.Position > 0 and self._entry_price > 0:
if self._stop_loss_points.Value > 0 and close <= self._entry_price - self._stop_loss_points.Value * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 60
self._prev_sma = sma_val
return
if self._take_profit_points.Value > 0 and close >= self._entry_price + self._take_profit_points.Value * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 60
self._prev_sma = sma_val
return
elif self.Position < 0 and self._entry_price > 0:
if self._stop_loss_points.Value > 0 and close >= self._entry_price + self._stop_loss_points.Value * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 60
self._prev_sma = sma_val
return
if self._take_profit_points.Value > 0 and close <= self._entry_price - self._take_profit_points.Value * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 60
self._prev_sma = sma_val
return
if close > sma_val and open_price <= self._prev_sma and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 60
elif close < sma_val and open_price >= self._prev_sma and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 60
self._prev_sma = sma_val
def CreateClone(self):
return close_all_positions_strategy()