Стратегия Alligator Trend
Стратегия воспроизводит классическую систему Билла Вильямса Alligator из оригинального скрипта MetaTrader (Alligator.mq5). Используются три сглаженные скользящие средние, построенные по медианной цене и сдвинутые вперёд, чтобы отразить фазу рынка. Длинная позиция открывается, когда быстрая линия Губ находится выше Зубов, а Зубы — выше Челюсти. Короткая позиция открывается при обратном расположении линий. Одновременно допускается только одна позиция.
После входа позиция защищается стоп-лоссом и тейк-профитом, заданными в пунктах. Когда цена проходит в прибыльную сторону расстояние нулевого уровня, стоп переносится в безубыток. Далее включается трейлинг-стоп, который следует за максимальным максимумом (для покупок) или минимальным минимумом (для продаж) с минимальным шагом обновления, чтобы избежать частых модификаций. Позиция закрывается при срабатывании стоп-лосса, трейлинг-стопа или тейк-профита.
Параметры по умолчанию рассчитаны на 30-минутные свечи и форексные пункты, однако их можно оптимизировать под другие рынки. Поскольку исходная MQL-версия использует брокерские правила округления, конвертация опирается на PriceStep инструмента для перевода пунктов в абсолютные цены.
Торговые правила
Вход
- Покупка: Нет открытых позиций и выполняется условие
Lips > Teeth > Jawна последней завершённой свече. - Продажа: Нет открытых позиций и выполняется условие
Lips < Teeth < Jawна последней завершённой свече.
Выход и управление рисками
- Начальный стоп: Устанавливается на
StopLossPipsниже (для лонга) или выше (для шорта) цены открытия. - Тейк-профит: Устанавливается на расстоянии
TakeProfitPipsот цены входа. - Нулевой уровень: При движении цены на
ZeroLevelPipsв прибыль стоп переносится на цену входа. - Трейлинг-стоп: После активации нулевого уровня стоп следует за экстремумом на расстоянии
TrailingStopPips, обновляясь только при улучшении не менееTrailingStepPips. - Позиция закрывается сразу, как только свечные данные пересекают стоп или тейк-профит.
Параметры
| Параметр | Значение по умолчанию | Описание |
|---|---|---|
CandleType |
Таймфрейм 30 минут | Свечная серия для расчёта индикатора и сигналов. |
JawLength |
13 | Период сглаженной скользящей средней для линии челюсти. |
TeethLength |
8 | Период сглаженной скользящей средней для линии зубов. |
LipsLength |
5 | Период сглаженной скользящей средней для линии губ. |
JawShift |
8 | Сдвиг линии челюсти вперёд в барах. |
TeethShift |
5 | Сдвиг линии зубов вперёд в барах. |
LipsShift |
3 | Сдвиг линии губ вперёд в барах. |
EnableLong |
true |
Разрешить или запретить длинные сделки. |
EnableShort |
true |
Разрешить или запретить короткие сделки. |
StopLossPips |
45 | Расстояние стоп-лосса от цены входа в пунктах. |
TakeProfitPips |
145 | Расстояние тейк-профита от цены входа в пунктах. |
ZeroLevelPips |
30 | Движение в пунктах, необходимое для переноса стопа в безубыток. |
TrailingStopPips |
50 | Расстояние между текущим экстремумом и трейлинг-стопом. |
TrailingStepPips |
10 | Минимальное улучшение в пунктах перед обновлением трейлинг-стопа. |
Примечания
- Индикатор Alligator рассчитывается по медианной цене
(High + Low) / 2, как в оригинале MetaTrader. - Значения сдвинутых линий эмулируются внутренними буферами, что позволяет сравнивать те же данные, что и в исходном советнике.
- Предполагается, что сделка исполняется до обработки следующего сигнала на той же свече, что соответствует пошаговому выполнению исходного эксперта.
- Рекомендуется оптимизировать размеры пунктов под шаг цены и волатильность выбранного инструмента.
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()