Секреты TSLab | Торговые роботы | События
17 Авг

Тормозят скрипты TSLab? Ускоряемся 2!

Прошлая статья Тормозят скрипты TSLab? Ускоряемся! рассказывала о некоторых хитростях, которые не одобряются разработчиками TSLab и в будущих версиях просто будут отсутствовать. В этой статье разберем возможности, которые одобряются разработчиками, и помогут нашим скриптам работать быстрее в 10 и более раз. Если уж совсем коротко, мы покажем как не надо делать.

Почему тормозят скрипты TSLab?

Как ранее мы уже говорили, это бывает по разным причинам, но мы будем разбирать только связанные с TSLab API.

В процессе написания скриптов, самый распространенный элемент API это запрос активных/закрытых позиций. Как показала практика, часто это становится узким местом нашего скрипта, и оптимизация данной части позволяет получить серьезное ускорение. В большинстве случаев, ускорение нужно для использования оптимизатора (дабы не ждать до конца света), а иногда и просто для однократного запуска алгоритма (если очень тяжелые расчеты). Попробуем потестировать разные способы запроса позиций и найти самые толковые, которые можно будет использовать в будущих версиях, включая TSLab 2.х.

Список проверяемых вариантов:

var le = sec.Positions.GetLastActiveForSignal("LE", i);      // 1
var le = sec.Positions.GetLastPositionActive(i);             // 2
var le = sec.Positions.GetLastLongPositionActive(i);         // 3
var le = sec.Positions.GetActiveForBar(i).FirstOrDefault();  // 4
var le = sec.Positions.LastOrDefault(p => p.IsActiveForbar(i));  // 5

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

Для теста используем историю фьючерса RTS с таймфреймом 1М. Общее число свечей = 1885381.

Тестовый скрипт выглядит так:

            var fastPeriod = 5;
            var slowPeriod = 10;
            var count = ctx.BarsCount;

            var smaFast = Series.SMA(sec.ClosePrices, fastPeriod);
            var smaSlow = Series.SMA(sec.ClosePrices, slowPeriod);
            var crossings = smaFast.Crossings(smaSlow);

            var sw = new Stopwatch();
            sw.Start();

            for (int i = slowPeriod; i < count; i++)
            {
                var le = sec.Positions.GetLastActiveForSignal("LE", i);
                //var le = sec.Positions.GetLastPositionActive(i);
                //var le = sec.Positions.GetLastLongPositionActive(i);
                //var le = sec.Positions.GetActiveForBar(i).FirstOrDefault();
                //var le = sec.Positions.LastOrDefault(p => p.IsActiveForbar(i));
                if (le == null)
                {
                    if (crossings[i] > 0)
                        sec.Positions.BuyAtMarket(i + 1, 1, "LE");
                }
                else
                {
                    if (crossings[i] < 0)
                        le.CloseAtMarket(i + 1, "x" + le.EntrySignalName);
                }
            }

            sw.Stop();
            ctx.Log("elapsed: {0}".Put(sw.Elapsed), Colors.Red, true);

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

Малый период индикаторов выбран для того, чтобы увеличить число позиций и, как следствие, повысить влияние этого фактора на работу скрипта. Помимо этого, я расчет пересечений тоже произвожу ДО того как включается таймер, с целью максимально снизить влияние всех посторонних факторов на время обсчета непосредственно торгового цикла.

GetLastActiveForSignal(“LE”, i)

Самый распространенный способ работы с позициями. Он абсолютно необходим в случае если ваш скрипт открывает больше одной позиции в одном направлении. Ведь нет других способов отличить одну лонг позицию от другой, кроме как по имени сигнала. Помимо этого, данный способ самый простой и удобный в использовании. Я пользуюсь им повсеместно и другие применяю очень редко.

Полученное число сделок = 107382.

elapsed: 00:00:01.2277593
elapsed: 00:00:01.2464743
elapsed: 00:00:01.3578913
elapsed: 00:00:01.2592283

Ну очень быстро. Чуть больше 1 секунды.

GetLastPositionActive(i)

Этот вариант удобен когда у нас одновременно может быть активна ТОЛЬКО одна позиция. Мы просто запрашиваем последнюю активную позицию и работаем с ней. Удобно, просто, быстро. Нет проблем с именами сигналов. Подходит для простых скриптов.

Полученное число сделок = 107382.

elapsed: 00:00:00.9938352
elapsed: 00:00:01.0900126
elapsed: 00:00:01.0986774

Еще быстрее. Это логично, ведь не нужно искать имя сигнала, а просто взять активную и вернуть ее.

GetLastLongPositionActive(i)

Этот способ подойдет для скриптов где невозможно одновременное открытие двух позиций в одном направлении. Допустимо открывать одновременно в лонг и шорт по одной позиции.

Полученное число сделок = 107382.

elapsed: 00:00:00.9723426
elapsed: 00:00:01.0560687
elapsed: 00:00:01.0850675

Скорость такая же как в предыдущем случае. Оба варианта очень быстры и можно выбирать любой.

GetActiveForBar(i)

