La Estrategia Ang Zad C Time MM Recovery es un port en C# del asesor experto de MetaTrader 5 Exp_Ang_Zad_C_Tm_MMRec. La estrategia combina el indicador de canal personalizado Ang_Zad_C con un filtro de sesión de trading configurable y un modelo de tamaño de posición adaptativo que reduce el riesgo después de un número configurable de trades perdedores.
Lógica del indicador
El indicador Ang_Zad_C construye dos envolventes adaptativas alrededor del precio. Cada envolvente se actualiza comparando el precio aplicado elegido de la vela actual y la anterior, moviéndose hacia el nuevo precio con el factor de suavizado Ki. Las líneas superior e inferior se evalúan en barras históricas definidas por Signal Bar para evitar actuar en velas no terminadas.
Reglas de trading
Entrada larga – Cuando la línea superior estaba por encima de la línea inferior en la barra de referencia anterior y cruza por debajo o toca la línea inferior en la barra de referencia más reciente. Cuando esto ocurre, cualquier posición corta abierta se cierra antes de abrir una nueva posición larga (si está habilitado).
Entrada corta – Cuando la línea superior estaba por debajo de la línea inferior en la barra de referencia anterior y cruza por encima o toca la línea inferior en la barra de referencia más reciente. Cualquier posición larga abierta se cierra antes de abrir una nueva posición corta (si está habilitado).
Salida larga – Cuando la línea superior está por debajo de la línea inferior en la barra de referencia anterior. La salida puede deshabilitarse mediante Enable Long Exit.
Salida corta – Cuando la línea superior está por encima de la línea inferior en la barra de referencia anterior. La salida puede deshabilitarse mediante Enable Short Exit.
Gestión monetaria y protecciones
El trading solo se permite dentro de la ventana de tiempo configurada cuando Use Time Filter está habilitado. Las posiciones abiertas anteriormente se cierran una vez que finaliza la sesión.
El volumen del trade se selecciona entre Normal Volume y Small Volume dependiendo de cuántos trades perdedores ocurrieron para cada lado. Después de Buy Loss Trigger trades largos perdedores (o Sell Loss Trigger trades cortos perdedores) se usa el volumen reducido hasta que un trade rentable resetea el contador.
Los niveles opcionales de stop-loss y take-profit se registran usando distancias en pasos de precio definidas por Stop Loss Steps y Take Profit Steps.
Parámetros
Nombre
Descripción
Candle Type
Marco temporal de las velas usadas por el indicador y las señales.
Ki
Coeficiente de suavizado de las envolventes Ang_Zad_C.
Applied Price
Qué precio de la vela se alimenta al indicador.
Signal Bar
Cuántas barras atrás se usan para la evaluación de señales (1 = barra cerrada anterior).
Use Time Filter / Trade Start / Trade End
Habilitar trading basado en sesión y establecer la hora de inicio y fin de la sesión.
Enable Long/Short Entry
Permitir la apertura de nuevos trades largos o cortos.
Enable Long/Short Exit
Permitir que la estrategia cierre posiciones en la reversión del indicador.
Buy/Sell Loss Trigger
Número de trades perdedores antes de aplicar el volumen reducido.
Small Volume / Normal Volume
Tamaños de orden usados para riesgo reducido y normal.
Stop Loss Steps / Take Profit Steps
Distancia para órdenes protectoras expresada en pasos de precio.
Notas de conversión
La lógica sigue el código MQL5 original, incluyendo las verificaciones de cruce direccional y el comportamiento de la ventana de tiempo.
La gestión monetaria adaptativa se implementa rastreando el beneficio y pérdida realizados por dirección y cambiando al volumen reducido después del número configurado de pérdidas.
Los cálculos del indicador evitan cualquier acceso directo al buffer y se procesan en velas terminadas usando la API de alto nivel de StockSharp.
using System;
using System.Collections.Generic;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Ang_Zad_C adaptive channel strategy.
/// Trades when the upper/lower lines cross, indicating trend reversal.
/// </summary>
public class AngZadCTimeMMRecoveryStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<decimal> _ki;
private bool _hasState;
private decimal _upperLine;
private decimal _lowerLine;
private decimal _previousPrice;
private decimal? _prevUp;
private decimal? _prevDn;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public decimal Ki
{
get => _ki.Value;
set => _ki.Value = value;
}
public AngZadCTimeMMRecoveryStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Timeframe", "General");
_ki = Param(nameof(Ki), 4.000001m)
.SetDisplay("Ki", "Smoothing coefficient", "Indicator")
.SetGreaterThanZero();
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_hasState = false;
_upperLine = 0m;
_lowerLine = 0m;
_previousPrice = 0m;
_prevUp = null;
_prevDn = null;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_hasState = false;
_prevUp = null;
_prevDn = null;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var price = candle.ClosePrice;
var (upper, lower) = UpdateIndicator(price);
if (_prevUp == null || _prevDn == null)
{
_prevUp = upper;
_prevDn = lower;
return;
}
var prevUp = _prevUp.Value;
var prevDn = _prevDn.Value;
// Buy signal: previous upper was below lower, now crossing above
if (prevUp <= prevDn && upper > lower)
{
if (Position < 0)
BuyMarket();
if (Position <= 0)
BuyMarket();
}
// Sell signal: previous upper was above lower, now crossing below
else if (prevUp >= prevDn && upper < lower)
{
if (Position > 0)
SellMarket();
if (Position >= 0)
SellMarket();
}
_prevUp = upper;
_prevDn = lower;
}
private (decimal Up, decimal Down) UpdateIndicator(decimal price)
{
if (!_hasState)
{
_upperLine = price;
_lowerLine = price;
_previousPrice = price;
_hasState = true;
return (_upperLine, _lowerLine);
}
var ki = Ki;
if (price > _upperLine && price > _previousPrice)
_upperLine += (price - _upperLine) / ki;
if (price < _upperLine && price < _previousPrice)
_upperLine += (price - _upperLine) / ki;
if (price > _lowerLine && price < _previousPrice)
_lowerLine += (price - _lowerLine) / ki;
if (price < _lowerLine && price > _previousPrice)
_lowerLine += (price - _lowerLine) / ki;
_previousPrice = price;
return (_upperLine, _lowerLine);
}
}
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.Strategies import Strategy
class ang_zad_c_time_mm_recovery_strategy(Strategy):
def __init__(self):
super(ang_zad_c_time_mm_recovery_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._ki = self.Param("Ki", 4.000001) \
.SetDisplay("Ki", "Smoothing coefficient", "Indicator")
self._has_state = False
self._upper_line = 0.0
self._lower_line = 0.0
self._previous_price = 0.0
self._prev_up = None
self._prev_dn = None
@property
def CandleType(self):
return self._candle_type.Value
@property
def Ki(self):
return self._ki.Value
def OnReseted(self):
super(ang_zad_c_time_mm_recovery_strategy, self).OnReseted()
self._has_state = False
self._upper_line = 0.0
self._lower_line = 0.0
self._previous_price = 0.0
self._prev_up = None
self._prev_dn = None
def OnStarted2(self, time):
super(ang_zad_c_time_mm_recovery_strategy, self).OnStarted2(time)
self._has_state = False
self._prev_up = None
self._prev_dn = None
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _update_indicator(self, price):
if not self._has_state:
self._upper_line = price
self._lower_line = price
self._previous_price = price
self._has_state = True
return (self._upper_line, self._lower_line)
ki = float(self.Ki)
if price > self._upper_line and price > self._previous_price:
self._upper_line += (price - self._upper_line) / ki
if price < self._upper_line and price < self._previous_price:
self._upper_line += (price - self._upper_line) / ki
if price > self._lower_line and price < self._previous_price:
self._lower_line += (price - self._lower_line) / ki
if price < self._lower_line and price > self._previous_price:
self._lower_line += (price - self._lower_line) / ki
self._previous_price = price
return (self._upper_line, self._lower_line)
def _on_process(self, candle):
if candle.State != CandleStates.Finished:
return
price = float(candle.ClosePrice)
upper, lower = self._update_indicator(price)
if self._prev_up is None or self._prev_dn is None:
self._prev_up = upper
self._prev_dn = lower
return
prev_up = self._prev_up
prev_dn = self._prev_dn
# Buy signal: previous upper was below lower, now crossing above
if prev_up <= prev_dn and upper > lower:
if self.Position < 0:
self.BuyMarket()
if self.Position <= 0:
self.BuyMarket()
# Sell signal: previous upper was above lower, now crossing below
elif prev_up >= prev_dn and upper < lower:
if self.Position > 0:
self.SellMarket()
if self.Position >= 0:
self.SellMarket()
self._prev_up = upper
self._prev_dn = lower
def CreateClone(self):
return ang_zad_c_time_mm_recovery_strategy()