Your interactive trading laboratory!
 • 
9 users online

Earnings Date in code

Is there a way in QS to get the next earnings date by code? The reason i'm asking for is pretty simple. I would like to prevent any trades which are N days in advance of the next earnings release.

Attachment

Cancel

Responses

Are you looking for a way to...

  1. backtest this i.e. avoid a period around earnings dates (using a fundamental data source)?
  2. in live trading, don't place new trades if earnings date is upcoming?
Are you looking for a way to... 1. backtest this i.e. avoid a period around earnings dates (using a fundamental data source)? 2. in live trading, don't place new trades if earnings date is upcoming?

Just for live trading: don't place new trades if earnings date is upcoming.

Just for live trading: don't place new trades if earnings date is upcoming.

Just for live trading: don't place new trades if earnings date is upcoming.

Just for live trading: don't place new trades if earnings date is upcoming.

So your model needs to query some kind of web-based earnings calendar. This is easy to do with the help of a third party library but you'd have to put one in the GAC for the model to run. I read that Glitch is about to add this feature in upcoming build so let's wait for Q167 to come out first.

So your model needs to query some kind of web-based earnings calendar. This is easy to do with the help of a third party library but you'd have to put one in the GAC for the model to run. I read that Glitch is about to add this feature in upcoming build so let's wait for [Q167](https://www.quantacula.com/Forum/ForumTopic/137) to come out first.

Here's code that should get the job done for you. Its first time setup is a little tricky:

  1. Update to Q167
  2. Forum doesn't let me attach HtmlAgilityPack.dll so download it from Nuget, unzip and take the right version from /lib/Net45 folder and finally unblock: How to Unblock Files Downloaded from Internet?
  3. Put it to Quantacula's main folder

Now start Q and create a new model.

  1. From there, click "References" and add references to System.Core and System.Xml.
  2. Click "Reference a custom assembly" and point it to HtmlAgilityPack.dll

The code parses an upcoming earnings table at seekingalpha.com and checks if the current symbol has upcoming earnings within X days:

using QuantaculaBacktest;
using System;
using QuantaculaCore;
using QuantaculaIndicators;
using QuantaculaChart;
using System.Drawing;
using System.Collections.Generic;
using System.Net;
using System.IO;
using System.IO.Compression;
using System.Globalization;
using System.Linq;
using HtmlAgilityPack;

namespace Quantacula
{
    public class MyModel : UserModelBase
    {
		static Dictionary<string, DateTime> list = new Dictionary<string, DateTime>();
	    
		public override void BacktestBegin()
		{			
			base.BacktestBegin();
			list = GetUpcomingEarnings();
		}

		public static Dictionary<string, DateTime> GetUpcomingEarnings()
		{
			var lst = new Dictionary<string, DateTime>();

			using (WebClient wc = new WebClient())
			{
				wc.Headers.Add("Referer", "https://seekingalpha.com/");
				wc.Headers.Add("UserAgent", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:62.0) Gecko/20100101 Firefox/62.0");
				wc.Headers.Add("Accept-Encoding", "gzip");
				wc.Headers.Add("Content-Type", "text/html, application/xhtml+xml, image/jxr, */*");

				var responseStream = new GZipStream(wc.OpenRead("https://seekingalpha.com/earnings/earnings-calendar"), CompressionMode.Decompress);
				var reader = new StreamReader(responseStream);
				var text = reader.ReadToEnd();
				
				var doc = new HtmlAgilityPack.HtmlDocument();
				doc.LoadHtml(text);

				var table = doc.DocumentNode.SelectSingleNode("//table[@class='earningsTable']")
					.Descendants("tr")
					.Skip(1)
					.Where(tr => tr.Elements("td").Count() > 1)
					.Select(tr => tr.Elements("td").Select(td => td.InnerText.Trim()).ToList())
					.ToList().Select(n =>
					new
					{
						Symbol = n[0],
						Date = DateTime.ParseExact(n[2], "MM/dd/yyyy", CultureInfo.InvariantCulture)
					});

				foreach (var t in table)
				{
					lst.Add(t.Symbol, t.Date);
				}
			}

			return lst;
		}
	    
        //create indicators and other objects here, this is executed prior to the main trading loop
        public override void Initialize(BarHistory bars)
		{
			WriteToDebugLog("Upcoming earnings:");
			foreach (var e in list)
	        { 
				WriteToDebugLog(e.ToString());
			}
		}

        //execute the strategy rules here, this is executed once for each bar in the backtest history
        public override void Execute(BarHistory bars, int idx)
        {
			var withinBars = 5;
	        var sym = bars.Symbol;

			if (!HasOpenPosition(bars, PositionType.Long))
            {
				//code your buy conditions here
				if (idx == bars.DateTimes.Count - 1)
				{
					var dt = bars.DateTimes[idx].Date;
					
					bool condition = list.Any(item => (item.Key == sym && item.Value >= dt.AddDays(withinBars)));
					if (condition)
					{
						WriteToDebugLog(string.Format("Will not trade {0}: earnings date upcoming: {1}", sym, list[sym]));
					}
	            }
			}
            else
            {
                //code your sell conditions here
            }
        }

        //declare private variables below

    }
}

Every time you run this code on a symbol (e.g. RECN as of today) it will request the table from SeekingAlpha and warn if the symbol won't be traded because it has earnings soon:

Upcoming earnings:
[NXGPF, 03.01.2019 0:00:00]
[RECN, 03.01.2019 0:00:00]
[ANGO, 04.01.2019 0:00:00]
[LW, 04.01.2019 0:00:00]
[RPM, 04.01.2019 0:00:00]
[CMC, 07.01.2019 0:00:00]
[SGH, 08.01.2019 0:00:00]
[AYI, 09.01.2019 0:00:00]
[MSM, 09.01.2019 0:00:00]
[STZ, 09.01.2019 0:00:00]
[SNX, 10.01.2019 0:00:00]
[INFO, 15.01.2019 0:00:00]
[PNC, 16.01.2019 0:00:00]
[USB, 16.01.2019 0:00:00]
[BBT, 17.01.2019 0:00:00]
[MTB, 17.01.2019 0:00:00]
[FBC, 22.01.2019 0:00:00]
[VAR, 23.01.2019 0:00:00]
[BIIB, 29.01.2019 0:00:00]
[PFG, 29.01.2019 0:00:00]
[BDN, 30.01.2019 0:00:00]
[HON, 31.01.2019 0:00:00]
[KIM, 31.01.2019 0:00:00]
[ARE, 04.02.2019 0:00:00]
[HIG, 04.02.2019 0:00:00]
[CMG, 06.02.2019 0:00:00]
[NOV, 06.02.2019 0:00:00]
[CBL, 07.02.2019 0:00:00]
[RIOCF, 12.02.2019 0:00:00]
[FRT, 13.02.2019 0:00:00]
[PXD, 13.02.2019 0:00:00]
[REG, 13.02.2019 0:00:00]
[UE, 13.02.2019 0:00:00]
[TRU, 14.02.2019 0:00:00]
[WRE, 14.02.2019 0:00:00]
[ROIC, 19.02.2019 0:00:00]
[SUI, 20.02.2019 0:00:00]
[AMKAF, 21.02.2019 0:00:00]
[B, 22.02.2019 0:00:00]
[PESAF, 26.02.2019 0:00:00]
[SUBCY, 28.02.2019 0:00:00]
[ZEAL, 07.03.2019 0:00:00]
[SPLV, 04.01.2019 0:00:00]
Will not trade RECN: earnings date upcoming: 03.01.2019 0:00:00
Here's code that should get the job done for you. Its first time setup is a little tricky: 1. Update to Q167 2. Forum doesn't let me attach HtmlAgilityPack.dll so download it from [Nuget](https://www.nuget.org/api/v2/package/HtmlAgilityPack/1.4.6), unzip and take the right version from **/lib/Net45** folder and finally unblock: [How to Unblock Files Downloaded from Internet?](https://www.winhelponline.com/blog/bulk-unblock-files-downloaded-internet/) 3. Put it to Quantacula's main folder Now start Q and create a new model. 1. From there, click "References" and add references to **System.Core** and **System.Xml**. 2. Click "Reference a custom assembly" and point it to **HtmlAgilityPack.dll** The code parses an upcoming earnings table at [seekingalpha.com](https://seekingalpha.com) and checks if the current symbol has upcoming earnings within X days: [CODE] using QuantaculaBacktest; using System; using QuantaculaCore; using QuantaculaIndicators; using QuantaculaChart; using System.Drawing; using System.Collections.Generic; using System.Net; using System.IO; using System.IO.Compression; using System.Globalization; using System.Linq; using HtmlAgilityPack; namespace Quantacula { public class MyModel : UserModelBase { static Dictionary<string, DateTime> list = new Dictionary<string, DateTime>(); public override void BacktestBegin() { base.BacktestBegin(); list = GetUpcomingEarnings(); } public static Dictionary<string, DateTime> GetUpcomingEarnings() { var lst = new Dictionary<string, DateTime>(); using (WebClient wc = new WebClient()) { wc.Headers.Add("Referer", "https://seekingalpha.com/"); wc.Headers.Add("UserAgent", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:62.0) Gecko/20100101 Firefox/62.0"); wc.Headers.Add("Accept-Encoding", "gzip"); wc.Headers.Add("Content-Type", "text/html, application/xhtml+xml, image/jxr, */*"); var responseStream = new GZipStream(wc.OpenRead("https://seekingalpha.com/earnings/earnings-calendar"), CompressionMode.Decompress); var reader = new StreamReader(responseStream); var text = reader.ReadToEnd(); var doc = new HtmlAgilityPack.HtmlDocument(); doc.LoadHtml(text); var table = doc.DocumentNode.SelectSingleNode("//table[@class='earningsTable']") .Descendants("tr") .Skip(1) .Where(tr => tr.Elements("td").Count() > 1) .Select(tr => tr.Elements("td").Select(td => td.InnerText.Trim()).ToList()) .ToList().Select(n => new { Symbol = n[0], Date = DateTime.ParseExact(n[2], "MM/dd/yyyy", CultureInfo.InvariantCulture) }); foreach (var t in table) { lst.Add(t.Symbol, t.Date); } } return lst; } //create indicators and other objects here, this is executed prior to the main trading loop public override void Initialize(BarHistory bars) { WriteToDebugLog("Upcoming earnings:"); foreach (var e in list) { WriteToDebugLog(e.ToString()); } } //execute the strategy rules here, this is executed once for each bar in the backtest history public override void Execute(BarHistory bars, int idx) { var withinBars = 5; var sym = bars.Symbol; if (!HasOpenPosition(bars, PositionType.Long)) { //code your buy conditions here if (idx == bars.DateTimes.Count - 1) { var dt = bars.DateTimes[idx].Date; bool condition = list.Any(item => (item.Key == sym && item.Value >= dt.AddDays(withinBars))); if (condition) { WriteToDebugLog(string.Format("Will not trade {0}: earnings date upcoming: {1}", sym, list[sym])); } } } else { //code your sell conditions here } } //declare private variables below } } [/CODE] Every time you run this code on a symbol (e.g. RECN as of today) it will request the table from SeekingAlpha and warn if the symbol won't be traded because it has earnings soon: [CODE] --RECN-- Upcoming earnings: [NXGPF, 03.01.2019 0:00:00] [RECN, 03.01.2019 0:00:00] [ANGO, 04.01.2019 0:00:00] [LW, 04.01.2019 0:00:00] [RPM, 04.01.2019 0:00:00] [CMC, 07.01.2019 0:00:00] [SGH, 08.01.2019 0:00:00] [AYI, 09.01.2019 0:00:00] [MSM, 09.01.2019 0:00:00] [STZ, 09.01.2019 0:00:00] [SNX, 10.01.2019 0:00:00] [INFO, 15.01.2019 0:00:00] [PNC, 16.01.2019 0:00:00] [USB, 16.01.2019 0:00:00] [BBT, 17.01.2019 0:00:00] [MTB, 17.01.2019 0:00:00] [FBC, 22.01.2019 0:00:00] [VAR, 23.01.2019 0:00:00] [BIIB, 29.01.2019 0:00:00] [PFG, 29.01.2019 0:00:00] [BDN, 30.01.2019 0:00:00] [HON, 31.01.2019 0:00:00] [KIM, 31.01.2019 0:00:00] [ARE, 04.02.2019 0:00:00] [HIG, 04.02.2019 0:00:00] [CMG, 06.02.2019 0:00:00] [NOV, 06.02.2019 0:00:00] [CBL, 07.02.2019 0:00:00] [RIOCF, 12.02.2019 0:00:00] [FRT, 13.02.2019 0:00:00] [PXD, 13.02.2019 0:00:00] [REG, 13.02.2019 0:00:00] [UE, 13.02.2019 0:00:00] [TRU, 14.02.2019 0:00:00] [WRE, 14.02.2019 0:00:00] [ROIC, 19.02.2019 0:00:00] [SUI, 20.02.2019 0:00:00] [AMKAF, 21.02.2019 0:00:00] [B, 22.02.2019 0:00:00] [PESAF, 26.02.2019 0:00:00] [SUBCY, 28.02.2019 0:00:00] [ZEAL, 07.03.2019 0:00:00] [SPLV, 04.01.2019 0:00:00] Will not trade RECN: earnings date upcoming: 03.01.2019 0:00:00 [/CODE]

Thanks for the sample. Just made a quick check on seekingalpha. The earnings list seems to be incomplete. For example, AAPL, AMZN, FB have earnings on Feb 4. 2019 but they do not appear? Any ideas why?

Thanks for the sample. Just made a quick check on seekingalpha. The earnings list seems to be incomplete. For example, AAPL, AMZN, FB have earnings on Feb 4. 2019 but they do not appear? Any ideas why?

You better ask SeekingAlpha on that. However, February is not at the door yet so they may update the table later.

You better ask SeekingAlpha on that. However, February is not at the door yet so they may update the table later.

HTML parsing is nonsense and totally error prone (...already got a bloody nose years a go...). It would be better to use an managed API like Zacks. Unfortunately it's not for free.

HTML parsing is nonsense and totally error prone (...already got a bloody nose years a go...). It would be better to use an managed API like Zacks. Unfortunately it's not for free.

I have to disagree with you because it's an exaggeration. Nevertheless, this sample model uses Zacks and doesn't parse HTML nor relies on any libraries. ;-) It's output is valid for one day (hardcoded for tomorrow's earnings):

