Кеширование индикаторов

Для кеширования результатов индикаторов существует методы IContext.GetData(...), которые могут работать с тремя типами списков: double, int, bool.

В прошлой статье мы рассчитывали индикатор SMA таким образом:

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

При проведении оптимизации расчет SMA с одинаковым периодом будет повторяться много раз. Чтобы убрать лишние расчеты и уменьшить время оптимизации существует кеширование. Предыдущий пример можно переписать так:

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

Метод GetData работает с кешем данных, кеш представляет из себя таблицу КЛЮЧ - ЗНАЧЕНИЕ, где:

  • КЛЮЧ - это строка, она всегда уникальная в таблице, не может существовать несколько одинаковых ключей.

  • ЗНАЧЕНИЕ - это список значений (double, int, bool).

Метод GetData принимает три параметра:

Первый параметр - префикс ключа. В данном примере это "SMA".

Второй параметр - это массив строк из которых строится окончание ключа. В данном примере передается значение периода в виде строки (PeriodFast.ToString()), например оно равно 100. Тогда полное название ключа будет таким "SMA:100". Если бы передавалось несколько значений, например 100, 20, 40, то ключ был бы таким "SMA:100:20:40".

Третий параметр - это делегат, который вызывается если нету в кеше данных по заданному ключу. То есть это и есть расчет значений индикатора. Делегат должен возвращать список значений double, int или bool.

Как это работает. Изначально кеш данных пустой. Когда мы запрашиваем расчет SMA с периодом 100, то формируется ключ "SMA:100". Этот ключ ищется в кеш таблице, но его там нету, так как таблица пустая. Тогда вызывается делегат, который рассчитывает значение SMA. Результат записывается в кеш таблицу с ключом "SMA:100" и метод GetData возвращает этот результат. Если мы снова запрашиваем расчет SMA с периодом 100, то в кеш таблице будет искаться ключ "SMA:100". Так как он есть, то метод GetData сразу вернет значение по этому ключу, при этом не будет вызываться делегат.

Кеш значений сбрасывается на каждом пересчете скрипта. В режиме оптимизации кеш не сбрасывается.

Пример скрипта с кеширированием двух скользящих средних:

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

namespace MyLib
{
    public class SimpleCache : IExternalScript
    {
        // Настраиваемые параметры
        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)
        {
            // Расчет быстрой SMA
            var smaFast = ctx.GetData("SMA", new[] { PeriodFast.ToString() },
                () => Series.SMA(sec.ClosePrices, PeriodFast));

            // Расчет медленной SMA
            var smaSlow = ctx.GetData("SMA", new[] { PeriodSlow.ToString() },
                () => Series.SMA(sec.ClosePrices, PeriodSlow));

            // Торговый цикл
            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 (ctx.IsOptimization)
            {
                return;
            }

            // Построение графиков
            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);
        }
    }
}

Рассмотрим пример со сложным кешированием. К примеру, нам надо построить границу верхнего канала по максимальным ценам, а потом сгладить эту границу по SMA. Получается что расчет канала зависит от периода канала, а расчет SMA зависит от периода SMA и от периода канала. Поэтому при расчете SMA надо передавать два параметра, период SMA и период канала. Полный пример:

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));

Помните, что если неверно задать параметры кеширования, то результаты оптимизации будут неверные.

Last updated