View on GitHub

Awesome Fx Trader

This strategy reproduces the MetaTrader setup from MQL/8539, which consists of the custom indicators AwesomeFxTradera.mq4 and t_ma.mq4. The original code paints the Bill Williams Awesome Oscillator histogram in green or red depending on whether the value is rising or falling, and overlays a 34-period linear weighted moving average (LWMA) alongside a smoothed clone of the same curve. The StockSharp port keeps the same calculations and converts the indicator colours into trading signals.

Original MQL logic

  1. AwesomeFxTradera.mq4 computes two exponential moving averages applied to the open price with periods 8 and 13. Their difference is stored in ExtBuffer0. The buffer is painted green when the current value is higher than the previous bar and red when it is lower. This effectively encodes the direction of momentum, not only its sign.
  2. t_ma.mq4 plots a 34-period LWMA of the open price (ExtMapBuffer1) and a 6-period simple moving average of that LWMA (ExtMapBuffer2). The smoother tracks whether the trend average accelerates or decelerates.

The MetaTrader chart therefore highlights bullish momentum when the oscillator is above zero and keeps increasing while price trades above the smoothed LWMA. Bearish momentum is the opposite configuration.

StockSharp implementation

The AwesomeFxTraderStrategy subscribes to a configurable candle type (default M15) and feeds the indicators with the candle open price to match the MetaTrader buffers.

  1. The fast and slow EMAs are recalculated on every finished candle; their difference reproduces the oscillating histogram.
  2. The LWMA tracks the 34-bar trend and a 6-bar SMA smooths it. Comparing both series reveals whether the trend curve is rising or falling.
  3. The oscillator colour is rebuilt by comparing the current histogram value with the previous bar, following the bool up logic from the MQL implementation.
  4. Entry rules:
    • Enter long when the oscillator is positive, rising (green buffer) and the LWMA is above its smoother.
    • Enter short when the oscillator is negative, falling (red buffer) and the LWMA is below its smoother.
  5. Exit/reversal rules: an opposite signal reverses the position. The order size is automatically increased by the absolute current position so that shorts are closed before a long is established and vice versa.

No extra stop-loss or take-profit levels are defined in the source code, so the port relies solely on momentum flips for exits. Logging statements document each trade trigger together with the indicator readings.

Parameters

Name Default Description
FastEmaPeriod 8 Length of the fast EMA used in the oscillator replica.
SlowEmaPeriod 13 Length of the slow EMA.
TrendLwmaPeriod 34 Period of the LWMA trend filter taken from t_ma.mq4.
TrendSmoothingPeriod 6 Window of the SMA applied to the LWMA values.
CandleType 15-minute time-frame Candle data type used for both momentum and trend calculations.

All parameters can be optimised through the StockSharp UI thanks to the StrategyParam metadata.

File mapping

MetaTrader file StockSharp counterpart Notes
MQL/8539/AwesomeFxTradera.mq4 CS/AwesomeFxTraderStrategy.cs Recreates the EMA-on-open oscillator and its rising/falling colour logic.
MQL/8539/t_ma.mq4 CS/AwesomeFxTraderStrategy.cs Implements the 34-period LWMA with a 6-period SMA smoother for trend detection.

The Python version is intentionally omitted as requested.

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>
/// Awesome Fx Trader strategy.
/// Recreates the MetaTrader logic that paints the Awesome Oscillator histogram and trend moving averages.
/// Goes long when the oscillator turns bullish while the linear weighted average stays above its smoother.
/// Opens shorts on the opposite momentum and trend alignment.
/// </summary>
public class AwesomeFxTraderStrategy : Strategy
{
	private readonly StrategyParam<int> _fastEmaPeriod;
	private readonly StrategyParam<int> _slowEmaPeriod;
	private readonly StrategyParam<int> _trendLwmaPeriod;
	private readonly StrategyParam<int> _trendSmoothingPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private EMA _fastEma;
	private EMA _slowEma;
	private WeightedMovingAverage _trendLwma;

	private decimal _previousAo;
	private bool _hasPreviousAo;
	private bool _isAoIncreasing;
	private decimal _previousLwma;

	/// <summary>
	/// Period for the fast EMA used in the oscillator.
	/// </summary>
	public int FastEmaPeriod
	{
		get => _fastEmaPeriod.Value;
		set => _fastEmaPeriod.Value = value;
	}

