Table of Contents

Using Rules

Creating Rules

  • Creating a rule for order registration condition:

    private void btnBuy_Click(object sender, RoutedEventArgs e)
    {
       var order = new Order
       { 
           Portfolio = Portfolio.SelectedPortfolio,
           Price = _instr1.BestAsk.Price,
           Security = _instr1,
           Volume = 1,
           Direction = Sides.Buy,
       };
       order
           .WhenRegistered(Connector)
           .Do(() => Connector.AddInfoLog("Order successfully registered"))
           .Once()
           .Apply(this);
    
       // order registration
       Connector.RegisterOrder(order);
    }
    

    Now, when the event fires (the order is registered on the exchange), the action specified through the IMarketRule.Do(System.Action action ) method will be called.

    At the end of rule formation, the MarketRuleHelper.Apply(StockSharp.Algo.IMarketRule rule ) method is called. Until this method is called for the rule, it is inactive (the handler in IMarketRule.Do(System.Action action ) will not be called).

  • Creating rules within a strategy:

    class FirstStrategy : Strategy
    {
        protected override void OnStarted(DateTimeOffset time)
        {
            // Subscription to candles
            var candleSubscription = new Subscription(TimeSpan.FromMinutes(5).TimeFrame(), Security);
            this
                .WhenCandlesStarted(candleSubscription)
                .Do(ProcessCandle)
                .Apply(this);
    
            // Subscription to tick trades
            var tickSubscription = new Subscription(DataType.Ticks, Security);
            tickSubscription
                .WhenTickTradeReceived(this)
                .Do(ProcessTick)
                .Apply(this);
    
            // Send subscription requests
            Subscribe(candleSubscription);
            Subscribe(tickSubscription);
    
            base.OnStarted(time);
        }
    
        // Methods for event processing
        private void ProcessCandle(ICandleMessage candle) { /* ... */ }
        private void ProcessTick(ITickTradeMessage tick) { /* ... */ }
    }    
    
  • Removing unnecessary rules.

    IMarketRule has IMarketRule.Token - a token of the rule with which it is associated. For example, for the rule [WhenCanceled](xref:StockSharp.Algo.MarketRuleHelper.WhenCanceled(StockSharp.BusinessEntities.Order,StockSharp.BusinessEntities.ISubscriptionProvider), the token will be the order.

    When a rule for successful order cancellation has been triggered, it's better to remove all other rules related to this order:

    var order = this.CreateOrder(direction, (decimal)Security.GetCurrentPrice(direction), Volume);
    var ruleCanceled = order.WhenCanceled(Connector);
    ruleCanceled
        .Do(() =>
        {
            this.AddInfoLog("Order successfully canceled");
            // removing all rules associated with order
            Rules.RemoveRulesByToken(ruleCanceled, (IMarketRule)ruleCanceled.Token);
        })
        .Once()
        .Apply(this);
    order
        .WhenRegistered(Connector)
        .Do(() => this.AddInfoLog("Order successfully registered"))
        .Once()
        .Apply(this);
    order
        .WhenRegisterFailed(Connector)
        .Do(() => this.AddInfoLog("Order not accepted by the exchange"))
        .Once()
        .Apply(this);
    order
        .WhenMatched(Connector)
        .Do(() => this.AddInfoLog("Order fully executed"))
        .Once()
        .Apply(this);
    // order registration
    RegisterOrder(order);
    
  • Combining rules with condition MarketRuleHelper.Or(StockSharp.Algo.IMarketRule rule, StockSharp.Algo.IMarketRule[] rules ) / MarketRuleHelper.And(StockSharp.Algo.IMarketRule rule, StockSharp.Algo.IMarketRule[] rules ).

    When time expires OR a candle closes:

    // Create a subscription to candles
    var subscription = new Subscription(TimeSpan.FromMinutes(5).TimeFrame(), Security);
    var timeInterval = TimeSpan.FromMilliseconds(5000);
    
    Connector
        .WhenIntervalElapsed(timeInterval)
        .Or(this.WhenCandlesStarted(subscription))
        .Do(() => this.AddInfoLog("Candle closed or time expired"))
        .Once()
        .Apply(this);
    
    // Send subscription request
    Subscribe(subscription);
    

    Or this format:

    // Create a subscription to candles
    var subscription = new Subscription(TimeSpan.FromMinutes(5).TimeFrame(), Security);
    var timeInterval = TimeSpan.FromMilliseconds(5000);
    
    MarketRuleHelper
        .Or(new IMarketRule[] {
            Connector.WhenIntervalElapsed(timeInterval), 
            this.WhenCandlesStarted(subscription)
        })
        .Do(() => this.AddInfoLog("Candle closed or time expired"))
        .Once()
        .Apply(this);
    
    // Send subscription request
    Subscribe(subscription);
    

    When the last trade price is above 135000 AND below 140000:

    // Create a subscription to tick trades
    var subscription = new Subscription(DataType.Ticks, Security);
    var priceMore = new Unit(135000m, UnitTypes.Limit);
    var priceLess = new Unit(140000m, UnitTypes.Limit);
    
    MarketRuleHelper
        .And(new IMarketRule[] {
            subscription.WhenLastTradePriceMore(this, 135000m), 
            subscription.WhenLastTradePriceLess(this, 140000m)
        })
        .Do(() => this.AddInfoLog($"Last trade price is in the range from {priceMore} to {priceLess}"))
        .Apply(this);
    
    // Send subscription request
    Subscribe(subscription);
    
    Tip

    The handler in IMarketRule.Do(System.Action action ) will be called after the last rule added through MarketRuleHelper.And(StockSharp.Algo.IMarketRule rule, StockSharp.Algo.IMarketRule[] rules ) is triggered.

  • Rule operation periodicity - IMarketRule.Until(System.Func<System.Boolean> canFinish ):

    bool flag = false;
    
    // Create a subscription to tick trades
    var subscription = new Subscription(DataType.Ticks, Security);
    
    subscription
        .WhenTickTradeReceived(this)
        .Do((tick) =>
        {
            if(condition) flag = true;
        })
        .Until(() => flag)			
        .Apply(this);
    
    // Send subscription request
    Subscribe(subscription);
    

Examples of Using Rules

Rules on Candles

// Create a subscription to 5-minute candles
var subscription = new Subscription(TimeSpan.FromMinutes(5).TimeFrame(), Security);

// Variable for counting candles
var i = 0;
var diff = "10%".ToUnit();

// Rule that activates when a new candle starts
this.WhenCandlesStarted(subscription)
    .Do((candle) =>
    {
        i++;

        // Nested rule: check when total volume exceeds threshold
        this
            .WhenTotalVolumeMore(candle, diff)
            .Do((candle1) =>
            {
                LogInfo($"Rule WhenCandlesStarted and WhenTotalVolumeMore candle={candle1}");
                LogInfo($"Rule WhenCandlesStarted and WhenTotalVolumeMore i={i}");
            })
            .Once().Apply(this);

    }).Apply(this);
    
// Send subscription request
Subscribe(subscription);

Rules on Order Books (Market Depth)

// Subscription to order book data
var mdSub = new Subscription(DataType.MarketDepth, Security);

// Method 1: Creating a rule in a chain
mdSub.WhenOrderBookReceived(this).Do((depth) =>
{
    LogInfo($"Rule WhenOrderBookReceived #1 BestBid={depth.GetBestBid()}, BestAsk={depth.GetBestAsk()}");
}).Once().Apply(this);

// Method 2: First create a rule variable
var whenMarketDepthChanged = mdSub.WhenOrderBookReceived(this);

whenMarketDepthChanged.Do((depth) =>
{
    LogInfo($"Rule WhenOrderBookReceived #2 BestBid={depth.GetBestBid()}, BestAsk={depth.GetBestAsk()}");
}).Once().Apply(this);

// Rule within a rule
mdSub.WhenOrderBookReceived(this).Do((depth) =>
{
    LogInfo($"Rule WhenOrderBookReceived #3 BestBid={depth.GetBestBid()}, BestAsk={depth.GetBestAsk()}");

    // Rule without specifying Once()
    mdSub.WhenOrderBookReceived(this).Do((depth1) =>
    {
        LogInfo($"Rule WhenOrderBookReceived #4 BestBid={depth1.GetBestBid()}, BestAsk={depth1.GetBestAsk()}");
    }).Apply(this);
}).Once().Apply(this);

// Send subscription request
Subscribe(mdSub);

Rules with Completion Condition

// Subscription to order book data
var mdSub = new Subscription(DataType.MarketDepth, Security);

// Counter
var i = 0;

// Create a rule that processes order books until i reaches 10
mdSub.WhenOrderBookReceived(this).Do(depth =>
{
    i++;
    LogInfo($"Rule WhenOrderBookReceived BestBid={depth.GetBestBid()}, BestAsk={depth.GetBestAsk()}");
    LogInfo($"Rule WhenOrderBookReceived i={i}");
})
.Until(() => i >= 10)
.Apply(this);

// Send subscription request
Subscribe(mdSub);

Rules on Orders

// Subscription to tick trades
var sub = new Subscription(DataType.Ticks, Security);

// When we receive the first tick, we'll create an order
sub.WhenTickTradeReceived(this).Do(() =>
{
    var order = CreateOrder(Sides.Buy, default, 1);

    var ruleReg = order.WhenRegistered(this);
    var ruleRegFailed = order.WhenRegisterFailed(this);

    ruleReg
        .Do(() => LogInfo("Order #1 registered"))
        .Once()
        .Apply(this)
        .Exclusive(ruleRegFailed);  // Rules are mutually exclusive

    ruleRegFailed
        .Do(() => LogInfo("Order #1 not registered"))
        .Once()
        .Apply(this)
        .Exclusive(ruleReg);  // Rules are mutually exclusive

    RegisterOrder(order);
}).Once().Apply(this);

// Send subscription request
Subscribe(sub);

Rules on Price Changes

// Subscription to tick trades
var sub = new Subscription(DataType.Ticks, Security);

// Rule activates on the first tick and creates another rule
sub.WhenTickTradeReceived(this).Do(t =>
{
    // Create a rule that activates when the price moves 2 points in any direction
    sub
        .WhenLastTradePriceMore(this, t.Price + 2)
        .Or(sub.WhenLastTradePriceLess(this, t.Price - 2))
        .Do(t =>
        {
            LogInfo($"Rule WhenLastTradePriceMore or WhenLastTradePriceLess triggered: tick={t}");
        })
        .Apply(this);
})
.Once() // call this rule only once
.Apply(this);

// Send subscription request
Subscribe(sub);