Estrategia de Tendencia Alligator
La estrategia reproduce el sistema clásico Alligator de Bill Williams del script original de MetaTrader (Alligator.mq5). Utiliza tres medias móviles suavizadas construidas sobre el precio mediano y desplazadas hacia adelante para visualizar la fase del mercado. Se abre una posición larga cuando la línea rápida Lips está por encima de Teeth, y Teeth está por encima de Jaw. Se abre una posición corta cuando la alineación está invertida. Solo puede estar activa una posición al mismo tiempo.
Una vez en una operación, la estrategia protege la posición con un stop-loss y take-profit expresados en pips. Cuando el mercado se mueve a favor de la operación una distancia de nivel cero configurable, el stop se mueve a punto de equilibrio. Un trailing stop sigue el máximo más alto (para largos) o el mínimo más bajo (para cortos) con un paso mínimo para evitar actualizaciones frecuentes del stop. Las posiciones se cierran cuando se alcanzan los niveles de stop-loss, trailing stop o take-profit.
La configuración predeterminada apunta a velas de 30 minutos y valores de pip estilo Forex, pero los parámetros se pueden optimizar para otros mercados. Dado que la versión MQL original usa el manejo de pips específico del bróker, la conversión depende del PriceStep del instrumento para traducir distancias en pips a precios absolutos.
Reglas de Trading
Entrada
- Largo: Sin posición abierta y
Lips > Teeth > Jawen la última vela completada. - Corto: Sin posición abierta y
Lips < Teeth < Jawen la última vela completada.
Salida y Gestión de Riesgos
- Stop Inicial: Colocado
StopLossPipspor debajo (largo) o por encima (corto) del precio de llenado. - Take Profit: Colocado a
TakeProfitPipsdel precio de llenado. - Nivel Cero: Cuando el precio avanza
ZeroLevelPips, el stop se mueve al precio de entrada. - Trailing Stop: Después de la activación del nivel cero, el stop sigue el extremo con
TrailingStopPips, actualizándose solo cuando la mejora superaTrailingStepPips. - Las posiciones se aplanan inmediatamente cuando cualquier stop o el nivel de take-profit es tocado en los datos de la vela.
Parámetros
| Parámetro | Predeterminado | Descripción |
|---|---|---|
CandleType |
Marco temporal de 30 minutos | Serie de velas utilizada para cálculos de indicadores y evaluación de señales. |
JawLength |
13 | Período de media móvil suavizada para la línea de mandíbula azul. |
TeethLength |
8 | Período de media móvil suavizada para la línea de dientes roja. |
LipsLength |
5 | Período de media móvil suavizada para la línea de labios verde. |
JawShift |
8 | Desplazamiento hacia adelante de la línea de mandíbula, expresado en barras. |
TeethShift |
5 | Desplazamiento hacia adelante de la línea de dientes, expresado en barras. |
LipsShift |
3 | Desplazamiento hacia adelante de la línea de labios, expresado en barras. |
EnableLong |
true |
Permite o bloquea entradas largas. |
EnableShort |
true |
Permite o bloquea entradas cortas. |
StopLossPips |
45 | Distancia de stop-loss en pips desde el precio de llenado. |
TakeProfitPips |
145 | Distancia de take-profit en pips desde el precio de llenado. |
ZeroLevelPips |
30 | Distancia en pips requerida para mover el stop a punto de equilibrio. |
TrailingStopPips |
50 | Distancia entre el extremo actual y el trailing stop. |
TrailingStepPips |
10 | Mejora mínima en pips requerida antes de actualizar el trailing stop. |
Notas
- El indicador Alligator se calcula sobre el precio mediano
(High + Low) / 2para coincidir con la implementación de MetaTrader. - Los valores de línea desplazados se emulan con búferes internos para que las comparaciones usen los mismos datos desplazados que el script original.
- La estrategia asume que una operación se ejecuta antes de que se procese una nueva señal en la misma barra, reflejando la ejecución barra a barra del EA fuente.
- Optimice las distancias en pips para que coincidan con el tamaño del tick y la volatilidad del instrumento operado.
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Classic Bill Williams Alligator strategy with stop management rules.
/// </summary>
/// <remarks>
/// The strategy opens a long position when the Lips, Teeth, and Jaw lines are aligned upward
/// and opens a short position when they are aligned downward. Once in a trade the algorithm
/// applies the zero level rule to move the stop to break-even, updates a trailing stop with a
/// configurable step, and closes the position at the stop or take-profit levels.
/// </remarks>
public class AlligatorTrendStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _jawLength;
private readonly StrategyParam<int> _teethLength;
private readonly StrategyParam<int> _lipsLength;
private readonly StrategyParam<int> _jawShift;
private readonly StrategyParam<int> _teethShift;
private readonly StrategyParam<int> _lipsShift;
private readonly StrategyParam<bool> _enableLong;
private readonly StrategyParam<bool> _enableShort;
private readonly StrategyParam<decimal> _stopLossPips;
private readonly StrategyParam<decimal> _takeProfitPips;
private readonly StrategyParam<decimal> _zeroLevelPips;
private readonly StrategyParam<decimal> _trailingStopPips;
private readonly StrategyParam<decimal> _trailingStepPips;
private readonly List<decimal> _jawBuffer = new();
private readonly List<decimal> _teethBuffer = new();
private readonly List<decimal> _lipsBuffer = new();
private decimal _entryPrice;
private decimal? _longStop;
private decimal? _longTake;
private bool _longBreakevenActivated;
private decimal _longBestPrice;
private decimal? _shortStop;
private decimal? _shortTake;
private bool _shortBreakevenActivated;
private decimal _shortBestPrice;
/// <summary>
/// Candle type used by the strategy.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Jaw length.
/// </summary>
public int JawLength
{
get => _jawLength.Value;
set => _jawLength.Value = value;
}
/// <summary>
/// Teeth length.
/// </summary>
public int TeethLength
{
get => _teethLength.Value;
set => _teethLength.Value = value;
}
/// <summary>
/// Lips length.
/// </summary>
public int LipsLength
{
get => _lipsLength.Value;
set => _lipsLength.Value = value;
}
/// <summary>
/// Forward shift applied to the jaw line.
/// </summary>
public int JawShift
{
get => _jawShift.Value;
set => _jawShift.Value = value;
}
/// <summary>
/// Forward shift applied to the teeth line.
/// </summary>
public int TeethShift
{
get => _teethShift.Value;
set => _teethShift.Value = value;
}
/// <summary>
/// Forward shift applied to the lips line.
/// </summary>
public int LipsShift
{
get => _lipsShift.Value;
set => _lipsShift.Value = value;
}
/// <summary>
/// Enable long trades.
/// </summary>
public bool EnableLong
{
get => _enableLong.Value;
set => _enableLong.Value = value;
}
/// <summary>
/// Enable short trades.
/// </summary>
public bool EnableShort
{
get => _enableShort.Value;
set => _enableShort.Value = value;
}
/// <summary>
/// Stop-loss distance expressed in pips.
/// </summary>
public decimal StopLossPips
{
get => _stopLossPips.Value;
set => _stopLossPips.Value = value;
}
/// <summary>
/// Take-profit distance expressed in pips.
/// </summary>
public decimal TakeProfitPips
{
get => _takeProfitPips.Value;
set => _takeProfitPips.Value = value;
}
/// <summary>
/// Distance to move the stop to break-even.
/// </summary>
public decimal ZeroLevelPips
{
get => _zeroLevelPips.Value;
set => _zeroLevelPips.Value = value;
}
/// <summary>
/// Distance between price extreme and trailing stop.
/// </summary>
public decimal TrailingStopPips
{
get => _trailingStopPips.Value;
set => _trailingStopPips.Value = value;
}
/// <summary>
/// Minimum increment for trailing stop updates.
/// </summary>
public decimal TrailingStepPips
{
get => _trailingStepPips.Value;
set => _trailingStepPips.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="AlligatorTrendStrategy"/> class.
/// </summary>
public AlligatorTrendStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles used for calculations", "General");
_jawLength = Param(nameof(JawLength), 13)
.SetGreaterThanZero()
.SetDisplay("Jaw Length", "Smoothed moving average period for the jaw", "Alligator")
;
_teethLength = Param(nameof(TeethLength), 8)
.SetGreaterThanZero()
.SetDisplay("Teeth Length", "Smoothed moving average period for the teeth", "Alligator")
;
_lipsLength = Param(nameof(LipsLength), 5)
.SetGreaterThanZero()
.SetDisplay("Lips Length", "Smoothed moving average period for the lips", "Alligator")
;
_jawShift = Param(nameof(JawShift), 8)
.SetDisplay("Jaw Shift", "Forward shift applied to the jaw line", "Alligator")
;
_teethShift = Param(nameof(TeethShift), 5)
.SetDisplay("Teeth Shift", "Forward shift applied to the teeth line", "Alligator")
;
_lipsShift = Param(nameof(LipsShift), 3)
.SetDisplay("Lips Shift", "Forward shift applied to the lips line", "Alligator")
;
_enableLong = Param(nameof(EnableLong), true)
.SetDisplay("Enable Long", "Allow long entries", "Trading");
_enableShort = Param(nameof(EnableShort), true)
.SetDisplay("Enable Short", "Allow short entries", "Trading");
_stopLossPips = Param(nameof(StopLossPips), 500m)
.SetDisplay("Stop Loss", "Stop-loss distance in pips", "Risk")
;
_takeProfitPips = Param(nameof(TakeProfitPips), 2000m)
.SetDisplay("Take Profit", "Take-profit distance in pips", "Risk")
;
_zeroLevelPips = Param(nameof(ZeroLevelPips), 300m)
.SetDisplay("Zero Level", "Distance to move stop to break-even", "Risk")
;
_trailingStopPips = Param(nameof(TrailingStopPips), 500m)
.SetDisplay("Trailing Stop", "Trailing stop distance in pips", "Risk")
;
_trailingStepPips = Param(nameof(TrailingStepPips), 100m)
.SetDisplay("Trailing Step", "Minimum trailing stop increment in pips", "Risk")
;
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_jawBuffer.Clear();
_teethBuffer.Clear();
_lipsBuffer.Clear();
_entryPrice = 0m;
ResetLong();
ResetShort();
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var jaw = new SmoothedMovingAverage { Length = JawLength };
var teeth = new SmoothedMovingAverage { Length = TeethLength };
var lips = new SmoothedMovingAverage { Length = LipsLength };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ProcessCandle);
subscription.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, jaw);
DrawIndicator(area, teeth);
DrawIndicator(area, lips);
DrawOwnTrades(area);
}
void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var medianPrice = (candle.HighPrice + candle.LowPrice) / 2m;
var inputValue = new DecimalIndicatorValue(jaw, medianPrice, candle.CloseTime) { IsFinal = true };
var jawValue = jaw.Process(inputValue);
var teethValue = teeth.Process(new DecimalIndicatorValue(teeth, medianPrice, candle.CloseTime) { IsFinal = true });
var lipsValue = lips.Process(new DecimalIndicatorValue(lips, medianPrice, candle.CloseTime) { IsFinal = true });
if (!jawValue.IsFormed || !teethValue.IsFormed || !lipsValue.IsFormed)
return;
var jawShifted = GetShiftedValue(_jawBuffer, jawValue.GetValue<decimal>(), JawShift);
var teethShifted = GetShiftedValue(_teethBuffer, teethValue.GetValue<decimal>(), TeethShift);
var lipsShifted = GetShiftedValue(_lipsBuffer, lipsValue.GetValue<decimal>(), LipsShift);
if (!jawShifted.HasValue || !teethShifted.HasValue || !lipsShifted.HasValue)
return;
if (!jaw.IsFormed)
return;
if (ManagePosition(candle))
return;
var bullishAlignment = lipsShifted.Value > teethShifted.Value && teethShifted.Value > jawShifted.Value;
var bearishAlignment = lipsShifted.Value < teethShifted.Value && teethShifted.Value < jawShifted.Value;
if (Position == 0)
{
if (bullishAlignment && EnableLong)
{
BuyMarket();
}
else if (bearishAlignment && EnableShort)
{
SellMarket();
}
}
}
}
/// <inheritdoc />
protected override void OnOwnTradeReceived(MyTrade trade)
{
base.OnOwnTradeReceived(trade);
var tradeMsg = trade.Trade;
if (tradeMsg is null)
return;
var price = tradeMsg.Price;
var direction = trade.Order.Side;
var distanceToStop = StopLossPips > 0m ? GetPriceByPips(StopLossPips) : (decimal?)null;
var distanceToTake = TakeProfitPips > 0m ? GetPriceByPips(TakeProfitPips) : (decimal?)null;
if (direction == Sides.Buy)
{
if (Position > 0)
{
_entryPrice = price;
_longStop = distanceToStop.HasValue ? price - distanceToStop.Value : null;
_longTake = distanceToTake.HasValue ? price + distanceToTake.Value : null;
_longBreakevenActivated = false;
_longBestPrice = price;
}
else if (Position == 0)
{
ResetShort();
}
}
else if (direction == Sides.Sell)
{
if (Position < 0)
{
_entryPrice = price;
_shortStop = distanceToStop.HasValue ? price + distanceToStop.Value : null;
_shortTake = distanceToTake.HasValue ? price - distanceToTake.Value : null;
_shortBreakevenActivated = false;
_shortBestPrice = price;
}
else if (Position == 0)
{
ResetLong();
}
}
if (Position == 0)
{
ResetLong();
ResetShort();
}
}
private bool ManagePosition(ICandleMessage candle)
{
if (Position > 0)
{
var entryPrice = _entryPrice;
if (entryPrice == 0m)
return false;
_longBestPrice = Math.Max(_longBestPrice, candle.HighPrice);
if (_longTake.HasValue && candle.HighPrice >= _longTake.Value)
{
SellMarket();
ResetLong();
return true;
}
if (_longStop.HasValue && candle.LowPrice <= _longStop.Value)
{
SellMarket();
ResetLong();
return true;
}
if (ZeroLevelPips > 0m && !_longBreakevenActivated && _longStop.HasValue && entryPrice > _longStop.Value)
{
var zeroDistance = GetPriceByPips(ZeroLevelPips);
if (_longBestPrice - entryPrice >= zeroDistance)
{
_longStop = entryPrice;
_longBreakevenActivated = true;
}
}
if (TrailingStopPips > 0m)
{
var trailingDistance = GetPriceByPips(TrailingStopPips);
var step = GetPriceByPips(TrailingStepPips);
var candidate = _longBestPrice - trailingDistance;
if (!_longStop.HasValue || candidate - _longStop.Value >= step)
_longStop = candidate;
}
}
else if (Position < 0)
{
var entryPrice = _entryPrice;
if (entryPrice == 0m)
return false;
_shortBestPrice = _shortBestPrice == 0m ? candle.LowPrice : Math.Min(_shortBestPrice, candle.LowPrice);
if (_shortTake.HasValue && candle.LowPrice <= _shortTake.Value)
{
BuyMarket();
ResetShort();
return true;
}
if (_shortStop.HasValue && candle.HighPrice >= _shortStop.Value)
{
BuyMarket();
ResetShort();
return true;
}
if (ZeroLevelPips > 0m && !_shortBreakevenActivated && _shortStop.HasValue && entryPrice < _shortStop.Value)
{
var zeroDistance = GetPriceByPips(ZeroLevelPips);
if (entryPrice - candle.LowPrice >= zeroDistance)
{
_shortStop = entryPrice;
_shortBreakevenActivated = true;
}
}
if (TrailingStopPips > 0m)
{
var trailingDistance = GetPriceByPips(TrailingStopPips);
var step = GetPriceByPips(TrailingStepPips);
var candidate = _shortBestPrice + trailingDistance;
if (!_shortStop.HasValue || _shortStop.Value - candidate >= step)
_shortStop = candidate;
}
}
else
{
ResetLong();
ResetShort();
}
return false;
}
private static decimal? GetShiftedValue(List<decimal> buffer, decimal value, int shift)
{
if (shift <= 0)
return value;
buffer.Add(value);
if (buffer.Count <= shift)
return null;
var result = buffer[0];
try { buffer.RemoveAt(0); } catch { }
return result;
}
private decimal GetPriceByPips(decimal pips)
{
if (pips <= 0m)
return 0m;
var step = Security?.PriceStep ?? 0m;
if (step <= 0m)
step = 1m;
return pips * step;
}
private void ResetLong()
{
_longStop = null;
_longTake = null;
_longBreakevenActivated = false;
_longBestPrice = 0m;
}
private void ResetShort()
{
_shortStop = null;
_shortTake = null;
_shortBreakevenActivated = false;
_shortBestPrice = 0m;
}
}
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, Decimal
from StockSharp.Messages import DataType, CandleStates, Sides
from StockSharp.Algo.Indicators import SmoothedMovingAverage
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class alligator_trend_strategy(Strategy):
"""
Classic Bill Williams Alligator strategy with stop management rules.
Opens long when Lips > Teeth > Jaw, short when reversed.
Applies break-even, trailing stop, and take-profit logic.
"""
def __init__(self):
super(alligator_trend_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles used for calculations", "General")
self._jaw_length = self.Param("JawLength", 13) \
.SetGreaterThanZero() \
.SetDisplay("Jaw Length", "Smoothed moving average period for the jaw", "Alligator")
self._teeth_length = self.Param("TeethLength", 8) \
.SetGreaterThanZero() \
.SetDisplay("Teeth Length", "Smoothed moving average period for the teeth", "Alligator")
self._lips_length = self.Param("LipsLength", 5) \
.SetGreaterThanZero() \
.SetDisplay("Lips Length", "Smoothed moving average period for the lips", "Alligator")
self._jaw_shift = self.Param("JawShift", 8) \
.SetDisplay("Jaw Shift", "Forward shift applied to the jaw line", "Alligator")
self._teeth_shift = self.Param("TeethShift", 5) \
.SetDisplay("Teeth Shift", "Forward shift applied to the teeth line", "Alligator")
self._lips_shift = self.Param("LipsShift", 3) \
.SetDisplay("Lips Shift", "Forward shift applied to the lips line", "Alligator")
self._enable_long = self.Param("EnableLong", True) \
.SetDisplay("Enable Long", "Allow long entries", "Trading")
self._enable_short = self.Param("EnableShort", True) \
.SetDisplay("Enable Short", "Allow short entries", "Trading")
self._stop_loss_pips = self.Param("StopLossPips", 500.0) \
.SetDisplay("Stop Loss", "Stop-loss distance in pips", "Risk")
self._take_profit_pips = self.Param("TakeProfitPips", 2000.0) \
.SetDisplay("Take Profit", "Take-profit distance in pips", "Risk")
self._zero_level_pips = self.Param("ZeroLevelPips", 300.0) \
.SetDisplay("Zero Level", "Distance to move stop to break-even", "Risk")
self._trailing_stop_pips = self.Param("TrailingStopPips", 500.0) \
.SetDisplay("Trailing Stop", "Trailing stop distance in pips", "Risk")
self._trailing_step_pips = self.Param("TrailingStepPips", 100.0) \
.SetDisplay("Trailing Step", "Minimum trailing stop increment in pips", "Risk")
self._jaw_buffer = []
self._teeth_buffer = []
self._lips_buffer = []
self._entry_price = 0.0
self._long_stop = None
self._long_take = None
self._long_breakeven = False
self._long_best = 0.0
self._short_stop = None
self._short_take = None
self._short_breakeven = False
self._short_best = 0.0
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, v): self._candle_type.Value = v
@property
def JawLength(self): return self._jaw_length.Value
@JawLength.setter
def JawLength(self, v): self._jaw_length.Value = v
@property
def TeethLength(self): return self._teeth_length.Value
@TeethLength.setter
def TeethLength(self, v): self._teeth_length.Value = v
@property
def LipsLength(self): return self._lips_length.Value
@LipsLength.setter
def LipsLength(self, v): self._lips_length.Value = v
@property
def JawShift(self): return self._jaw_shift.Value
@JawShift.setter
def JawShift(self, v): self._jaw_shift.Value = v
@property
def TeethShift(self): return self._teeth_shift.Value
@TeethShift.setter
def TeethShift(self, v): self._teeth_shift.Value = v
@property
def LipsShift(self): return self._lips_shift.Value
@LipsShift.setter
def LipsShift(self, v): self._lips_shift.Value = v
@property
def EnableLong(self): return self._enable_long.Value
@EnableLong.setter
def EnableLong(self, v): self._enable_long.Value = v
@property
def EnableShort(self): return self._enable_short.Value
@EnableShort.setter
def EnableShort(self, v): self._enable_short.Value = v
@property
def StopLossPips(self): return self._stop_loss_pips.Value
@StopLossPips.setter
def StopLossPips(self, v): self._stop_loss_pips.Value = v
@property
def TakeProfitPips(self): return self._take_profit_pips.Value
@TakeProfitPips.setter
def TakeProfitPips(self, v): self._take_profit_pips.Value = v
@property
def ZeroLevelPips(self): return self._zero_level_pips.Value
@ZeroLevelPips.setter
def ZeroLevelPips(self, v): self._zero_level_pips.Value = v
@property
def TrailingStopPips(self): return self._trailing_stop_pips.Value
@TrailingStopPips.setter
def TrailingStopPips(self, v): self._trailing_stop_pips.Value = v
@property
def TrailingStepPips(self): return self._trailing_step_pips.Value
@TrailingStepPips.setter
def TrailingStepPips(self, v): self._trailing_step_pips.Value = v
def OnReseted(self):
super(alligator_trend_strategy, self).OnReseted()
self._jaw_buffer = []
self._teeth_buffer = []
self._lips_buffer = []
self._entry_price = 0.0
self._reset_long()
self._reset_short()
def OnStarted2(self, time):
super(alligator_trend_strategy, self).OnStarted2(time)
jaw = SmoothedMovingAverage()
jaw.Length = self.JawLength
teeth = SmoothedMovingAverage()
teeth.Length = self.TeethLength
lips = SmoothedMovingAverage()
lips.Length = self.LipsLength
self._jaw_ind = jaw
self._teeth_ind = teeth
self._lips_ind = lips
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self.ProcessCandle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, jaw)
self.DrawIndicator(area, teeth)
self.DrawIndicator(area, lips)
self.DrawOwnTrades(area)
def ProcessCandle(self, candle):
if candle.State != CandleStates.Finished:
return
median = (float(candle.HighPrice) + float(candle.LowPrice)) / 2.0
jaw_res = process_float(self._jaw_ind, Decimal(median), candle.CloseTime, True)
teeth_res = process_float(self._teeth_ind, Decimal(median), candle.CloseTime, True)
lips_res = process_float(self._lips_ind, Decimal(median), candle.CloseTime, True)
if not jaw_res.IsFormed or not teeth_res.IsFormed or not lips_res.IsFormed:
return
jaw_val = float(jaw_res.Value)
teeth_val = float(teeth_res.Value)
lips_val = float(lips_res.Value)
jaw_shifted = self._get_shifted(self._jaw_buffer, jaw_val, self.JawShift)
teeth_shifted = self._get_shifted(self._teeth_buffer, teeth_val, self.TeethShift)
lips_shifted = self._get_shifted(self._lips_buffer, lips_val, self.LipsShift)
if jaw_shifted is None or teeth_shifted is None or lips_shifted is None:
return
if not self._jaw_ind.IsFormed:
return
if self._manage_position(candle):
return
bullish = lips_shifted > teeth_shifted and teeth_shifted > jaw_shifted
bearish = lips_shifted < teeth_shifted and teeth_shifted < jaw_shifted
if self.Position == 0:
if bullish and self.EnableLong:
self.BuyMarket()
price = float(candle.ClosePrice)
self._entry_price = price
dist_stop = self._get_price_by_pips(self.StopLossPips) if float(self.StopLossPips) > 0 else None
dist_take = self._get_price_by_pips(self.TakeProfitPips) if float(self.TakeProfitPips) > 0 else None
self._long_stop = price - dist_stop if dist_stop else None
self._long_take = price + dist_take if dist_take else None
self._long_breakeven = False
self._long_best = price
elif bearish and self.EnableShort:
self.SellMarket()
price = float(candle.ClosePrice)
self._entry_price = price
dist_stop = self._get_price_by_pips(self.StopLossPips) if float(self.StopLossPips) > 0 else None
dist_take = self._get_price_by_pips(self.TakeProfitPips) if float(self.TakeProfitPips) > 0 else None
self._short_stop = price + dist_stop if dist_stop else None
self._short_take = price - dist_take if dist_take else None
self._short_breakeven = False
self._short_best = price
def _manage_position(self, candle):
if self.Position > 0:
if self._entry_price == 0:
return False
self._long_best = max(self._long_best, float(candle.HighPrice))
if self._long_take is not None and float(candle.HighPrice) >= self._long_take:
self.SellMarket()
self._reset_long()
return True
if self._long_stop is not None and float(candle.LowPrice) <= self._long_stop:
self.SellMarket()
self._reset_long()
return True
if self.ZeroLevelPips > 0 and not self._long_breakeven and self._long_stop is not None and self._entry_price > self._long_stop:
zero_dist = self._get_price_by_pips(self.ZeroLevelPips)
if self._long_best - self._entry_price >= zero_dist:
self._long_stop = self._entry_price
self._long_breakeven = True
if self.TrailingStopPips > 0:
trail_dist = self._get_price_by_pips(self.TrailingStopPips)
step = self._get_price_by_pips(self.TrailingStepPips)
candidate = self._long_best - trail_dist
if self._long_stop is None or candidate - self._long_stop >= step:
self._long_stop = candidate
elif self.Position < 0:
if self._entry_price == 0:
return False
self._short_best = min(self._short_best, float(candle.LowPrice)) if self._short_best > 0 else float(candle.LowPrice)
if self._short_take is not None and float(candle.LowPrice) <= self._short_take:
self.BuyMarket()
self._reset_short()
return True
if self._short_stop is not None and float(candle.HighPrice) >= self._short_stop:
self.BuyMarket()
self._reset_short()
return True
if self.ZeroLevelPips > 0 and not self._short_breakeven and self._short_stop is not None and self._entry_price < self._short_stop:
zero_dist = self._get_price_by_pips(self.ZeroLevelPips)
if self._entry_price - float(candle.LowPrice) >= zero_dist:
self._short_stop = self._entry_price
self._short_breakeven = True
if self.TrailingStopPips > 0:
trail_dist = self._get_price_by_pips(self.TrailingStopPips)
step = self._get_price_by_pips(self.TrailingStepPips)
candidate = self._short_best + trail_dist
if self._short_stop is None or self._short_stop - candidate >= step:
self._short_stop = candidate
else:
self._reset_long()
self._reset_short()
return False
def _get_shifted(self, buffer, value, shift):
if shift <= 0:
return value
buffer.append(value)
if len(buffer) <= shift:
return None
result = buffer[0]
buffer.pop(0)
return result
def _get_price_by_pips(self, pips):
if pips <= 0:
return 0.0
sec = self.Security
step = float(sec.PriceStep) if sec is not None and sec.PriceStep is not None and float(sec.PriceStep) > 0 else 1.0
return float(pips) * step
def _reset_long(self):
self._long_stop = None
self._long_take = None
self._long_breakeven = False
self._long_best = 0.0
def _reset_short(self):
self._short_stop = None
self._short_take = None
self._short_breakeven = False
self._short_best = 0.0
def CreateClone(self):
"""!! REQUIRED!! Creates a new instance of the strategy."""
return alligator_trend_strategy()