Самый универсальный способ. Позволяет получить все активные позиции сразу и дальше с ними проводить любые манипуляции через циклы. Очень удобно использовать если у нас много одновременных позиций, и получив их все мы можем отработать их очень гибко используя циклы. Выглядит чудесным и прекрасным, посмотрим что покажет наш тест.

elapsed: больше 5 минут, скрипт остановлен по таймауту

Не вышло дождаться окончания работы скрипта. Видимо данный метод очень медленный. Для проверки данного факта я изменил периоды скользящих на 1000 и 2000 чтобы было меньше сделок и запустил еще раз.

Полученное число сделок = 512.

elapsed: 00:00:40.2369298
elapsed: 00:00:43.8513545

Ну ооочень медленно. Очень плохой вариант. Использовать можно только если очень сильно прижало и других вариантов нет. Разработчики подтвердили, что данный метод не использует кэширование и поэтому очень медленный. Вместо данного метода был найден интересный способ описанный в статье Торговый робот Хомяк.

приведу для сравнения время работы скрипта с применением тех же периодов но запроса по GetLastActiveForSignal()

elapsed: 00:00:00.4400800
elapsed: 00:00:00.3955160
elapsed: 00:00:00.4217931

Время уменьшилось всего в 3 раза, и это говорит о том, что запрос позиций вносит весьма скромную лепту в общее время выполнения скрипта. Это прекрасно.

p.IsActiveForbar(i))

Это полностью лобовая атака, когда мы вообще не полагаемся ни на какие методы TSLab а пытаемся сами выбирать нужные нам позиции из общей кучи. Естественно никакой тут нам оптимизации по скорости нет и не будет.

Полученное число сделок = 512.

elapsed: 00:00:36.8720777
elapsed: 00:00:40.0092690

Время работы соизмеримо с методов выше, но выглядит даже чуть более шустрым. В зависимости от способа выборки позиций это время будет колебаться в большую сторону, а вот в меньшую уже вряд ли.

Выводы

  • Самый удобный и быстрый метод – GetLastActiveForSignal(“LE”, i). Удобно применять везде где нужно. Почти не оказывает замедляющего воздействия на скрипт.
  • Методы без имени сигнала (GetLastPositionActive, GetLastLongPositionActive) удобно применять для простых скриптов, где нет множества одновременно активных позиций. Самые быстрые из всех. Нужна максимальная скорость? Используйте их.
  • Все остальные способы выглядят очень медленными и не рекомендуются к использованию нигде. Только если очень необходимо, тогда можно. Но чаще всего дополнительная порция кода позволяет избежать использования метода GetActiveForBar

PS: Ложка дегтя как обычно. На текущий момент для версии 1.2.26.49 в методах GetLastPositionActive, GetLastLongPositionActive присутствует баг, который вроде бы уже поправили но до сих по не поправили. В общем надо быть осторожным :). Он проявляется не всегда, но можно легко на него попасть.


comments powered by HyperComments

Станислав Зимен
2016-09-17 22:18:46
Продам годовой ключ от торгового робота ichimoku-trade действительный до 1 октября 2017 года. Проверен на личном опыте, показал отличную доходность. Могу предоставить скрины, торгов. При продаже работаю через гаранта. Цена договорная. Нехорошо спамить
Иван
2017-01-01 03:23:16
Секс знакомства http://bit.ly/2hkF2s9
Anatolye
2017-01-25 21:13:49
Родион, добрый день! Спасибо за очень полезную статью! Я только сейчас столкнулся с проблемой производительности, и похоже слабое место именно запрос позиций. Я использовал для запроса списка позиций следующую конструкцию: leList = sec.Positions.GetActiveForBar(i).Where(p => p.EntrySignalName.StartsWith("LE")).ToList(). Хочу использовать более скоростной вариант для запроса списка sec.Positions.GetLastActiveForSignal("LE", i), но что-то не выходит. Помогите, пожалуйста, как правильно запросить список с использованием GetLastActiveForSignal. С уважением, Анатолий
Anatolye
2017-01-26 10:30:00
Думаю, как вариант. Заранее задать названия позиций и загнать полученные по запросу позиции в массив.
8
Сен
2017

Доверительное управление. Результаты в августе 2017 года.

Доверительное управление. Результаты в августе 2017 года. Стратегия “Опционы” принесла в августе прибыль в размере… »

6
Авг
2017

Доверительное управление. Результаты в июле 2017 года.

Доверительное управление. Результаты в июле 2017 года. В июле индекс РТС вновь колебался в достаточно узком… »

14
Июл
2017

Доверительное управление. Результаты в июне 2017 года.

Доверительное управление. Результаты в июне 2017 года. Июнь индекс РТС вновь провел преимущественно в боковых движениях, а… »

11
Июн
2017

Доверительное управление. Результаты в мае 2017 года.

Доверительное управление. Результаты в мае 2017 года. В мае “болтанка” индекса РТС продолжилась, на паре… »

7
Май
2017

Доверительное управление. Результаты в апреле 2017 года.

Доверительное управление. Результаты в апреле 2017 года. В апреле мы наблюдали очередной месяц “боковика” по… »