View on GitHub

TCPivotStop Floor Breakout Strategy

Overview

The TCPivotStop Floor Breakout Strategy is a direct port of the MetaTrader expert advisor gpfTCPivotStop. The logic revolves around classical floor pivot calculations performed on the previous trading day. At the start of every new daily session the strategy:

  1. Aggregates the prior day's high, low, and close to compute the pivot point plus the first three support and resistance tiers.
  2. Checks whether the latest completed hourly bar crossed the pivot from above or below.
  3. Opens a market order in the direction of the crossover while attaching stop-loss and take-profit levels that mirror the original expert's behaviour.

Only one position can be active at a time. Optional session management allows flattening exposure when a new day begins.

Trading Rules

  • Timeframe – Designed for 1-hour candles (configurable).
  • Pivot calculation – Uses the high, low, and close of the previous day to compute Pivot, R1, R2, R3, S1, S2, S3.
  • Entry conditions
    • Enter short when the last completed bar closed below the pivot while the preceding bar closed above it.
    • Enter long when the last completed bar closed above the pivot while the preceding bar closed below it.
  • Position sizing – Fixed lot size defined by the OrderVolume parameter.
  • Exit conditions
    • Stop-loss and take-profit prices are mapped to the classic pivot levels.
    • If the CloseAtSessionEnd flag is enabled the strategy liquidates open trades before the next session starts.
    • Protective levels are monitored on candle highs/lows and executed with market orders when touched.

Parameters

Name Description Default
OrderVolume Trade size for market entries. 0.1
TakeProfitTarget Chooses which pivot tier acts as the profit target (1 = nearest, 3 = farthest). 1
CloseAtSessionEnd Close any open position once a new daily session begins. false
CandleType Timeframe used for all calculations (hourly by default). H1

Notes

  • The strategy executes orders only once per day when a new pivot set is available, just like the source EA that triggers on the first tick of the daily session.
  • The MetaTrader version recalculated lot sizes using account margin history. This port keeps position sizing fixed and delegates money management to other components if needed.
  • Protective orders are emulated by monitoring candle extremes and sending market orders once a threshold is crossed.

Files

  • CS/TcpFloorPivotBreakoutStrategy.cs – C# implementation of the trading logic.
  • README.md – English documentation (this file).
  • README_zh.md – Simplified Chinese translation.
  • README_ru.md – Russian translation.
using System;

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

namespace StockSharp.Samples.Strategies;

public class TcpFloorPivotBreakoutStrategy : Strategy
{
	private readonly StrategyParam<int> _channelPeriod;
	private readonly StrategyParam<int> _cooldownCandles;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevClose;
	private decimal _prevMid;
	private bool _hasPrev;
	private int _cooldownRemaining;

	public int ChannelPeriod { get => _channelPeriod.Value; set => _channelPeriod.Value = value; }
	public int CooldownCandles { get => _cooldownCandles.Value; set => _cooldownCandles.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public TcpFloorPivotBreakoutStrategy()
	{
		_channelPeriod = Param(nameof(ChannelPeriod), 48).SetDisplay("Channel Period", "Channel lookback", "Indicators");
		_cooldownCandles = Param(nameof(CooldownCandles), 150).SetDisplay("Cooldown", "Candles between signals", "General");
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame()).SetDisplay("Candle Type", "Candle timeframe", "General");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevClose = default;
		_prevMid = default;
		_hasPrev = default;
		_cooldownRemaining = default;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevClose = 0;
		_prevMid = 0;
		_hasPrev = false;
		_cooldownRemaining = 0;

		var highest = new Highest { Length = ChannelPeriod };
		var lowest = new Lowest { Length = ChannelPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(highest, lowest, ProcessCandle).Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal highest, decimal lowest)
	{
		if (candle.State != CandleStates.Finished) return;
		var close = candle.ClosePrice;
		var mid = (highest + lowest) / 2;
		if (!_hasPrev) { _prevClose = close; _prevMid = mid; _hasPrev = true; return; }

		if (_cooldownRemaining > 0)
		{
			_cooldownRemaining--;
			_prevClose = close;
			_prevMid = mid;
			return;
		}

		if (_prevClose <= _prevMid && close > mid && Position <= 0)
		{
			if (Position < 0) BuyMarket();
			BuyMarket();
			_cooldownRemaining = CooldownCandles;
		}
		else if (_prevClose >= _prevMid && close < mid && Position >= 0)
		{
			if (Position > 0) SellMarket();
			SellMarket();
			_cooldownRemaining = CooldownCandles;
		}
		_prevClose = close;
		_prevMid = mid;
	}
}