Auf GitHub ansehen

Chaos Trader Lite Strategie

Die Chaos Trader Lite Strategie repliziert Bill Williams' drei weisen Männer Einstiegstechniken unter Verwendung der High-Level-API von StockSharp. Sie analysiert jede fertige Kerze des konfigurierten Zeitrahmens (standardmäßig 1 Stunde) und platziert Stop-Orders, wenn eine der folgenden Bedingungen erfüllt ist:

  1. Erster Weiser Mann – Divergenzbalken: erkennt bullische oder bärische Divergenzkerzen und erfordert einen Mindestabstand zwischen dem Preis und der Alligator-Lippen-Linie.
  2. Zweiter Weiser Mann – Awesome Oscillator-Beschleunigung: wartet auf fünf aufeinanderfolgende Awesome Oscillator-Werte, die beschleunigendes Momentum zeigen.
  3. Dritter Weiser Mann – Fraktal-Ausbruch: bestätigt ein Fraktal zwei Kerzen zurück und prüft, ob der Preis weit genug von der Alligator-Zähne-Linie entfernt handelt, bevor eine Ausbruchsorder eingereiht wird.

Wann immer ein Long-Setup erscheint, storniert die Strategie bestehende Sell-Stops, schließt Short-Positionen, platziert einen neuen Buy-Stop knapp über dem vorherigen Hoch und registriert einen Schutz-Stop unter der Kerze. Das Gegenteil geschieht für Short-Setups. Schutz-Stops werden bei jedem Balken überwacht; wenn der Preis das gespeicherte Niveau kreuzt, wird die Position zum Marktpreis geschlossen.

Indikatoren und Berechnungen

  • Alligator-Lippen: 5-Perioden geglätteter gleitender Durchschnitt des Median-Preises, um drei Kerzen nach vorne verschoben. Die Strategie hält eine Warteschlange, damit der mit der aktuellen Kerze ausgerichtete Wert der MetaTrader-Implementierung entspricht.
  • Alligator-Zähne: 8-Perioden geglätteter gleitender Durchschnitt des Median-Preises, um fünf Kerzen nach vorne verschoben. Der verschobene Wert treibt den Dritter-Weiser-Mann-Filter an.
  • Awesome Oscillator: StockSharp's eingebauter Indikator (5 vs 34 SMA des Median-Preises) liefert die Momentum-Reihe für den zweiten weisen Mann.
  • Fraktale: Der Code inspiziert das Hoch/Tief der Kerze, die zwei Balken hinter dem letzten Balken liegt. Ein gültiges Fraktal erfordert, dass diese Kerze höher (oder niedriger) ist als die zwei Kerzen auf beiden Seiten.

Handelslogik

  1. Abonnieren Sie den angeforderten Kerzentyp und verarbeiten Sie nur fertige Kerzen.
  2. Aktualisieren Sie Alligator- und Awesome Oscillator-Indikatoren und speichern Sie verschobene Werte.
  3. Bewerten Sie die Bedingungen der weisen Männer:
    • Divergenzbalken muss in der oberen (für Bullen) oder unteren (für Bären) Hälfte der Kerze schließen und einen Abstand von den Lippen größer als MagnitudePips * PriceStep zeigen.
    • AO-Beschleunigung erfordert fünf Werte: AO[1] > AO[2] > AO[3] > AO[4] und AO[4] < AO[5] für Longs, gespiegelt für Shorts.
    • Fraktal-Ausbruch prüft, ob der Preis über (oder unter) dem bestätigten Fraktal und über (oder unter) den Alligator-Zähnen plus dem Magnitudenschwellenwert schließt.
  4. Wenn ein Setup aktiv ist, platzieren Sie eine BuyStop- oder SellStop-Order mit Volumen Volume beim Kerzenhoch plus einem Kursschritt (oder Tief minus einem Schritt). Stornieren Sie den entgegengesetzten Stop und glätten Sie Gegenpositionen.
  5. Aktualisieren Sie gespeicherte Stop-Loss-Niveaus: Long-Stops folgen nach oben, Short-Stops nach unten. Wenn eine Kerze den gespeicherten Stop durchbricht, schließt die Strategie die offene Position zum Marktpreis.

