Announcement

Collapse

Looking for a User App or Add-On built by the NinjaTrader community?

Visit NinjaTrader EcoSystem and our free User App Share!

Have a question for the NinjaScript developer community? Open a new thread in our NinjaScript File Sharing Discussion Forum!
See more
See less

Partner 728x90

Collapse

Exit Methods Instead of Set Methods for Stop Loss and Profit Target

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

    Exit Methods Instead of Set Methods for Stop Loss and Profit Target

    Do you have a good example of how to properly use Exit methods to set both a stop loss and profit target when entering a new position?

    In the OnOrderUpdate method I create both an ExitLongLimit and an ExitLongStopMarket (Let's just talk about long positions for the sake of simplicity.)

    Then I resubmit those with each OnBarUpdate to keep them alive during the life of the position.

    This is sort of working, but I keep running into various issues and I'd just like to make sure I'm following the recommended pattern.

    Steve L
    NinjaTrader Ecosystem Vendor - Ninja Mastery

    #2
    Hello Steve,

    Thanks for your post.

    The SampleOnOrderUpdate strategy demonstrates how this can be done. We typically advise using OnOrderUpdate to assign orders to Order objects, and then to submit Exit orders from OnExecutionUpdate. It is permissible to assign Order objects directly from Exit methods in OnExecutionUpdate.

    I highly recommend this framework for robust strategies. Please also see the documentation linked with the sample.

    SampleOnOrderUpdate - https://ninjatrader.com/support/help...and_onexec.htm

    Let us know if you have any questions and we will be happy to assist further.
    JimNinjaTrader Customer Service

    Comment


      #3
      Thanks, Jim.

      So, using the sample code, I'm still running into an anomaly I saw previously.

      I've taken the exact strategy code from the example and just changed the size of the stop loss and profit target.

      My data series is ES ##-## daily bars from 1/1/2018 to 1/1/2019. Kinetick data.

      If I run the strategy code (included below) on my chart, it makes a few trades, then halts.

      Click image for larger version  Name:	2019-05-27_9-51-24.png Views:	1 Size:	20.6 KB ID:	1058895

      The last order is a market long entry on 2/14/2018. It is submitted, accepted, then working, but it never fills.

      Click image for larger version  Name:	2019-05-27_9-54-05.png Views:	1 Size:	41.2 KB ID:	1058896

      If I change the stop and target size to say, 120, instead of 100 ticks, then it just moves the date where the halt occurs to 4/4/2018 I believe.

      Can you help me understand what is happening here?

      Code:
      //
      // Copyright (C) 2015, NinjaTrader LLC <www.ninjatrader.com>.
      // NinjaTrader reserves the right to modify or overwrite this NinjaScript component with each release.
      //
      
      #region Using declarations
      
      using System;
      using System.Collections.Generic;
      using System.ComponentModel;
      using System.ComponentModel.DataAnnotations;
      using System.Linq;
      using System.Text;
      using System.Threading.Tasks;
      using System.Windows;
      using System.Windows.Input;
      using System.Windows.Media;
      using System.Xml.Serialization;
      using NinjaTrader.Cbi;
      using NinjaTrader.Gui;
      using NinjaTrader.Gui.Chart;
      using NinjaTrader.Gui.SuperDom;
      using NinjaTrader.Data;
      using NinjaTrader.NinjaScript;
      using NinjaTrader.Core.FloatingPoint;
      using NinjaTrader.NinjaScript.Indicators;
      using NinjaTrader.NinjaScript.DrawingTools;
      
      #endregion Using declarations
      
      // This namespace holds all strategies and is required. Do not change it.
      namespace NinjaTrader.NinjaScript.Strategies
      {
          public class SampleOnOrderUpdate : Strategy
          {
              private Order entryOrder = null; // This variable holds an object representing our entry order
              private Order stopOrder = null; // This variable holds an object representing our stop loss order
              private Order targetOrder = null; // This variable holds an object representing our profit target order
      
              protected override void OnBarUpdate()
              {
                  // Submit an entry market order if we currently don't have an entry
                  // order open and are past the BarsRequiredToTrade bars amount
                  if (entryOrder == null && Close[0] > Open[0] && CurrentBar > BarsRequiredToTrade)
                  {
                      // Enter Long. We will assign the resulting Order object to
                      // entryOrder in OnOrderUpdate()
                      EnterLong(1, "MyEntry");
                  }
      
                  // If we have a long position and the current price is 4 ticks in
                  // profit, raise the stop-loss order to breakeven. We use (7 *
                  // (TickSize / 2)) to denote 4 ticks because of potential precision
                  // issues with doubles. Under certain conditions (4 * TickSize) could
                  // end up being 3.9999 instead of 4 if the TickSize was 1. Using our
                  // method of determining 4 ticks helps cope with the precision issue
                  // if it does arise.
                  //if (Position.MarketPosition == MarketPosition.Long && Close[0] >= Position.AveragePrice + (7 * (TickSize / 2)))
                  //{
                  //    // Checks to see if our Stop Order has been submitted already
                  //    if (stopOrder != null && stopOrder.StopPrice < Position.AveragePrice)
                  //    {
                  //        // Modifies stop-loss to breakeven
                  //        stopOrder = ExitLongStopMarket(0, true, stopOrder.Quantity, Position.AveragePrice, "MyStop", "MyEntry");
                  //    }
                  //}
              }
              protected override void OnExecutionUpdate(Execution execution, string executionId, double price, int quantity, MarketPosition marketPosition, string orderId, DateTime time)
              {
                  // We advise monitoring OnExecution to trigger submission of
                  // stop/target orders instead of OnOrderUpdate() since OnExecution()
                  // is called after OnOrderUpdate() which ensures your strategy has
                  // received the execution which is used for internal signal tracking.
                  if (entryOrder != null && entryOrder == execution.Order)
                  {
                      if (execution.Order.OrderState == OrderState.Filled || execution.Order.OrderState == OrderState.PartFilled || (execution.Order.OrderState == OrderState.Cancelled && execution.Order.Filled > 0))
                      {
                          int ticks = 100;
      
                          // Stop-Loss order 4 ticks below our entry price
                          stopOrder = ExitLongStopMarket(0, true, execution.Order.Filled, execution.Order.AverageFillPrice - ticks * TickSize, "MyStop", "MyEntry");
      
                          // Target order 8 ticks above our entry price
                          targetOrder = ExitLongLimit(0, true, execution.Order.Filled, execution.Order.AverageFillPrice + ticks * TickSize, "MyTarget", "MyEntry");
      
                          // Resets the entryOrder object to null after the order has been filled
                          if (execution.Order.OrderState != OrderState.PartFilled)
                              entryOrder = null;
                      }
                  }
      
                  // Reset our stop order and target orders' Order objects after our position is closed.
                  if ((stopOrder != null && stopOrder == execution.Order) || (targetOrder != null && targetOrder == execution.Order))
                  {
                      if (execution.Order.OrderState == OrderState.Filled || execution.Order.OrderState == OrderState.PartFilled)
                      {
                          stopOrder = null;
                          targetOrder = null;
                      }
                  }
              }
              protected override void OnOrderUpdate(Order order, double limitPrice, double stopPrice, int quantity, int filled, double averageFillPrice, OrderState orderState, DateTime time, ErrorCode error, string nativeError)
              {
                  Print(time.ToString() + " -- " + order.ToString());
      
                  // Handle entry orders here. The entryOrder object allows us to
                  // identify that the order that is calling the OnOrderUpdate() method
                  // is the entry order. Assign entryOrder in OnOrderUpdate() to ensure
                  // the assignment occurs when expected. This is more reliable than
                  // assigning Order objects in OnBarUpdate, as the assignment is not
                  // gauranteed to be complete if it is referenced immediately after
                  // submitting
                  if (order.Name == "MyEntry")
                  {
                      entryOrder = order;
      
                      // Reset the entryOrder object to null if order was cancelled
                      // without any fill
                      if (order.OrderState == OrderState.Cancelled && order.Filled == 0)
                          entryOrder = null;
                  }
              }
              protected override void OnPositionUpdate(Position position, double averagePrice, int quantity, MarketPosition marketPosition)
              {
                  // Print our current position to the lower right hand corner of the chart
                  Draw.TextFixed(this, "MyTag", position.ToString(), TextPosition.BottomRight, false, "");
              }
              protected override void OnStateChange()
              {
                  if (State == State.SetDefaults)
                  {
                      Description = @"Sample Using OnOrderUpdate() and OnExecution() methods to submit protective orders";
                      Name = "SampleOnOrderUpdate";
                      Calculate = Calculate.OnBarClose;
                      EntriesPerDirection = 1;
                      EntryHandling = EntryHandling.AllEntries;
                      IsExitOnSessionCloseStrategy = true;
                      ExitOnSessionCloseSeconds = 30;
                      IsFillLimitOnTouch = false;
                      MaximumBarsLookBack = MaximumBarsLookBack.TwoHundredFiftySix;
                      OrderFillResolution = OrderFillResolution.Standard;
                      Slippage = 0;
                      StartBehavior = StartBehavior.WaitUntilFlat;
                      TimeInForce = TimeInForce.Gtc;
                      TraceOrders = true;
                      RealtimeErrorHandling = RealtimeErrorHandling.StopCancelClose;
                      StopTargetHandling = StopTargetHandling.PerEntryExecution;
                      BarsRequiredToTrade = 20;
                  }
                  else if (State == State.Realtime)
                  {
                      // one time only, as we transition from historical
                      // convert any old historical order object references
                      // to the new live order submitted to the real-time account
                      if (entryOrder != null)
                          entryOrder = GetRealtimeOrder(entryOrder);
                      if (stopOrder != null)
                          stopOrder = GetRealtimeOrder(stopOrder);
                      if (targetOrder != null)
                          targetOrder = GetRealtimeOrder(targetOrder);
                  }
              }
          }
      }
      Steve L
      NinjaTrader Ecosystem Vendor - Ninja Mastery

      Comment


        #4
        Hello Steve,

        Thanks for providing a scenario for us to test.

        I have seen this behavior when combining Set and Exit methods, but I am looking into what is happening further in this case.

        I will follow up after I have been able to fully analyze.
        JimNinjaTrader Customer Service

        Comment


          #5
          There appears to be a pattern when the trading halts. By changing the start date of my ES ##-## Day bars series, I can get the halt date to occur on different days.

          I'm using slightly different code than the code posted above, but the basics are the same. You can observe similar patterns with the posted code by changing the date range. I'm still using Exit methods, not Set methods.

          Not sure if these patterns are significant, but
          • The last active bar is always an up bar with a long lower shadow
          • The three bars (the last active bar, the one preceding it, and the one following it) are always trending up.


          Click image for larger version  Name:	Halt_2018_2019-05-28_17-15-11.png Views:	1 Size:	13.4 KB ID:	1059083

          Click image for larger version  Name:	Halt_2016_2019-05-28_17-13-46.png Views:	1 Size:	15.5 KB ID:	1059084

          Click image for larger version  Name:	Halt_2015_2019-05-28_17-12-28.png Views:	1 Size:	12.8 KB ID:	1059085

          Click image for larger version  Name:	Halt_2011_2019-05-28_17-10-21.png Views:	1 Size:	15.3 KB ID:	1059086

          Click image for larger version  Name:	Halt_2008_2019-05-28_17-06-20.png Views:	1 Size:	19.1 KB ID:	1059087






          Last edited by Steve L; 05-28-2019, 05:07 PM.
          Steve L
          NinjaTrader Ecosystem Vendor - Ninja Mastery

          Comment


            #6
            Hello Steve,

            Thanks for your patience.

            This comes down to the backtest coming across an over fill scenario on both exits. Since the bar's data points are used to simulate the order fill without actual price action, a scenario where both the target and stop fill on the same bar can be encountered. This creates an overfill which then prevents the next entry order from reaching a Filled state from a Working state.

            To workaround this, you can use High Order Fill Resolution, or you can add a single tick data series to the script and submit orders to the single tick data series.

            High Order Fill Resolution - https://ninjatrader.com/support/help...FillResolution

            Backtesting with Intrabar granularity - https://ninjatrader.com/support/help...ipt_strate.htm

            Please let me know if there is anything else I can do to assist.
            JimNinjaTrader Customer Service

            Comment


              #7
              Thanks, Jim.

              OK. That makes sense. I can see now that the pattern where this occurs is on bars where both the stop and the target are triggered.

              I can also confirm that using Historical Order Fill Processing = High (HOFP=High) with some multiple of minute bars bypasses the problem.

              I'm good to go and can work around this in a number of ways, but are you submitting a ticket on this?

              Obviously, with HOFP=Standard, the backtest should not halt. The fill engine should be able to choose either the target or the stop and proceed.

              HOFP=Standard is a useful part of a strategy development methodology because of it's speed. It's a good first pass giving ball-park performance numbers which can later be refined using additional, more time consuming, methods.

              Thanks for your assistance,
              Steve
              Steve L
              NinjaTrader Ecosystem Vendor - Ninja Mastery

              Comment


                #8
                Hello Steve,

                Thanks for the reply.

                A ticket was submitted previously on this behavior and our Development team is aware of this limitation with standard fill and orders filling on the same bar.

                We are tracking impact to consider improving this with the feature request ticket ID SFT-2778. I have added a vote on your behalf.

                We cannot guarantee any timeframe or promise fulfillment, but your impact and interest is tracked.

                Let me know if there is anything else I can do here.
                JimNinjaTrader Customer Service

                Comment


                  #9
                  Please add my vote for feature request ticket ID SFT-2778.

                  Comment


                    #10
                    Thanks for sharing interest caveat_lector,

                    Your vote has been added.
                    JimNinjaTrader Customer Service

                    Comment


                      #11
                      Hi Jim - i wanted to ask you if there might be another example of using ExitLongStopMarket, ExitLongLimit (and short equivalent) that might have some more complex use cases. I am trying to manage brackets (up to 3) that have different targets, but same stop. My code works just fine when using Sim, but i consistently get errors when switching to broker account. E.g. Order rejected due to reprice, etc. I just switched from using SetStopLoss and SetProfitTarget because i was seeing some anomalies there too (stop getting double entered - but only occassionally).

                      Any additional examples would be greatly appreciated.

                      Thanks!

                      Comment


                        #12
                        Hello shadowfax,

                        Unless you are using Interactive Brokers or Rithmic, I would expect a strategy that follows the SampleOnOrderUpdate approach (use OnOrderUpdate and OnExecutionUpdate to submit stop/target with Exit methods) to behave consistently between Sim accounts and your live account.

                        The rejections may be something we could give more insight on, but we would need your log and trace files. I'd also like to see the strategy code. If it is not too complex and close enough to our example code, I could give some insight into behaviors or possibly provide some recommendation.

                        Could you write in with the strategy's source code and your log and trace files? Please email platformsupport [at] ninjatrader [dot] com with the text "Attn Jim 2167087" and please include the log and trace files and the strategy source code. Finally, I'll need order ID's and rejection times for the orders that have gotten rejected so I may review.

                        Attaching Log and Trace files

                        Please follow the steps below to manually attach your log and trace files to your response so I may investigate this matter further.
                        • Open your NinjaTrader folder under My Documents.
                        • Right click on the 'log' and 'trace' folders and select Send To> Compressed (zipped) Folder.
                        • Send the 2 compressed folders as attachments to us at platformsupport [at] ninjatrader [dot] com.
                        • Once complete, you can delete these compressed folders.
                        Export as source code - https://ninjatrader.com/support/help...tAsSourceFiles

                        I look forward to assisting.
                        JimNinjaTrader Customer Service

                        Comment


                          #13
                          Jim/support,

                          why did you say in your last reply:
                          "Unless you are using Interactive Brokers... "

                          I use IB and try to get hold of the procedure to enter a long order and set stop loss and profit target. Especially stop loss do not seem to get entered in IB when I follow the sample in this thread by using OnOrderUpdate. Is there a way for ninjascript to place a long order and on fill set the stoploss and profittarget?

                          Regards
                          Stefan

                          Comment


                            #14
                            Hello Stefan,

                            SampleOnOrderUpdate depends on Execution updates to follow Order updates. For Rithmic and Interactive Brokers connections, this may not always be the case as these feeds have different ordering for Order updates Executions, and Position updates.

                            Since these feeds provide different ordering for Order/Executions/Positions, you could consider writing the strategy so it only depends on OrderUpdate events. Please note that strategy's Position object is updated from Executions, so you would need to track your own position from OrderState.Filled and OrderState.PartialFill orders in OnOrderUpdate if you are not using OnExecutionUpdate for the order fills.

                            Note from Help Guide:

                            Rithmic and Interactive Brokers Users: Due to provider API design these adapters do not guarantee the sequence of events of OnOrderUpdate, OnExecution, and OnPositionUpdate. Therefore when working on a strategy that will run on these connections it is best practice to only work with passed by value data from that callback to eliminate the dependency on the sequence of events.
                            https://ninjatrader.com/support/help...rderupdate.htm

                            I have also attached an unmanaged strategy that can help set a path for using OnOrderUpdate alone.

                            We look forward to assisting.
                            Attached Files
                            JimNinjaTrader Customer Service

                            Comment

                            Latest Posts

                            Collapse

                            Topics Statistics Last Post
                            Started by alifarahani, Today, 09:40 AM
                            6 responses
                            36 views
                            0 likes
                            Last Post alifarahani  
                            Started by Waxavi, Today, 02:10 AM
                            1 response
                            17 views
                            0 likes
                            Last Post NinjaTrader_LuisH  
                            Started by Kaledus, Today, 01:29 PM
                            5 responses
                            14 views
                            0 likes
                            Last Post NinjaTrader_Jesse  
                            Started by Waxavi, Today, 02:00 AM
                            1 response
                            12 views
                            0 likes
                            Last Post NinjaTrader_LuisH  
                            Started by gentlebenthebear, Today, 01:30 AM
                            3 responses
                            17 views
                            0 likes
                            Last Post NinjaTrader_Jesse  
                            Working...
                            X