Table of Contents

Countertrend Strategy with Quoting

Overview

StairsCountertrendStrategy is a countertrend trading strategy that opens positions against an established trend of a specific length, using a quoting mechanism for more precise market entry.

Main Components

public class StairsCountertrendStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleDataType;
	private readonly StrategyParam<int> _length;
	private QuotingProcessor _quotingProcessor;

	private int _bullLength;
	private int _bearLength;
}

Strategy Parameters

The strategy allows customizing the following parameters:

  • CandleDataType - candle type to work with (default 1-minute)
  • Length - number of consecutive candles in one direction to identify a trend (default 5)

The Length parameter is available for optimization in the range from 2 to 10 with a step of 1.

Strategy Initialization

In the OnStarted method, counters are reset, candle subscription is created, and visualization is prepared:

protected override void OnStarted(DateTimeOffset time)
{
	// Reset counters at start
	_bullLength = 0;
	_bearLength = 0;

	// Create candle subscription
	var subscription = SubscribeCandles(CandleDataType);

	subscription
		.Bind(ProcessCandle)
		.Start();

	// Set up visualization on the chart
	var area = CreateChartArea();
	if (area != null)
	{
		DrawCandles(area, subscription);
		DrawOwnTrades(area);
	}

	base.OnStarted(time);
}

Processing Candles

The ProcessCandle method is called for each completed candle and implements the logic for trend detection and quoting processor management:

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

	// Identify bullish or bearish candle
	if (candle.OpenPrice < candle.ClosePrice)
	{
		_bullLength++;
		_bearLength = 0;

		this.AddInfoLog($"Bullish candle detected. Streak: {_bullLength}");
	}
	else if (candle.OpenPrice > candle.ClosePrice)
	{
		_bullLength = 0;
		_bearLength++;

		this.AddInfoLog($"Bearish candle detected. Streak: {_bearLength}");
	}

	// Stop existing processor when direction change is needed
	if (_quotingProcessor != null)
	{
		// Check if processor needs to be cleared (trend change or position)
		var shouldClearProcessor = false;

		// Need to sell if bullish trend and no short position
		if (_bullLength >= Length && Position >= 0)
			shouldClearProcessor = true;
		// Need to buy if bearish trend and no long position
		else if (_bearLength >= Length && Position <= 0)
			shouldClearProcessor = true;

		if (shouldClearProcessor)
		{
			_quotingProcessor?.Dispose();
			_quotingProcessor = null;
		}
	}

	// Create new quoting processor when needed
	if (_quotingProcessor == null && IsFormedAndOnlineAndAllowTrading())
	{
		if (_bullLength >= Length && Position >= 0)
		{
			// Bullish trend - open short position
			CreateQuotingProcessor(Sides.Sell);
			this.AddInfoLog($"Starting SELL quoting after {_bullLength} bullish candles");
		}
		else if (_bearLength >= Length && Position <= 0)
		{
			// Bearish trend - open long position
			CreateQuotingProcessor(Sides.Buy);
			this.AddInfoLog($"Starting BUY quoting after {_bearLength} bearish candles");
		}
	}
}

Creating Quoting Processor

The CreateQuotingProcessor method creates a quoting processor with the specified direction:

private void CreateQuotingProcessor(Sides side)
{
	// Create behavior for market quoting
	var behavior = new MarketQuotingBehavior(
		0, // No price offset
		new Unit(0.1m, UnitTypes.Percent), // Use 0.1% as minimum deviation
		MarketPriceTypes.Following // Follow market price
	);

	// Create quoting processor
	_quotingProcessor = new(
		behavior,
		Security,
		Portfolio,
		side,
		Volume, // Quoting volume
		Volume, // Maximum order volume
		TimeSpan.Zero, // No timeout
		this, // Strategy implements ISubscriptionProvider
		this, // Strategy implements IMarketRuleContainer
		this, // Strategy implements ITransactionProvider
		this, // Strategy implements ITimeProvider
		this, // Strategy implements IMarketDataProvider
		IsFormedAndOnlineAndAllowTrading, // Check trading permission
		true, // Use order book prices
		true // Use last trade price if order book is empty
	)
	{
		Parent = this
	};

	// Subscribe to processor events
	_quotingProcessor.OrderRegistered += order =>
		this.AddInfoLog($"Order {order.TransactionId} registered at price {order.Price}");

	_quotingProcessor.OrderFailed += fail =>
		this.AddInfoLog($"Order failed: {fail.Error.Message}");

	_quotingProcessor.OwnTrade += trade =>
		this.AddInfoLog($"Trade executed: {trade.Trade.Volume} at {trade.Trade.Price}");

	_quotingProcessor.Finished += isOk =>
	{
		_quotingProcessor?.Dispose();
		_quotingProcessor = null;
	};

	// Initialize processor
	_quotingProcessor.Start();
}

Trading Logic

  • Sell signal: Length consecutive bullish candles (close price above open price) when there is no short position
  • Buy signal: Length consecutive bearish candles (close price below open price) when there is no long position
  • Quoting processor is used for market entry, following the market price

Features

  • The strategy automatically determines instruments to work with via the GetWorkingSecurities() method
  • The strategy only works with completed candles
  • Quoting is used instead of market orders for more efficient market entry
  • The strategy applies a countertrend approach, opening positions against the established trend
  • Detailed logging of main events for debugging is implemented
  • The quoting processor is automatically cleared when the trend direction changes or when targets are reached
  • Candles and trades visualization on the chart is supported
  • The sequence length parameter optimization is implemented for strategy configuration