Ver no GitHub

Timed Buy Order Strategy

Overview

The Timed Buy Order Strategy replicates the MetaTrader expert advisor buy_order.mq4, which submits a stream of market buy orders driven by a one-second timer. The StockSharp port keeps the same trading rhythm: it waits until the timer aligns with the expected second within the current minute and then pushes the next order. After a pre-defined number of fills the strategy stops itself automatically.

This implementation relies on the high-level StockSharp Timer service instead of manual loops. No market indicators or candle subscriptions are required, making the logic deterministic and timing-focused.

Core Logic

  1. When the strategy starts it activates risk protection via StartProtection() and starts a timer with the configured interval (default: one second).
  2. Each timer callback checks whether the strategy is online and allowed to trade, and whether the current exchange second matches the expected sequence value.
  3. If all checks succeed, the strategy sends a market buy order with the configured volume.
  4. The process repeats until the target number of orders has been sent, after which the strategy stops.

The second-synchronization behaviour mirrors the original MQL expert: the first order is only dispatched when the seconds component reaches zero, and each following order is tied to the next second value.

Parameters

Name Type Default Description
OrderVolume decimal 0.01 Quantity for each market buy order. A validation guard stops the strategy if the value is non-positive.
OrdersToPlace int 60 Total number of sequential buy orders to submit before stopping.
Interval TimeSpan 1s Delay between timer callbacks. Keeping it at one second best reproduces the MQL timing, but other values are possible for experimentation.

All parameters are exposed through StockSharp StrategyParam<T> objects, so they can be optimised or configured from UI tooling.

Execution Flow

  • Initialisation – resetting counters in OnReseted() ensures clean state when restarting or re-optimising.
  • Start – in OnStarted() the timer begins and counters reset; protection is enabled once per lifecycle.
  • Timer Tick – method OnTimer() performs the sequencing checks, logs the outgoing order, and stops the strategy when the final order is sent.
  • Completion – helper CompleteStrategy() prevents duplicate shutdown attempts and calls Stop() exactly once.

Conversion Notes

  • The MQL function EventSetTimer(1) is mapped to Timer.Start(TimeSpan.FromSeconds(1), OnTimer).
  • Order comments and magic numbers used in MetaTrader do not have direct equivalents in StockSharp, so logging is used instead to trace progress.
  • The strategy keeps the “60 orders per minute” concept by matching the seconds component rather than counting timer triggers.

Usage Tips

  1. Assign the desired security and portfolio before starting the strategy.
  2. Adjust OrderVolume to match the instrument’s lot size and broker rules.
  3. If you need fewer orders, reduce OrdersToPlace; to disable the second-based pacing entirely, set Interval to any value and remove the second matching in the code (advanced modification).
  4. Monitor the log output to track order submissions and ensure that the timer alignment behaves as expected.

Limitations

  • The strategy only buys; there is no exit logic other than manual intervention or protective stops managed by the broker.
  • Order placement is limited by the accuracy of the timer service provided by the connection and operating system; large delays could desynchronise the sequence.

Files

  • CS/TimedBuyOrderStrategy.cs – main C# implementation.
  • README_zh.md – Chinese documentation.
  • README_ru.md – Russian documentation.

A Python port is intentionally omitted per project instructions; create it later if required.

namespace StockSharp.Samples.Strategies;

using System;

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

/// <summary>
/// Strategy that submits a sequence of market buy orders on each candle close.
/// After a configurable number of orders have been placed, it stops.
/// </summary>
public class TimedBuyOrderStrategy : Strategy
{
	private readonly StrategyParam<int> _ordersToPlace;
	private readonly StrategyParam<DataType> _candleType;

	private int _ordersPlaced;

	/// <summary>
	/// Initializes a new instance of the <see cref="TimedBuyOrderStrategy"/> class.
	/// </summary>
	public TimedBuyOrderStrategy()
	{
		_ordersToPlace = Param(nameof(OrdersToPlace), 60)
			.SetGreaterThanZero()
			.SetDisplay("Orders To Place", "Number of sequential buy orders before stopping", "Trading");

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle type", "Candle type for strategy calculation.", "General");
	}

	/// <summary>
	/// Total number of buy orders to submit before the strategy stops.
	/// </summary>
	public int OrdersToPlace
	{
		get => _ordersToPlace.Value;
		set => _ordersToPlace.Value = value;
	}

	/// <summary>
	/// Candle type.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_ordersPlaced = 0;
	}

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

		_ordersPlaced = 0;

		var sma = new SMA { Length = 5 };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(sma, OnProcess)
			.Start();
	}

	private void OnProcess(ICandleMessage candle, decimal smaValue)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		if (_ordersPlaced >= OrdersToPlace)
			return;

		BuyMarket();

		_ordersPlaced++;
	}
}