Esta estratégia porta o expert advisor do MetaTrader Triangle v1 para a API de alto nível do StockSharp. O EA original combinava filtros de média móvel ponderada em um período mais alto, uma verificação de divergência de momentum e uma confirmação MACD de prazo muito longo antes de colocar ordens no estilo rompimento. A versão StockSharp mantém a lógica multitemporal, substituindo a gestão de dinheiro tick a tick por ordens de proteção baseadas em candles.
Como funciona
Filtros multitemporais. O período de trabalho (CandleType, padrão 15 minutos) é usado para executar operações. Filtros de tendência e momentum são calculados em um período mais alto (TrendCandleType, padrão 1 hora) para espelhar as chamadas MQL que referenciavam T.
Portão de tendência LWMA. Médias móveis ponderadas rápida e lenta (equivalente LWMA) devem estar alinhadas. Configurações compradas exigem que a LWMA rápida permaneça acima da LWMA lenta; vendidas exigem a relação oposta.
Desvio de momentum. Uma série de momentum de 14 períodos no período mais alto deve se desviar do nível neutro (100) em pelo menos MomentumThreshold em qualquer um dos três últimos candles concluídos, reproduzindo as verificações MomLevelB/MomLevelS.
Confirmação MACD. Um período muito alto (MacdCandleType, padrão candles de 30 dias ≈ mensal) deve mostrar a linha principal MACD no lado correto da linha de sinal antes que operações sejam permitidas, copiando a condição MacdMAIN0 versus MacdSIGNAL0.
Saídas de proteção. Distâncias de stop loss e take profit são configuradas em passos de preço. Quando qualquer nível é atingido em uma barra concluída, a estratégia fecha a posição com uma ordem a mercado.
Parâmetros
Parâmetro
Descrição
FastMaPeriod, SlowMaPeriod
Comprimentos das médias móveis ponderadas do período mais alto.
MomentumPeriod
Período do filtro de momentum no período mais alto.
MomentumThreshold
Desvio absoluto mínimo a partir de 100 exigido em qualquer uma das três últimas leituras de momentum. Defina como 0 para desabilitar o filtro.
MacdFastLength, MacdSlowLength, MacdSignalLength
Parâmetros MACD aplicados a MacdCandleType.
StopLossSteps, TakeProfitSteps
Stop de proteção e distâncias de alvo medidos em passos de preço do instrumento (ticks). Use 0 para desabilitar.
CandleType
Período de negociação usado para execução de ordens.
TrendCandleType
Período mais alto que alimenta LWMAs e momentum.
MacdCandleType
Período usado para o filtro de confirmação MACD.
Uso
Selecione um ativo e configure CandleType, TrendCandleType e MacdCandleType para corresponder aos períodos que deseja analisar.
Ajuste comprimentos de MA, momentum e MACD se quiser adaptar o sistema a outro mercado ou regime de volatilidade.
Defina StopLossSteps e TakeProfitSteps de acordo com o tamanho do tick do instrumento. A estratégia converte automaticamente as contagens de passos em distâncias reais de preço.
Inicie a estratégia. Ela assina todos os fluxos de candles necessários, atualiza indicadores com a API de alto nível Bind e gerencia a posição quando stops ou alvos são atingidos.
Diferenças em relação ao EA original
Saídas baseadas em dinheiro (Use_TP_In_Money, Use_TP_In_percent) e o bloco de proteção de saldo não são recriados porque o StockSharp trabalha em unidades do instrumento. Comportamento equivalente pode ser obtido ajustando StopLossSteps/TakeProfitSteps.
Lógicas de trailing-stop, break-even e equity-stop do EA dependiam de processamento de ticks e chamadas de modificação de ordens específicas do MetaTrader. A versão mantém a abordagem mais simples de stop fixo por clareza; usuários podem estender UpdatePositionState com regras de trailing se desejarem.
Linhas de tendência manuais (TREND/TRENDLOW) e arrays de fractais eram usados como filtros discricionários no EA. Eles são omitidos intencionalmente para que a estratégia StockSharp permaneça totalmente sistemática.
A estratégia sempre mantém no máximo uma posição líquida, o que corresponde ao uso típico, embora o EA expusesse um parâmetro Max_Trades.
Ajuste os limiares e parâmetros de período ao instrumento negociado. Valores mais amplos geralmente são necessários para mercados voláteis, a fim de evitar filtragem por pequenas flutuações de momentum.
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;
public class TriangleStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private ExponentialMovingAverage _fast;
private ExponentialMovingAverage _slow;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
private int _cooldown;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }
public TriangleStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
protected override void OnReseted()
{
base.OnReseted();
_fast = null; _slow = null;
_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fast = new ExponentialMovingAverage { Length = FastPeriod };
_slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
subscription.Bind(_fast, _slow, ProcessCandle);
subscription.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished) return;
if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }
var close = candle.ClosePrice;
var step = Security?.PriceStep ?? 1m;
if (Position > 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }
_prevFast = fastValue; _prevSlow = slowValue;
}
}
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 ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class triangle_strategy(Strategy):
def __init__(self):
super(triangle_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 14) \
.SetDisplay("Fast Period", "Fast MA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 50) \
.SetDisplay("Slow Period", "Slow MA period", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 200) \
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 400) \
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk")
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def stop_loss_points(self):
return self._stop_loss_points.Value
@property
def take_profit_points(self):
return self._take_profit_points.Value
def OnReseted(self):
super(triangle_strategy, self).OnReseted()
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(triangle_strategy, self).OnStarted2(time)
self._fast = ExponentialMovingAverage()
self._fast.Length = self.fast_period
self._slow = ExponentialMovingAverage()
self._slow.Length = self.slow_period
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(5)))
subscription.Bind(self._fast, self._slow, self._process_candle)
subscription.Start()
def _process_candle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_value)
slow_val = float(slow_value)
if not self._fast.IsFormed or not self._slow.IsFormed:
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = float(candle.ClosePrice)
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
if self.Position > 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close <= self._entry_price - self.stop_loss_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close >= self._entry_price + self.take_profit_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close >= self._entry_price + self.stop_loss_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close <= self._entry_price - self.take_profit_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._prev_fast <= self._prev_slow and fast_val > slow_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 100
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return triangle_strategy()