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

How to enter new buy/sell stop order for entry upon new bar

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

    How to enter new buy/sell stop order for entry upon new bar

    In my strategy, my entry signal tells me to enter a buy at the top of the bar that just closed, ie this is a BUYSTOP order. If the price moves up to that entry price over the next bar, then all is well. But if the price does not move there, my understanding is that for managed orders, this BUYSTOP order automatically gets cancelled. Is this correct?

    If this is the case, then in OnBarUpdate I want to detect that the order was not filled, and resubmit it at the high of the new bar that just closed. How do I do that? How can I tell if the order was not filled?

    In my class I am using the managed approach to submit orders, and I have a private method like this:

    PHP Code:
    private void enterBuyStop(int quantitydouble entryPriceint stopTicksint takeProfitTicks) {
        
    SetStopLoss(LONG_ENTRY_NAMECalculationMode.Ticks, (double)stopTicksfalse);
        
    SetProfitTarget(LONG_ENTRY_NAMECalculationMode.Ticks, (double)takeProfitTicks);
        
    EnterLongStopMarket(quantityentryPriceLONG_ENTRY_NAME);

    In my Strategy class I have a const string definition for LONG_ENTRY_NAME. Also, I have a private Order variable called longEntry, and I implement OnOrderUpdate to save longEntry:

    PHP Code:

    namespace NinjaTrader.NinjaScript.Strategies
    {
        public class 
    MyStrategyStrategy
        
    {
            private const 
    string LONG_ENTRY_NAME "Long Entry";

            private 
    Order longEntry null;

            private 
    void enterBuyStop(int quantitydouble entryPriceint stopTicksint takeProfitTicks) {
                
    SetStopLoss(LONG_ENTRY_NAMECalculationMode.Ticks, (double)stopTicksfalse);
                
    SetProfitTarget(LONG_ENTRY_NAMECalculationMode.Ticks, (double)takeProfitTicks);
                
    EnterLongStopMarket(quantityentryPriceLONG_ENTRY_NAME);
            }

            protected 
    override void OnOrderUpdate(Cbi.Order orderdouble limitPricedouble stopPrice,
                   
    int quantityint filleddouble averageFillPrice,
                  
    Cbi.OrderState orderStateDateTime timeCbi.ErrorCode errorstring comment)
            {
                
    // Assign Order objects here
                // This is more reliable than assigning Order objects in OnBarUpdate, as the assignment is not guaranteed to be complete if it is referenced immediately after submitting
                
    if (order.Name == LONG_ENTRY_NAME)
                    
    longEntry order;

                if (
    longEntry != null && longEntry == order) {
                    
    // Reset the longEntry object to null if order was cancelled without any fill
                    
    if (order.OrderState == OrderState.Cancelled && order.Filled == 0)
                    {
                        
    longEntry null;
                    }
                }


            }

        }

    In the code above, I will know that the order did not fill in OnOrderUpdate, but I don't know if this is called before or after OnBarUpdate. If OnOrderUpdate is called after OnBarUpdate, I will not know that the order was not filled until after OnBarUpdate is called, and then it is too late for me to resubmit the order.

    Please tell me how to implement this.

    NOTE: I don't think using a test for MarketPosition.Flat would quite work - what happens if the order gets filled and stopped out in the same bar?
    Last edited by westofpluto; 10-08-2020, 11:24 AM.

    #2
    Hello westofpluto,

    Where you have mentioned:
    "If the price moves up to that entry price over the next bar, then all is well. But if the price does not move there, my understanding is that for managed orders, this BUYSTOP order automatically gets cancelled. Is this correct?"

    An order would be automatically cancelled when using the managed approach if the order method does not use the isLiveUntilCancelled bool, as this defaults as false, or uses parameter as false in the method call and the order is not re-submitted on the next bar.


    "If this is the case, then in OnBarUpdate I want to detect that the order was not filled, and resubmit it at the high of the new bar that just closed. How do I do that? How can I tell if the order was not filled?"
    You could check the Position.MarketPosition to see if the position has changed. (Which you have mentioned is not sufficient)


    Or you can assign the order to a variable from OnOrderUpdate(), and then check the variable on the next update of OnBarUpdate to see if the <order>.OrderState is OrderState.Filled.



    OnBarUpdate and OnOrderUpdate are driven from different events. OnBarUpdate is data driven and updates when data is received and the bar is updated. OnOrderUpdate is run from order events every time the order state changes.


    Last, below are links to examples that keep exit orders alive.

    Chelsea B.NinjaTrader Customer Service

    Comment


      #3
      So here is a Strategy class I created that can serve as a base class for managed order Strategies. It has methods to enter long and short orders via stop order (that's all I need - others can add market or limit entries). The order entry methods set the stoploss and profit targets in ticks, and there are methods to move these to new prices or cancel them if not filled. Finally, it uses and internal method called computeTradability that will determine (1) can we enter a new trade (are we flat), (2) if not flat, then did our last long trade fill and exit and (3) if not flat, then did our last short trade fill and exit. A custom strategy can extend this class instead of Strategy so that trades are handled properly.

      NinjaTrader support - does this look right to you? Have I forgotten anything or are there any gotchas that I missed?

      PHP Code:
      namespace NinjaTrader.NinjaScript.Strategies
      {
          public class 
      ManagedSLTPOrderStrategy Strategy
          
      {
              protected const 
      int DATA_SERIES_MAIN 0;
              protected const 
      string SHORT_ENTRY_NAME "Short Entry";
              protected const 
      string LONG_ENTRY_NAME "Long Entry";

              protected 
      double TICK_VALUE_DOLLARS;

              protected 
      Order shortEntry null;
              protected 
      Order longEntry null;

              protected 
      MarketPosition priorMarketPosition;
              protected 
      MarketPosition currentMarketPosition;

              protected 
      bool canEnterNewTrade=false;
              protected 
      bool longFilledAndClosed=false;
              protected 
      bool shortFilledAndClosed=false;

              protected 
      override void OnStateChange()
              {
                  if (
      State == State.SetDefaults)
                  {
                      
      Description = @"A strategy that sets stoploss and take profit using managed orders";
                      
      Name "ManagedSLTPOrderStrategy";
                      
      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 false;
                      
      RealtimeErrorHandling RealtimeErrorHandling.StopCancelClose;
                      
      StopTargetHandling StopTargetHandling.PerEntryExecution;
                      
      BarsRequiredToTrade 20;
                      
      // Disable this property for performance gains in Strategy Analyzer optimizations
                      // See the Help Guide for additional information
                      
      IsInstantiatedOnEachOptimizationIteration true;
                  } else if (
      State == State.Configure) {
                      
      // nothing here
                  
      } else if (State == State.DataLoaded) {
                      
      TICK_VALUE_DOLLARS Instrument.MasterInstrument.PointValue Instrument.MasterInstrument.TickSize;

                      
      priorMarketPosition Position.MarketPosition;
                      
      currentMarketPosition Position.MarketPosition;
                  } else if (
      State == State.Realtime) {
                      if (
      shortEntry != null)
                           
      shortEntry GetRealtimeOrder(shortEntry);
                      if (
      longEntry != null)
                          
      longEntry GetRealtimeOrder(longEntry);
                  }
              }    

              
      //
              // Order entry and adjustment methods
              //
              
      protected void enterBuyStop(int quantitydouble entryPriceint stopTicksint takeProfitTicks) {
                  
      SetStopLoss(LONG_ENTRY_NAMECalculationMode.Ticks, (double)stopTicksfalse);
                  
      SetProfitTarget(LONG_ENTRY_NAMECalculationMode.Ticks, (double)takeProfitTicks);
                  
      EnterLongStopMarket(quantityentryPriceLONG_ENTRY_NAME);
              }

              protected 
      void enterSellStop(int quantitydouble entryPriceint stopTicksint takeProfitTicks) {
                  
      SetStopLoss(SHORT_ENTRY_NAMECalculationMode.Ticks, (double)stopTicksfalse);
                  
      SetProfitTarget(SHORT_ENTRY_NAMECalculationMode.Ticks, (double)takeProfitTicks);
                  
      EnterLongStopMarket(quantityentryPriceSHORT_ENTRY_NAME);
              }

              protected 
      void moveStopLossTo(double newPrice) {
                  
      SetStopLoss(SHORT_ENTRY_NAMECalculationMode.PricenewPricefalse);
              }

              protected 
      void moveProfitTargetTo(double newPrice) {
                  
      SetStopLoss(SHORT_ENTRY_NAMECalculationMode.PricenewPricefalse);
              }

              protected 
      void cancelLongEntry() {
                  if (
      this.longEntry != null) {
                      
      CancelOrder(this.longEntry);
                      
      this.longEntry=null;
                  }
              }

              protected 
      void cancelShortEntry() {
                  if (
      this.shortEntry != null) {
                      
      CancelOrder(this.shortEntry);
                      
      this.shortEntry=null;
                  }
              }

              protected 
      override void OnBarUpdate()
              {
                  
      this.priorMarketPosition this.currentMarketPosition;
                  
      this.currentMarketPosition Position.MarketPosition;

                  
      //
                  // check if we can trade at all, and if we can resubmit a stop entry order that didn't fill:
                  //
                  
      computeTradability();
              }

              
      //
              // This method sets three member variables in OnBarUpdate:
              // canEnterNewTrade: This is true if we are currently Flat
              // longFilledAndClosed: This is true if we are currently Flat AND if we detect that a prior long position was filled and is now closed
              // shortFilledAndClosed: This is true if we are currently Flat AND if we detect that a prior short position was filled and is now closed
              //
              
      protected void computeTradability() {
                  
      this.canEnterNewTrade false;
                  
      this.longFilledAndClosed=false;
                  
      this.shortFilledAndClosed=false;
                  if (
      this.currentMarketPosition == MarketPosition.Flat) {
                      
      //
                      // If we are flat then we can enter a new trade if we get a new signal
                      //
                      
      this.canEnterNewTrade true;

                      if (
      this.longEntry != null && this.longEntry.OrderState == OrderState.Filled) {
                          
      this.longFilledAndClosed=true// prior long entry order was filled but we are now flat, so it was filled and exited
                          
      this.longEntry=null;
                      } else {
                          
      this.longFilledAndClosed=false;
                      }
                      if (
      this.shortEntry != null && this.shortEntry.OrderState == OrderState.Filled) {
                          
      this.shortFilledAndClosed=true// prior short entry order was filled but we are now flat, so it was filled and exited
                          
      this.shortEntry=null;
                      } else {
                          
      this.shortFilledAndClosed=false;
                      }
                  }
              }

              protected 
      override void OnOrderUpdate(Cbi.Order orderdouble limitPricedouble stopPrice,
                      
      int quantityint filleddouble averageFillPrice,
                      
      Cbi.OrderState orderStateDateTime timeCbi.ErrorCode errorstring comment)
              {
                  
      // Assign Order objects here
                  // This is more reliable than assigning Order objects in OnBarUpdate, as the assignment is not guaranteed to be
                  // complete if it is referenced  immediately after submitting
                  
      if (order.Name == SHORT_ENTRY_NAME)
                      
      shortEntry order;
                  else if (
      order.Name == LONG_ENTRY_NAME)
                      
      longEntry order;

                  if (
      longEntry != null && longEntry == order)
                  {
                      
      // Reset the longEntry object to null if order was cancelled without any fill
                      
      if (order.OrderState == OrderState.Cancelled && order.Filled == 0)
                      {
                          
      longEntry null;
                      }
                  }

                  if (
      shortEntry != null && shortEntry == order)
                  {
                      
      // Reset the shortTop object to null if order was cancelled without any fill
                      
      if (order.OrderState == OrderState.Cancelled && order.Filled == 0)
                      {
                          
      shortEntry null;
                      }
                  }
              }

              protected 
      override void OnPositionUpdate(Cbi.Position positiondouble averagePrice,
                      
      int quantityCbi.MarketPosition marketPosition)
              {

              }

              protected 
      override void OnConnectionStatusUpdate(ConnectionStatusEventArgs connectionStatusUpdate)
              {

              }
          }

      Last edited by westofpluto; 10-08-2020, 02:15 PM.

      Comment


        #4
        Hello westofpluto,

        Below I am providing a link to the internal order handling rules for the managed approach.
        https://ninjatrader.com/support/help...antedPositions

        The order variables are assigned from OnOrderUpdate() so this is good.
        I can't comment on your custom logic, but I don't see any obvious violations of the internal order handling rules.

        I'm not certain what you mean by "a Startegy class I created that can serve as a base class for managed order". This is a strategy class and would be run as a strategy. The base class this inherits from is behind the scenes in the core of NinjaTrader. Inheriting from another strategy is not supported.

        If you are wanting someone to comment on your custom logic this thread will remain open for any community members that would like to comment.

        Are you experiencing errors or unexpected behavior you would like assistance with?
        Chelsea B.NinjaTrader Customer Service

        Comment


          #5
          I'm not certain what you mean by "a Startegy class I created that can serve as a base class for managed order". This is a strategy class and would be run as a strategy. The base class this inherits from is behind the scenes in the core of NinjaTrader. Inheriting from another strategy is not supported.
          What I meant is that is could serve as a base class for other Strategies that need to use managed orders.

          This class (ManagedSLTPOrderStrategy) inherits from Strategy. I should be able to create a new class (say, MyCustomStrategy) that inherits from ManagedSLTPOrderStrategy (which in turn inherits from Strategy) as follows:

          PHP Code:
          public class MyCustomStrategy ManagedSLTPOrderStrategy {
               
          // code here

          Since all the methods and variables in ManagedSLTPOrderStrategy are protected, then MyCustomStrategy should have access to all these.

          Are you saying that we are not allowed to do this? This is a standard thing to do in C#. Why is this "not supported"?
          Last edited by westofpluto; 10-08-2020, 02:08 PM.

          Comment


            #6
            Hello westofpluto,

            No, inheritance of indicators and strategies is not supported and will break the NinjaTrader generated code and will break optimizations.

            Don't inherit, use partial classes.
            Chelsea B.NinjaTrader Customer Service

            Comment


              #7
              Inheritance is a standard approach for breaking up large code into more manageable chunks. It is a serious drawback of NinjaTrader that this is not supported.

              In the class I provided, I have variables and methods and implementations of lifecycle methods (like OnBarUpdate). I want to keep these in a separate file. Please provide a more detailed example and discussion on how to achieve this with partial classes.

              Comment


                #8
                Hello westofpluto,

                Please see below for an example that uses partial classes.

                JimNinjaTrader Customer Service

                Comment

                Latest Posts

                Collapse

                Topics Statistics Last Post
                Started by kevinenergy, 02-17-2023, 12:42 PM
                117 responses
                2,764 views
                1 like
                Last Post jculp
                by jculp
                 
                Started by Mongo, Today, 11:05 AM
                5 responses
                15 views
                0 likes
                Last Post NinjaTrader_ChelseaB  
                Started by SightCareAubetter, Today, 12:55 PM
                0 responses
                3 views
                0 likes
                Last Post SightCareAubetter  
                Started by traderqz, Today, 12:06 AM
                8 responses
                16 views
                0 likes
                Last Post traderqz  
                Started by SightCareAubetter, Today, 12:50 PM
                0 responses
                2 views
                0 likes
                Last Post SightCareAubetter  
                Working...
                X