Search Framework:
TimeSeries
Namespace: WealthLab.Core
Parent: TimeSeriesBase
Descendants: IndicatorBase

The TimeSeries class manages a series of numeric double values keyed off a list of DateTimes. The two primary properties of TimeSeries are:

  • Values - A list of floating point double values
  • DateTimes - a list of DateTime values

These two lists are always synchronized.

When you wish to access values from the TimeSeries' Values property, you need not specify the Values property name. Instead, you can use the default indexer property. For example, these two lines of code are functionally equivalent:

//assume ts is an instance of a TimeSeries
double n1 = ts.Values[10];
double n2 = ts[10];
Constructors
TimeSeries
public TimeSeries()
public TimeSeries(List<DateTime> dateTimes, bool fillNaN = true)
public TimeSeries(List<DateTime> dateTimes, double fillValue)

The TimeSeries class has three constructors. The first, parameterless constructor, creates a TimeSeries instance that manages its own DateTimes. When adding values to this instance, use the Add method that includes a DateTime parameter. The second and third constructors include a dateTimes parameter. This List of DateTimes becomes the TimeSeries' DateTimes property. The second constructor, by default, pre-populates the TimeSeries Values with Double.Nan. You can disable this by passing false in the fillNaN parameter. If you do so, be sure to Add values to the TimeSeries yourself (using the version that adds values only, not DateTimes), a number of values equal to the number of DateTimes. The last constructor accept a fillValue parameter which determines what value the TimeSeries will be pre-populated with.



Members
Abs
public TimeSeries Abs()

Returns a new TimeSeries that is the absolute value of the source TimeSeries.

Example Code
using WealthLab.Backtest;
using System;
using WealthLab.Core;
using WealthLab.Indicators;
using System.Drawing;
using System.Collections.Generic;

namespace WealthScript4
{
	public class MyStrategy : UserStrategyBase
	{
		TimeSeries _barsAboveSMA;

		public override void Initialize(BarHistory bars)
		{
			//calculate the number of days the Close is above the 10-day SMA for the last 20 bars
			TimeSeries sma = SMA.Series(bars.Close, 10);
			PlotTimeSeriesLine(sma, "SMA(10)", "Price", WLColor.Blue, 2);
			TimeSeries tmp = bars.Close - sma;
			tmp = tmp / tmp.Abs();
			tmp = (tmp + 1d) / 2d;

			_barsAboveSMA = tmp.Sum(20);
			PlotTimeSeries(_barsAboveSMA, "Closes Above SMA for last 20 bars", "BarsAbovePane", WLColor.Blue, PlotStyle.Histogram);
		}

		public override void Execute(BarHistory bars, int idx)
		{

		}
	}
}

Cache
public Dictionary<string, object> Cache

A generic cache Dictionary that can be used to store objects during a backtest run. Certain indicators such as ADX, ADXR, DIPlus and DIMinus use the Cache to store the collection of indicators that are all calculated together as a group.

Example Code
using WealthLab.Backtest;
using System;
using WealthLab.Core;
using WealthLab.Indicators;
using WealthLab.ChartWPF;
using System.Drawing;
using System.Collections.Generic;

