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

Relative Strength Sector Rotation Strategy

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

    Relative Strength Sector Rotation Strategy

    Attached is my first ever Ninjascript that implements my sector rotation strategy using the Select Sector ETFs



    The strategy is based mainly on the work done by Mike Carr in his book "Smarter Investing in Any Economy: The Definitive Guide to Relative Strength Investing"

    The strategy is as follows:

    1.) Each week, calculate the relative strength rank of each ETF. This can be done in any number of ways. I chose to use a ratio of moving averages as done by Carr. The attached file contains the custom indicator used to calculate the RS rank.

    2.) Hold the top X-number of ETFs based on relative strength rank.

    3.) Sell these ETFs if they fall below a programmed sell-rank and replace with the highest ranked ETF given by that week's calculation.

    I've also added a hedge factor to reduce drawdowns. I do not buy ETFs if the S&P is below a programmed moving average. 200-day SMA works well. When backtesting, call it on the "SPY" ETF which tracks the S&P. This is used as the hedging instrument.

    I've been backtesting with Yahoo data and I'm showing cumulative profit of close to 400% since 1/1/1999--I'm still trying to optimize.

    Also note that I had to manually add the SPDR ETFs via the Instrument manager.

    Before it gets picked apart, please know it's my first time developing on the NT platform and this is also free I'm still trying to optimize the input parameters
    Attached Files
    Last edited by MidnightRider228; 02-10-2010, 03:01 PM. Reason: Added some notes

    #2
    MidnightRider228,

    Thanks for the contribution!

    One thing to note is that I don't think commissions are on a per execution basis? Probably would make more sense to do that off of a per entry order basis.
    Josh P.NinjaTrader Customer Service

    Comment


      #3
      Date of Execution

      Josh,

      One thing I noticed while backtesting is that if I print the date in OnExecution it's the day I submitted the order as if its filled instantly, but it shows up as executing on the next trading day in the Strategy Analyzer window... why is that?

      Comment


        #4
        MidnightRider228, this is unfortunately expected - the order will be executed on the next possible trade location after your bar has closed, which for your daily chart approach is the open of the next day then.
        BertrandNinjaTrader Customer Service

        Comment


          #5
          That still does explain why OnExecution() prints the wrong time the order was executed. I would think that the routine would be ran after execution, not before.

          Comment


            #6
            What are you using to print? You likely are not printing the execution time, but just a Time[] time.

            To get execution time you need to access the execution.Time property.
            Josh P.NinjaTrader Customer Service

            Comment


              #7
              MidnightRider,

              Very interesting that you are doing rotation (and seem to be the first, which is surprising). I am trying to backtest various strategies such as rotation, candle patterns, trend following and stacked timeframes - I have been evaluating tradestation & metastock but these strategies seem very hard to do in TS and the backtesting is MS seems weak.

              So, like you, I am experimenting with ninja - but I am not as far along so I really appriciate the work you have already put into this. I have a lot of software background but none with NT or C# (But it is a lot like Java and C++). I hope to be able to contribute to this investigation.

              First try it didn't work, but as a NT new guy it is probably my fault - no trades happen.

              You may also want to check out this: http://www.copstrat.com/ - this person also seems to have done a lot of work on validating a rotation strategy that returns about 20% with infrequent trading.

              One suggestion - it would be great if the list of stocks to rotate could be in an instrument list, not that I know how to access one from a script {perhaps support could lend a hand here!}

              Comment


                #8
                @Midnight, thanks for sharing. This is actually exactly what I'm looking to do (not RSI but rotation).

                I'm trying a slightly different approach that I had used successfully in my previous backtesting environment (WealthLab), also C# based. Let me preface by saying that I'm not a good programmer, or even mediocre. The below is something that I got to work from various reference code bits, I don't completely understand why they work.

                The approach uses List.Sort() methods on lists of holders, each of which contains an index i, a symbol name (not needed just for debugging, and a ranking score. The holder objects are bin sorted much like you're doing except using the Compare() method available in the System.Collections.General library.

                The benefit of this approach is that you don't have to maintain your own sorting method. The downside is that I'm not able to get it to work in NT. I keep getting the following error message:
                Code:
                **NT** Error on calling 'OnBarUpdate' method for strategy 'FirstRotationPDStrategy/f13b47a4902142b6ae99d89a39e4fdd1': Failed to compare two elements in the array.
                within the output window.

                If anyone is familiar with these methods and can spot something that I'm missing in the below skeleton code, I'd greatly appreciate the knowledge. @Midnight, maybe there are parts of this reference code that you'd want to borrow too.

                Code:
                // This namespace holds all strategies and is required. Do not change it.
                namespace NinjaTrader.Strategy
                {
                    public class FirstRotationPDStrategy : Strategy, IComparer<PDHolder> //Creates IComparer called PDHolder
                    {
                        #region Variables
                               int number = 3;
                        List<PDHolder> list = new List <PDHolder>(); // Unsure if this is necessary too????
                        #endregion
                
                        protected override void Initialize()
                        {
                            CalculateOnBarClose = true;
                
                             //Add DailyBars of the investment universe
                            Add("ABC", PeriodType.Day, 1);    //1 
                            Add("DEF", PeriodType.Day, 1);    //2
                            Add("GHI", PeriodType.Day, 1);  //3
                            Add("JKL", PeriodType.Day, 1);    //4
                            Add("MNO", PeriodType.Day, 1);  //5
                            Add("PQR", PeriodType.Day, 1);  //6
                            Add("STU", PeriodType.Day, 1);  //7
                            Add("VWX", PeriodType.Day, 1);  //8
                            Add("YZZ", PeriodType.Day, 1);  //9
                            Add("ZYX", PeriodType.Day, 1);  //10
                    
                    }
                
                
                        protected override void OnBarUpdate()
                        {
                        
                                    
                            
                            for (int i = 0; i < 9; i++)  //Loop to go thru each symbol
                
                            { 
                                PDHolder holder = new PDHolder();
                                holder.index = i;
                                holder.score = ScoringIndicator(BarsArray[i]).ScorePane[0]; // a Call to indicator that generates score
                                holder.symbol = BarsArray[i].Instrument.FullName;
                                
                                list.Add(holder);
                                
                            } 
                            
                            List <PDHolder> listShort = new List<PDHolder>(list);
                            listShort.Sort();
                            
                            List <PDHolder> listLong = new List<PDHolder>(list);
                            listLong.Sort();
                            listLong.Reverse();
                
                            int c = list.Count;
                
                            for(int r = 1; r<= c - number; r++)
                            {
                                listShort.RemoveAt(0);
                                listLong.RemoveAt(0);
                            }
                   
                            ///  TRADE EXITS HERE
                            ///  for each current position, check to see ifin the listShort, 
                            ///  if not then exit short position - if still in the list, leave in place    
                            
                            ///  for each current position, check to see if in the listLong, 
                            ///  if not then exit long position - if still in the list, leave in place    
                
                
                            ///  TRADE ENTRIES HERE
                            ///  for each holder in listShort, check to see if already short
                            ///  if not, enter a short position.  If so, do nothing
                
                            ///  for each holder in listLong, check to see if already long
                            ///  if not, enter a long position.  If so, do nothing
                
                        }
                 
                    public int Compare(PDHolder item1, PDHolder item2)
                    {
                        return item1.score.CompareTo(item2.score);
                    } 
                        
                    }
                
                    public class PDHolder // defines a holder object
                    {
                        internal string symbol;
                        internal double PD;
                        internal double score;
                        internal double index;
                    } 
                }

                Comment


                  #9
                  @sf631, thanks a million for your code, I found it most helpful. I'm also getting the same sort error and can't figure out a way around it... did you ever resolve that error?

                  Comment


                    #10
                    Thanks for the code.

                    How would one calculate based on historical returns? For example, purchase the top 2 ETFs based on the strongest returns over the past xx bars?

                    Comment


                      #11
                      My ETF rotation script

                      Hi I am doing something very similar based on http://www.etfreplay.com

                      They use a relative strength + volatility based rating. I am sorting by returns for two different periods (A, B) and volatility over period V.

                      I had the code working and for some reason it stopped. The BarsInProgress var stays at 0 even though BarsArray has 10 elements in it. I can't understand why. The only thing I did is redefined session templates under instrument manager and downloaded more data from kinetic.

                      I would appreciate if someone could look at this prelim code and see if I am doing something stupid that I have overlooked.

                      Since I was only playing around I coded it as an indicator where you would be able to selects ETFs defined by an ENUM that would then be added to the primary data series. One would then be able to select the dates and the calculations would be done. For now the dates are hard coded and the script looks for the bar# of the selected date and does the computation. Print() statements are used to debug the code ... I have included a screen shot from yesterday when it was working ,,, today it only runs BarsInProgress = 0 and does't even end up doing the computation as it is called only once BarsInProgress == BarsArray.Length - 1

                      Please any input would be appreciated!


                      Code:
                             protected override void Initialize()
                             {		
                                             // instantiate the etf collections		
                      			etf = new List<EtfData>();
                      			
                      			// make sure this charts series is added to the list
                      			if (!etf.Exists(delegate(EtfData item) { return item.Label == Instrument.FullName; }))
                      				etf.Add(new EtfData(Instrument.FullName));							
                      
                      			// fill the collections with the selected ETFs	
                      			if (etfsel != EtfType.ALL)
                      			{		
                      				if (!etf.Exists(delegate(EtfData item) { return item.Label == etfsel.ToString(); }))
                      					etf.Add(new EtfData(etfsel.ToString()));							
                      			}
                      			else
                      			{// add all the ETFs ...
                      				
                      				// ... by itterating over the values in the enum
                      				foreach (EtfType value in Enum.GetValues(typeof(EtfType)))
                      				{
                      					if (value == EtfType.ALL) 
                      						continue;	// with the exception of EtfType.ALL ...
                      					
                      					if (!etf.Exists(delegate(EtfData item) { return item.Label == value.ToString(); }))
                      						etf.Add(new EtfData(value.ToString()));							
                      				}
                      			}
                      			
                      			// debug: added etfs
                      			
                      				Print("added etf(s) :");
                      				foreach (EtfData item in etf)
                      					Print(item.Label);
                      				Print("");
                      				ClearOutputWindow();
                      						
                      
                      			
                      		    // go through the collection of ETFs and add them as dataseries to BarsArray
                      			// note: the indicator's chart series is already in BarsArray on index 0, 
                      			// with each addition the corresponding value of BarsInProgress increases by 1 
                      			foreach (EtfData item in etf)
                      			{
                      				if (item.Label == Instrument.FullName) 
                      					continue; // dont add it (already in by default)
                      				else
                      					Add(item.Label, PeriodType.Day, ndays);
                      			}
                      			
                      			// temp code ...
                      			endDate = new DateTime(2012, 11, 05);
                      			startDate = new DateTime(2012, 08, 05);
                      			// .............
                      			
                      			// create the DateTimeSeries for getting the date of current bar
                      			date = new DateTimeSeries(this);
                      			
                                  Overlay	= false;	
                      			
                      		}
                      				
                              /// <summary>
                              /// Called on each bar update event (incoming tick)
                              /// </summary>
                              protected override void OnBarUpdate()
                              {					
                      			// make sure we have the right bars period type
                      			if (BarsPeriod.Id != PeriodType.Day && BarsPeriod.Value < 1)
                      				return;	
                      	
                      			if (CurrentBar == 0)
                      			{
                      				Print("");
                      				Print("all data series in BarsArray :");
                      				for (int i = 0; i < BarsArray.Length; i++)
                      					Print(BarsArray[i].Instrument.FullName);
                      				Print("");				
                      			
                      				Print("");
                      				Print("start date: " + startDate.ToString());		
                      				Print("end date: " + endDate.ToString());
                      				Print("");			
                      				
                      			}
                      				
                      			// make sure there are enough bars
                      			if (CurrentBar <= GetMaxPeriod()) // in this case <= because [barsago+1] below
                      				return;
                      
                      			/// Bars are already loaded and OnBarUpdate() will run only on a new bar !
                      			/// need to use GetBar() to get data for bars with the correct dates
                      			
                      			int endbar   = Bars.GetBar(endDate);
                      			int barsago  = CurrentBar - endbar;
                      			int barsagoA = barsago + periodA;
                      			int barsagoB = barsago + periodB;
                      					
                      			if (CurrentBar == endbar)
                      			{// perform computation once CurrentBar # and endDate bar # match
                      				
                      				// debug: flow control
                      					
                      					Print("Processing ETF << " + etf[BarsInProgress].Label + " >>");
                      					Print("BarsInProgress = " + BarsInProgress.ToString());
                      					Print("Current bar is # " + CurrentBar.ToString()); 
                      					Print("Current end bar is # " + CurrentBar.ToString()); 
                      					Print("GetMaxPeriod() = " + GetMaxPeriod().ToString());
                      					Print("");
                      				
                      				// degug: start/end bar prices
                      				/*
                      					Print("start bar close : " + Close[barsagoA].ToString());		
                      					Print("end bar close   : " + Close[barsagoB].ToString());
                      				*/
                      								
                      				/// use BarsInProgress to make sure it is saved to the correct data series
                      				
                      				// get the 1st return
                      				double rA = (Close[barsago] - Close[barsagoA]) / Close[barsagoA];
                      				
                      				// get the 2nd return
                      				double rB = (Close[barsago] - Close[barsagoB]) / Close[barsagoB];
                      			
                      				// volatility calculation ...
                      				// a) get the individual returns for the defined period and 
                      				// b) store them in the data set
                      				double[] vreturns = new double[periodV];
                      				for (int i = barsago + (periodV-1); i >= 0; i--) // itterate down from 19 to 0 values ago from barsago (20 values)
                      					vreturns[barsago] = ((Close[barsago] - Close[barsago+1]) / Close[barsago+1]);
                      				
                      				// compute  volatility
                      				// a) using standard deviation and 
                      				// b) anualize it by multipling it by 252 
                      				//    where 252 = trading days in a year 
                      				double v = StandardDeviation(vreturns, periodV) * Math.Sqrt(252);				
                      				
                      				// save results to etf data
                      				etf[BarsInProgress].ReturnA = rA;		
                      				etf[BarsInProgress].ReturnB = rB;		
                      				etf[BarsInProgress].Volatility = v;			
                      				
                      				if (BarsInProgress == etf.Count-1) // with all series saved
                      				{ // go ahead computing the rank by ...
                      				
                      					// make a copy of the original data
                      					List<List<EtfData>> data = new List<List<EtfData>>();
                      
                      					// add 3 copies of etf data (one for each sort)
                      					data.Add(etf);
                      					data.Add(etf);
                      					data.Add(etf);
                      					
                      					// sort the data by returns (A, B) and Volatility (V)
                      					// ... need to reverse order for sorting by returns because we need high to low (default sort is low to high)
                      					foreach(List<EtfData> container in data)
                      					{
                      						switch(data.IndexOf(container))
                      						{
                      							case A: 
                      								container.Sort( delegate (EtfData item1, EtfData item2) 
                      									{ return item1.ReturnA.CompareTo(item2.ReturnA); });
                      								container.Reverse();
                      							break;
                      							case B: container.Sort( delegate (EtfData item1, EtfData item2) 
                      									{ return item1.ReturnB.CompareTo(item2.ReturnB); });
                      								container.Reverse();
                      							break;
                      							case V: container.Sort( delegate (EtfData item1, EtfData item2) 
                      									{ return item1.Volatility.CompareTo(item2.Volatility); });
                      							break;
                      						}
                      					}
                      					
                      					
                      					// compute rankby order of all data
                      					foreach(List<EtfData> container in data)
                      					{
                      						foreach (EtfData item in container)
                      						{		
                      							switch(data.IndexOf(container))
                      							{
                      								case A: item.RankByA = container.IndexOf(item); break;
                      								case B: item.RankByB = container.IndexOf(item); break;
                      								case V: item.RankByV = container.IndexOf(item); break;
                      							}
                      						}
                      					}
                      				
                      					// compute overall rank	using weights & save the results
                      					foreach(EtfData item in etf)
                      					{
                      						item.RankByA = data[0].Find(delegate (EtfData d) { return d.Label == item.Label; }).RankByA;
                      						item.RankByB = data[0].Find(delegate (EtfData d) { return d.Label == item.Label; }).RankByB;
                      						item.RankByV = data[0].Find(delegate (EtfData d) { return d.Label == item.Label; }).RankByV;
                      						item.FinalRank = item.RankByA*weightA + item.RankByB*weightB + item.RankByV*weightV;  
                      					}
                      				
                      				
                      					// sort the final etf results
                      					etf.Sort( delegate (EtfData item1, EtfData item2) 
                      						{
                      							return item1.FinalRank.CompareTo(item2.FinalRank);
                      						}
                      					);
                      								
                      				
                      					// etf should now contain the final result order !
                      						
                      					// debug: contents of data
                      					
                      						Print("");
                      						Print("*********************");
                      						Print("       RESULTS       ");
                      						Print("*********************");		
                      						foreach (EtfData item in etf)
                      						{
                      							Print("# " + (etf.IndexOf(item) + 1).ToString()+ " Etf << " + item.Label + " >>");
                      							Print("RA = " + (item.ReturnA * 100).ToString("0.0"));
                      							Print("RB = " + (item.ReturnB * 100).ToString("0.0"));
                      							Print("V  = " + (item.Volatility * 100).ToString("0.0"));
                      							Print("");
                      						}
                      						Print("");		
                      				}
                      			
                      			}		
                              }
                      Attached Files

                      Comment


                        #12
                        Hi fx_pete. I am very interested in your work too. Did you find a way to fix your code?

                        Comment


                          #13
                          Fildelity Select Trend System

                          I took midnightrader's code and modified it to implement a trend following system with the Fildelity select sector funds.
                          My contribution back to the community

                          I'm new to NinjaTrader and have a few questions:
                          1) When you backtest with multiple instruments the percentage numbers (drawdown, cumulative profit etc.) in the summary of the strategy tester seems to be messed up. So I had to print the peak to valley drawdown on the output screen. You can see that in the attached code. Is there a way to fix that so that it's doesn't have to be manually calculated?

                          2) To access multiple instruments, do you have to necessarily add all of them to the chart with Add() method? Is there a way to access the price data of multiple instruments without adding them to the chart? I can't imagine adding 2K instruments say if I wanted to access the price data of all Russell 2K stocks in the strategy.
                          Attached Files

                          Comment


                            #14
                            nvtrader, thanks for the shared script and welcome to our forums.

                            For basket testing those #'s would be a weighted average in the report, so different from what you've calculated manually - we have this for potential enhancement on our suggestion lists for our next major update.

                            Correct, all instruments would need to be added if you wanted to have programmatic access to them from the script - they would not be charted through but programmatically added internally only so the script could use them for it's calculations.
                            BertrandNinjaTrader Customer Service

                            Comment


                              #15
                              Bertrand,

                              Thanks for the prompt reply. There's lot of good stuff here.

                              I have one more question. But it does not belong to this thread. I will create a separate thread and ask the question.

                              Comment

                              Latest Posts

                              Collapse

                              Topics Statistics Last Post
                              Started by jclose, Yesterday, 09:37 PM
                              1 response
                              11 views
                              0 likes
                              Last Post NinjaTrader_Gaby  
                              Started by firefoxforum12, Yesterday, 08:53 PM
                              1 response
                              14 views
                              0 likes
                              Last Post NinjaTrader_BrandonH  
                              Started by kmunroe478, Yesterday, 05:39 PM
                              2 responses
                              15 views
                              0 likes
                              Last Post NinjaTrader_Jesse  
                              Started by kevinenergy, 02-17-2023, 12:42 PM
                              115 responses
                              2,700 views
                              1 like
                              Last Post kevinenergy  
                              Started by prdecast, Today, 06:07 AM
                              1 response
                              5 views
                              0 likes
                              Last Post NinjaTrader_LuisH  
                              Working...
                              X