A Estratégia Ang Zad C Time MM Recovery é uma porta em C# do consultor especialista MetaTrader 5 Exp_Ang_Zad_C_Tm_MMRec. A estratégia combina o indicador de canal personalizado Ang_Zad_C com um filtro de sessão de trading configurável e um modelo de tamanho de posição adaptativo que reduz o risco após um número configurável de trades perdedores.
Lógica do indicador
O indicador Ang_Zad_C constrói dois envelopes adaptativos em torno do preço. Cada envelope é atualizado comparando o preço aplicado escolhido da vela atual e da anterior, movendo-se em direção ao novo preço com o fator de suavização Ki. As linhas superior e inferior são avaliadas em barras históricas definidas por Signal Bar para evitar agir em velas não terminadas.
Regras de trading
Entrada comprada – Quando a linha superior estava acima da linha inferior na barra de referência anterior e cruza abaixo ou toca a linha inferior na barra de referência mais recente. Quando isso acontece, qualquer posição vendida aberta é fechada antes de abrir uma nova posição comprada (se habilitado).
Entrada vendida – Quando a linha superior estava abaixo da linha inferior na barra de referência anterior e cruza acima ou toca a linha inferior na barra de referência mais recente. Qualquer posição comprada aberta é fechada antes de abrir uma nova posição vendida (se habilitado).
Saída comprada – Quando a linha superior está abaixo da linha inferior na barra de referência anterior. A saída pode ser desabilitada via Enable Long Exit.
Saída vendida – Quando a linha superior está acima da linha inferior na barra de referência anterior. A saída pode ser desabilitada via Enable Short Exit.
Gestão monetária e proteções
O trading é permitido apenas dentro da janela de tempo configurada quando Use Time Filter está habilitado. Posições abertas anteriormente são fechadas assim que a sessão termina.
O volume do trade é selecionado entre Normal Volume e Small Volume dependendo de quantos trades perdedores ocorreram para cada lado. Após Buy Loss Trigger trades comprados perdedores (ou Sell Loss Trigger trades vendidos perdedores) o volume reduzido é usado até que um trade lucrativo resete o contador.
Níveis opcionais de stop-loss e take-profit são registrados usando distâncias em passos de preço definidas por Stop Loss Steps e Take Profit Steps.
Parâmetros
Nome
Descrição
Candle Type
Período das velas usadas pelo indicador e sinais.
Ki
Coeficiente de suavização dos envelopes Ang_Zad_C.
Applied Price
Qual preço da vela é alimentado ao indicador.
Signal Bar
Quantas barras atrás são usadas para avaliação de sinais (1 = barra fechada anterior).
Use Time Filter / Trade Start / Trade End
Habilitar trading baseado em sessão e definir o horário de início e fim da sessão.
Enable Long/Short Entry
Permitir a abertura de novos trades comprados ou vendidos.
Enable Long/Short Exit
Permitir que a estratégia feche posições na reversão do indicador.
Buy/Sell Loss Trigger
Número de trades perdedores antes de aplicar o volume reduzido.
Small Volume / Normal Volume
Tamanhos de ordem usados para risco reduzido e normal.
Stop Loss Steps / Take Profit Steps
Distância para ordens protetoras expressa em passos de preço.
Notas de conversão
A lógica segue o código MQL5 original, incluindo as verificações de cruzamento direcional e o comportamento da janela de tempo.
A gestão monetária adaptativa é implementada rastreando o lucro e prejuízo realizados por direção e mudando para o volume reduzido após o número configurado de perdas.
Os cálculos do indicador evitam qualquer acesso direto ao buffer e são processados em velas terminadas usando a API de alto nível do 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()