namespace WealthLab
{
	public class MyStrategy : UserStrategyBase
	{
		//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);
			//store the RSI indicator in the cache, so it can be accessed when PreExecute is called
			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)
		{
			//store the symbols' RSI value in their BarHistory instances
			foreach (BarHistory bh in participants)
			{
				RSI rsi = (RSI)bh.Cache["RSI"];
				int idx = GetCurrentIndex(bh);  //this returns the index of the BarHistory for the bar currently being processed
				double rsiVal = rsi[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)
		{
			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 RSI rsi;
	}
}

Count
public int Count

Returns the number of items contained in the time series. The DateTimes property list contains this many DateTimes. For TimeSeries, the Values property contains this many values. And for BarHistory, the Open, High, Low, Close and Volume properties (all instances of the TimeSeries class) contain this many values.

Example Code
using WealthLab.Backtest;
using System;
using WealthLab.Core;
using WealthLab.Indicators;
using WealthLab.ChartWPF;
using System.Drawing;
using System.Collections.Generic;

namespace WealthLab
{
	public class MyStrategy : UserStrategyBase
	{
		//do some analysis of 10, 20, 50, 100 bars ago
		public override void Initialize(BarHistory bars)
		{
			SetTextDrawingOptions(WLColor.Beige, WLColor.Black, 1);
			ReportGainAfterNBars(bars, 10);
			ReportGainAfterNBars(bars, 20);
			ReportGainAfterNBars(bars, 50);
			ReportGainAfterNBars(bars, 100);
		}
		private void ReportGainAfterNBars(BarHistory bars, int numBars)
		{
			int idx = bars.Count - numBars;
			if (idx >= 0)
			{
				double gain = bars.Close[bars.Count - 1] - bars.Close[idx];
				gain = gain * 100.0 / bars.Close[idx];
				DrawBarAnnotation(numBars + " bar gain: " + gain.ToString("N2") + "%", idx, false, WLColor.Black, 7);
				DrawLine(idx, bars.Close[idx], bars.Count - 1, bars.Close[bars.Count - 1], WLColor.Gray, 2, LineStyle.Dotted);
			}
		}

		public override void Execute(BarHistory bars, int idx)
		{
		}
	}
}

CrossesOver
public bool CrossesOver(double value, int idx)
public bool CrossesOver(TimeSeries ts, int idx)

Returns true if the TimeSeries value "crosses over" a specific value or ts at the specified bar index. A "cross over" occurs when the TimeSeries' value is greater than the value or ts, and it had previously been below.

Example Code
using WealthLab.Backtest;
using System;
using WealthLab.Core;
using WealthLab.Indicators;
using WealthLab.ChartWPF;
using System.Drawing;
using System.Collections.Generic;

namespace WealthScript4
{
	public class MyStrategy : UserStrategyBase
	{
		//Create indicators
		public override void Initialize(BarHistory bars)
		{
			macd = new MACD(bars.Close);
			macdHist = new MACDHist(bars.Close, 10);
			PlotIndicatorLine(macd);
			PlotIndicator(macdHist, WLColor.Blue, PlotStyle.Histogram);
		}

		//flag the bars where the MACD Histogram crosses above and below zero
		public override void Execute(BarHistory bars, int idx)
		{
			if (macdHist.CrossesOver(0, idx))
				SetBarColor(bars, idx, WLColor.Green);
			else if (macdHist.CrossesUnder(0, idx))
				SetBarColor(bars, idx, WLColor.Red);
			else
				SetBarColor(bars, idx, WLColor.Silver);
		}

		//declare variables
		private MACD macd;
		private MACDHist macdHist;
	}
}

CrossesUnder
public bool CrossesUnder(double value, int idx)
public bool CrossesUnder(TimeSeries ts, int idx)

Returns true if the TimeSeries value "crosses under" a specific value or ts at the specified bar index. A "cross under" occurs when the TimeSeries' value is less than the value or ts, and it had previously been above.

Example Code
using WealthLab.Backtest;
using System;
using WealthLab.Core;
using WealthLab.Indicators;
using WealthLab.ChartWPF;
using System.Drawing;
using System.Collections.Generic;

namespace WealthScript4
{
	public class MyStrategy : UserStrategyBase
	{
		//Create indicators
		public override void Initialize(BarHistory bars)
		{
			macd = new MACD(bars.Close);
			macdHist = new MACDHist(bars.Close, 10);
			PlotIndicatorLine(macd);
			PlotIndicator(macdHist, WLColor.Blue, PlotStyle.Histogram);
		}

		//flag the bars where the MACD Histogram crosses above and below zero
		public override void Execute(BarHistory bars, int idx)
		{
			if (macdHist.CrossesOver(0, idx))
				SetBarColor(bars, idx, WLColor.Green);
			else if (macdHist.CrossesUnder(0, idx))
				SetBarColor(bars, idx, WLColor.Red);
			else
				SetBarColor(bars, idx, WLColor.Silver);
		}

		//declare variables
		private MACD macd;
		private MACDHist macdHist;
	}
}

DateTimes
public virtual List<DateTime> DateTimes

A list of DateTime objects that represents the date/time of each item in the time series. The derived classes maintain other lists that are synchronized with DateTimes. TimeSeries maintains a list of floating point double Values. BarHistory has TimeSeries properties for Open, High, Low, Close, and Volume. All of these other lists will have exactly the same number of elements as the DateTImes list, allowing you to use the same index to access their elements interchangably.

Example Code
using WealthLab.Backtest;
using System;
using WealthLab.Core;
using WealthLab.Indicators;
using WealthLab.ChartWPF;
using System.Drawing;
using System.Collections.Generic;

namespace WealthLab
{
    public class MyStrategy : UserStrategyBase
    {
        //annotate the 100 bar high and low
        public override void Initialize(BarHistory bars)
        {
			double lowVal = Double.MaxValue;
			double highVal = Double.MinValue;
			int lowIdx = -1;
			int highIdx = -1;
			for (int n = bars.Count - 1; n > bars.Count - 100; n--)
			{
				if (n >= 0)
				{
					if (bars.Low[n] < lowVal)
					{
						lowVal = bars.Low[n];
						lowIdx = n;
					}
					if (bars.High[n] > highVal)
					{
						highVal = bars.High[n];
						highIdx = n;
					}
				}
			}
			if (lowIdx >= 0)
			{
				string txt = "100 bar low occurred on " + bars.DateTimes[lowIdx].ToLongDateString();
				DrawBarAnnotation(txt, lowIdx, false, WLColor.Red, 8);
			}
			if (highIdx >= 0)
			{
				string txt = "100 bar high occurred on " + bars.DateTimes[highIdx].ToLongDateString();
				DrawBarAnnotation(txt, highIdx, true, WLColor.Green, 8);
			}
        }

        public override void Execute(BarHistory bars, int idx)
        {
        }
    }
}

Description
public string Description

A text description of the TimeSeries. You typically won't need to access or set this directly. Indicators set this value automatically. And, when you Plot a plain vanilla TimeSeries in a C# coded Strategy, you provide a "name" parameter which is used to set the Description property.

Example Code
using WealthLab.Backtest;
using System;
using WealthLab.Core;
using WealthLab.Indicators;
using WealthLab.ChartWPF;
using System.Drawing;
using System.Collections.Generic;

namespace WealthScript5
{
	public class MyStrategy : UserStrategyBase
	{
		//Display indicator's description
		public override void Initialize(BarHistory bars)
		{
			ADX adx = new ADX(bars, 20);
			PlotIndicatorLine(adx);
			DrawHeaderText("Plotted " + adx.Description);
		}

		public override void Execute(BarHistory bars, int idx)
		{
		}
	}
}

EndDate
public DateTime EndDate

Returns the last DateTime in the DateTimes property list. If the list is empty, returns DateTime.MaxValue.

Example Code
using WealthLab.Backtest;
using System;
using WealthLab.Core;
using WealthLab.Indicators;
using WealthLab.ChartWPF;
using System.Drawing;
using System.Collections.Generic;

namespace WealthLab
{
    public class MyStrategy : UserStrategyBase
    {
        //create indicators and other objects here, this is executed prior to the main trading loop
        public override void Initialize(BarHistory bars)
        {
			SetTextDrawingOptions(WLColor.AliceBlue, WLColor.Black, 1);
			string s = "Chart end date is " + bars.EndDate.ToLongDateString();
			DrawHeaderText(s);   
        }
	    
        public override void Execute(BarHistory bars, int idx)
        {
        }
    }
}

Fill
public void Fill(double value)

Fills the TimeSeries with the specified value.


FillNaN
public void FillNaN()

Fills the TimeSeries values with Double.NaN.


FirstValidIndex
public int FirstValidIndex

Returns the first index which contains valid data (not Double.NaN). WealthLab indicators populate initial values with Double.NaN until a valid value can be calculated. For example, a Simple Moving Average (SMA) indicator with a period of 20 will have its first 19 values set to Double.NaN.

Example Code
using WealthLab.Backtest;
using System;
using WealthLab.Core;
using WealthLab.Indicators;
using WealthLab.ChartWPF;
using System.Drawing;
using System.Collections.Generic;

namespace WealthLab
{
	public class MyStrategy : UserStrategyBase
	{
		//create indicators and other objects here, this is executed prior to the main trading loop
		public override void Initialize(BarHistory bars)
		{
			sma = new SMA(bars.Close, 200);
			PlotIndicatorLine(sma);
		}

		//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 (idx < sma.FirstValidIndex)
				SetBackgroundColor(bars, idx, c);
		}

		//declare private variables below
		SMA sma;
		WLColor c = WLColor.FromArgb(32, 128, 0, 0);
	}
}

GetHighest
public double GetHighest(int bar, int range)

Returns the highest value in the TimeSeries, looking back starting from bar, for a number of values equal to range.

Example Code
using WealthLab.Backtest;
using System;
using WealthLab.Core;
using WealthLab.Indicators;
using WealthLab.ChartWPF;
using System.Drawing;
using System.Collections.Generic;

namespace WealthLab
{
	public class MyStrategy : UserStrategyBase
	{
		//Flag 60 bar high and low
		public override void Initialize(BarHistory bars)
		{
			if (bars.Count < 60)
				return;
			double high60 = bars.High.GetHighest(bars.Count - 1, 60);
			double low60 = bars.Low.GetLowest(bars.Count - 1, 60);
			WLColor c = WLColor.FromArgb(32, 128, 128, 128);
			FillRectangle(bars.Count - 60, high60, bars.Count - 1, low60, c);
		}