Parameter

  • MagnitudePips (Standard 10) – minimaler Pip-Abstand zwischen dem Divergenzbalken und den Alligator-Lippen.
  • UseFirstWiseMan (Standard true) – Einstieg per Divergenzbalken aktivieren oder deaktivieren.
  • UseSecondWiseMan (Standard true) – Einstieg per Awesome Oscillator-Beschleunigung aktivieren oder deaktivieren.
  • UseThirdWiseMan (Standard true) – Einstieg per Fraktal-Ausbruch aktivieren oder deaktivieren.
  • Volume (Standard 0.01) – Ordergröße für Stop-Einstiege.
  • CandleType (Standard 1 Stunde) – von der Strategie verarbeiteter Datentyp.

Hinweise

  • Bid/Ask-Prüfungen aus dem ursprünglichen MQL4-Code werden mit dem Kerzen-Schlusskurs in StockSharp approximiert.
  • Margin- und Volumen-Validierungsroutinen aus MetaTrader werden ausgelassen, weil StockSharp die Order-Validierung intern behandelt.
  • Stop-Orders werden storniert, wenn das entgegengesetzte Setup erscheint, um conflicting Pending Orders zu vermeiden, entsprechend dem CloseAll-Verhalten des EA.
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>
/// Chaos Trader Lite strategy implementing Bill Williams' three wise men entry concepts.
/// Uses market orders when divergent bars, Awesome Oscillator accelerations or confirmed fractals appear.
/// </summary>
public class ChaosTraderLiteStrategy : Strategy
{
	private readonly StrategyParam<int> _lipsShift;
	private readonly StrategyParam<int> _teethShift;

	private readonly StrategyParam<int> _magnitudePips;
	private readonly StrategyParam<bool> _useFirstWiseMan;
	private readonly StrategyParam<bool> _useSecondWiseMan;
	private readonly StrategyParam<bool> _useThirdWiseMan;
	private readonly StrategyParam<DataType> _candleType;

	private SimpleMovingAverage _lipsSmma = null!;
	private SimpleMovingAverage _teethSmma = null!;
	private AwesomeOscillator _awesomeOscillator = null!;

	private readonly List<decimal> _lipsShiftQueue = new();
	private readonly List<decimal> _teethShiftQueue = new();

	private CandleInfo? _bar0;
	private CandleInfo? _bar1;
	private CandleInfo? _bar2;
	private CandleInfo? _bar3;
	private CandleInfo? _bar4;

	private decimal? _lips0;
	private decimal? _teeth0;
	private decimal? _teeth1;

	private decimal? _ao0;
	private decimal? _ao1;
	private decimal? _ao2;
	private decimal? _ao3;
	private decimal? _ao4;
	private decimal? _ao5;

	private decimal? _longStopLoss;
	private decimal? _shortStopLoss;

	// Pending entry prices for stop-like behavior
	private decimal? _pendingBuyPrice;
	private decimal? _pendingSellPrice;
	private decimal? _pendingBuyStop;
	private decimal? _pendingSellStop;

	/// <summary>
	/// Magnitude threshold in pips between price and Alligator lips.
	/// </summary>
	public int MagnitudePips
	{
		get => _magnitudePips.Value;
		set => _magnitudePips.Value = value;
	}

	/// <summary>
	/// Number of candles used to shift the Alligator lips line.
	/// </summary>
	public int LipsShift
	{
		get => _lipsShift.Value;
		set => _lipsShift.Value = value;
	}

	/// <summary>
	/// Number of candles used to shift the Alligator teeth line.
	/// </summary>
	public int TeethShift
	{
		get => _teethShift.Value;
		set => _teethShift.Value = value;
	}

