Your interactive trading laboratory!
 • 
8 users online

A new way to Rotate!

Q172 is shaping up to be a nice upgrade! We're extending the UserModelBase class to provide 2 new hooks into the backtest process. PreExecute and PostExecte are called before/after the main processing loop (where Execute is called), and pass you the date being processed as well as a list of all of the symbols that have data for this iteration. You can do things like craft a rotation model in code using these methods. I'll provide a Dev Blog article illustrating this. It's an exciting development, and while our feature requests for Filters in Rotation Models are still in the queue, this can be a way to get this functionality quickly in a code based model.

Attachment

Cancel

Responses

Wow, excellent. I'm trial running QStudio now and it's exciting advances like this (and your amazing productivity toward new releases) that's going to push me over the edge and buy the license soon. I've been playing with some promising rotators and I need this kind of flexibility. Looking forward to Q172!

Wow, excellent. I'm trial running QStudio now and it's exciting advances like this (and your amazing productivity toward new releases) that's going to push me over the edge and buy the license soon. I've been playing with some promising rotators and I need this kind of flexibility. Looking forward to Q172!

New Dev Blog Post: A New Way to Rotate

New Dev Blog Post: [A New Way to Rotate](https://www.quantacula.com/Help/OpenHelp/devblog/665)

@Glitch. Just used the new way to rotate for the first time. One question I have. How do I set the rebalance frequency to weekly, monthly or every two weeks/months in code?

@Glitch. Just used the new way to rotate for the first time. One question I have. How do I set the rebalance frequency to weekly, monthly or every two weeks/months in code?

Here's how I would approach this …

  1. Create a static boolean variable called rebalance.
  2. In the PreExecute method, examine the dt parameter and determine if it is desired rebalance date. Set the rebalance variable true or false.
  3. If rebalance is false, simply fall out of the PreExecute method instead of doing the normal processing.
  4. In the Execute method, if the rebalance variable is false, simply return out of the method, otherwise do normal processing.

This should let you control the rebalance intervals. Let me know how it goes!

Here's how I would approach this … 1. Create a **static** boolean variable called rebalance. 2. In the **PreExecute** method, examine the **dt** parameter and determine if it is desired rebalance date. Set the rebalance variable true or false. 3. If rebalance is false, simply fall out of the **PreExecute** method instead of doing the normal processing. 4. In the **Execute** method, if the rebalance variable is false, simply return out of the method, otherwise do normal processing. This should let you control the rebalance intervals. Let me know how it goes!

Here's my sample code snippet with 3 conditions: It's a monday and the first day of a new month and a valid trading day. Any thoughts?

using System.Linq;

		private bool rebalance;
		public bool IsFirstDayOfMonth(DateTime dt)
		{
			int month = dt.Month;
			while (true)
			{
				dt = dt.AddDays(1);
				if (dt.Month != month)
					return true;
			}
		}

		public override void PreExecute(DateTime dt, List<BarHistory> participants)
		{			
			if (dt.DayOfWeek != DayOfWeek.Monday)
			{
				rebalance = false;
				return;
			}

			if (IsFirstDayOfMonth(dt) == false)
			{
				rebalance = false;
				return;
			}

			MarketDetails marketDetails = participants.Select(x => x.Market).FirstOrDefault();
			if (marketDetails.IsTradingDay(dt) == false)
			{
				rebalance = false;
				return;
			}

			rebalance = true;
      Continue....
    }
Here's my sample code snippet with 3 conditions: It's a monday and the first day of a new month and a valid trading day. Any thoughts? [CODE] using System.Linq; private bool rebalance; public bool IsFirstDayOfMonth(DateTime dt) { int month = dt.Month; while (true) { dt = dt.AddDays(1); if (dt.Month != month) return true; } } public override void PreExecute(DateTime dt, List<BarHistory> participants) { if (dt.DayOfWeek != DayOfWeek.Monday) { rebalance = false; return; } if (IsFirstDayOfMonth(dt) == false) { rebalance = false; return; } MarketDetails marketDetails = participants.Select(x => x.Market).FirstOrDefault(); if (marketDetails.IsTradingDay(dt) == false) { rebalance = false; return; } rebalance = true; Continue.... } [/CODE]

Is the intention to rebalance the first trading day of every month?

Is the intention to rebalance the first trading day of every month?

Yes. Otherwise an option would be to rebalance weekly (on monday, or do I have to catch the friday?).

The code above is just a sample snippet. I would not use both options in a single RM. For this purpose, let's say that we only trade on the first trading day of every month.

Yes. Otherwise an option would be to rebalance weekly (on monday, or do I have to catch the friday?). The code above is just a sample snippet. I would not use both options in a single RM. For this purpose, let's say that we only trade on the first trading day of every month.

For first day of month I would do this, just track the dates and rebalance when the day of the month of the current date is less than the day of the month of the previous date.

	private DateTime lastDate = DateTime.MinValue;
	private bool rebalance = false;
        public override void PreExecute(DateTime dt, List<BarHistory> participants)
        {
		rebalance = lastDate != DateTime.MinValue && dt.Day < lastDate.Day;
		lastDate = dt;
	        if (!rebalance)
			return;
      }
For first day of month I would do this, just track the dates and rebalance when the day of the month of the current date is less than the day of the month of the previous date. [CODE] private DateTime lastDate = DateTime.MinValue; private bool rebalance = false; public override void PreExecute(DateTime dt, List<BarHistory> participants) { rebalance = lastDate != DateTime.MinValue && dt.Day < lastDate.Day; lastDate = dt; if (!rebalance) return; } [/CODE]

I've tried your approach, but I still have a little problem when a new month starts on or after a weekend. Any idea how I can fix that?

I've tried your approach, but I still have a little problem when a new month starts on or after a weekend. Any idea how I can fix that?

This version should ensure that orders are placed on the first trading day of every month.

using QuantaculaBacktest;
using System;
using QuantaculaCore;
using QuantaculaIndicators;
using System.Collections.Generic;

namespace Quantacula
{
	public class MyModel : UserModelBase
	{
		//the list of symbols that we should buy each bar
		private static List<BarHistory> buys = new List<BarHistory>();

		//create the weight indicator and stash it into the BarHistory object for reference in PreExecute
		public override void Initialize(BarHistory bars)
		{
			rsi = new RSI(bars.Close, 14);
			bars.Cache["RSI"] = rsi;
		}

		//this is called prior to the Execute loop, determine which symbols have the lowest RSI
		public override void PreExecute(DateTime dt, List<BarHistory> participants)
		{
			//rebalance so orders are placed on the first trading day of a new month
			MarketDetails md = participants[0].Market;
			DateTime nextTradingDay = dt;
			do
			{
				nextTradingDay = nextTradingDay.AddDays(1);
			}
			while (!md.IsTradingDay(nextTradingDay));
			rebalance = nextTradingDay.Month != dt.Month;
			if (!rebalance)
				return;
			
			//store the symbols' RSI value in their BarHistory instances
			foreach (BarHistory bh in participants)
			{
				RSI symbolRsi = (RSI)bh.Cache["RSI"];
				int idx = GetCurrentIndex(bh);  //this returns the index of the BarHistory for the bar currently being processed
				double rsiVal = symbolRsi[idx];
				bh.UserData = rsiVal; //save the current RSI value along with the BarHistory instance
			}

			//sort the participants by RSI value (lowest to highest)
			participants.Sort((a, b) => a.UserDataAsDouble.CompareTo(b.UserDataAsDouble));

			//keep the top 3 symbols
			buys.Clear();
			for (int n = 0; n < 3; n++)
			{
				if (n >= participants.Count)
					break;
				buys.Add(participants[n]);
			}
		}

		//execute the strategy rules here, this is executed once for each bar in the backtest history
		public override void Execute(BarHistory bars, int idx)
		{
			if (!rebalance)
				return;
			
			bool inBuyList = buys.Contains(bars);
			if (!HasOpenPosition(bars, PositionType.Long))
			{
				//buy logic - buy if it's in the buys list
				if (inBuyList)
					PlaceTrade(bars, TransactionType.Buy, OrderType.Market);
			}
			else
			{
				//sell logic, sell if it's not in the buys list
				if (!inBuyList)
					PlaceTrade(bars, TransactionType.Sell, OrderType.Market);
			}
		}

		//declare private variables below
		private static bool rebalance = false;
		private RSI rsi;
	}
}
This version should ensure that orders are placed on the first trading day of every month. [CODE] using QuantaculaBacktest; using System; using QuantaculaCore; using QuantaculaIndicators; using System.Collections.Generic; namespace Quantacula { public class MyModel : UserModelBase { //the list of symbols that we should buy each bar private static List<BarHistory> buys = new List<BarHistory>(); //create the weight indicator and stash it into the BarHistory object for reference in PreExecute public override void Initialize(BarHistory bars) { rsi = new RSI(bars.Close, 14); bars.Cache["RSI"] = rsi; } //this is called prior to the Execute loop, determine which symbols have the lowest RSI public override void PreExecute(DateTime dt, List<BarHistory> participants) { //rebalance so orders are placed on the first trading day of a new month MarketDetails md = participants[0].Market; DateTime nextTradingDay = dt; do { nextTradingDay = nextTradingDay.AddDays(1); } while (!md.IsTradingDay(nextTradingDay)); rebalance = nextTradingDay.Month != dt.Month; if (!rebalance) return; //store the symbols' RSI value in their BarHistory instances foreach (BarHistory bh in participants) { RSI symbolRsi = (RSI)bh.Cache["RSI"]; int idx = GetCurrentIndex(bh); //this returns the index of the BarHistory for the bar currently being processed double rsiVal = symbolRsi[idx]; bh.UserData = rsiVal; //save the current RSI value along with the BarHistory instance } //sort the participants by RSI value (lowest to highest) participants.Sort((a, b) => a.UserDataAsDouble.CompareTo(b.UserDataAsDouble)); //keep the top 3 symbols buys.Clear(); for (int n = 0; n < 3; n++) { if (n >= participants.Count) break; buys.Add(participants[n]); } } //execute the strategy rules here, this is executed once for each bar in the backtest history public override void Execute(BarHistory bars, int idx) { if (!rebalance) return; bool inBuyList = buys.Contains(bars); if (!HasOpenPosition(bars, PositionType.Long)) { //buy logic - buy if it's in the buys list if (inBuyList) PlaceTrade(bars, TransactionType.Buy, OrderType.Market); } else { //sell logic, sell if it's not in the buys list if (!inBuyList) PlaceTrade(bars, TransactionType.Sell, OrderType.Market); } } //declare private variables below private static bool rebalance = false; private RSI rsi; } } [/CODE]
Forum Tips

Please sign in if you want to participate in our forum.

Our forum uses Markdown syntax to format posts.

To embed code snippets, enclose them in [CODE][/CODE] tags.