Announcement

Collapse
No announcement yet.

Partner 728x90

Collapse

Ninja PairsTrader

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

    Ninja PairsTrader

    Hi All,

    I've been working on a NT7 Strategy to trade pairs/spreads and have reached the limits of my current coding ability. Attached please find Strategy NinjaPairsv1.00. It works for daily bars in backtesting, but seems to have issues if you try minute bars.

    I would not suggest you use this in any way for live trading.

    The "alpha" logic behind it is is to long/short a pair when the spread diverges more than two standard deviations from the mean. To measure that I created an indicator called SpreadZScore, which is also in the zip file.

    I am posting this here in the hopes that this code can be made better.

    I'll walk through what I've done in the next post.
    Attached Files

    #2
    The Variables section is pretty straight forward:

    PHP Code:
    // Variables
    private double upperLineValue = 2.0; // Upper Standard Deviation Line
    private double lowerLineValue = -2.0; // Lower Standard Deviation Line
    private int zPeriod = 14; // Loopback period for z-score calculation
    private int quotedQuantity = 100; // Default setting for QuotedQuantity
    private int hedgeQuantity = 90; // Default setting for HedgeQuantity
    private string hedgeInstrument = @"EWT"; // Default setting for HedgeInstrument
    private bool dynamicHedgeRatio = false; // Future use 
    
    private double priceErrorCheck = 2; // Multiplier for "unusual" deviations to ignore
    private double coverSpread = 0; // ZScore to cover spread
    
    private IOrder entryOrderL = null;
    private IOrder entryOrderHedgeL = null;
    private IOrder entryOrderS = null;
    private IOrder entryOrderHedgeS = null;
    private IOrder exitOrderL = null;
    private IOrder exitOrderHedgeL = null;
    private IOrder exitOrderS = null;
    private IOrder exitOrderHedgeS = null;
    
    private int barValue;
    private SpreadZScore mySZScore;
    private bool backtest = true;
    
    private int inventory =0; // Our Market Position indicator 
    
    I'm using IOrders, and for testing purposes I've been using 100 shares of EWS against 90 shares of EWT. There are probably better pairs, and if anyone has a suggestion so we are all testing the same thing, great!

    In Initialize, I have this:

    PHP Code:
    CalculateOnBarClose = false;
    EntriesPerDirection = 1;
    EntryHandling = EntryHandling.UniqueEntries;//
    ExitOnClose = false; 
    BarsRequired = 14;
    barValue = Math.Max(1, base.BarsPeriod.Value);
    Add(hedgeInstrument,BarsPeriod.Id, barValue);
    
    //TraceOrders = true;
    
    mySZScore = SpreadZScore(hedgeInstrument, hedgeQuantity, lowerLineValue, quotedQuantity, upperLineValue, zPeriod);
    Add(mySZScore); 
    
    So far pretty straight forward...

    Comment


      #3
      OnStartUp has some definations in it. For this, I'm using market orders but eventualy want to change that to limit orders and have the option to quote on one leg of the pair/spread. Here is the code:

      PHP Code:
      protected override void OnStartUp()//////////////////////////////////////////////////////////////
      {
      Print(DateTime.Now+" STRATEGY PairsTrader "+ Instrument.FullName +"/" + hedgeInstrument + " ENABLED");
      }
      
      private void GoLongSpread()
      {
      if (BarsInProgress == 0)
      {
      if (entryOrderL == null)
      entryOrderL= EnterLong(0 ,quotedQuantity, "Long Spread");
      }
      }
      
      private void CloseLongSpread()
      {
      if (BarsInProgress == 0)
      {
      if (exitOrderL == null)
      exitOrderL= ExitLong(0 ,quotedQuantity, "Close Long Spread", "Long Spread");
      }
      }
      
      private void GoShortSpread()
      {
      if (BarsInProgress == 0)
      {
      if (entryOrderS == null)
      entryOrderS = EnterShort(0, quotedQuantity, "Short Spread");
      }
      }
      
      private void CloseShortSpread()
      {
      if (BarsInProgress == 0)
      {
      if (exitOrderS == null)
      exitOrderS = ExitShort(0, quotedQuantity, "Close Short Spread","Short Spread");
      }
      } 
      
      The OnExecution part is set up so that you don't take a position in the second leg until the first leg is filled.

      PHP Code:
      protected override void OnExecution(IExecution execution)/////////////////////////////////////////////////////////
      {
      try
      { 
      if (entryOrderL != null && entryOrderL.Token == execution.Order.Token)
      {
      if (execution.Order.OrderState == OrderState.Filled || execution.Order.OrderState == OrderState.PartFilled
      || (execution.Order.OrderState == OrderState.Cancelled && execution.Order.Filled > 0))
      {
      entryOrderHedgeL = EnterShort(1, hedgeQuantity, "Long Spread Hedge");
      if (execution.Order.OrderState != OrderState.PartFilled)
      {
      entryOrderL = null;
      inventory ++;
      } 
      }
      }
      if (exitOrderL != null && exitOrderL.Token == execution.Order.Token)
      {
      if (execution.Order.OrderState == OrderState.Filled || execution.Order.OrderState == OrderState.PartFilled
      || (execution.Order.OrderState == OrderState.Cancelled && execution.Order.Filled > 0))
      {
      exitOrderHedgeL = ExitShort(1, hedgeQuantity, "Close Long Spread Hedge", "Long Spread Hedge");
      if (execution.Order.OrderState != OrderState.PartFilled)
      {
      exitOrderL = null;
      inventory = 0;
      } 
      }
      }
      }
      catch (Exception e)
      {
      Log("TryCatch Error: STRATEGY PairsTrader. OnExecution/Long Logic.", LogLevel.Error);
      Print(Time[0] + " " + e.ToString());
      }
      try
      {
      if (entryOrderS != null && entryOrderS.Token == execution.Order.Token)
      {
      if (execution.Order.OrderState == OrderState.Filled || execution.Order.OrderState == OrderState.PartFilled
      || (execution.Order.OrderState == OrderState.Cancelled && execution.Order.Filled < 0))
      {
      entryOrderHedgeS = EnterLong(1, hedgeQuantity, "Short Spread Hedge");
      if (execution.Order.OrderState != OrderState.PartFilled)
      {
      entryOrderS = null;
      inventory --;
      } 
      }
      }
      if (exitOrderS != null && exitOrderS.Token == execution.Order.Token)
      {
      if (execution.Order.OrderState == OrderState.Filled || execution.Order.OrderState == OrderState.PartFilled
      || (execution.Order.OrderState == OrderState.Cancelled && execution.Order.Filled < 0))
      {
      exitOrderHedgeS = ExitLong(1, hedgeQuantity, "Close Short Spread Hedge", "Short Spread Hedge");
      if (execution.Order.OrderState != OrderState.PartFilled)
      {
      exitOrderS = null;
      inventory = 0;
      } 
      }
      }
      }
      catch (Exception e)
      {
      Log("TryCatch Error: STRATEGY PairsTrader. OnExecution/Short Logic.", LogLevel.Error);
      Print(Time[0] + " " + e.ToString());
      }
      } 
      

      Comment


        #4
        Lastly, OnBarUpdate:

        PHP Code:
        protected override void OnBarUpdate()///////////////////////////////////////////////////////////
        {
        if (backtest == false)
        {
        if (Historical)
        return;
        }
        
        if( inventory >= 0
        && mySZScore[0] > upperLineValue
        && mySZScore[0] < (upperLineValue*priceErrorCheck))
        {
        PrintWithTimeStamp("STRATEGY PairsTrader " +"Upper Line Value "+upperLineValue+ " Breached. Spread Z Score is: "+ mySZScore[0]);
        GoShortSpread(); 
        }
        if( inventory < 0
        && CrossBelow(mySZScore, coverSpread,1))
        {
        PrintWithTimeStamp("STRATEGY PairsTrader " +"Center Line Value "+coverSpread+ " Breached. Inventory is "+ inventory +
        " Spread Z Score is: "+ mySZScore[0]);
        CloseShortSpread(); 
        }
        if( inventory <= 0
        && mySZScore[0] < lowerLineValue
        && mySZScore[0] > (lowerLineValue*priceErrorCheck))
        {
        PrintWithTimeStamp("STRATEGY PairsTrader " +"Lower Line Value "+lowerLineValue+" Breached. Spread Z Score is: "+ mySZScore[0]);
        GoLongSpread();
        }
        if( inventory > 0
        && CrossAbove(mySZScore, coverSpread, 1))
        {
        PrintWithTimeStamp("STRATEGY PairsTrader " +"Center Line Value "+coverSpread+ " Breached. Inventory is "+ inventory +
        " Spread Z Score is: "+ mySZScore[0]);
        CloseLongSpread(); 
        } 
        
        }
        protected override void OnTermination()
        {
        Print(DateTime.Now+" STRATEGY PairsTrader "+ Instrument.FullName +"/" +hedgeInstrument + " DISABLED");
        } 
        

        Comment


          #5
          While there has been a lot of discussion about trading pairs here I have not come across any code to actually execute those trades. The "alpha" idea in that strategy may not be the best, but I am keen to get the trade execution side to be robust.

          Any input is most welcome!

          Comment


            #6
            And a quick word of warning: That strategy will crash NT7 if used in real-time. Again backtesting daily works but anything else is currently not working as expected.

            Comment


              #7
              Thanks sharing your work and offer for collaboration to streamline efforts, I'll have a look this PM here...

              Comment


                #8
                Bertrand did you get a chance to look at that code? I'm wondering if I should wait for the next beta release... or if I messed up and need to relook at how I coded it. Thanks.

                Comment


                  #9
                  Sorry, didn't really get a chance to dive into, however can confirm it breaks down here to on my end...have you hard coding the ADD for the instruments in?

                  Comment


                    #10
                    It looks like the indicator used in the strategy was throwing exceptions, which I've asked about in another thread. I've taken out that code for now and replaced it an it seems to work in both backest and real-time.

                    One unusual thing is how two instruments are diplayed in the Strategies tab vs. how they show up in the Positions tab. In the attached screen shots I'm flat (according to Positions and Orders), but the Stratgies tab makes it look like I have 2L open on one of the legs.

                    Is that expected?
                    Attached Files

                    Comment


                      #11
                      That does look strange MXASJ, I agree - so you're long NQ and short ES giving you a net flat position like Positions shows?

                      Thanks

                      Comment


                        #12
                        I'll email you the code shortly, Bertrand. Thanks for looking at it.

                        Comment


                          #13
                          Great work

                          Thanks for posting the strategy, I've been interested in backtesting/implementing this strategy for futures and I was looking for a good place to start.

                          I plan on testing this out over the weekend, looks great so far though!

                          Comment


                            #14
                            Hi chrisdc5,

                            You should consider taking a look at this code instead of the earlier code I posted. It uses an updated version of the SpreadZScore indicator. Again I'm not looking for a money-making idea with this code, I'm looking for code that will execute long and short positions in different instruments reliably. It is not there yet...

                            NT7b15 compile.
                            Attached Files

                            Comment


                              #15
                              Hello MAXASJ

                              I used your PairsTraderBeta strategy and your SpreadZScore indicator as a basis to try out a few
                              things.
                              I left untouched your algoritm for triggering the entry-exits, but I rewrote much of the code in
                              order to understand it better myself.
                              Attached you find the modified strategy, it seems to work fine in backtesting and under
                              simulated data feed. It should also work with real time data, but I could not test as we have
                              saturday.
                              As you see I changed the entry-exit logic and for me it works!
                              Feel free to do further testing/optimizing finding the best pair and standard deviation values
                              for entry/exit.

                              For real-time testing (also under Market replay) I prefere to keep a strategy running and change
                              the parameters on the fly ( As per StrategyParametersEditor on my site).
                              I added this feature into a compiled version of Pairs_Zweistein and I invite you to try it out
                              and to give me some feedback on it. It is a zip file and you do File->Utilities->import
                              NinjaScript-> Pairs_Zweistein.zip
                              all should work then.

                              I have some more ideas, among them:
                              - refInstrument : make it of type Instrument so that the user must always enter a valid
                              instrument.
                              - run optimization on input parameters (maybe type change of parameters necessary)
                              - Compare traded instrument to a basket of reference (hedged) instruments.
                              - use limit orders for entries (more important for thinly traded stocks)
                              Attached Files

                              Comment

                              Latest Posts

                              Collapse

                              Topics Statistics Last Post
                              Started by Geovanny Suaza, 02-11-2026, 06:32 PM
                              0 responses
                              648 views
                              0 likes
                              Last Post Geovanny Suaza  
                              Started by Geovanny Suaza, 02-11-2026, 05:51 PM
                              0 responses
                              369 views
                              1 like
                              Last Post Geovanny Suaza  
                              Started by Mindset, 02-09-2026, 11:44 AM
                              0 responses
                              108 views
                              0 likes
                              Last Post Mindset
                              by Mindset
                               
                              Started by Geovanny Suaza, 02-02-2026, 12:30 PM
                              0 responses
                              572 views
                              1 like
                              Last Post Geovanny Suaza  
                              Started by RFrosty, 01-28-2026, 06:49 PM
                              0 responses
                              573 views
                              1 like
                              Last Post RFrosty
                              by RFrosty
                               
                              Working...
                              X