	/// <summary>
	/// Period for the slow EMA used in the oscillator.
	/// </summary>
	public int SlowEmaPeriod
	{
		get => _slowEmaPeriod.Value;
		set => _slowEmaPeriod.Value = value;
	}

	/// <summary>
	/// Length of the trend linear weighted moving average.
	/// </summary>
	public int TrendLwmaPeriod
	{
		get => _trendLwmaPeriod.Value;
		set => _trendLwmaPeriod.Value = value;
	}

	/// <summary>
	/// Length of the smoothing simple moving average applied to the trend LWMA.
	/// </summary>
	public int TrendSmoothingPeriod
	{
		get => _trendSmoothingPeriod.Value;
		set => _trendSmoothingPeriod.Value = value;
	}

	/// <summary>
	/// Type of candles to use for calculations.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Initialize <see cref="AwesomeFxTraderStrategy"/>.
	/// </summary>
	public AwesomeFxTraderStrategy()
	{
		_fastEmaPeriod = Param(nameof(FastEmaPeriod), 8)
			.SetGreaterThanZero()
			.SetDisplay("Fast EMA", "Period of the fast EMA driving the oscillator", "Awesome Oscillator")
			
			.SetOptimize(4, 20, 1);

		_slowEmaPeriod = Param(nameof(SlowEmaPeriod), 13)
			.SetGreaterThanZero()
			.SetDisplay("Slow EMA", "Period of the slow EMA driving the oscillator", "Awesome Oscillator")
			
			.SetOptimize(8, 40, 1);

		_trendLwmaPeriod = Param(nameof(TrendLwmaPeriod), 34)
			.SetGreaterThanZero()
			.SetDisplay("Trend LWMA", "Length of the linear weighted trend average", "Trend Filter")
			
			.SetOptimize(20, 80, 2);

		_trendSmoothingPeriod = Param(nameof(TrendSmoothingPeriod), 6)
			.SetGreaterThanZero()
			.SetDisplay("Trend Smoother", "Length of the SMA applied to the trend LWMA", "Trend Filter")
			
			.SetOptimize(3, 10, 1);

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Time-frame used for calculations", "General");
	}

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

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

		_fastEma = null;
		_slowEma = null;
		_trendLwma = null;
		_previousAo = 0m;
		_hasPreviousAo = false;
		_isAoIncreasing = false;
		_previousLwma = 0m;
	}

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

		_fastEma = new EMA { Length = FastEmaPeriod };
		_slowEma = new EMA { Length = SlowEmaPeriod };
		_trendLwma = new WeightedMovingAverage { Length = TrendLwmaPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(_fastEma, _slowEma, _trendLwma, ProcessCandle).Start();

		var priceArea = CreateChartArea();
		if (priceArea != null)
		{
			DrawCandles(priceArea, subscription);
			DrawIndicator(priceArea, _trendLwma);
			DrawOwnTrades(priceArea);
		}
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastEma, decimal slowEma, decimal lwma)
	{
		if (candle.State != CandleStates.Finished)
			return;

		var ao = fastEma - slowEma;

		if (!_hasPreviousAo)
		{
			_previousAo = ao;
			_hasPreviousAo = true;
			_isAoIncreasing = ao >= 0m;
			_previousLwma = lwma;
			return;
		}

		if (ao > _previousAo)
			_isAoIncreasing = true;
		else if (ao < _previousAo)
			_isAoIncreasing = false;

		var isTrendBullish = lwma > _previousLwma;
		var isTrendBearish = lwma < _previousLwma;
		var bullishSignal = _isAoIncreasing && ao > 0m && isTrendBullish;
		var bearishSignal = !_isAoIncreasing && ao < 0m && isTrendBearish;

		if (bullishSignal && Position <= 0)
		{
			var volume = Volume + Math.Abs(Position);
			if (volume <= 0)
				volume = 1;

			BuyMarket(volume);
		}
		else if (bearishSignal && Position >= 0)
		{
			var volume = Volume + Math.Abs(Position);
			if (volume <= 0)
				volume = 1;

			SellMarket(volume);
		}

		_previousAo = ao;
		_previousLwma = lwma;
	}
}