using QuantaculaBacktest;
using System;
using QuantaculaCore;
using QuantaculaIndicators;
using QuantaculaChart;
using System.Drawing;
using System.Collections.Generic;
using System.Net;
using System.Text.RegularExpressions;
using System.Linq;

namespace Quantacula
{
	public class MyModel : UserModelBase
	{
		static List<string> list = new List<string>();

		public override void BacktestBegin()
		{
			base.BacktestBegin();
			list = GetUpcomingEarnings();
		}

		public static List<string> GetUpcomingEarnings()
		{
			var lst = new List<string>();

			var dt = DateTime.Today.AddDays(1 + 1);
			TimeSpan ts = (new DateTime(dt.Year, dt.Month, dt.Day, 0, 0, 0) - new DateTime(1970, 1, 1, 0, 0, 0));
			double unixTime = ts.TotalSeconds;
			var url = string.Format("https://www.zacks.com/includes/classes/z2_class_calendarfunctions_data.php?calltype=eventscal&date={0}&type=1&search_trigger=0&0.5&_=1234567890000", unixTime);
			var _remove = "www.zacks.com/stock/quote/";

			using (WebClient wc = new WebClient())
			{
				wc.Headers.Add("Referer", "https://www.zacks.com/");
				wc.Headers.Add("UserAgent", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:62.0) Gecko/20100101 Firefox/62.0");
				wc.Headers.Add("Content-Type", "text/html, application/xhtml+xml, image/jxr, */*");

				string text = wc.DownloadString(new Uri(url));
				foreach (var m in Regex.Matches(text, _remove + "[A-Z]{1,4}"))
					lst.Add(m.ToString().Replace(_remove, ""));
			}

			return lst;
		}

