Caching

Caching indicator results

There are IContext.GetData (...) methods for caching indicator results, which can work with three types of lists: double, int, bool.

In the last article, we calculated the SMA indicator in this way:

var smaFast = Series.SMA(sec.ClosePrices, PeriodFast)

During optimization, the calculation of SMAs with the same period will be repeated many times. To remove unnecessary calculations and reduce optimization time, caching exists. The previous example can be rewritten as follows:

var smaFast = ctx.GetData("SMA", new[] { PeriodFast.ToString() }, () => Series.SMA(sec.ClosePrices, PeriodFast));

The GetData method works with the data cache, the cache is a table KEY - VALUE. Where KEY is a row, it is always unique in the table, several identical keys cannot exist. VALUE is a list of values (double, int, bool).

The GetData method takes three parameters:

The first parameter is the key prefix. In this example, it is “SMA”.

The second parameter is an array of strings from which the end of the key is built. In this example, the value of the period is transmitted in the form of a string (PeriodFast.ToString ()), for example, it is 100. Then the full name of the key will be "SMA: 100". If several values were transmitted, for example 100, 20, 40, then the key would be "SMA: 100: 20: 40".

The third parameter is a delegate that is called if there is no data in the cache for the given key. That is, this is the calculation of the indicator values. The delegate should return a list of double, int, or bool values.

How it works. Initially, the data cache is empty. When we request the calculation of SMA with a period of 100, the key "SMA: 100" is generated. This key is looked up in the cache table, but it is not there, since the table is empty. Then a delegate is called that calculates the SMA value. The result is written to the cache table with the key "SMA: 100" and the GetData method returns this result. If we again request SMA calculation with a period of 100, then the key "SMA: 100" will be searched in the cache table. Since it is, the GetData method will immediately return the value for this key, and the delegate will not be called.

The value cache is flushed on each recount of the script. In optimization mode, the cache is not flushed.

An example of a script with caching of two moving averages:

using TSLab.Script;
using TSLab.Script.Handlers;
using TSLab.Script.Helpers;
using TSLab.Script.Optimization;

namespace MyLib
{
    public class SimpleCache : IExternalScript
    {
        // Customizable options
        public OptimProperty PeriodFast = new OptimProperty(100, 10, 200, 10);
        public OptimProperty PeriodSlow = new OptimProperty(500, 200, 2000, 100);

        public void Execute(IContext ctx, ISecurity sec)
        {
            // Fast SMA Calculation
            var smaFast = ctx.GetData("SMA", new[] { PeriodFast.ToString() },
                () => Series.SMA(sec.ClosePrices, PeriodFast));

            // Slow SMA Calculation
            var smaSlow = ctx.GetData("SMA", new[] { PeriodSlow.ToString() },
                () => Series.SMA(sec.ClosePrices, PeriodSlow));

            // Trading cycle
            for (int i = 0; i < ctx.BarsCount; i++)
            {
                var posLong = sec.Positions.GetLastActiveForSignal("LE", i);

                if (posLong == null)
                {
                    if (smaFast[i] > smaSlow[i] && smaFast[i - 1] <= smaSlow[i - 1])
                        sec.Positions.BuyAtMarket(i + 1, 1, "LE");
                }
                else
                {
                    if (smaFast[i] < smaSlow[i] && smaFast[i - 1] >= smaSlow[i - 1])
                        posLong.CloseAtMarket(i + 1, "LX");
                }
            }

            // If the script is in optimization mode, then you do not need to build graphs
            if (ctx.IsOptimization)
            {
                return;
            }

            // Plotting
            ctx.First.AddList(string.Format("SMA fast ({0})", PeriodFast), smaFast, ListStyles.LINE, ScriptColors.Green,
                LineStyles.SOLID, PaneSides.RIGHT);
            ctx.First.AddList(string.Format("SMA slow ({0})", PeriodSlow), smaSlow, ListStyles.LINE, ScriptColors.Red,
                LineStyles.SOLID, PaneSides.RIGHT);
        }
    }
}

Consider an example with complex caching. For example, we need to build the border of the upper channel at maximum prices, and then smooth this border by SMA. It turns out that the channel calculation depends on the channel period, and the SMA calculation depends on the SMA period and on the channel period. Therefore, when calculating the SMA, two parameters must be passed, the SMA period and the channel period. Full example:

var highest = ctx.GetData("Highest", new[] { PeriodHighest.ToString() }, 
    () => Series.Highest(sec.HighPrices, PeriodHighest));

var highestSMA = ctx.GetData("HighestSMA", new[] { PeriodHighest.ToString(), PeriodHighestSMA.ToString() }, 
    () => Series.SMA(highest, PeriodHighestSMA));

Remember that if the caching parameters are set incorrectly, the optimization results will be incorrect.

Last updated