Your interactive trading laboratory!
 • 
3 users online

Quantacula Help

How-To
C# API Reference
Extensions
Development Blog
API-Extensions

TASC-Extensions
The open-source GitHub repository of source code for the TASCExtensions Quantacula extension. Contains indicators and other extensions adapted from the Traders' Tips articles in Technical Analysis of Stocks & Commodities magazine.
Using Static Variables
Published by Q Glitch 23 days ago

In a previous article I talked about the five places where you can hook into the Quantacula backtest process, from within your C# coded Models. We saw that the backtester first calls BacktestBegin, on the first symbol in your Universe, followed by Initialize for every symbol in the Universe. You might be wondering about the purpose of BeginBacktest. Can't we just perform model initialization in the Initialize method?

True, you should perform your Model initialization in the Initialize method, including creating the instances of any indicators you will be using in your trading system. However, the BacktestBegin method is intended to be used for overall initialization, and static variables are a perfect example of when this method can come in handy.

Static Variables

Static variables are declared at the .NET class level, rather than the instance level, so they are available to all instances of a class. You can use static variables in your Models to track meta-information during a backtest run.

Consider the Model below, which implements a method of determine the "edge" of a technical indicator that I discussed in my Market Glitch YouTube channel.

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

namespace Quantacula
{
    public class MyModel : UserModelBase
    {
	static double obs = 0;
	static double profit = 0;
	static double obsRsi = 0;
	static double profitRsi = 0;

	//executes once, as first symbol in universe is being processed
	public override void BacktestBegin()
	{
		obs = 0;
		profit = 0;
		obsRsi = 0;
		profitRsi = 0;
	}
	    
        //executes once for every symbol in the universe
        public override void Initialize(BarHistory bars)
        {
		rsi = new RSI(bars.Close, 14);
		for (int n = 14; n < bars.Count - 5; n++)
		{
			obs++;
			double p = bars.Close[n + 5] - bars.Close[n];
			p = (p / bars.Close[n]) * 100.0;
			profit += p;
			if (rsi[n] <= 30)
			{
				obsRsi++;
				profitRsi += p;
			}
		}
        }

	//executes once on every symbol in the universe, display results of our computations
	public override void Cleanup(BarHistory bars)
	{
		DrawHeaderText("Obs = " + obs.ToString("N0"));
		double avgProfit = profit / obs;
		DrawHeaderText("Avg Profit = " + avgProfit.ToString("N2"));
		DrawHeaderText("Obs RSI = " + obsRsi.ToString("N0"));
		double avgProfitRsi = profitRsi / obsRsi;
		DrawHeaderText("Avg Profit RSI = " + avgProfitRsi.ToString("N2"));
		double rsiEdge = avgProfitRsi / avgProfit;
		DrawHeaderText("RSI Edge = " + rsiEdge.ToString("N2"), Color.Red, 10);
	}

        //Execute needs to be overridden, but in this case we do everything in Initialize
        public override void Execute(BarHistory bars, int idx)
        {
        }

	//declare private variables below
	private RSI rsi;
    }
}

The Model uses static variables to count the number of observations (bars) in the entire Universe of data, as well as the sum of the percentage return after 5 bars. It also calculates the 5 bar return for cases where the RSI indicator is oversold. It uses the BacktestBegin to initialize the static variables to zero. Then, it uses the Initialize method (which executes once for each symbol in the backtest) to process the history and add values to the static variables. By the time the Cleanup method is called by the backtester, the static variables are fully loaded with values for all of the symbols in the universe.

The code than calculates the average 5-bar return by dividing the sum of the 5-bar percentage return by total number of bars (observations). Likewise, it calculates the average 5-bar oversold return. In this way, the model lets us see whether the average return is better when we buy when the RSI indicator is oversold, across the entire Universe of symbols. Finally, the code uses DrawHeaderText to display the information on the chart.

I decided to implement the code that renders the information onto the chart in the Cleanup method rather than the BacktestComplete method. This is because Cleanup executes for every symbol in the Universe, and therefore my plotted text will be visible no matter what symbol I chart. Had I coded this in the BacktestComplete, the text would only be visible if I chart the last symbol in the Universe.

Summary

We saw here how static variables can be very useful in Model processing, to accumulate aggregate information for all of the symbols in the Universe. Judicious use of the BacktestBegin, Initialize, and Cleanup are all you need to implement this kind of meta analysis.

Static Variables