A Waddah Attar Win Grid Strategy replica o consultor especialista MetaTrader 4 do script MQL/8210. Ele mantém continuamente uma escada simétrica de ordens com limite de compra e venda em torno da oferta/venda atual. Quando o preço oscila em direção ao nível de grade mais recente, a estratégia empilha automaticamente uma nova ordem pendente um passo adiante, aumentando opcionalmente o volume de cada ordem adicional. O lucro flutuante é monitorado a cada atualização da carteira de ofertas e, uma vez atingido o ganho patrimonial configurado, todas as posições e ordens de trabalho são fechadas simultaneamente.
Como funciona
Inicialização
Assina atualizações do livro de pedidos para reagir instantaneamente às alterações de compra/venda.
Registra o valor atual do portfólio para usar como referência de patrimônio líquido de base.
Inicia o subsistema integrado de proteção contra riscos do StockSharp.
Gerenciamento de linha de base
Sempre que não existam ordens ativas e a posição líquida seja estável, o último valor da carteira passa a ser o novo saldo de referência. Isso reflete o consultor especialista original, que armazenou o saldo da conta corrente em cada tick.
Colocação inicial da grade
Assim que a negociação for permitida e nenhuma ordem estiver ativa, a estratégia coloca duas ordens pendentes:
Um limite de compra Step Points abaixo do preço de venda atual.
Um limite de venda Step Points acima do preço de oferta atual.
Ambos os pedidos usam o valor First Volume.
Acumulando novos pedidos
Quando o preço de venda se move dentro de cinco etapas de preço do último limite de compra, a estratégia coloca um novo limite de compra um passo abaixo do nível anterior.
Quando o preço de compra se move dentro de cinco etapas de preço do último limite de venda, a estratégia coloca um novo limite de venda um passo completo acima do nível anterior.
Cada nova ordem pendente aumenta o volume em Increment Volume, permitindo a pirâmide no estilo martingale, se desejado.
Captura de lucro
O lucro flutuante é calculado como a diferença entre o patrimônio atual da carteira e o saldo de referência armazenado.
Quando esse lucro exceder Min Profit, todas as ordens ativas serão canceladas e todas as posições abertas serão achatadas com uma única chamada de CloseAll.
O patrimônio da linha de base é atualizado, permitindo que a rede reinicie do zero.
Características da estratégia
Dados de mercado: operam exclusivamente com base em instantâneos da carteira de pedidos de nível 1 (melhor oferta/venda).
Tipos de pedidos: utiliza apenas pedidos com limite; nenhuma parada ou entrada no mercado é gerada automaticamente.
Exposição: pode manter posições compradas e vendidas simultaneamente em carteiras habilitadas para hedge.
Controle de risco: carece de stop-loss rígidos; depende da meta de lucro flutuante e de regras de risco externo.
Reentrada: após o nivelamento ou cancelamento manual de ordens, a grade inicial é recriada automaticamente na próxima vez que o loop de dados de mercado for executado.
Parâmetros
Parâmetro
Padrão
Descrição
Step Points
120
Distância entre níveis de grade consecutivos, expressa em pontos de preço (múltiplos de etapas de preço).
First Volume
0.1
Volume utilizado para o primeiro par de ordens pendentes.
Increment Volume
0.0
Volume adicional adicionado a cada pedido recém-empilhado; definido como zero para manter todos os pedidos do mesmo tamanho.
Min Profit
450
Lucro flutuante (na moeda da conta) necessário para fechar todas as posições abertas e ordens pendentes.
Notas e limitações
Certifique-se de que o PriceStep do instrumento esteja configurado corretamente; a estratégia multiplica Step Points por PriceStep para derivar os preços reais.
Como o algoritmo cancela e substitui ordens com frequência, os limites da corretora ou da bolsa nas contagens de ordens pendentes devem ser considerados.
Não há proteção integrada contra saques – considere combinar a estratégia com gerenciamento de risco externo ou paradas em nível de portfólio.
A rede pode expandir-se indefinidamente se os preços sofrerem tendências acentuadas sem atingir a meta de lucro; escolha Increment Volume com cuidado para controlar o uso da margem.
Arquivos
CS/WaddahAttarWinGridStrategy.cs — implementação em C# da lógica de negociação.
README.md — esta documentação (inglês).
README_ru.md — Tradução para russo com conteúdo idêntico.
README_zh.md — Tradução chinesa com conteúdo idêntico.
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Grid strategy converted from the "Waddah Attar Win" MetaTrader 4 expert advisor.
/// Places paired orders around the market, pyramids positions with an optional volume increment,
/// and closes the entire exposure once the floating profit target is achieved.
/// </summary>
public class WaddahAttarWinGridStrategy : Strategy
{
private readonly StrategyParam<int> _stepPoints;
private readonly StrategyParam<decimal> _firstVolume;
private readonly StrategyParam<decimal> _incrementVolume;
private readonly StrategyParam<decimal> _minProfit;
private readonly StrategyParam<DataType> _candleType;
private decimal _lastBuyGridPrice;
private decimal _lastSellGridPrice;
private decimal _currentBuyVolume;
private decimal _currentSellVolume;
private decimal _referenceBalance;
private bool _gridActive;
/// <summary>
/// Distance in price points between consecutive grid levels.
/// </summary>
public int StepPoints
{
get => _stepPoints.Value;
set => _stepPoints.Value = value;
}
/// <summary>
/// Volume for the very first pair of orders.
/// </summary>
public decimal FirstVolume
{
get => _firstVolume.Value;
set => _firstVolume.Value = value;
}
/// <summary>
/// Volume increment applied to each newly stacked order.
/// </summary>
public decimal IncrementVolume
{
get => _incrementVolume.Value;
set => _incrementVolume.Value = value;
}
/// <summary>
/// Floating profit target in account currency that closes all positions.
/// </summary>
public decimal MinProfit
{
get => _minProfit.Value;
set => _minProfit.Value = value;
}
/// <summary>
/// Candle type for price data.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes strategy parameters.
/// </summary>
public WaddahAttarWinGridStrategy()
{
_stepPoints = Param(nameof(StepPoints), 1500)
.SetGreaterThanZero()
.SetDisplay("Step (Points)", "Distance between grid levels in points", "Grid")
.SetOptimize(20, 400, 10);
_firstVolume = Param(nameof(FirstVolume), 0.1m)
.SetGreaterThanZero()
.SetDisplay("First Volume", "Volume for the initial orders", "Trading");
_incrementVolume = Param(nameof(IncrementVolume), 0m)
.SetDisplay("Increment Volume", "Additional volume added when stacking new orders", "Trading");
_minProfit = Param(nameof(MinProfit), 450m)
.SetNotNegative()
.SetDisplay("Min Profit", "Floating profit target in account currency", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Candle type for price data", "General");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_lastBuyGridPrice = 0m;
_lastSellGridPrice = 0m;
_currentBuyVolume = 0m;
_currentSellVolume = 0m;
_referenceBalance = 0m;
_gridActive = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_referenceBalance = Portfolio?.CurrentValue ?? 0m;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var priceStep = Security?.PriceStep ?? 0.01m;
if (priceStep <= 0m)
priceStep = 0.01m;
var stepOffset = StepPoints * priceStep;
if (stepOffset <= 0m)
return;
var price = candle.ClosePrice;
// Check profit target
var floatingProfit = (Portfolio?.CurrentValue ?? 0m) - _referenceBalance;
if (MinProfit > 0m && floatingProfit >= MinProfit && _gridActive)
{
if (Position > 0)
SellMarket(Position);
else if (Position < 0)
BuyMarket(Math.Abs(Position));
_referenceBalance = Portfolio?.CurrentValue ?? _referenceBalance;
_gridActive = false;
_lastBuyGridPrice = 0m;
_lastSellGridPrice = 0m;
_currentBuyVolume = 0m;
_currentSellVolume = 0m;
return;
}
// Initialize grid on first candle
if (!_gridActive)
{
_lastBuyGridPrice = price;
_lastSellGridPrice = price;
_currentBuyVolume = FirstVolume;
_currentSellVolume = FirstVolume;
_gridActive = true;
_referenceBalance = Portfolio?.CurrentValue ?? _referenceBalance;
return;
}
// Check if price dropped enough to trigger a buy grid level
if (price <= _lastBuyGridPrice - stepOffset)
{
BuyMarket(_currentBuyVolume);
_lastBuyGridPrice = price;
_currentBuyVolume += IncrementVolume;
}
// Check if price rose enough to trigger a sell grid level
if (price >= _lastSellGridPrice + stepOffset)
{
SellMarket(_currentSellVolume);
_lastSellGridPrice = price;
_currentSellVolume += IncrementVolume;
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Strategies import Strategy
class waddah_attar_win_grid_strategy(Strategy):
def __init__(self):
super(waddah_attar_win_grid_strategy, self).__init__()
self._step_points = self.Param("StepPoints", 1500).SetDisplay("Step (Points)", "Distance between grid levels in points", "Grid")
self._first_volume = self.Param("FirstVolume", 0.1).SetDisplay("First Volume", "Volume for the initial orders", "Trading")
self._increment_volume = self.Param("IncrementVolume", 0.0).SetDisplay("Increment Volume", "Additional volume added when stacking new orders", "Trading")
self._min_profit = self.Param("MinProfit", 450.0).SetDisplay("Min Profit", "Floating profit target in account currency", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Candle type for price data", "General")
self._last_buy_grid_price = 0.0
self._last_sell_grid_price = 0.0
self._current_buy_volume = 0.0
self._current_sell_volume = 0.0
self._reference_balance = 0.0
self._grid_active = False
@property
def StepPoints(self): return self._step_points.Value
@property
def FirstVolume(self): return self._first_volume.Value
@property
def IncrementVolume(self): return self._increment_volume.Value
@property
def MinProfit(self): return self._min_profit.Value
@property
def CandleType(self): return self._candle_type.Value
def OnStarted2(self, time):
super(waddah_attar_win_grid_strategy, self).OnStarted2(time)
self._reference_balance = float(self.Portfolio.CurrentValue) if self.Portfolio is not None else 0.0
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self.ProcessCandle).Start()
def ProcessCandle(self, candle):
if candle.State != CandleStates.Finished:
return
price_step = 0.01
if self.Security is not None and self.Security.PriceStep is not None and float(self.Security.PriceStep) > 0:
price_step = float(self.Security.PriceStep)
step_offset = int(self.StepPoints) * price_step
if step_offset <= 0:
return
price = float(candle.ClosePrice)
current_value = float(self.Portfolio.CurrentValue) if self.Portfolio is not None else 0.0
floating_profit = current_value - self._reference_balance
if float(self.MinProfit) > 0 and floating_profit >= float(self.MinProfit) and self._grid_active:
if self.Position > 0:
self.SellMarket(self.Position)
elif self.Position < 0:
self.BuyMarket(abs(self.Position))
self._reference_balance = float(self.Portfolio.CurrentValue) if self.Portfolio is not None else self._reference_balance
self._grid_active = False
self._last_buy_grid_price = 0.0
self._last_sell_grid_price = 0.0
self._current_buy_volume = 0.0
self._current_sell_volume = 0.0
return
if not self._grid_active:
self._last_buy_grid_price = price
self._last_sell_grid_price = price
self._current_buy_volume = float(self.FirstVolume)
self._current_sell_volume = float(self.FirstVolume)
self._grid_active = True
self._reference_balance = float(self.Portfolio.CurrentValue) if self.Portfolio is not None else self._reference_balance
return
if price <= self._last_buy_grid_price - step_offset:
self.BuyMarket(self._current_buy_volume)
self._last_buy_grid_price = price
self._current_buy_volume += float(self.IncrementVolume)
if price >= self._last_sell_grid_price + step_offset:
self.SellMarket(self._current_sell_volume)
self._last_sell_grid_price = price
self._current_sell_volume += float(self.IncrementVolume)
def OnReseted(self):
super(waddah_attar_win_grid_strategy, self).OnReseted()
self._last_buy_grid_price = 0.0
self._last_sell_grid_price = 0.0
self._current_buy_volume = 0.0
self._current_sell_volume = 0.0
self._reference_balance = 0.0
self._grid_active = False
def CreateClone(self):
return waddah_attar_win_grid_strategy()