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

Fill trades on currect close in backtesting

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

    Fill trades on currect close in backtesting

    Hi,

    I run a backtest on EOD data. In NT the execution price for market order are the next open, and for intraday data it makes a lot of sense, but for my test of EOD I want to get the statistic for the EOD filling.
    I looked at the 2 Fill classes DefaultFill and LiberalFill and both use NextOpen for the fill price. I didn't see any represntive to the current price, can I use Close[0] to get the current price? If not how can I change it to do fill on current price?

    TX in advance!

    #2
    freewind,

    I am happy to assist you.

    Unfortunately this level of coding is unsupported. I would suggest experimenting with it using print statements.

    Please let me know if I may assist further.
    Adam P.NinjaTrader Customer Service

    Comment


      #3
      freewind,

      Hi. it's been a while since you posted this issue about using current closing price as fill price -instead of next bar's opening price. I am currently facing the same issue and would like to know if someone has managed to sort this thing out and would like to share it.

      thank you.

      Comment


        #4
        This would unfortunately only be possible by adding intraday granularity to the strategy so you could for example place an order on the added finer stream a few minutes for the EOD close - http://www.ninjatrader.com/support/f...ead.php?t=6652
        BertrandNinjaTrader Customer Service

        Comment


          #5
          Here is my code that solve the problem

          Code:
          // 
          // Copyright (C) 2006, NinjaTrader LLC <www.ninjatrader.com>.
          //
          #region Using declarations
          using System;
          using System.ComponentModel;
          using System.Drawing;
          using NinjaTrader.Cbi;
          using NinjaTrader.Data;
          using NinjaTrader.Indicator;
          using NinjaTrader.Strategy;
          #endregion
          
          // This namespace holds all strategies and is required. Do not change it.
          namespace NinjaTrader.Strategy
          {
              /// <summary>
              /// </summary>
              [Gui.Design.DisplayName("FillOnClose")]
              public class FillOnClose : FillType
              {
                  private const double epsilon = 0.00000001;
          
                  /// <summary>
                  /// Processes a historical order and checks for potential fills.
                  /// </summary>
                  /// <param name="order">Historical order to process</param>
                  public override void Fill(Order order)
                  {
                      /* *** Slippage ***
                      * Slippage values are optionally set in the UI and only applied to market and stop market orders
                      * Slippage can only be applied to if the bar the order is filled on could have accomodated slippage
                      * 
                      * *** Limit Orders ***
                      * Limit orders are filled if the limit price is touched 
                      */
          
                      if (order.OrderType == OrderType.Market)
                      {
                          //Roy Change for buy on Close
                          if (order.OrderAction == Cbi.OrderAction.Buy || order.OrderAction == Cbi.OrderAction.BuyToCover)            // set fill price
                              FillPrice = Math.Min(Strategy.Closes[0][0], Strategy.Closes[0][0] + SlippagePoints);
                          else
                              FillPrice = Math.Max(Strategy.Closes[0][0], Strategy.Closes[0][0] - SlippagePoints);
          
          
                          //if (order.OrderAction == Cbi.OrderAction.Buy || order.OrderAction == Cbi.OrderAction.BuyToCover)            // set fill price
                          //    FillPrice = Math.Min(Strategy.Closes[0][0], Strategy.Closes[0][0] + SlippagePoints);
                          //else
                          //    FillPrice = Math.Max(NextLow, NextOpen - SlippagePoints);
                      }
                      else if (order.OrderType == OrderType.Limit)
                      {
                          // Orders are filled when traded through the limit price not at the limit price
                          double nextLow = NextLow;
                          double nextHigh = NextHigh;
                          if ((order.OrderAction == Cbi.OrderAction.Buy && order.LimitPrice >= nextLow - epsilon)
                                  || (order.OrderAction == Cbi.OrderAction.BuyToCover && order.LimitPrice >= nextLow - epsilon)
                                  || (order.OrderAction == Cbi.OrderAction.Sell && order.LimitPrice <= nextHigh + epsilon)
                                  || (order.OrderAction == Cbi.OrderAction.SellShort && order.LimitPrice <= nextHigh + epsilon))
                              FillPrice = (order.OrderAction == Cbi.OrderAction.Buy || order.OrderAction == Cbi.OrderAction.BuyToCover) ? Math.Min(order.LimitPrice, nextHigh) : Math.Max(order.LimitPrice, nextLow);                                                // set fill price
                      }
                      else if (order.OrderType == OrderType.Stop)
                      {
                          // Stop orders are triggered when traded at the stop price
                          double nextLow = NextLow;
                          double nextHigh = NextHigh;
                          double nextOpen = NextOpen;
                          if ((order.OrderAction == Cbi.OrderAction.Buy && order.StopPrice <= nextHigh + epsilon)
                                  || (order.OrderAction == Cbi.OrderAction.BuyToCover && order.StopPrice <= nextHigh + epsilon)
                                  || (order.OrderAction == Cbi.OrderAction.Sell && order.StopPrice >= nextLow - epsilon)
                                  || (order.OrderAction == Cbi.OrderAction.SellShort && order.StopPrice >= nextLow - epsilon))
                          {
                              if (order.OrderAction == Cbi.OrderAction.Buy || order.OrderAction == Cbi.OrderAction.BuyToCover)
                                  FillPrice = order.StopPrice < nextOpen - epsilon ? Math.Min(nextHigh, nextOpen + SlippagePoints) : Math.Min(nextHigh, order.StopPrice + SlippagePoints);
                              else
                                  FillPrice = order.StopPrice > nextOpen + epsilon ? Math.Max(nextLow, nextOpen - SlippagePoints) : Math.Max(nextLow, order.StopPrice - SlippagePoints);
                          }
                      }
                      else if (order.OrderType == OrderType.StopLimit)
                      {
                          // Stop limit orders are triggered when traded at the stop price and traded through the limit price
                          double nextLow = NextLow;
                          double nextHigh = NextHigh;
                          if (!order.StopTriggered
                              && ((order.OrderAction == Cbi.OrderAction.Buy && order.StopPrice <= nextHigh + epsilon)
                                  || (order.OrderAction == Cbi.OrderAction.BuyToCover && order.StopPrice <= nextHigh + epsilon)
                                  || (order.OrderAction == Cbi.OrderAction.Sell && order.StopPrice >= nextLow - epsilon)
                                  || (order.OrderAction == Cbi.OrderAction.SellShort && order.StopPrice >= nextLow - epsilon)))
                              order.StopTriggered = true;                                                    // stop limit order was triggered
          
                          if (order.StopTriggered
                              && ((order.OrderAction == Cbi.OrderAction.Buy && order.LimitPrice >= nextLow - epsilon)
                                  || (order.OrderAction == Cbi.OrderAction.BuyToCover && order.LimitPrice >= nextLow - epsilon)
                                  || (order.OrderAction == Cbi.OrderAction.Sell && order.LimitPrice <= nextHigh + epsilon)
                                  || (order.OrderAction == Cbi.OrderAction.SellShort && order.LimitPrice <= nextHigh + epsilon)))
                              FillPrice = (order.OrderAction == Cbi.OrderAction.Buy || order.OrderAction == Cbi.OrderAction.BuyToCover) ? Math.Min(order.LimitPrice, nextHigh) : Math.Max(order.LimitPrice, nextLow);
                      }
                  }
              }
          }

          Comment


            #6
            I asked that same question on this thread:


            My personal solution was to create a new fill type.
            Just copy any of the other fill types into a new file, then I rewrote the limit order type fill to:

            FillPrice = order.LimitPrice;

            erasing everything else. This fill type will essentially fill the order at whatever limit price you set (which you can then set to the corresponding close), although the entry date will still be counted as next day.

            Comment


              #7
              pretender, freewind,

              thanks for sharing your workarounds. I'll check which one works the best for what I am trying to achieve.

              Cheers.

              Comment


                #8
                Originally posted by NinjaTrader_Bertrand View Post
                This would unfortunately only be possible by adding intraday granularity to the strategy so you could for example place an order on the added finer stream a few minutes for the EOD close - http://www.ninjatrader.com/support/f...ead.php?t=6652
                I have a question about that. I have a strategy that works on the primary series. If I try to increase granularity by adding another series of a smaller time frame, I get no fills at all.

                I suspect the problem might be the way NinjaTrader is handling instances of indicators called in the strategy. Correct me if I'm wrong. Let me explain.

                In the example you reference above, the "if (BarsInProgress==0)" block has four references to the EMA indicator. Does this mean there are four separate instances of EMA() calculated four times? What happens with the internals of these EMA() instances if this block is executed on two different BarsInProgress series, such as if one says "if (BarsInProgress<=1)"?

                In my case, I have a custom indicator that has several public properties that need to be accessed. Because this indicator is extremely complex and slow to execute, and must execute on every tick, I want it called only once, even though I must reference it and its properties multiple times. I absolutely don't want multiple calls to it in OnBarUpdate(). So, in the Variables section of my strategy, I declare:
                Code:
                Foo f;  // declare an instance of indicator Foo()
                In OnStartUp() I have:
                Code:
                f = Foo(parameter1, parameter2, parameter3, parameter4,...);
                Then in OnBarUpdate() I have:
                Code:
                if (BarsInProgress == 0) {
                   double x = f[0]; // force calculation of the indicator
                   y = f.property1;  // get a property of Foo()
                   z = f.property2;  // get another property of Foo()
                   if (some condition based on x, y, and z) {
                      EnterLong(1,1,"long");
                   }
                }
                My question is, if I declare my indicators this way, are they being executed on the primary series? My experience so far tells me this isn't the case.

                Or maybe my theory about my strategy's failure to execute orders when I add another price series, is completely off. But it's the only explanation I can think of.

                -Alex
                Last edited by anachronist; 03-28-2012, 09:21 PM.

                Comment


                  #9
                  Hi Alex, interesting post - thanks. Attached is a simplified version using a SMA, that would work ok in my testing, can you please confirm on your end it would trade as expected on the added 1min series? If you check with the TraceOrders feature, anything suspicious seen for your code?



                  Thanks,
                  Attached Files
                  BertrandNinjaTrader Customer Service

                  Comment


                    #10
                    Thanks, I didn't know about TraceOrders. Your code worked on my end, so my hypothesis about calling indicators by reference was wrong.

                    I turned on TraceOrders for my original strategy. When I run the strategy from the primary series, the orders are placed and executions happen (why don't I see executions in the output window? That would be nice). But if I run it on the secondary series, the orders are being placed and then cancelled on the same bar. Here's a sample of what happens when a signal causes an order to be placed along with a target and a stop:
                    Code:
                    3/29/2012 11:36:00 Entered internal PlaceOrder() method at 3/29/2012 11:36:00: BarsInProgress=1 Action=SellShort OrderType=Limit Quantity=1 LimitPrice=826.1 StopPrice=0 SignalName='Overlap' FromEntrySignal=''
                    3/29/2012 11:36:00 Entered internal SetStopTarget() method: Type=Stop FromEntrySignal='Overlap' Mode=Ticks Value=12 Currency=0 Simulated=False
                    3/29/2012 11:36:00 Entered internal SetStopTarget() method: Type=Target FromEntrySignal='Overlap' Mode=Ticks Value=10 Currency=0 Simulated=False
                    3/29/2012 11:36:00 Cancelled expired order: BarsInProgress=0: Order='NT-01926/Sim101' Name='Overlap' State=Working Instrument='TF 06-12' Action=SellShort Limit price=826.1 Stop price=0 Quantity=1 Strategy='TFOverlap' Type=Limit Tif=Day Oco='' Filled=0 Fill price=0 Token='978839d830c74e3eb1a3744a442e9b16' Gtd='12/1/2099 0:00:00'
                    See the last line, everything is cancelled because the order "expired". No order executions happen. If I run this off the primary series, it looks the same except the line about cancelling doesn't appear -- but the executions happen.

                    I have 2-minute bars in the main series and 20-second bars as the secondary series. CalculateOnBarClose=false. I changed the code you sent accordingly and it still works, but mine doesn't.

                    ----

                    Update: I believe I found the problem. I am using EnterLongLimit() with the LiveUntilCancelled parameter set to false, because a new order is placed on each new bar, and not placed when there's no signal. I re-wrote my code to cancel the orders when the signal is no longer valid, and it is now showing me fills on the secondary series.

                    I didn't see it in the documentation, but perhaps it should include a note explaining that backtesting on a secondary data series can cause entry orders to be cancelled prematurely if LiveUntilCancelled is set to false.

                    Update2: Now that I got it working on a secondary price series, unfortunately it doesn't solve the problem I reported in this thread. If the target price and entry price occur on the same bar, both will get filled even if the secondary time frame shows that the price passed through the target on its way to the entry -- which means the target should never get filled on that bar because the position hadn't been opened. This is a bug. This shouldn't be happening when backtesting on a finer granularity.

                    -Alex
                    Last edited by anachronist; 03-29-2012, 07:29 PM.

                    Comment


                      #11
                      Thanks for the updates Alex, correct this order expiration behavior would be expected in the managed order submission mode - 'By default, orders submitted via Entry() and Exit() methods automatically cancel at the end of a bar if not re-submitted'



                      In a MultiSeries it will be the next OnBarUpdate() seen causing the expiration.

                      What Exit methods do you use here? Set's or the more advanced Exits that could be submitted to an added series as well? Would you have an example I could give a run here on my end to check into?

                      Thanks,
                      BertrandNinjaTrader Customer Service

                      Comment


                        #12
                        Thanks Bertrand, hopefully your reply I quote below was in response to my Update2 above.
                        Originally posted by NinjaTrader_Bertrand View Post
                        What Exit methods do you use here? Set's or the more advanced Exits that could be submitted to an added series as well? Would you have an example I could give a run here on my end to check into?
                        The only exits I am using are via SetStopLoss() and SetProfitTarget(), which can get modified once the entry order is filled.

                        Because my strategy enters against the direction of a pullback, it must always use limit orders for entry. Now that I've rewritten it to cancel its own orders rather than letting them expire after 1 bar, an entry condition looks like this:
                        Code:
                        if (buyConditionActive) {
                        	oentry = EnterLongLimit(seriesindex, true, DefaultQuantity, ol.BuyLevel[0], "Overlap");
                        	SetStopLoss("Overlap", CalculationMode.Ticks, stopTicks, false);
                        	SetProfitTarget("Overlap", CalculationMode.Ticks, targetTicks);
                        } else if (oentry != null && oentry.OrderAction == OrderAction.Buy) {
                        	CancelOrder(oentry);
                        	oentry = null;
                        }
                        As I explained in my Update2 above, I am experiencing problems where NinjaTrader's backtesting gives highly optimistic rather than conservative results, when the limit entry order and the target price fall on the same bar. And this happens a lot on pullback-entry strategies with small targets.

                        It's easy to contrive a test for this.
                        • Open up a TF 06-12 chart with 2 minute bars and 20 second bars.
                        • Find a bar that is obviously a down bar, with the open near the top and the close near the bottom, with corresponding 20-second bars successively lower. We'll call this the 'test bar'.
                        • On today's chart, the the 3/30/2012 6:30 (pacific time) 2-minute bar qualifies as a test bar. It is a down bar with the the high at 835.6 and low at 834.4. The 20-second bars inside it clearly hit the high before the low.
                        • Run a simple strategy to to call EnterLongLimit() just before the test bar, to enter 1 tick from the bottom of the test bar and SetTargetProft() 1 tick from the top. Orders are placed on the secondary 20-second series.

                        The entry should be filled but the target should not be filled because price passed through it before the entry order filled. And indeed, in real time and in replay mode, it works properly. Backtest, however, is broken. It fills both orders.

                        Here's my code to perform the test described:
                        Code:
                        protected override void Initialize() {
                        	// primary series is TF 06-12 2-minute bars
                        	Add(PeriodType.Second, 20);
                        	TimeInForce = Cbi.TimeInForce.Day;
                        	CalculateOnBarClose = false;
                        }
                        
                        protected override void OnBarUpdate() {
                        	if (BarsInProgress == 0) {
                        		
                        		// We place a limit order before the 3/30/2012 6:30 (Pacific time)
                        		// 2 minute bar. The 6:30 bar is a down bar. The buy price is
                        		// near the bottom of the bar, the exit price is near the top.
                        		// Only the entry price should get filled. Instead, both get filled.
                        
                        		if (ToTime(Time[0]) > 62600 && ToTime(Time[0]) < 63000) {
                        			order = EnterLongLimit(1, true, 1, 834.5, "longtest");
                        			SetStopLoss("longtest", CalculationMode.Price, 833.5, false);
                        			SetProfitTarget("longtest", CalculationMode.Price, 835.5);
                        		} else if (order != null) {
                        			CancelOrder(order);
                        			order = null;
                        		}
                        	}
                        }
                        Attached is a picture showing the result of this strategy.

                        I was experiencing this problem when backtesting on just the primary series. I added in the secondary series in the hope that this would fix it. I consider this a serious bug, because it causes over-optimistic backtest results on any strategy that tries to trade pullbacks.

                        One solution would be if NinjaTrader internally made some assumptions about the price movement inside a bar, especially when it's obvious from the opening and closing price. Then a secondary series wouldn't be needed. But I thought the point of adding a secondary series was to prevent problems like this.

                        -Alex

                        (Edit) P.S. Apologies for drifting off the topic of this thread. I should have posted this separately.
                        Attached Files
                        Last edited by anachronist; 03-30-2012, 05:29 PM.

                        Comment


                          #13
                          Hi Alex, you're unfortunately running into a known limitation here : the Set's would only be evaluated in the primary bars context, to simulate what you're trying to do in backtesting please work with the Exit() methods for your stops and targets, those would offer a dedicated BarsInProgress paramter to execute against - with those used I'm confident your results seen are more in line with what you expected.
                          BertrandNinjaTrader Customer Service

                          Comment


                            #14
                            Ah. That explains it. Thanks. Please add a note to the documentation for SetStopLoss and SetProfitTarget that they operate on the primary series only, and adding finer-granularity price series won't change their behavior in backtesting.

                            I was using the Set methods because that seemed the easiest approach to changing stops and targets while a position is active. I don't see a good way to change orders in the managed approach, unless I cancel and re-submit exit orders.

                            Does cancelling an entry order in the managed approach also cancel any exit orders associated with the same fromEntrySignalName, or do I have to cancel them myself?

                            -Alex

                            Comment


                              #15
                              Thanks, Alex. I've submitted your feedback to our documentation team.

                              Does cancelling an entry order in the managed approach also cancel any exit orders associated with the same fromEntrySignalName, or do I have to cancel them myself?
                              An exit order isn't submitted until the entry order fills. You should not have exit and entry orders working at the same time for the same signal name.
                              Ryan M.NinjaTrader Customer Service

                              Comment

                              Latest Posts

                              Collapse

                              Topics Statistics Last Post
                              Started by CortexZenUSA, Today, 12:46 AM
                              0 responses
                              0 views
                              0 likes
                              Last Post CortexZenUSA  
                              Started by usazencortex, Today, 12:43 AM
                              0 responses
                              2 views
                              0 likes
                              Last Post usazencortex  
                              Started by sidlercom80, 10-28-2023, 08:49 AM
                              168 responses
                              2,262 views
                              0 likes
                              Last Post sidlercom80  
                              Started by Barry Milan, Yesterday, 10:35 PM
                              3 responses
                              10 views
                              0 likes
                              Last Post NinjaTrader_Manfred  
                              Started by WeyldFalcon, 12-10-2020, 06:48 PM
                              14 responses
                              1,429 views
                              0 likes
                              Last Post Handclap0241  
                              Working...
                              X