La Estrategia Waddah Attar Win Grid replica el asesor experto MetaTrader 4 del script MQL/8210. Mantiene continuamente una escalera simétrica de órdenes límite de compra y venta en torno a la oferta/demanda actual. Cuando el precio se desplaza hacia el nivel de cuadrícula más reciente, la estrategia automáticamente acumula una nueva orden pendiente un paso más lejos, aumentando opcionalmente el volumen de cada orden adicional. El beneficio flotante se supervisa en cada actualización del libro de órdenes y, una vez que se alcanza la ganancia de capital configurada, todas las posiciones y órdenes de trabajo se cierran simultáneamente.
como funciona
Inicialización
Suscríbase a las actualizaciones del libro de pedidos para reaccionar instantáneamente a los cambios de oferta/demanda.
Registra el valor actual de la cartera para utilizarlo como referencia de capital de referencia.
Inicia el subsistema de protección de riesgos integrado de StockSharp.
Gestión de línea base
Siempre que no haya órdenes activas y la posición neta sea plana, el último valor de la cartera se convierte en el nuevo saldo de referencia. Esto refleja el asesor experto original, que almacenaba el saldo de la cuenta corriente en cada tick.
Ubicación inicial de la cuadrícula
Tan pronto como se permite el comercio y no hay órdenes activas, la estrategia coloca dos órdenes pendientes:
Un límite de compra Step Points por debajo del precio de venta actual.
Un límite de venta Step Points por encima del precio de oferta actual.
Ambos pedidos utilizan el valor First Volume.
Acumulación de nuevos pedidos
Cuando el precio de venta se mueve dentro de cinco pasos de precio del último límite de compra, la estrategia coloca un nuevo límite de compra un paso completo por debajo del nivel anterior.
Cuando el precio de oferta se mueve dentro de cinco pasos de precio del último límite de venta, la estrategia coloca un nuevo límite de venta un paso completo por encima del nivel anterior.
Cada nueva orden pendiente aumenta el volumen en Increment Volume, lo que permite realizar una pirámide estilo martingala si lo desea.
Captura de beneficios
El beneficio flotante se calcula como la diferencia entre el capital actual de la cartera y el saldo de referencia almacenado.
Una vez que esta ganancia excede Min Profit, cada orden activa se cancela y todas las posiciones abiertas se nivelan con una sola llamada CloseAll.
El valor de referencia se actualiza, lo que permite que la red se reinicie desde cero.
Características de la estrategia
Datos de mercado: opera exclusivamente en instantáneas de la cartera de pedidos de nivel 1 (mejor oferta/demanda).
Tipos de órdenes: utiliza solo órdenes limitadas; no se generan paradas ni entradas de mercado automáticamente.
Exposición: puede mantener posiciones largas y cortas simultáneas en carteras habilitadas para cobertura.
Control de riesgos: carece de límites de pérdidas estrictos; se basa en el objetivo de beneficios flotantes y en normas de riesgo externas.
Reingreso: después de aplanar o cancelar manualmente las órdenes, la cuadrícula inicial se recrea automáticamente la próxima vez que se ejecuta el ciclo de datos del mercado.
Parámetros
Parámetro
Predeterminado
Descripción
Step Points
120
Distancia entre niveles de cuadrícula consecutivos, expresada en puntos de precio (múltiplos de pasos de precio).
First Volume
0.1
Volumen utilizado para el primer par de órdenes pendientes.
Increment Volume
0.0
Volumen adicional agregado a cada pedido recién acumulado; establezca en cero para mantener todos los pedidos del mismo tamaño.
Min Profit
450
Se requiere beneficio flotante (en la moneda de la cuenta) para cerrar todas las posiciones abiertas y órdenes pendientes.
Notas y limitaciones
Asegúrese de que el PriceStep del instrumento esté configurado correctamente; la estrategia multiplica Step Points por PriceStep para obtener precios reales.
Debido a que el algoritmo cancela y reemplaza órdenes con frecuencia, se deben considerar los límites del corredor o de la bolsa en el recuento de órdenes pendientes.
No existe una protección contra caídas incorporada; considere combinar la estrategia con gestión de riesgos externa o paradas a nivel de cartera.
La red puede expandirse indefinidamente si los precios evolucionan bruscamente sin alcanzar el objetivo de ganancias; elija Increment Volume cuidadosamente para controlar el uso del margen.
Archivos
CS/WaddahAttarWinGridStrategy.cs — Implementación en C# de la lógica comercial.
README.md — esta documentación (inglés).
README_ru.md — Traducción al ruso con contenido idéntico.
README_zh.md — Traducción al chino con contenido 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()