Auf GitHub ansehen

Chaos Trader Lite Strategy

The Chaos Trader Lite strategy replicates Bill Williams' three wise men entry techniques using StockSharp's high level API. It analyses each finished candle of the configured timeframe (1 hour by default) and places stop orders when any of the following conditions are met:

  1. First Wise Man – Divergent bar: detects bullish or bearish divergent candles and requires a minimum gap between price and the Alligator lips line.
  2. Second Wise Man – Awesome Oscillator acceleration: waits for five consecutive Awesome Oscillator readings to show accelerating momentum.
  3. Third Wise Man – Fractal breakout: confirms a fractal two candles back and checks that price is trading sufficiently far from the Alligator teeth line before queuing a breakout order.

Whenever a long setup appears the strategy cancels existing sell stops, closes short positions, places a new buy stop just above the previous high and records a protective stop below the candle. The opposite happens for short setups. Protective stops are monitored on each bar; if price crosses the stored level the position is exited at market.

Indicators and calculations

  • Alligator lips: 5-period smoothed moving average of the median price shifted three candles forward. The strategy keeps a queue so that the value aligned with the current bar matches the MetaTrader implementation.
  • Alligator teeth: 8-period smoothed moving average of the median price shifted five candles forward. The shifted value drives the third wise man filter.
  • Awesome Oscillator: StockSharp's built-in indicator (5 vs 34 SMA of the median price) provides the momentum series used by the second wise man.
  • Fractals: the code inspects the high/low of the candle that sits two bars behind the latest bar. A valid fractal requires that candle to be higher (or lower) than the two candles on both sides.

Trading logic

  1. Subscribe to the requested candle type and process only finished candles.
  2. Update Alligator and Awesome Oscillator indicators and store shifted values.
  3. Evaluate the wise men conditions:
    • Divergent bar must close in the upper (for bulls) or lower (for bears) half of the candle and show a distance from the lips greater than MagnitudePips * PriceStep.
    • AO acceleration requires five values: AO[1] > AO[2] > AO[3] > AO[4] and AO[4] < AO[5] for longs, mirrored for shorts.
    • Fractal breakout checks that price closes above (or below) the confirmed fractal and above (or below) the Alligator teeth plus the magnitude threshold.
  4. When a setup is active place a BuyStop or SellStop order with volume Volume at the candle high plus one price step (or low minus one step). Cancel the opposite stop and flatten contrary positions.
  5. Update stored stop-loss levels: long stops trail upwards, short stops trail downwards. If a candle pierces the stored stop the strategy exits the open position at market.

Parameters

  • MagnitudePips (default 10) – minimal pip distance between the divergent bar and the Alligator lips.
  • UseFirstWiseMan (default true) – enable or disable the divergent bar entry.
  • UseSecondWiseMan (default true) – enable or disable the Awesome Oscillator acceleration entry.
  • UseThirdWiseMan (default true) – enable or disable the fractal breakout entry.
  • Volume (default 0.01) – order size for stop entries.
  • CandleType (default 1 hour) – data type processed by the strategy.

Notes

  • Bid/ask checks from the original MQL4 code are approximated with the candle close price in StockSharp.
  • Margin and volume validation routines from MetaTrader are omitted because StockSharp handles order validation internally.
  • Stop orders are cancelled when the opposite setup appears to avoid conflicting pending orders, matching the CloseAll behaviour from the 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; }
	}
}