		//create indicators and other objects here, this is executed prior to the main trading loop
		public override void Initialize(BarHistory bars)
		{
			WriteToDebugLog("Upcoming earnings:");
			foreach (var e in list)
			{
				WriteToDebugLog(e.ToString());
			}
		}

		//execute the strategy rules here, this is executed once for each bar in the backtest history
		public override void Execute(BarHistory bars, int idx)
		{
			var sym = bars.Symbol;

			if (!HasOpenPosition(bars, PositionType.Long))
			{
				//code your buy conditions here
				if (idx == bars.DateTimes.Count - 1)
				{
					var dt = bars.DateTimes[idx].Date;

					bool condition = list.Any(item => (item == sym));
					if (condition)
					{
						WriteToDebugLog(string.Format("Will not trade: {0} has upcoming earnings date", sym));
					}
				}
			}
			else
			{
				//code your sell conditions here
			}
		}

		//declare private variables below

	}
}
I have to disagree with you because it's an exaggeration. Nevertheless, this sample model uses Zacks and doesn't parse HTML nor relies on any libraries. ;-) It's output is valid for one day (hardcoded for tomorrow's earnings): [CODE] using QuantaculaBacktest; using System; using QuantaculaCore; using QuantaculaIndicators; using QuantaculaChart; using System.Drawing; using System.Collections.Generic; using System.Net; using System.Text.RegularExpressions; using System.Linq; namespace Quantacula { public class MyModel : UserModelBase { static List<string> list = new List<string>(); public override void BacktestBegin() { base.BacktestBegin(); list = GetUpcomingEarnings(); } public static List<string> GetUpcomingEarnings() { var lst = new List<string>(); var dt = DateTime.Today.AddDays(1 + 1); TimeSpan ts = (new DateTime(dt.Year, dt.Month, dt.Day, 0, 0, 0) - new DateTime(1970, 1, 1, 0, 0, 0)); double unixTime = ts.TotalSeconds; var url = string.Format("https://www.zacks.com/includes/classes/z2_class_calendarfunctions_data.php?calltype=eventscal&date={0}&type=1&search_trigger=0&0.5&_=1234567890000", unixTime); var _remove = "www.zacks.com/stock/quote/"; using (WebClient wc = new WebClient()) { wc.Headers.Add("Referer", "https://www.zacks.com/"); wc.Headers.Add("UserAgent", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:62.0) Gecko/20100101 Firefox/62.0"); wc.Headers.Add("Content-Type", "text/html, application/xhtml+xml, image/jxr, */*"); string text = wc.DownloadString(new Uri(url)); foreach (var m in Regex.Matches(text, _remove + "[A-Z]{1,4}")) lst.Add(m.ToString().Replace(_remove, "")); } return lst; } //create indicators and other objects here, this is executed prior to the main trading loop public override void Initialize(BarHistory bars) { WriteToDebugLog("Upcoming earnings:"); foreach (var e in list) { WriteToDebugLog(e.ToString()); } } //execute the strategy rules here, this is executed once for each bar in the backtest history public override void Execute(BarHistory bars, int idx) { var sym = bars.Symbol; if (!HasOpenPosition(bars, PositionType.Long)) { //code your buy conditions here if (idx == bars.DateTimes.Count - 1) { var dt = bars.DateTimes[idx].Date; bool condition = list.Any(item => (item == sym)); if (condition) { WriteToDebugLog(string.Format("Will not trade: {0} has upcoming earnings date", sym)); } } } else { //code your sell conditions here } } //declare private variables below } } [/CODE]

Thanks for the snippet. I'll take a closer look

Thanks for the snippet. I'll take a closer look

Last minute fixes:

  1. If you run your model before U.S. market open make this change:
//var dt = DateTime.Today.AddDays(1 + 1);
var dt = DateTime.Today.AddDays(1);

The former line should do if you run having collected data after the close.

  1. If your Universe contains 5-letter symbols then edit this line:
//foreach (var m in Regex.Matches(text, _remove + "[A-Z]{1,4}"))
foreach (var m in Regex.Matches(text, _remove + "[A-Z]{1,5}"))
Last minute fixes: 1. If you run your model before U.S. market open make this change: [CODE] //var dt = DateTime.Today.AddDays(1 + 1); var dt = DateTime.Today.AddDays(1); [/CODE] The former line should do if you run having collected data after the close. 2. If your Universe contains 5-letter symbols then edit this line: [CODE] //foreach (var m in Regex.Matches(text, _remove + "[A-Z]{1,4}")) foreach (var m in Regex.Matches(text, _remove + "[A-Z]{1,5}")) [/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.