	/// <summary>
	/// Enable the first wise man divergent bar setup.
	/// </summary>
	public bool UseFirstWiseMan
	{
		get => _useFirstWiseMan.Value;
		set => _useFirstWiseMan.Value = value;
	}

	/// <summary>
	/// Enable the second wise man Awesome Oscillator acceleration setup.
	/// </summary>
	public bool UseSecondWiseMan
	{
		get => _useSecondWiseMan.Value;
		set => _useSecondWiseMan.Value = value;
	}

	/// <summary>
	/// Enable the third wise man fractal breakout setup.
	/// </summary>
	public bool UseThirdWiseMan
	{
		get => _useThirdWiseMan.Value;
		set => _useThirdWiseMan.Value = value;
	}

	/// <summary>
	/// Candle type processed by the strategy.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Initialize <see cref="ChaosTraderLiteStrategy"/>.
	/// </summary>
	public ChaosTraderLiteStrategy()
	{
		_magnitudePips = Param(nameof(MagnitudePips), 10)
			.SetGreaterThanZero()
			.SetDisplay("Magnitude", "Distance from lips in pips", "General");

		_lipsShift = Param(nameof(LipsShift), 3)
			.SetNotNegative()
			.SetDisplay("Lips Shift", "Shift applied to Alligator lips", "Alligator");

		_teethShift = Param(nameof(TeethShift), 5)
			.SetNotNegative()
			.SetDisplay("Teeth Shift", "Shift applied to Alligator teeth", "Alligator");

		_useFirstWiseMan = Param(nameof(UseFirstWiseMan), true)
			.SetDisplay("First Wise Man", "Enable divergent bar setup", "General");

		_useSecondWiseMan = Param(nameof(UseSecondWiseMan), true)
			.SetDisplay("Second Wise Man", "Enable Awesome Oscillator setup", "General");

		_useThirdWiseMan = Param(nameof(UseThirdWiseMan), true)
			.SetDisplay("Third Wise Man", "Enable fractal breakout setup", "General");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Type of candles", "General");
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_bar0 = _bar1 = _bar2 = _bar3 = _bar4 = null;

		_lipsShiftQueue.Clear();
		_teethShiftQueue.Clear();

		_lips0 = null;
		_teeth0 = null;
		_teeth1 = null;

		_ao0 = null;
		_ao1 = null;
		_ao2 = null;
		_ao3 = null;
		_ao4 = null;
		_ao5 = null;

		_longStopLoss = null;
		_shortStopLoss = null;

		_pendingBuyPrice = null;
		_pendingSellPrice = null;
		_pendingBuyStop = null;
		_pendingSellStop = null;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_lipsSmma = new SimpleMovingAverage { Length = 5 };
		_teethSmma = new SimpleMovingAverage { Length = 8 };
		_awesomeOscillator = new AwesomeOscillator { ShortMa = { Length = 5 }, LongMa = { Length = 34 } };

		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(ProcessCandle).Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, _lipsSmma);
			DrawIndicator(area, _teethSmma);
			DrawIndicator(area, _awesomeOscillator);
			DrawOwnTrades(area);
		}
	}

	private void ProcessCandle(ICandleMessage candle)
	{
		if (candle.State != CandleStates.Finished)
			return;

		// Check pending stop-like entries first
		CheckPendingEntries(candle);

		UpdateBarHistory(candle);

		var median = (candle.HighPrice + candle.LowPrice) / 2m;
		var candleInput = new CandleIndicatorValue(_lipsSmma, candle);

		var lipsValue = _lipsSmma.Process(new DecimalIndicatorValue(_lipsSmma, median, candle.ServerTime) { IsFinal = true });
		var teethValue = _teethSmma.Process(new DecimalIndicatorValue(_teethSmma, median, candle.ServerTime) { IsFinal = true });
		var awesomeValue = _awesomeOscillator.Process(new CandleIndicatorValue(_awesomeOscillator, candle));

		if (lipsValue.IsFinal)
		{
			var lips = lipsValue.ToDecimal();
			_lipsShiftQueue.Add(lips);
			if (_lipsShiftQueue.Count > LipsShift)
			{
				_lips0 = _lipsShiftQueue[0];
				try { _lipsShiftQueue.RemoveAt(0); } catch { }
			}
		}

		if (teethValue.IsFinal)
		{
			var teeth = teethValue.ToDecimal();
			_teethShiftQueue.Add(teeth);
			if (_teethShiftQueue.Count > TeethShift)
			{
				_teeth1 = _teeth0;
				_teeth0 = _teethShiftQueue[0];
				try { _teethShiftQueue.RemoveAt(0); } catch { }
			}
		}

		if (awesomeValue.IsFinal)
		{
			var ao = awesomeValue.ToDecimal();
			_ao5 = _ao4;
			_ao4 = _ao3;
			_ao3 = _ao2;
			_ao2 = _ao1;
			_ao1 = _ao0;
			_ao0 = ao;
		}

		var upFractal = GetUpFractal();
		var downFractal = GetDownFractal();

		if (_lipsSmma.IsFormed && _teethSmma.IsFormed)
			EvaluateSignals(candle, upFractal, downFractal);

		UpdateProtection(candle);
	}

	private void CheckPendingEntries(ICandleMessage candle)
	{
		// Simulate buy stop: if candle high reaches or exceeds pending buy price, enter long
		if (_pendingBuyPrice is decimal buyPrice && candle.HighPrice >= buyPrice)
		{
			if (Position <= 0)
			{
				if (Position < 0)
				{
					BuyMarket();
					_shortStopLoss = null;
				}
				if (Volume > 0)
				{
					BuyMarket();
					_longStopLoss = _pendingBuyStop;
				}
			}
			_pendingBuyPrice = null;
			_pendingBuyStop = null;
		}

		// Simulate sell stop: if candle low reaches or goes below pending sell price, enter short
		if (_pendingSellPrice is decimal sellPrice && candle.LowPrice <= sellPrice)
		{
			if (Position >= 0)
			{
				if (Position > 0)
				{
					SellMarket();
					_longStopLoss = null;
				}
				if (Volume > 0)
				{
					SellMarket();
					_shortStopLoss = _pendingSellStop;
				}
			}
			_pendingSellPrice = null;
			_pendingSellStop = null;
		}
	}

	private void EvaluateSignals(ICandleMessage candle, decimal? upFractal, decimal? downFractal)
	{
		if (_bar0 is not CandleInfo current || _bar1 is not CandleInfo previous)
			return;

		var point = Security?.PriceStep ?? 1m;
		var magnitudeThreshold = MagnitudePips * point;

		if (UseFirstWiseMan && _lips0 is decimal lips)
		{
			if (IsBullishDivergent(current, previous))
			{
				var distance = lips - current.High;
				if (distance > magnitudeThreshold)
					PlaceBuySetup(current, point);
			}

			if (IsBearishDivergent(current, previous))
			{
				var distance = current.Low - lips;
				if (distance > magnitudeThreshold)
					PlaceSellSetup(current, point);
			}
		}

		if (UseSecondWiseMan && _ao1.HasValue && _ao2.HasValue && _ao3.HasValue && _ao4.HasValue && _ao5.HasValue)
		{
			var currentAo = _ao1.Value;
			var bar2Ao = _ao2.Value;
			var bar3Ao = _ao3.Value;
			var bar4Ao = _ao4.Value;
			var bar5Ao = _ao5.Value;

			var bullishAcceleration = currentAo > bar2Ao && bar2Ao > bar3Ao && bar3Ao > bar4Ao && bar4Ao < bar5Ao;
			if (bullishAcceleration)
				PlaceBuySetup(current, point);

			var bearishAcceleration = currentAo < bar2Ao && bar2Ao < bar3Ao && bar3Ao < bar4Ao && bar4Ao > bar5Ao;
			if (bearishAcceleration)
				PlaceSellSetup(current, point);
		}

		if (UseThirdWiseMan && _teeth0.HasValue)
		{
			var teeth = _teeth0.Value;
			var offset = MagnitudePips * point;

			if (upFractal.HasValue && candle.ClosePrice > teeth + offset)
				PlaceBuySetup(current, point);

			if (downFractal.HasValue && candle.ClosePrice < teeth - offset)
				PlaceSellSetup(current, point);
		}
	}

	private void UpdateProtection(ICandleMessage candle)
	{
		if (Position > 0 && _longStopLoss is decimal longStop)
		{
			if (candle.LowPrice <= longStop)
			{
				SellMarket();
				_longStopLoss = null;
			}
		}
		else if (Position < 0 && _shortStopLoss is decimal shortStop)
		{
			if (candle.HighPrice >= shortStop)
			{
				BuyMarket();
				_shortStopLoss = null;
			}
		}
	}

	private void PlaceBuySetup(CandleInfo bar, decimal point)
	{
		if (Volume <= 0)
			return;

		var entryPrice = bar.High + point;
		if (entryPrice <= 0m)
			return;

		var stopPrice = bar.Low - point;

		// Cancel pending sell
		_pendingSellPrice = null;
		_pendingSellStop = null;

		// Set pending buy entry (simulates buy stop order)
		_pendingBuyPrice = entryPrice;
		_pendingBuyStop = stopPrice;
	}

	private void PlaceSellSetup(CandleInfo bar, decimal point)
	{
		if (Volume <= 0)
			return;

		var entryPrice = bar.Low - point;
		if (entryPrice <= 0m)
			return;

		var stopPrice = bar.High + point;

		// Cancel pending buy
		_pendingBuyPrice = null;
		_pendingBuyStop = null;

		// Set pending sell entry (simulates sell stop order)
		_pendingSellPrice = entryPrice;
		_pendingSellStop = stopPrice;
	}

	private static bool IsBullishDivergent(CandleInfo current, CandleInfo previous)
	{
		var median = (current.High + current.Low) / 2m;
		return current.Low < previous.Low && current.Close > median;
	}

	private static bool IsBearishDivergent(CandleInfo current, CandleInfo previous)
	{
		var median = (current.High + current.Low) / 2m;
		return current.High > previous.High && current.Close < median;
	}

	private decimal? GetUpFractal()
	{
		if (_bar0 is not CandleInfo bar0 || _bar1 is not CandleInfo bar1 || _bar2 is not CandleInfo bar2 ||
			_bar3 is not CandleInfo bar3 || _bar4 is not CandleInfo bar4)
			return null;

		return bar2.High > bar3.High && bar2.High > bar4.High && bar2.High > bar1.High && bar2.High > bar0.High
			? bar2.High
			: null;
	}

	private decimal? GetDownFractal()
	{
		if (_bar0 is not CandleInfo bar0 || _bar1 is not CandleInfo bar1 || _bar2 is not CandleInfo bar2 ||
			_bar3 is not CandleInfo bar3 || _bar4 is not CandleInfo bar4)
			return null;

		return bar2.Low < bar3.Low && bar2.Low < bar4.Low && bar2.Low < bar1.Low && bar2.Low < bar0.Low
			? bar2.Low
			: null;
	}

	private void UpdateBarHistory(ICandleMessage candle)
	{
		_bar4 = _bar3;
		_bar3 = _bar2;
		_bar2 = _bar1;
		_bar1 = _bar0;

		_bar0 = new CandleInfo
		{
			Open = candle.OpenPrice,
			High = candle.HighPrice,
			Low = candle.LowPrice,
			Close = candle.ClosePrice
		};
	}

	private struct CandleInfo
	{
		public decimal Open { get; init; }
		public decimal High { get; init; }
		public decimal Low { get; init; }
		public decimal Close { get; init; }
	}
}