		public override void Execute(BarHistory bars, int idx)
		{
		}
	}
}

GetHighestBar
public int GetHighestBar(int bar, int range)

Returns the bar number on which the highest value in the TimeSeries was found, looking back starting from bar, for a number of values equal to range.


GetLowest
public double GetLowest(int bar, int range)

Returns the lowest value in the TimeSeries, looking back starting from bar, for a number of values equal to range.

Example Code
using WealthLab.Backtest;
using System;
using WealthLab.Core;
using WealthLab.Indicators;
using WealthLab.ChartWPF;
using System.Drawing;
using System.Collections.Generic;

namespace WealthLab
{
	public class MyStrategy : UserStrategyBase
	{
		//Flag 60 bar high and low
		public override void Initialize(BarHistory bars)
		{
			if (bars.Count < 60)
				return;
			double high60 = bars.High.GetHighest(bars.Count - 1, 60);
			double low60 = bars.Low.GetLowest(bars.Count - 1, 60);
			WLColor c = WLColor.FromArgb(32, 128, 128, 128);
			FillRectangle(bars.Count - 60, high60, bars.Count - 1, low60, c);
		}

		public override void Execute(BarHistory bars, int idx)
		{
		}
	}
}

GetLowestBar
public int GetLowestBar(int bar, int range)

Returns the bar number on which the lowest value in the TimeSeries was found, looking back starting from bar, for a number of values equal to range.


IndexOf
public int IndexOf(DateTime dt, bool exactMatchOnly = false)

Returns the index into the DateTimes property list that contains the DateTime specified in the dt parameter. If exactMatchOnly is false, and an exact match does not exist, the method returns the index following the closest matching DateTime, unless the requested DateTime falls completely outside the range of the list, in which case it returns -1. If exactMatchOnly is true and no exact match exists, the method returns -1.

Example Code
using WealthLab.Backtest;
using System;
using WealthLab.Core;
using WealthLab.Indicators;
using WealthLab.ChartWPF;
using System.Drawing;
using System.Collections.Generic;

namespace WealthLab
{
    public class MyStrategy : UserStrategyBase
    {
        //whoever figures out the significance of this date will receive a one month free Premium membership credit
        public override void Initialize(BarHistory bars)
        {
			WLColor green = WLColor.FromArgb(128, 0, 255, 0);
	        if (bars.Count == 0)
				return;
			for (int yr = bars.StartDate.Year; yr <= bars.EndDate.Year; yr++)
			{
				DateTime dt = new DateTime(yr, 8, 25);
				int idx = bars.IndexOf(dt);
				if (idx >= 0)
					SetBackgroundColor(bars, idx, green);
			}
        }

        public override void Execute(BarHistory bars, int idx)
        {
        }
    }
}

IsUpToDate
public virtual bool IsUpToDate(DateTime endDate)

Returns whether the historical data appears up to date, as of the DateTime specified in the endDate parameter. The method accounts for weekends, market holidays, and market open and closing times, when called from a BarHistory object.

Example Code
using WealthLab.Backtest;
using System;
using WealthLab.Core;
using WealthLab.Indicators;
using WealthLab.ChartWPF;
using System.Drawing;
using System.Collections.Generic;

namespace WealthLab
{
    public class MyStrategy : UserStrategyBase
    {
        //report on whether data needs updating
        public override void Initialize(BarHistory bars)
        {
			string txt;
			WLColor c;
			if (bars.IsUpToDate(DateTime.Now))
			{
				txt = "Data appears to be up to date";
				c = WLColor.Green;
			}
			else
			{
				txt = "Data appears to need updating!";
				c = WLColor.Red;
			}
			DrawHeaderText(txt, c);
        }

