Timer System

Overview

Strategies in StockSharp support a built-in timer system that allows executing actions at specified time intervals. Timers are based on the connector's WhenIntervalElapsed mechanism and work correctly both in live trading and during backtesting (using the emulator's virtual time).

Timers are convenient for:

  • Periodic market condition checks
  • Portfolio rebalancing at fixed intervals
  • Closing positions by time
  • Strategy state monitoring

ITimerHandler Interface

The timer is managed through the ITimerHandler interface, which provides the following members:

Member Description
Start() Starts the timer. Returns ITimerHandler for method chaining
Stop() Stops the timer. Returns ITimerHandler for method chaining
Interval Timer trigger interval (read and write)
IsStarted Indicates whether the timer is running
Dispose() Releases resources and stops the timer

Timer Creation Methods

CreateTimer

Creates a timer but does not start it. Starting is done by a separate Start() call:

ITimerHandler CreateTimer(TimeSpan interval, Action callback);

StartTimer

Creates and immediately starts a timer. Equivalent to CreateTimer(...).Start():

ITimerHandler StartTimer(TimeSpan interval, Action callback);

The interval must be a positive value (> TimeSpan.Zero), otherwise an exception will be thrown.

Timer Management

A timer can be stopped and restarted:

var timer = CreateTimer(TimeSpan.FromMinutes(1), MyCallback);

// Start
timer.Start();

// Stop
timer.Stop();

// Change interval
timer.Interval = TimeSpan.FromMinutes(5);

// Start again
timer.Start();

// Release resources
timer.Dispose();

Usage Example

public class TimerStrategy : Strategy
{
    private ITimerHandler _checkTimer;
    private ITimerHandler _closeTimer;

    private readonly StrategyParam<TimeSpan> _checkInterval;
    private readonly StrategyParam<TimeSpan> _maxHoldTime;

    public TimeSpan CheckInterval
    {
        get => _checkInterval.Value;
        set => _checkInterval.Value = value;
    }

    public TimeSpan MaxHoldTime
    {
        get => _maxHoldTime.Value;
        set => _maxHoldTime.Value = value;
    }

    public TimerStrategy()
    {
        _checkInterval = Param(nameof(CheckInterval), TimeSpan.FromMinutes(1));
        _maxHoldTime = Param(nameof(MaxHoldTime), TimeSpan.FromHours(4));
    }

    protected override void OnStarted2(DateTime time)
    {
        base.OnStarted2(time);

        // Timer for periodic market condition checks
        _checkTimer = StartTimer(CheckInterval, OnCheckTimer);

        // Timer for forced position closing (created but not started)
        _closeTimer = CreateTimer(MaxHoldTime, OnCloseTimer);

        var subscription = SubscribeCandles(TimeSpan.FromMinutes(5));

        subscription
            .Bind(ProcessCandle)
            .Start();
    }

    private void OnCheckTimer()
    {
        if (!IsFormedAndOnlineAndAllowTrading())
            return;

        this.AddInfoLog("Checking market conditions at {0}", CurrentTime);

        // Periodic position state check
        if (Position != 0)
        {
            this.AddInfoLog("Current position: {0}", Position);
        }
    }

    private void OnCloseTimer()
    {
        if (Position != 0)
        {
            this.AddInfoLog("Position hold time expired, closing");
            ClosePosition();

            // Stop the close timer after it fires
            _closeTimer.Stop();
        }
    }

    private void ProcessCandle(ICandleMessage candle)
    {
        if (!IsFormedAndOnlineAndAllowTrading())
            return;

        if (candle.OpenPrice < candle.ClosePrice && Position == 0)
        {
            BuyMarket();

            // Start the forced close timer
            _closeTimer.Start();
        }
    }
}

In this example, two timers are used: one for periodic state checking (StartTimer -- created and immediately started), and another for limiting the position hold time (CreateTimer -- created but started only upon entering a position).