Auf GitHub ansehen

Triple Top Triple Bottom Strategy

The Triple Top Triple Bottom Strategy is a port of the MetaTrader Expert Advisor with the same name. The original system combines several confirmation layers (trend direction, momentum strength and a MACD filter) before entering the market. This StockSharp implementation keeps the same core ideas while exposing the important thresholds as strategy parameters.

Core Logic

  1. Trend filter – two linear weighted moving averages (LWMA) calculated on the typical price (H+L+C)/3 define the trading direction. The fast LWMA must be above the slow LWMA to allow long trades and below the slow LWMA to permit short trades.
  2. Momentum confirmation – the built-in momentum indicator with a configurable lookback length must deviate from the neutral 100 level by at least the user-defined threshold within the latest three completed candles. The EA required the same behaviour by analysing the previous momentum values and we mirror this validation to avoid entries in flat markets.
  3. MACD filter – a classic 12/26/9 MACD signal line filter prevents fading a strong trend. The strategy only buys when the MACD line is above the signal line and sells when it is below.
  4. Risk management – market orders are protected with both stop-loss and take-profit targets measured in absolute price units. The parameters are optional; setting them to zero disables the respective order. The code also closes the position if the opposite risk threshold is reached during candle processing.

Parameters

  • Entry CandleDataType that defines the timeframe of the working candles.
  • Fast LWMA / Slow LWMA – lengths of the fast and slow trend filters.
  • Momentum Period / Momentum Threshold – lookback for the momentum indicator and the minimal deviation from 100 that confirms a trade idea.
  • Stop Loss / Take Profit – protective distances in absolute price units; they are also sent as native protective orders via SetStopLoss and SetTakeProfit so that risk control is enforced even if the strategy session stops.

Differences vs. MQL Version

  • All money-management extras (lot multipliers, equity protection, candle trailing, break-even and manual trend-line checks) were omitted because the StockSharp high-level API already offers position sizing utilities and because the graphical objects used in the original EA are specific to MetaTrader.
  • Risk thresholds are expressed in absolute price units instead of pips. This keeps the implementation broker-neutral; users can easily convert their preferred pip distance by multiplying the broker's pip size with the desired number of pips.
  • Chart output uses StockSharp areas for the price candles, moving averages, momentum and MACD indicators.

Usage Notes

  1. Attach the strategy to an instrument and configure the desired candle type before starting.
  2. Adjust the momentum threshold and the stop distances according to the instrument's volatility.
  3. The strategy trades a single net position. When an opposite signal appears the current exposure is closed first, preventing overlapping trades.

The code is fully commented in English and follows the StockSharp high-level API guidelines provided in the repository.

using System;
using System.Collections.Generic;

using Ecng.Common;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

public class TripleTopTripleBottomStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;

	private ExponentialMovingAverage _fast;
	private ExponentialMovingAverage _slow;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;
	private int _cooldown;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
	public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }

	public TripleTopTripleBottomStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
		_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
		_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
		_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
	}

	protected override void OnReseted()
	{
		base.OnReseted();
		_fast = null; _slow = null;
		_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_fast = new ExponentialMovingAverage { Length = FastPeriod };
		_slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
		subscription.Bind(_fast, _slow, ProcessCandle);
		subscription.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
		if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }

		var close = candle.ClosePrice;
		var step = Security?.PriceStep ?? 1m;

		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}
		else if (Position < 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}

		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }

		_prevFast = fastValue; _prevSlow = slowValue;
	}
}