        public override void Execute(BarHistory bars, int idx)
        {
        }
    }
}

LogReturn
public TimeSeries LogReturn(int period)

Returns a new TimeSeries containing the log returns of a TimeSeries for the specified period.


MassageColors
public bool MassageColors

Indicates whether the TimeSeries' plotted color should be adjusted so that it can be seen better in the currently selected WL8 Theme (Light or Dark). If you set a value, it overrides the default value that is established under the WL8 Theme menu.


ReadFromBinaryFile
public void ReadFromBinaryFile(string fileName, DateTime? startDate = null, DateTime? endDate = null, int maxBars = 0)

Populates the TimeSeries with data from a file created by WriteToBinaryFile.

Example Code
using WealthLab.Backtest;
using System;
using WealthLab.Core;
using WealthLab.Data;
using WealthLab.Indicators;
using System.Collections.Generic;

namespace WealthScript223
{
	public class MyStrategy : UserStrategyBase
	{
		//create indicators and other objects here, this is executed prior to the main trading loop
		public override void Initialize(BarHistory bars)
		{
			//read RSI data from a file
			string fileName = System.IO.Path.Join(WLHost.Instance.DataFolder, "RSIData.WL8");
			TimeSeries ts = new TimeSeries();
			ts.ReadFromBinaryFile(fileName);
			
			//sychronize the TimeSeries with the current bars
			ts = TimeSeriesSynchronizer.Synchronize(ts, bars);
			PlotTimeSeries(ts, "RSI", "RSI");
		}

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

ReadFromFile
public void ReadFromFile(string fileName, DateTime? startDate = null, DateTime? endDate = null, int maxBars = 0, string dateTimeFormat = "yyyyMMddHHmmss", char separator = ',', bool exactMatchOnly = false, int skipLines = 0)

Populates a TimeSeries with data from a file. The file should contain 1 row of data per bar. The format of each row can be:

Date, Value
where Value is a CultureInfo.Invariant number, i.e., "." is the decimal character - always.

Using only the fileName parameter, ReadFromFile will read a file created by WriteToFile. By default yyyyMMddHHmmss is the DateTime format and comma (",") is used as separator character. If the format of your data file is different, additional control is provided by specifying custom separator (data type char) and DateTime Format string (according to this Microsoft KB article). However, only a period "." can be used as the decimal character for Value.

Example Code
using WealthLab.Backtest;
using System;
using WealthLab.Core;
using WealthLab.Data;
using WealthLab.Indicators;
using System.Collections.Generic;

namespace WealthScript221
{
	public class MyStrategy : UserStrategyBase
	{
		//create indicators and other objects here, this is executed prior to the main trading loop
		public override void Initialize(BarHistory bars)
		{
			//read RSI data from a file
			string fileName = System.IO.Path.Join(WLHost.Instance.DataFolder, "RSIData.txt");
			TimeSeries ts = new TimeSeries();
			ts.ReadFromFile(fileName);
			
			//sychronize the TimeSeries with the current bars
			ts = TimeSeriesSynchronizer.Synchronize(ts, bars);
			PlotTimeSeries(ts, "RSI", "RSI");
		}

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

ReturnNDays
public double ReturnNDays(int idx, int days)

Calculates the percentage gain from the point in time specified by idx, going back the specified number of calendar days.

Example Code
using WealthLab.Backtest;
using WealthLab.Core;
using System.Drawing;

namespace WealthLab
{
	public class MyStrategy1 : UserStrategyBase
	{
		//create indicators and other objects here, this is executed prior to the main trading loop
		public override void Initialize(BarHistory bars)
		{
			double oneYearReturn = bars.Close.ReturnNDays(bars.Count - 1, 365);
			SetTextDrawingOptions(WLColor.White, WLColor.Black, 1);
			DrawHeaderText("One year return of " + bars.Symbol + " is: " + oneYearReturn.ToString("N2") + "%", WLColor.Black, 12);
		}

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

StartDate
public DateTime StartDate

Returns the first DateTime in the DateTimes property list. If the list is empty, returns DateTime.MinValue.

Example Code
using WealthLab.Backtest;
using System;
using WealthLab.Core;
using WealthLab.Indicators;
using WealthLab.ChartWPF;
using System.Drawing;
using System.Collections.Generic;

namespace WealthLab
{
    public class MyStrategy : UserStrategyBase
    {
        //create indicators and other objects here, this is executed prior to the main trading loop
        public override void Initialize(BarHistory bars)
        {
			SetTextDrawingOptions(WLColor.AliceBlue, WLColor.Black, 1);
			string s = "Chart start date is " + bars.StartDate.ToLongDateString();
			DrawHeaderText(s);   
        }
	    
        public override void Execute(BarHistory bars, int idx)
        {
        }
    }
}

Sum
public TimeSeries Sum(int period)

Returns a new TimeSeries that sums the TimeSeries over the specified period.

Example Code
using WealthLab.Backtest;
using System;
using WealthLab.Core;
using WealthLab.Indicators;
using System.Drawing;
using System.Collections.Generic;

namespace WealthScript4
{
	public class MyStrategy : UserStrategyBase
	{
		TimeSeries _barsAboveSMA;
		TimeSeries _barsAboveSMA2;

		public override void Initialize(BarHistory bars)
		{
			//calculate the number of days the Close is above the 10-day SMA for the last 20 bars
			TimeSeries sma = SMA.Series(bars.Close, 10);
			PlotTimeSeriesLine(sma, "SMA(10)", "Price", WLColor.Blue, 2);
			TimeSeries tmp = bars.Close - sma;
			tmp = tmp / tmp.Abs();
			tmp = (tmp + 1d) / 2d;

			//using the static method
			_barsAboveSMA = TimeSeries.Sum(tmp, 20);

			//or using the memeber method
			_barsAboveSMA2 = tmp.Sum(20);

			PlotTimeSeries(_barsAboveSMA, "Bars Above SMA 20 bars", "BarsAbovePane", WLColor.Blue, PlotStyle.Histogram);
			PlotTimeSeries(_barsAboveSMA2, "Bars Above Method 2", "BarsAbovePane", WLColor.Red, PlotStyle.ThickLine);
		}

