BARS Alligator-Strategie
Die BARS Alligator-Strategie ist ein direkter Port des MetaTrader-Expertenberaters mit demselben Namen. Sie verlässt sich auf Bill Williams' Alligator-Indikator, um erwachende Trends zu erkennen: Wenn die grüne Lips-Linie die blaue Jaw-Linie von unten kreuzt, behandelt das System dies als bullischen Ausbruch, während ein Abwärtskreuz bärisches Momentum signalisiert. Ausstiege beruhen darauf, dass Lips die rote Teeth-Linie kreuzt, damit Positionen geschlossen werden, sobald das Momentum nachlässt. Schutz-Stop-Loss-, Take-Profit- und Trailing-Stop-Abstände werden in Pips konfiguriert und automatisch in Preiseinheiten auf Basis des Preisschritts und der Dezimalgenauigkeit des Instruments umgerechnet.
Handelslogik
- Indikatoraufbau
- Drei gleitende Durchschnitte mit konfigurierbaren Längen, Verschiebungen und Typ (einfach, exponentiell, geglättet oder gewichtet) bilden den Alligator.
- Der angewendete Preis kann der Schluss-, Eröffnungs-, Hoch-, Tief-, Median-, Typisch- oder Gewichtetpreis jeder Kerze sein.
- Verschiebungen werden durch Speichern eines kleinen rollierenden Puffers für jede Linie respektiert, sodass Crossover dieselben Werte verwenden, die auf einem MetaTrader-Chart erscheinen würden.
- Einstiegsbedingungen
- Long: Die Lips-Linie der vorherigen Bar liegt über der Jaw und war zwei Bars zuvor darunter (bullischer Kreuzaufwärts).
- Short: Die Lips-Linie der vorherigen Bar liegt unter der Jaw und war zwei Bars zuvor darüber (bärischer Kreuzabwärts).
- Neue Einstiege sind nur erlaubt, wenn die aktuelle Position flach oder bereits in der Signalrichtung ausgerichtet ist und die Gesamtpositionsgröße unter
MaxPositions × OrderVolume bleibt (oder das risikoskalierte Äquivalent).
- Ausstiegsbedingungen
- Long-Ausstieg: Die Lips-Linie kreuzt unter die Teeth-Linie und die Position ist relativ zum gemittelten Einstiegspreis profitabel.
- Short-Ausstieg: Die Lips-Linie kreuzt über die Teeth-Linie und die Position ist profitabel.
- Ausstiege erfolgen auch, wenn statische Stop-Loss- oder Take-Profit-Niveaus verletzt werden.
- Trailing Stop
- Wenn aktiviert, repositioniert ein Trailing Stop den Schutz-Stop, sobald sich der Preis um
TrailingStopPips + TrailingStepPips in der Trade-Richtung bewegt. Der Stop folgt dann dem Preis in einem Abstand von TrailingStopPips Pips, rückt aber nur vor, wenn der Preis neuen Fortschritt von mindestens TrailingStepPips Pips macht.
- Geldverwaltung
- Mit
MoneyMode = FixedVolume verwenden Orders direkt die Größe OrderVolume.
- Mit
MoneyMode = RiskPercent weist die Strategie Volumen so zu, dass der konfigurierte Prozentsatz MoneyValue des Portfoliokapitals verloren gehen würde, wenn der Stop-Loss getroffen würde. Das Risiko pro Einheit entspricht dem Stop-Loss-Abstand ausgedrückt in Preiseinheiten. Das Ergebnis wird auf den nächsten VolumeStep abgerundet (oder auf 1, wenn Step-Informationen fehlen).
Parameter
| Parameter |
Typ |
Standard |
Beschreibung |
CandleType |
DataType |
TimeSpan.FromHours(1).TimeFrame() |
Zeitrahmen für Alligator-Berechnungen. |
OrderVolume |
decimal |
0.1 |
Festes Trade-Volumen wenn MoneyMode FixedVolume ist. |
MoneyMode |
MoneyManagementMode |
FixedVolume |
Wählt zwischen festem Volumen und risikoprozentualer Größenbestimmung. |
MoneyValue |
decimal |
1 |
Risikoprozentsatz wenn MoneyMode RiskPercent ist; sonst ignoriert. |
MaxPositions |
int |
1 |
Maximale Anzahl additiver Einstiege pro Richtung (ausgedrückt als Vielfaches des berechneten Ordervolumens). |
StopLossPips |
int |
150 |
Stop-Loss-Abstand in Pips. Null deaktiviert den Schutz-Stop. |
TakeProfitPips |
int |
150 |
Take-Profit-Abstand in Pips. Null deaktiviert das Gewinnziel. |
TrailingStopPips |
int |
5 |
Trailing-Stop-Abstand in Pips. Null deaktiviert das Trailing. |
TrailingStepPips |
int |
5 |
Extra-Abstand, den der Preis zurücklegen muss, bevor der Trailing Stop vorrückt. Muss positiv sein, wenn Trailing aktiviert ist. |
JawPeriod |
int |
13 |
Länge des gleitenden Jaw-Durchschnitts. |
JawShift |
int |
8 |
Vorwärtsverschiebung (in Bars) auf die Jaw-Serie angewendet. |
TeethPeriod |
int |
8 |
Länge des gleitenden Teeth-Durchschnitts. |
TeethShift |
int |
5 |
Vorwärtsverschiebung auf die Teeth-Serie angewendet. |
LipsPeriod |
int |
5 |
Länge des gleitenden Lips-Durchschnitts. |
LipsShift |
int |
3 |
Vorwärtsverschiebung auf die Lips-Serie angewendet. |
MaType |
MovingAverageType |
Smoothed |
Gleitender-Durchschnitt-Algorithmus für alle drei Alligator-Linien. |
AppliedPrice |
AppliedPriceType |
Median |
Den gleitenden Durchschnitten zugeführter Kerzenpreis (Schluss, Eröffnung, Hoch, Tief, Median, Typisch oder Gewichtet). |
Pip-Konvertierung
Die Strategie multipliziert Pip-Einstellungen mit dem Sicherheits-PriceStep. Wenn das Instrument 3 oder 5 Dezimalstellen verwendet, wird der Wert um ×10 angepasst, um MetaTraders Pip-Definition für Bruchkurse nachzuahmen. Wenn kein Preisschritt verfügbar ist, wird ein Wert von 1 angenommen.
Implementierungshinweise
MaxPositions wirkt auf die aggregierte Positionsgröße, da StockSharp im Netting-Modus arbeitet. Zusätzliche Einstiege erhöhen den Durchschnittspreis, anstatt separate Positionstickets zu erstellen.
- Stop-Loss und Take-Profit werden intern verfolgt und mit Marktorders auf der ersten Kerze ausgeführt, die die Schwellen verletzt, was dem Verhalten des ursprünglichen MQL-Experten entspricht.
- Risikobasiertes Sizing erfordert eine von Null verschiedene Stop-Loss-Distanz; andernfalls fällt das System auf das feste
OrderVolume zurück.
- Alle Indikatorwerte werden nur auf abgeschlossenen Kerzen (
CandleStates.Finished) aktualisiert, um vorzeitige Signale zu vermeiden.
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()