Estratégia BARS Alligator
A estratégia BARS Alligator é um port direto do consultor especialista do MetaTrader com o mesmo nome. Ela depende do indicador Alligator de Bill Williams para detectar tendências emergentes: quando a linha verde dos lábios (lips) cruza acima da linha azul da mandíbula (jaw), o sistema trata isso como um rompimento altista, enquanto um cruzamento para baixo sinaliza impulso baixista. As saídas dependem do lips cruzando a linha vermelha dos dentes (teeth) para que as posições sejam fechadas assim que o impulso desaparece. As distâncias de stop-loss de proteção, take-profit e trailing stop são configuradas em pips e automaticamente convertidas em unidades de preço com base no passo de preço do instrumento e na precisão decimal.
Lógica de trading
- Construção do indicador
- Três médias móveis com comprimentos, deslocamentos e tipo configuráveis (simples, exponencial, suavizada ou ponderada) formam o Alligator.
- O preço aplicado pode ser o fechamento, abertura, máxima, mínima, mediano, típico ou preço ponderado de cada vela.
- Os deslocamentos são respeitados armazenando um pequeno buffer rotativo para cada linha para que os cruzamentos usem os mesmos valores que apareceriam em um gráfico do MetaTrader.
- Condições de entrada
- Comprado: a linha lips na barra anterior está acima da jaw e estava abaixo duas barras atrás (cruzamento altista para cima).
- Vendido: a linha lips na barra anterior está abaixo da jaw e estava acima duas barras atrás (cruzamento baixista para baixo).
- Novas entradas são permitidas apenas se a posição atual está plana ou já alinhada com a direção do sinal e o tamanho agregado da posição permanece abaixo de
MaxPositions × OrderVolume (ou o equivalente dimensionado por risco).
- Condições de saída
- Saída comprada: a linha lips cruza abaixo da linha teeth e a posição é lucrativa em relação ao preço médio de entrada.
- Saída vendida: a linha lips cruza acima da linha teeth e a posição é lucrativa.
- As saídas também ocorrem quando os níveis estáticos de stop-loss ou take-profit são violados.
- Trailing stop
- Quando habilitado, um trailing stop reposiciona o stop de proteção assim que o preço se move além de
TrailingStopPips + TrailingStepPips na direção do trade. O stop então segue o preço a uma distância de TrailingStopPips pips, mas apenas avança se o preço fizer novo progresso de pelo menos TrailingStepPips pips.
- Gestão monetária
- Com
MoneyMode = FixedVolume, as ordens usam o tamanho de OrderVolume diretamente.
- Com
MoneyMode = RiskPercent, a estratégia aloca volume de forma que o percentual configurado MoneyValue do capital do portfólio seria perdido se o stop-loss fosse atingido. O risco por unidade equivale à distância do stop-loss expressa em unidades de preço. O resultado é arredondado para baixo até o VolumeStep mais próximo (ou para 1 quando informações de step estão faltando).
Parâmetros
| Parâmetro |
Tipo |
Padrão |
Descrição |
CandleType |
DataType |
TimeSpan.FromHours(1).TimeFrame() |
Período usado para cálculos do Alligator. |
OrderVolume |
decimal |
0.1 |
Volume de trade fixo quando MoneyMode é FixedVolume. |
MoneyMode |
MoneyManagementMode |
FixedVolume |
Escolhe entre volume fixo e dimensionamento por percentual de risco. |
MoneyValue |
decimal |
1 |
Percentual de risco aplicado quando MoneyMode é RiskPercent; ignorado caso contrário. |
MaxPositions |
int |
1 |
Número máximo de entradas aditivas por direção (expresso como múltiplos do volume de ordem calculado). |
StopLossPips |
int |
150 |
Distância de stop-loss em pips. Zero desabilita o stop de proteção. |
TakeProfitPips |
int |
150 |
Distância de take-profit em pips. Zero desabilita o alvo de lucro. |
TrailingStopPips |
int |
5 |
Distância do trailing stop em pips. Zero desabilita o trailing. |
TrailingStepPips |
int |
5 |
Distância extra que o preço deve percorrer antes de o trailing stop avançar. Deve ser positivo quando o trailing está habilitado. |
JawPeriod |
int |
13 |
Comprimento da média móvil jaw. |
JawShift |
int |
8 |
Deslocamento para frente (em barras) aplicado à série jaw. |
TeethPeriod |
int |
8 |
Comprimento da média móvil teeth. |
TeethShift |
int |
5 |
Deslocamento para frente aplicado à série teeth. |
LipsPeriod |
int |
5 |
Comprimento da média móvil lips. |
LipsShift |
int |
3 |
Deslocamento para frente aplicado à série lips. |
MaType |
MovingAverageType |
Smoothed |
Algoritmo de média móvil usado para as três linhas do Alligator. |
AppliedPrice |
AppliedPriceType |
Median |
Preço da vela fornecido às médias móveis (fechamento, abertura, máxima, mínima, mediano, típico ou ponderado). |
Conversão de pips
A estratégia multiplica as configurações de pip pelo PriceStep do ativo. Quando o instrumento usa 3 ou 5 casas decimais, o valor é ajustado em ×10 para imitar a definição de pip do MetaTrader para cotações fracionárias. Se nenhum passo de preço estiver disponível, assume-se um valor de 1.
Notas de implementação
MaxPositions atua sobre o tamanho agregado da posição porque o StockSharp opera em modo netting. Entradas adicionais aumentam o preço médio em vez de criar tickets de posição separados.
- O stop-loss e o take-profit são rastreados internamente e executados com ordens de mercado na primeira vela que viola os limites, correspondendo ao comportamento do especialista MQL original.
- O dimensionamento baseado em risco requer uma distância de stop-loss diferente de zero; caso contrário, o sistema recorre ao
OrderVolume fixo.
- Todos os valores dos indicadores são atualizados apenas em velas concluídas (
CandleStates.Finished) para evitar sinais prematuros.
namespace StockSharp.Samples.Strategies;
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Candles;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
/// <summary>
/// Bill Williams Alligator strategy: trades on lips/jaw crossover and exits on lips/teeth crossover.
/// </summary>
public class BarsAlligatorStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cooldownBars;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<decimal> _takeProfitPercent;
private readonly SmoothedMovingAverage _jaw = new() { Length = 13 };
private readonly SmoothedMovingAverage _teeth = new() { Length = 8 };
private readonly SmoothedMovingAverage _lips = new() { Length = 5 };
private decimal _previousJaw;
private decimal _previousTeeth;
private decimal _previousLips;
private bool _hasPrevious;
private decimal? _entryPrice;
private int _cooldownLeft;
public BarsAlligatorStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame()).SetDisplay("Candle Type", "Timeframe", "General");
_cooldownBars = Param(nameof(CooldownBars), 6).SetNotNegative().SetDisplay("Cooldown Bars", "Bars between completed trades", "Trading");
_stopLossPercent = Param(nameof(StopLossPercent), 3m).SetDisplay("Stop Loss %", "Stop distance as percentage of entry price", "Risk");
_takeProfitPercent = Param(nameof(TakeProfitPercent), 3m).SetDisplay("Take Profit %", "Take-profit distance as percentage of entry price", "Risk");
}
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int CooldownBars { get => _cooldownBars.Value; set => _cooldownBars.Value = value; }
public decimal StopLossPercent { get => _stopLossPercent.Value; set => _stopLossPercent.Value = value; }
public decimal TakeProfitPercent { get => _takeProfitPercent.Value; set => _takeProfitPercent.Value = value; }
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_previousJaw = 0m;
_previousTeeth = 0m;
_previousLips = 0m;
_hasPrevious = false;
_entryPrice = null;
_cooldownLeft = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
OnReseted();
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_jaw, _teeth, _lips, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal jaw, decimal teeth, decimal lips)
{
if (candle.State != CandleStates.Finished)
return;
if (_cooldownLeft > 0)
_cooldownLeft--;
if (Position != 0 && _entryPrice is null)
_entryPrice = candle.ClosePrice;
if (TryExitByRisk(candle))
{
UpdatePrevious(jaw, teeth, lips);
return;
}
if (!_hasPrevious)
{
UpdatePrevious(jaw, teeth, lips);
return;
}
// Exit conditions: lips crosses teeth against position
var closeLong = lips < teeth && _previousLips >= _previousTeeth && Position > 0;
var closeShort = lips > teeth && _previousLips <= _previousTeeth && Position < 0;
if (closeLong)
{
SellMarket(Position);
_entryPrice = null;
_cooldownLeft = CooldownBars;
UpdatePrevious(jaw, teeth, lips);
return;
}
if (closeShort)
{
BuyMarket(Math.Abs(Position));
_entryPrice = null;
_cooldownLeft = CooldownBars;
UpdatePrevious(jaw, teeth, lips);
return;
}
if (!IsFormedAndOnlineAndAllowTrading() || _cooldownLeft > 0)
{
UpdatePrevious(jaw, teeth, lips);
return;
}
// Entry: lips crosses jaw with proper Alligator ordering
var buySignal = lips > jaw && _previousLips <= _previousJaw && lips > teeth;
var sellSignal = lips < jaw && _previousLips >= _previousJaw && lips < teeth;
if (buySignal && Position <= 0)
{
if (Position < 0)
{
BuyMarket(Math.Abs(Position));
_entryPrice = null;
_cooldownLeft = CooldownBars;
}
else
{
BuyMarket();
_entryPrice = candle.ClosePrice;
_cooldownLeft = CooldownBars;
}
}
else if (sellSignal && Position >= 0)
{
if (Position > 0)
{
SellMarket(Position);
_entryPrice = null;
_cooldownLeft = CooldownBars;
}
else
{
SellMarket();
_entryPrice = candle.ClosePrice;
_cooldownLeft = CooldownBars;
}
}
UpdatePrevious(jaw, teeth, lips);
}
private bool TryExitByRisk(ICandleMessage candle)
{
if (_entryPrice is not decimal entryPrice || Position == 0 || entryPrice == 0)
return false;
var stopDistance = entryPrice * StopLossPercent / 100m;
var takeDistance = entryPrice * TakeProfitPercent / 100m;
if (Position > 0)
{
if ((stopDistance > 0 && candle.LowPrice <= entryPrice - stopDistance) ||
(takeDistance > 0 && candle.HighPrice >= entryPrice + takeDistance))
{
SellMarket(Position);
_entryPrice = null;
_cooldownLeft = CooldownBars;
return true;
}
}
else if (Position < 0)
{
var volume = Math.Abs(Position);
if ((stopDistance > 0 && candle.HighPrice >= entryPrice + stopDistance) ||
(takeDistance > 0 && candle.LowPrice <= entryPrice - takeDistance))
{
BuyMarket(volume);
_entryPrice = null;
_cooldownLeft = CooldownBars;
return true;
}
}
return false;
}
private void UpdatePrevious(decimal jaw, decimal teeth, decimal lips)
{
_previousJaw = jaw;
_previousTeeth = teeth;
_previousLips = lips;
_hasPrevious = true;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
clr.AddReference("StockSharp.BusinessEntities")
from System import TimeSpan, Math, Decimal
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import SmoothedMovingAverage, CandleIndicatorValue
from StockSharp.Algo.Strategies import Strategy
class bars_alligator_strategy(Strategy):
def __init__(self):
super(bars_alligator_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._cooldown_bars = self.Param("CooldownBars", 6) \
.SetDisplay("Cooldown Bars", "Bars between completed trades", "Trading")
self._stop_loss_percent = self.Param("StopLossPercent", Decimal(3)) \
.SetDisplay("Stop Loss %", "Stop distance as percentage of entry price", "Risk")
self._take_profit_percent = self.Param("TakeProfitPercent", Decimal(3)) \
.SetDisplay("Take Profit %", "Take-profit distance as percentage of entry price", "Risk")
self._jaw = SmoothedMovingAverage()
self._jaw.Length = 13
self._teeth = SmoothedMovingAverage()
self._teeth.Length = 8
self._lips = SmoothedMovingAverage()
self._lips.Length = 5
self._previous_jaw = Decimal(0)
self._previous_teeth = Decimal(0)
self._previous_lips = Decimal(0)
self._has_previous = False
self._entry_price = None
self._cooldown_left = 0
@property
def CandleType(self):
return self._candle_type.Value
@property
def CooldownBars(self):
return self._cooldown_bars.Value
@property
def StopLossPercent(self):
return self._stop_loss_percent.Value
@property
def TakeProfitPercent(self):
return self._take_profit_percent.Value
def OnReseted(self):
super(bars_alligator_strategy, self).OnReseted()
self._previous_jaw = Decimal(0)
self._previous_teeth = Decimal(0)
self._previous_lips = Decimal(0)
self._has_previous = False
self._entry_price = None
self._cooldown_left = 0
def OnStarted2(self, time):
super(bars_alligator_strategy, self).OnStarted2(time)
self.OnReseted()
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._jaw, self._teeth, self._lips, self._process_candle).Start()
def _process_candle(self, candle, jaw, teeth, lips):
if candle.State != CandleStates.Finished:
return
if self._cooldown_left > 0:
self._cooldown_left -= 1
if self.Position != 0 and self._entry_price is None:
self._entry_price = candle.ClosePrice
if self._try_exit_by_risk(candle):
self._update_previous(jaw, teeth, lips)
return
if not self._has_previous:
self._update_previous(jaw, teeth, lips)
return
# Exit conditions: lips crosses teeth against position
close_long = lips < teeth and self._previous_lips >= self._previous_teeth and self.Position > 0
close_short = lips > teeth and self._previous_lips <= self._previous_teeth and self.Position < 0
if close_long:
self.SellMarket(self.Position)
self._entry_price = None
self._cooldown_left = self.CooldownBars
self._update_previous(jaw, teeth, lips)
return
if close_short:
self.BuyMarket(Math.Abs(self.Position))
self._entry_price = None
self._cooldown_left = self.CooldownBars
self._update_previous(jaw, teeth, lips)
return
if not self.IsFormedAndOnlineAndAllowTrading() or self._cooldown_left > 0:
self._update_previous(jaw, teeth, lips)
return
# Entry: lips crosses jaw with proper Alligator ordering
buy_signal = lips > jaw and self._previous_lips <= self._previous_jaw and lips > teeth
sell_signal = lips < jaw and self._previous_lips >= self._previous_jaw and lips < teeth
if buy_signal and self.Position <= 0:
if self.Position < 0:
self.BuyMarket(Math.Abs(self.Position))
self._entry_price = None
self._cooldown_left = self.CooldownBars
else:
self.BuyMarket()
self._entry_price = candle.ClosePrice
self._cooldown_left = self.CooldownBars
elif sell_signal and self.Position >= 0:
if self.Position > 0:
self.SellMarket(self.Position)
self._entry_price = None
self._cooldown_left = self.CooldownBars
else:
self.SellMarket()
self._entry_price = candle.ClosePrice
self._cooldown_left = self.CooldownBars
self._update_previous(jaw, teeth, lips)
def _try_exit_by_risk(self, candle):
if self._entry_price is None or self.Position == 0 or self._entry_price == 0:
return False
entry_price = self._entry_price
stop_distance = entry_price * self.StopLossPercent / Decimal(100)
take_distance = entry_price * self.TakeProfitPercent / Decimal(100)
if self.Position > 0:
if (stop_distance > 0 and candle.LowPrice <= entry_price - stop_distance) or \
(take_distance > 0 and candle.HighPrice >= entry_price + take_distance):
self.SellMarket(self.Position)
self._entry_price = None
self._cooldown_left = self.CooldownBars
return True
elif self.Position < 0:
volume = Math.Abs(self.Position)
if (stop_distance > 0 and candle.HighPrice >= entry_price + stop_distance) or \
(take_distance > 0 and candle.LowPrice <= entry_price - take_distance):
self.BuyMarket(volume)
self._entry_price = None
self._cooldown_left = self.CooldownBars
return True
return False
def _update_previous(self, jaw, teeth, lips):
self._previous_jaw = jaw
self._previous_teeth = teeth
self._previous_lips = lips
self._has_previous = True
def CreateClone(self):
return bars_alligator_strategy()