		public override void Execute(BarHistory bars, int idx)
		{

		}
	}
}

TimeSpan
public TimeSpan TimeSpan

Returns a TimeSpan that encompasses the range of the DateTimes in the DateTimes property list. If there are fewer than two DateTimes in the list, returns a zero TimeSpan.

Example Code
using WealthLab.Backtest;
using System;
using WealthLab.Core;
using WealthLab.Indicators;
using WealthLab.ChartWPF;
using System.Drawing;
using System.Collections.Generic;

namespace WealthLab
{
    public class MyStrategy : UserStrategyBase
    {
        //display total amount of time covered in chart
        public override void Initialize(BarHistory bars)
        {
			string txt = "Chart data comprised of " + bars.TimeSpan.TotalHours.ToString("N0") + " total hours";
			DrawHeaderText(txt);
        }

        public override void Execute(BarHistory bars, int idx)
        {
        }
    }
}

TurnsDown
public bool TurnsDown(int idx)

Returns true if the TimeSeries "turns down" at the specified bar index. A TimeSeries "turns down" if its value decreases after an advance.

Example Code
using WealthLab.Backtest;
using System;
using WealthLab.Core;
using WealthLab.Indicators;
using WealthLab.ChartWPF;
using System.Drawing;
using System.Collections.Generic;

namespace WealthLab
{
	public class MyStrategy : UserStrategyBase
	{
		//Create indicators
		public override void Initialize(BarHistory bars)
		{
			macd = new MACD(bars.Close);
			macdHist = new MACDHist(bars.Close, 5);
			PlotIndicatorLine(macd);
			PlotIndicator(macdHist, WLColor.Blue, PlotStyle.Histogram);
		}

		//flag the bars where the MACD Histogram turns up and down
		public override void Execute(BarHistory bars, int idx)
		{
			if (macdHist.TurnsUp(idx))
				SetBarColor(bars, idx, WLColor.Green);
			else if (macdHist.TurnsDown(idx))
				SetBarColor(bars, idx, WLColor.Red);
			else
				SetBarColor(bars, idx, WLColor.Silver);
		}

		//declare variables
		private MACD macd;
		private MACDHist macdHist;
	}
}

TurnsUp
public bool TurnsUp(int idx)

Returns true if the TimeSeries "turns up" at the specified bar index. A TimeSeries "turns up" if its value increases after a decline.

Example Code
using WealthLab.Backtest;
using System;
using WealthLab.Core;
using WealthLab.Indicators;
using WealthLab.ChartWPF;
using System.Drawing;
using System.Collections.Generic;

namespace WealthLab
{
	public class MyStrategy : UserStrategyBase
	{
		//Create indicators
		public override void Initialize(BarHistory bars)
		{
			macd = new MACD(bars.Close);
			macdHist = new MACDHist(bars.Close, 5);
			PlotIndicatorLine(macd);
			PlotIndicator(macdHist, WLColor.Blue, PlotStyle.Histogram);
		}

		//flag the bars where the MACD Histogram turns up and down
		public override void Execute(BarHistory bars, int idx)
		{
			if (macdHist.TurnsUp(idx))
				SetBarColor(bars, idx, WLColor.Green);
			else if (macdHist.TurnsDown(idx))
				SetBarColor(bars, idx, WLColor.Red);
			else
				SetBarColor(bars, idx, WLColor.Silver);
		}

		//declare variables
		private MACD macd;
		private MACDHist macdHist;
	}
}

UserData
public object UserData

Allows you to store ad-hoc data in a property of a BarHistory or TimeSeries instance. You can store object instances, or primitive values like ints or doubles.


UserDataAsDouble
public double UserDataAsInt

Allows you to access a value you stored in a BarHistory or TimeSeries UserData property as a double.


UserDataAsInt
public int UserDataAsInt

Allows you to access a value you stored in a BarHistory or TimeSeries UserData property as an int.


Values
public List<double> Values

Returns the list of values. You typically do not need to reference the Values property directly, since the TimeSeries class can also access its values via its default property,

Example Code
using WealthLab.Backtest;
using System;
using WealthLab.Core;
using WealthLab.Indicators;
using WealthLab.ChartWPF;
using System.Drawing;
using System.Collections.Generic;

namespace WealthLab
{
	public class MyStrategy : UserStrategyBase
	{
		//Show how to use the "default" property of TimeSeries
		public override void Initialize(BarHistory bars)
		{
			TimeSeries closingPrices = bars.Close;
			int lastIdx = closingPrices.Count - 1;

			//doing this ...
			double lastCloseA = closingPrices[lastIdx];

			//is the same as doing this, only shorter ...
			double lastCloseB = closingPrices.Values[lastIdx];

			DrawHeaderText("These two values should be equal: " + lastCloseA.ToString("N2") + " and " + lastCloseB.ToString("N2"));
		}

