тормозят-скрипты

В этой статье разберем возможности, которые одобряются разработчиками, и помогут нашим скриптам работать быстрее в 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

Родион Скуратовский - ra81
 
 
 


Вы уже сейчас можете начать изучать Видео курс- роботы в TSLab и научиться самому делать любых роботов!
 
Можно записаться на следующий поток ОнЛайн курса "Создание роботов в TSLab без программирования", информацию по которому можно посмотреть тут->
 
Также можете научиться программировать роботов на нашем Видео курсе "Роботы для QUIK на языке Lua"
 
Если же вам не хочется тратить время на обучение, то вы просто можете выбрать уже готовые роботы из тех, что представлены у нас ДЛЯ TSLab, ДЛЯ QUIK, ДЛЯ MT5, ДЛЯ КРИПТОВАЛЮТЫ!
 
Также можете посмотреть совершенно бесплатные наработки для МТ4, Квика, МТ5. Данный раздел также постоянно пополняется.
 
Не откладывайте свой шанс заработать на бирже уже сегодня!