• If this is your first visit, you will have to register before you can post. To view messages, please scroll below and select the forum that you would like to visits. Questions? Be sure to check out the Forum FAQ.


No announcement yet.

Partner 728x90


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

  • 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

    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


      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?

      // 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


        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


          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


            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


              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 L
              NinjaTrader Ecosystem Vendor - Ninja Mastery


                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


                Latest Posts


                Topics Statistics Last Post
                Started by yqksqhhh, Today, 09:12 PM
                0 responses
                1 view
                Last Post yqksqhhh  
                Started by CriticalMind, 11-29-2015, 12:39 PM
                3 responses
                Last Post NinjaTrader_ChelseaB  
                Started by novel168, 06-14-2019, 09:20 PM
                1 response
                Last Post NinjaTrader_ChelseaB  
                Started by johnnybegoode, Today, 08:00 PM
                3 responses
                Last Post NinjaTrader_ChelseaB  
                Started by digibob, Today, 03:00 PM
                1 response
                Last Post NinjaTrader_ChelseaB