		public override void Execute(BarHistory bars, int idx)
		{
		}
	}
}

WriteToBinaryFile
public void WriteToBinaryFile(string fileName)

Creates a binary file containing the TimeSeries' date and value data. Binary files are usually optimal for both storage and access speed.

Use ReadFromBinaryFile to read a file created by WriteToBinaryFile.

Example Code
using WealthLab.Backtest;
using System;
using WealthLab.Core;
using WealthLab.Data;
using WealthLab.Indicators;
using System.Collections.Generic;

namespace WealthScript12
{
	public class MyStrateGy : UserStrategyBase
	{
		//create indicators and other objects here, this is executed prior to the main trading loop
		public override void Initialize(BarHistory bars)
		{			
			RSI rsi = RSI.Series(bars.Close, 14);
			PlotIndicator(rsi); 
			
			//Write RSI data to a binary file
			string fileName = System.IO.Path.Join(WLHost.Instance.DataFolder, "RSIData.wl8");
			rsi.WriteToBinaryFile(fileName);
			
		}

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

WriteToFile
public void WriteToFile(string fileName)

Creates an ASCII text file with the TimeSeries data. The first row will be an integer file version number for internal use. The rest of the file will contain 1 row of data per bar with the format:

Date, Value
where the Date format is yyyyMMddHHmmss and Value is a CultureInfo.Invariant number, i.e., "." is the decimal character - always.

Use ReadFromFile to read a file created by WriteToFile.

Example Code
using WealthLab.Backtest;
using System;
using WealthLab.Core;
using WealthLab.Data;
using WealthLab.Indicators;
using System.Collections.Generic;

namespace WealthScript12
{
	public class MyStrateGy : UserStrategyBase
	{
		//create indicators and other objects here, this is executed prior to the main trading loop
		public override void Initialize(BarHistory bars)
		{
			RSI rsi = RSI.Series(bars.Close, 14);
			PlotIndicator(rsi); 
			
			//Write RSI data to a file
			string fileName = System.IO.Path.Join(WLHost.Instance.DataFolder, "RSIData.txt");
			rsi.WriteToFile(fileName);			
		}

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


Operator Overloads
operator-
public static TimeSeries operator -(TimeSeries s)

Returns a new TimeSeries that contains the unary negative of the TimeSeries.


operator*
public static TimeSeries operator *(TimeSeries s1, TimeSeries s2)
public static TimeSeries operator *(TimeSeries s, double value)
public static TimeSeries operator *(double value, TimeSeries s)

The overloaded multiplication operator allows you to perform multiplication on two TimeSeries instances, or on a TimeSeries and a constant floating point double value.

Example Code
using WealthLab.Backtest;
using System;
using WealthLab.Core;
using WealthLab.Indicators;
using WealthLab.ChartWPF;
using System.Drawing;
using System.Collections.Generic;

namespace WealthLab
{
	public class MyStrategy : UserStrategyBase
	{
		//Create and plot a simple indicator that multiples closing price by volume
		public override void Initialize(BarHistory bars)
		{
			TimeSeries vc = bars.Close * bars.Volume;
			PlotTimeSeries(vc, "Volume x Close", "VC", WLColor.DarkOrchid, PlotStyle.ThickHistogram);
		}

		public override void Execute(BarHistory bars, int idx)
		{
		}
	}
}

operator/
public static TimeSeries operator /(TimeSeries s1, TimeSeries s2)
public static TimeSeries operator /(TimeSeries s, double value)
public static TimeSeries operator /(double value, TimeSeries s)

The overloaded division operator allows you to perform division on two TimeSeries instances, or on a TimeSeries and a constant floating point double value. In the case of division by zero, a zero is placed in the resulting values index.

Example Code
using WealthLab.Backtest;
using System;
using WealthLab.Core;
using WealthLab.Indicators;
using WealthLab.ChartWPF;
using System.Drawing;
using System.Collections.Generic;

namespace WealthLab
{
	public class MyStrategy : UserStrategyBase
	{
		//Create and plot a simple indicator that gets the percentage of how much close is above/below average 5 bar close
		public override void Initialize(BarHistory bars)
		{
			SMA sma = new SMA(bars.Close, 5);
			PlotIndicatorLine(sma, WLColor.DarkBlue);
			TimeSeries cma = 100.0 - (sma * 100.0 / bars.Close);
			PlotTimeSeries(cma, "Volume x Close", "SMA", WLColor.DarkBlue, PlotStyle.ThickLine);
		}

		public override void Execute(BarHistory bars, int idx)
		{
		}
	}
}

operator&
public static TimeSeries operator &(TimeSeries s1, TimeSeries s2)

Returns a new TimeSeries containing 1's where values of both of the TimeSeries being compared are greater than zero, and -1 where they are not.

Example Code
using WealthLab.Backtest;
using System;
using WealthLab.Core;
using WealthLab.Indicators;
using WealthLab.ChartWPF;
using System.Drawing;
using System.Collections.Generic;

namespace WealthLab
{
	public class MyStrategy : UserStrategyBase
	{
		//create indicators and other objects here, this is executed prior to the main trading loop
		public override void Initialize(BarHistory bars)
		{
			//highlight areas where price is above 2 moving averages
			SMA smaLong = new SMA(bars.Close, 200);
			SMA smaShort = new SMA(bars.Close, 50);
			TimeSeries aboveBoth = bars.Close > smaLong & bars.Close > smaShort;
			PlotIndicator(smaLong);
			PlotIndicator(smaShort);
			PlotTimeSeries(aboveBoth, "Above 2 SMAs", "Price", WLColor.DarkGreen, PlotStyle.BooleanDots);
		}

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

operator+
public static TimeSeries operator +(TimeSeries s1, TimeSeries s2)
public static TimeSeries operator +(TimeSeries s, double value)
public static TimeSeries operator +(double value, TimeSeries s)

The overloaded addition operator allows you to perform addition on two TimeSeries instances, or on a TimeSeries and a constant floating point double value.

Example Code
using WealthLab.Backtest;
using System;
using WealthLab.Core;
using WealthLab.Indicators;
using WealthLab.ChartWPF;
using System.Drawing;
using System.Collections.Generic;

namespace WealthLab
{
	public class MyStrategy : UserStrategyBase
	{
		//Take average of QID/QLD rate of changes and plot it
		public override void Initialize(BarHistory bars)
		{
			BarHistory qld = GetHistory(bars, "QLD");
			ROC qldRoc = new ROC(qld.Close, 20);
			BarHistory qid = GetHistory(bars, "QID");
			ROC qidRoc = new ROC(qid.Close, 20);
			TimeSeries rocAvg = (qldRoc + qidRoc) / 2;
			PlotTimeSeries(rocAvg, "QLD/QID ROC average", "Avg", WLColor.Navy, PlotStyle.ThickHistogram);
		}

