Announcement

Collapse
No announcement yet.

Partner 728x90

Collapse

Tip: How to round a calculated price by the tick size of an instrument?

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

    Tip: How to round a calculated price by the tick size of an instrument?

    When you calculate the price based on a formula within your custom indicator or strategy, you often need to round it by the tick size of the instrument in question. For example, your indicator initially gives 1408.673 as the stop price for your E-mini S&P 500 trading. However, the tick size of the instrument is 0.25. You need to round 1408.673 to 1408.75 before using it to enter an order.

    Here is the NinjaScript expression that just does that:

    price - price % TickSize + ((price % TickSize < TickSize / 2) ? 0.0 : TickSize);

    If you are already familiar with the % and ? operators, the expression should make sense to you immediately. For those who are not, let me explain it to you one step a time.

    First, TickSize is a property of any indicator or strategy in NinjaScript. It represents the minimum fluctuation in price of an instrument. For E-mini S&P 500, its value is 0.25.

    Second, "%" is the modulus operator in C#. It computes the remainder after dividing its first operand by its second. To illustrate, 9 % 3 yields 0, 9 % 2 yields 1, and 9 % 0.7 yields 0.6. Therefore, price % TickSize or 1408.673 % 0.25 yields 0.173. Now you can see that the first part of the expression, price - price % TickSize, yields 1408.50, a valid price that is closet to but smaller than the initial one.

    However, this is not the final result yet. Did you notice the remainder, 0.173 is more than half of the tick size, 0.25 / 2 or 0.125? So the third step is to add a tick size to the result when the situation occurs.

    The second part of the expression, ((price % TickSize < TickSize / 2) ? 0.0 : TickSize), just does that. It uses the conditional operator in C#, "?". Here is a general expression using the operator, condition ? value 1 : value 2. The expression equals to value 1 when the condition is true; it equals to value 2 when the condition is false. To illustrate, the value of (100 > 50) ? 1 : 2 is 1 because 100 is larger than 50. On the other hand, the value of (10 > 50) ? 1 : 2 is 2 because 10 is smaller than 50.

    The condition in our expression is price % TickSize < TickSize / 2, that is, the remainder is smaller than half of the tick size. When this is true, 0.0 as value 1 is added to the result. When it is false, the tick size as value 2 is added to the result. The final result is a “legitimate” price for the instrument rounded from the initial one.

    To recap, simply replace the price variable in the expression above with any value and you can round it by the tick size of any instrument.

    #2
    Originally posted by KamaCoder Eric View Post
    When you calculate the price based on a formula within your custom indicator or strategy, you often need to round it by the tick size of the instrument in question. For example, your indicator initially gives 1408.673 as the stop price for your E-mini S&P 500 trading. However, the tick size of the instrument is 0.25. You need to round 1408.673 to 1408.75 before using it to enter an order.

    Here is the NinjaScript expression that just does that:

    price - price % TickSize + ((price % TickSize < TickSize / 2) ? 0.0 : TickSize);

    If you are already familiar with the % and ? operators, the expression should make sense to you immediately. For those who are not, let me explain it to you one step a time.

    First, TickSize is a property of any indicator or strategy in NinjaScript. It represents the minimum fluctuation in price of an instrument. For E-mini S&P 500, its value is 0.25.

    Second, "%" is the modulus operator in C#. It computes the remainder after dividing its first operand by its second. To illustrate, 9 % 3 yields 0, 9 % 2 yields 1, and 9 % 0.7 yields 0.6. Therefore, price % TickSize or 1408.673 % 0.25 yields 0.173. Now you can see that the first part of the expression, price - price % TickSize, yields 1408.50, a valid price that is closet to but smaller than the initial one.

    However, this is not the final result yet. Did you notice the remainder, 0.173 is more than half of the tick size, 0.25 / 2 or 0.125? So the third step is to add a tick size to the result when the situation occurs.

    The second part of the expression, ((price % TickSize < TickSize / 2) ? 0.0 : TickSize), just does that. It uses the conditional operator in C#, "?". Here is a general expression using the operator, condition ? value 1 : value 2. The expression equals to value 1 when the condition is true; it equals to value 2 when the condition is false. To illustrate, the value of (100 > 50) ? 1 : 2 is 1 because 100 is larger than 50. On the other hand, the value of (10 > 50) ? 1 : 2 is 2 because 10 is smaller than 50.

    The condition in our expression is price % TickSize < TickSize / 2, that is, the remainder is smaller than half of the tick size. When this is true, 0.0 as value 1 is added to the result. When it is false, the tick size as value 2 is added to the result. The final result is a “legitimate” price for the instrument rounded from the initial one.

    To recap, simply replace the price variable in the expression above with any value and you can round it by the tick size of any instrument.

    Nice to know thanks, I always use Bars.Instrument.MasterInstrument.Round2TickSize( price ), but never think the way you explain it.

    Comment


      #3
      I was not aware that Bars.Instrument.MasterInstrument has a method called Round2TickSize. Neither the IntelliSense in the NinnjaScript Editor nor the Search in the Help Guide gives a clue that such a method exists. But it does! Thank you, PrTester!

      My test did show one unexpected behavior of Round2TickSize. When the price is at half the tick size, the method rounds it down instead of rounding it up.

      Comment


        #4
        >> When the price is at half the tick size, the method rounds it down instead of rounding it up.
        Unfortunately your logic below exhibits the same problem: please try 9.995 and 0.01 as tick size. Returns 9.99 here.

        Please let me know as you found a way to reliably round up on half a tick size. Thanks

        Comment


          #5
          My earlier test used 1410.125 as the price and 0.25 as the tick size. My expression rounded it correctly, but not Round2TickSize.

          Now with 9.995 as the price and 0.01 as the tick size, both failed to round it up.

          I did find a solution - cast both price and tick size to decimal first.

          Here is the updated expression...actually, it is now a function:

          private double Round(double price)
          {
          decimal p = (decimal)price;
          decimal t = (decimal)TickSize;

          return (double)(p - p % t + ((p % t < t / 2) ? 0M : t));
          }
          Last edited by KamaCoder Eric; 05-12-2008, 01:33 AM.

          Comment


            #6
            Excellent! I'll amend Round2TickSize accordingly. Thanks for pointing that out.

            Comment


              #7
              I am wondering why neither intellisense nor the help guide reveals the existence of Round2TickSize. Did I miss something? Thanks!

              Comment


                #8
                Nope. It just was not part of documented NinjaScript. We'll reconsider that.

                Comment


                  #9
                  Does this code work as expected in NT7?

                  If yes, it would be the solution to my problem in the other thread.

                  Comment


                    #10
                    I believe so symphys, saw no issues on a quick check here - will look into your other thread and issue shortly.
                    BertrandNinjaTrader Customer Service

                    Comment


                      #11
                      While rounding to the nearest tick has it's uses you really need to be aware of why you are rounding. Think of a stop order on a stock with a tick of .01. I need all of my buy prices rounded up while I need my Sell stops rounded down.

                      If my math creates a buy point at 87.0175, I need this rounded up to 87.02 as my prices isn't touched until then. Even a price of 87.0100001 needs to be rounded up to 87.02.

                      if I create a sell point at 87.0175, I need this rounded down to 87.01 as my price isn't touched until then. Even 87.01999999 needs to be rounded down to 87.01.

                      Might not seem like much, but if you want precision you need to allow this to happen.

                      W2

                      Comment


                        #12
                        UPDATE: The posted technique for rounding to tick value using the mod operator (%) is very fast, but as pointed out in post #4 above, still suffers from floating point accumulation errors. In some cases, the result of mod is just blatantly wrong. For example, 9.95 % 0.05 = 0.04999999999999874. The result should be zero, of course.

                        I wrote this post before realizing that mod suffered from floating point issues. I'm leaving it up, though, because someone might still find the post useful.

                        For the most reliable rounding to tick you'll need to convert to decimal (Version #2 below and post #5 above). Unfortunately, as shown below, this is much slower. But for most purposes, the slowness won't matter.


                        This thread is brilliant. Thank you for posting.

                        I needed a good RoundToTickSize method for a class library so I couldn't use the NT method.

                        Version #1 - Standard Way

                        The standard way of rounding to an interval (or tick size) is:

                        Code:
                        double rounded = Math.Round(price / TickSize) * TickSize;
                        The problem with this is that certain price values will result in floating point accumulation errors as follows:

                        Code:
                        double price = 0.123456789;
                        double rounded = Math.Round(price / TickSize) * TickSize;
                        Console.WriteLine(rounded.ToString("G17"));
                        // 0.12346000000000001
                        Kinda defeats the whole purpose of rounding to a tick.

                        Version #2 - Converting to Decimal

                        We need a way to avoid these errors. The logical way is to convert to the decimal type:

                        Code:
                        decimal mPrice = (decimal)price;
                        decimal mTickSize = (decimal)TickSize;
                        decimal mRounded = (Math.Round(mPrice / mTickSize) * mTickSize;
                        double rounded = (double)mRounded;
                        This approach works perfectly well, but it's nearly 50x slower than Version #1.

                        Version #3: Using Mod Operator (The original post in this thread)

                        The mod operator (%) method is fast and (mostly) avoids the problem with floating point accumulation errors:

                        Code:
                        double tickSize = 0.25;
                        double rounded = price - price % tickSize + ((price % tickSize < tickSize / 2) ? 0.0 : tickSize);


                        Version #4: Round Up To Tick

                        Rounding up to tick is even faster than rounding to tick.

                        Code:
                        double tickSize = 0.25;
                        double rounded = price - price % tickSize + (price % tickSize == 0 ? 0.0 : tickSize);
                        Version #5: Round Down To Tick

                        Rounding down is just the straight mod operation, so slightly faster even than rounding up.

                        Code:
                        double tickSize = 0.25;
                        double rounded = price - price % tickSize;


                        Performance Summary

                        Here is the performance for 10 million conversions using the various methods:
                        Version #1 (Standard Way) 54ms
                        Version #2 (Converting to decimal types) 2453ms
                        Version #3 (Mod operator) [Method in this post] 131ms
                        Version #4: Round Up To Tick 75ms
                        Version #5: Round Down To Tick 72ms
                        ---
                        NT8 MasterInstrument.RoundToTickSize 102ms
                        NT8 MasterInstrument.RoundDownToTickSize 128ms
                        Last edited by BarzTrading; 11-11-2024, 07:39 PM.

                        Comment

                        Latest Posts

                        Collapse

                        Topics Statistics Last Post
                        Started by Vilavani, Today, 01:08 AM
                        0 responses
                        4 views
                        0 likes
                        Last Post Vilavani  
                        Started by Playdc, Today, 12:38 AM
                        0 responses
                        4 views
                        0 likes
                        Last Post Playdc
                        by Playdc
                         
                        Started by Mykro, 12-04-2024, 10:18 AM
                        2 responses
                        14 views
                        1 like
                        Last Post GrumpyDude  
                        Started by DerkWehler, 12-04-2024, 09:00 AM
                        3 responses
                        29 views
                        2 likes
                        Last Post GrumpyDude  
                        Started by Blaze212, 12-04-2024, 10:05 PM
                        2 responses
                        23 views
                        0 likes
                        Last Post Blaze212  
                        Working...
                        X