		public override void Execute(BarHistory bars, int idx)
		{
		}
	}
}

operator<
public static TimeSeries operator <(TimeSeries s1, TimeSeries s2)
public static TimeSeries operator <(TimeSeries s1, double value)

Returns a new TimeSeries containing 1's where the first TimeSeries argument is less than the second, and -1 where it is not. You can compare TimeSeries against another TimeSeries or a constant numeric value.


operator<<
public static TimeSeries operator <<(TimeSeries ser, int period)

Returns a new TimeSeries that is shifted backward in time a number of bars specified in the period parameter. The shifted series is not safe to use in backtesting, because the shifted series introduces future information to the backtester. The right side of the shifted series will appear blank on the chart, these values contain Double.NaN.

Example Code
using WealthLab.Backtest;
using System;
using WealthLab.Core;
using WealthLab.Indicators;
using WealthLab.ChartWPF;
using System.Drawing;
using System.Collections.Generic;

namespace WealthLab
{
	public class MyStrategy : UserStrategyBase
	{
		//Create an idealized "centered" moving average
		public override void Initialize(BarHistory bars)
		{
			SMA sma = new SMA(bars.Close, 13);
			TimeSeries shifted = sma << 6;
			PlotTimeSeries(shifted, "Idealized SMA", "Price", WLColor.DarkOrange, PlotStyle.ThickLine);
		}

		public override void Execute(BarHistory bars, int idx)
		{
		}
	}
}

operator<=
public static TimeSeries operator <=(TimeSeries s1, TimeSeries s2)
public static TimeSeries operator <=(TimeSeries s1, double value)

Returns a new TimeSeries containing 1's where the first TimeSeries argument is less than or equal to the second, and -1 where it is not. You can compare TimeSeries against another TimeSeries or a constant numeric value.


operator>
public static TimeSeries operator >(TimeSeries s1, TimeSeries s2)
public static TimeSeries operator >(TimeSeries s1, double value)

Returns a new TimeSeries containing 1's where the first TimeSeries argument is greater than the second, and -1 where it is not. You can compare TimeSeries against another TimeSeries or a constant numeric value.


operator>=
public static TimeSeries operator >=(TimeSeries s1, TimeSeries s2)
public static TimeSeries operator >=(TimeSeries s1, double value)

Returns a new TimeSeries containing 1's where the first TimeSeries argument is greater than or equal to the second, and -1 where it is not. You can compare TimeSeries against another TimeSeries or a constant numeric value.


operator>>
public static TimeSeries operator >>(TimeSeries ser, int period)

Returns a new TimeSeries that is shifted forward in time a number of bars specified in the period parameter. The shifted series is safe to use in backtesting, because the operation effectively delays the information in the TimeSeries. For example, imagine you shift a TimeSeries to the right by one bar, and examine a value at index X. The actual data you are seeing is the data that occupied X-1 of the original TimeSeries.

Example Code
using WealthLab.Backtest;
using System;
using WealthLab.Core;
using WealthLab.Indicators;
using WealthLab.ChartWPF;
using System.Drawing;
using System.Collections.Generic;

namespace WealthLab
{
	public class MyStrategy : UserStrategyBase
	{
		//Create and plot an SMA and a right shifted version
		public override void Initialize(BarHistory bars)
		{
			SMA sma = new SMA(bars.Close, 50);
			PlotIndicator(sma, WLColor.DarkBlue, PlotStyle.ThickLine);
			TimeSeries shifted = sma >> 10;
			PlotTimeSeries(shifted, "Shifted SMA", "Price", WLColor.DarkGreen, PlotStyle.ThickLine);
		}

		public override void Execute(BarHistory bars, int idx)
		{
		}
	}
}

operator|
public static TimeSeries operator |(TimeSeries s1, TimeSeries s2)

Returns a new TimeSeries containing 1's where values of either of the TimeSeries being compared are greater than zero, and -1 where neither is.

Example Code
using WealthLab.Backtest;
using System;
using WealthLab.Core;
using WealthLab.Indicators;
using WealthLab.ChartWPF;
using System.Drawing;
using System.Collections.Generic;

namespace WealthLab
{
	public class MyStrategy : UserStrategyBase
	{
		//create indicators and other objects here, this is executed prior to the main trading loop
		public override void Initialize(BarHistory bars)
		{
			//highlight areas where price is above either of 2 moving averages
			SMA smaLong = new SMA(bars.Close, 200);
			SMA smaShort = new SMA(bars.Close, 50);
			TimeSeries aboveBoth = bars.Close > smaLong | bars.Close > smaShort;
			PlotIndicator(smaLong);
			PlotIndicator(smaShort);
			PlotTimeSeries(aboveBoth, "Above one of 2 SMAs", "Price", WLColor.DarkGreen, PlotStyle.BooleanDots);
		}

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


Static Methods
BarsSince
public static TimeSeries BarsSince(TimeSeries ts)

Returns a new TimeSeries containing the number of consecutive bars a TimeSeries has been above zero since a previous bar whose value was zero or negative.

Usage:
Use boolean operators to compare two TimeSeries to pass as ts. The result is a TimeSeries of +/-1, corresponding to true/false for each bar. This sample code illustrates getting the number of bars elapsed since reaching the highest high of 200 bars.

Example Code
using WealthLab.Backtest;
using System;
using WealthLab.Core;
using WealthLab.Indicators;
using System.Collections.Generic;

namespace WealthScript123
{
    public class MyStrategy : UserStrategyBase
    {
        //create indicators and other objects here, this is executed prior to the main trading loop
        public override void Initialize(BarHistory bars)
        {
			IndicatorBase hhSeries = Highest.Series(bars.High, 200);
			PlotIndicator(hhSeries, WLColor.Green);
			
			// find the number of bars since a new highest high 
			TimeSeries barsSince = TimeSeries.BarsSince(bars.High >= hhSeries);
			PlotTimeSeries(barsSince, "Bars Since High", "BSHPane", WLColor.Green, PlotStyle.Histogram);			
        }

        public override void Execute(BarHistory bars, int idx)
        {
            if (!HasOpenPosition(bars, PositionType.Long))
            {
                //code your buy conditions here
            }
            else
            {
                //code your sell conditions here
            }
        }
    }
}

BooleanTest
public static TimeSeries BooleanTest(TimeSeries s, TimeSeries resultTrue, TimeSeries resultFalse)

Returns a new TimeSeries where each value is determined by comparing the value in source to 0 - If the value is greater than zero, the corresponding result value is obtained from resultTrue, otherwise from resultFalse.

The example illustrates switching between two different RSI values based on the relationship of the Close to its 20-bar moving average.

Example Code
using WealthLab.Backtest;
using System;
using WealthLab.Core;
using WealthLab.Indicators;
using System.Collections.Generic;

namespace WealthScript123
{
    public class MyStrategy : UserStrategyBase
    {
        //create indicators and other objects here, this is executed prior to the main trading loop
        public override void Initialize(BarHistory bars)
        {
			_rsi1 = RSI.Series(bars.High, 10);
			_rsi2 = RSI.Series(bars.Close, 8);

			PlotIndicatorLine(_rsi1, WLColor.Red, 4, LineStyle.Dashed, false, "RSI");
			PlotIndicatorLine(_rsi2, WLColor.Green, 4, LineStyle.Dashed, false, "RSI");

			_rsi = TimeSeries.BooleanTest(bars.Close > SMA.Series(bars.Close, 20), _rsi1, _rsi2);
			PlotTimeSeries(_rsi, "Mixed RSI", "RSI", WLColor.Gold);

			PlotIndicatorLine(SMA.Series(bars.Close, 20), WLColor.Gold);
		}
		
        //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 (!HasOpenPosition(bars, PositionType.Long))
            {
                //code your buy conditions here
            }
            else
            {
                //code your sell conditions here
            }
        }

		//declare private variables below
		IndicatorBase _rsi1;
		IndicatorBase _rsi2;
		TimeSeries _rsi;
	}	
}

Kurtosis
public TimeSeries Kurtosis(int period)

Returns a new TimeSeries containing the kurtosis of a specified TimeSeries over the specified period. Kurtosis is a statistical measure that helps in describing distribution. It is the degree of peakedness of a frequency curve compared to a normal distribution.


Log
public static TimeSeries Log(TimeSeries s)

Returns a new TimeSeries containing the logarithm of a specified TimeSeries.


Max
public static TimeSeries Max(TimeSeries s, int period)

Returns a new TimeSeries containing the highest values over the specified period.


MedianAbsoluteDeviation
public TimeSeries MedianAbsoluteDeviation(int period)

Returns a new TimeSeries containing the median absolute deviation of a specified TimeSeries which is a robust measure of variability.


Min
public static TimeSeries Min(TimeSeries s, int period)

Returns a new TimeSeries containing the lowest values over the specified period.


PercentRank
public TimeSeries PercentRank(int period)

Returns a new TimeSeries containing the percent rank of a specified TimeSeries within all elements over the specified period. Matches Excel's function.


Pow
public static TimeSeries Pow(TimeSeries s, double power)

Returns a new TimeSeries raising the source TimeSeries values to the specified power.


ReturnYTD
public TimeSeries ReturnYTD()

Returns a new TimeSeries containing the YTD (Year To Date) change, in %, of a symbol's last Closing price compared to the last Close of the previous year.


Round
public static TimeSeries Round(TimeSeries s)

Returns a new TimeSeries that rounds the data in the TimeSeries.


Skewness
public TimeSeries Skewness(int period)

Returns a new TimeSeries containing the skewness of a specified TimeSeries over the specified period. Skewness represents the extent to which a given distribution varies from a normal distribution.


Sqrt
public static TimeSeries Sqrt(TimeSeries s)

Returns a new TimeSeries containing the square root of the TimeSeries.


Sum
public static TimeSeries Sum(TimeSeries s, int length)

Returns a new TimeSeries that sums the TimeSeries over the specified length.

Example Code
using WealthLab.Backtest;
using System;
using WealthLab.Core;
using WealthLab.Indicators;
using System.Drawing;
using System.Collections.Generic;

namespace WealthScript4
{
	public class MyStrategy : UserStrategyBase
	{
		TimeSeries _barsAboveSMA;
		TimeSeries _barsAboveSMA2;

		public override void Initialize(BarHistory bars)
		{
			//calculate the number of days the Close is above the 10-day SMA for the last 20 bars
			TimeSeries sma = SMA.Series(bars.Close, 10);
			PlotTimeSeriesLine(sma, "SMA(10)", "Price", WLColor.Blue, 2);
			TimeSeries tmp = bars.Close - sma;
			tmp = tmp / tmp.Abs();
			tmp = (tmp + 1d) / 2d;

			//using the static method
			_barsAboveSMA = TimeSeries.Sum(tmp, 20);

			//or using the memeber method
			_barsAboveSMA2 = tmp.Sum(20);

			PlotTimeSeries(_barsAboveSMA, "Bars Above SMA 20 bars", "BarsAbovePane", WLColor.Blue, PlotStyle.Histogram);
			PlotTimeSeries(_barsAboveSMA2, "Bars Above Method 2", "BarsAbovePane", WLColor.Red, PlotStyle.ThickLine);
		}

		public override void Execute(BarHistory bars, int idx)
		{

		}
	}
}