Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Для написания скриптов и индикаторов для TSLab будем использовать среду разработки Visual Studio. Версия Community является бесплатной и по функционалу она нам полностью подходит.
Итак, переходим на сайт Visual Studio, и скачиваем последнюю версию студии.
При установке надо выбрать компонент Разработка классических приложение .NET.
На вкладке Языковые пакеты можно выбрать язык по желанию.
Далее нажимаем кнопку Установить.
При первом запуске Visual Studio можно выбрать параметры разработки и тему оформления. Выберем Visual C# и светлую тему.
TSLab может использовать индикаторы написанные на .NET и собранные в библиотеку .dll.
Для примера напишем индикатор, который считает цену OHLC4 по формуле:
Создадим новый проект в Visual Studio и назовем MyHandlers. Как создавать проект смотрите в статье Создание проекта в Visual Studio.
В проект добавим библиотеки TSLab.Script.dll, TSLab.Script.Handlers.dll, TSLab.DataSource.dll, TSLab.Utility.dll. Создадим новый класс OHLC4 и напишем следующий код:
Мы создали класс OHLC4 от интерфейса IBar2DoubleHandler. Этот интерфейс реализует один метод Execute, который принимает на вход инструмент и выдает список чисел.
У класса мы указали атрибут HandlerCategory, с названием MyHandlers. Это название категории куда будет помещен наш индикатор в TSLab. HelperName - указывает название индикатора, которое будет отображаться с TSLab, можно написать любое другое название.
В методе Execute мы сделали расчет значения OHLC4 простым выражением:
Теперь можно собрать проект нажав F6. Если все сделали правильно, то студия сохранит, соберет проект и создаст файл MyHandlers.dll в папке проекта \bin\Debug.
Этот файл надо скопировать в папку TSLab: %APPDATA%\..\Local\TSLab\TSLab 2.0\Handlers
Теперь запустим TSLab и в режиме редактора должен отобразиться наш новый индикатор.
Теперь подключим наш новый индикатор, выберем инструмент и запустим.
Видим что на графике отобразилась синяя линия, это и есть цена OHLC4.
Чтобы каждый раз вручную не копировать файл .dll можно в студии прописать команду для автоматического копирования. Файл будет сам копироваться после каждой сборки проекта.
Но следует помнить, что перед сборкой нужно закрыть TSLab, т.к. файл не сможет перезаписаться.
Команда (устарело): xcopy /Y MyHandlers.dll "%APPDATA%\..\Local\TSLab\TSLab 2.0\Handlers"
Команда (новая): xcopy /Y "$(TargetDir)$(TargetFileName)" "%LOCALAPPDATA%\TSLab\TSLab 2.0\Handlers"
Для того чтобы написать скрипт на TSLab API необходимо создать класс наследованный от интерфейса IExternalScript.
Данный интерфейс имеет следующую реализацию:
Как видим, интерфейс имеет всего один метод Execute, который принимает два параметра.
IContext - содержит общий контекст скрипта, нужен для рисования графиков, кеширования.
ISecurity - позволяет работать с инструментом, содержит свечи, тики, можно работать с позициями.
Метод Execute вызывается каждый раз когда TSLab запускает пересчет скрипта.
Также можно использовать другие интерфейсы, они различаются только количеством инструментов в параметрах:
IExternalScript2 – на входе два инструмента (ISecurity)
IExternalScript3 – на входе три инструменты (ISecurity)
IExternalScript4 – на входе четыре инструмента (ISecurity)
IExternalScriptMultiSec – на входе массив инструментов (ISecurity)
Для написания скриптов можно использовать следующий шаблон:
Основный свойства и методы:
Интерфейс IContext
IsLastBarClosed
IsLastBarUsed
IsFixedBarsCount
Для определения количества баров в расчете.
IsOptimization
Показывает, что идет процесс оптимизации.
TradeFromBar
Указывает с какого бара начинать торговлю.
ScriptResult
Выводит результат в таблицу оптимизации.
GetData(...)
Работа с кешем. Используется для расчета индикаторов.
LoadObject(...)
StoreObject(...)
Работа с кешем скрипта, который не стирается между расчетами.
LoadGlobalObject(...)
StoreGlobalObject(...)
Работа с глобальным кешем, который является общим между различными агентами и скриптами.
Данные в кеше могут быть стерты, если программе не хватает памяти.
Log(...)
Вывод логов.
Recalc(...)
Запустить заново скрипт.
Интерфейс ISecurity
FinInfo
Текущие данные по ценной бумаге (bid, ask, цена последней сделки, открытый интерес и т.д.).
Bars
Список свечей.
OpenPrices
HighPrices
LowPrices
ClosePrices
Volumes
Список цен открытия.
Список максимумов.
Список минимумов.
Список цен закрытия.
Список объемов.
Interval
IntervalBase
IntervalInstance
Интервал ценной бумаги.
LotSize
LotTick
Margin
Tick
Decimals
Размер лота для ценной бумаги.
Размер изменения лота для ценной бумаги.
Коэффициент маржи для ценной бумаги.
Минимально возможное изменение цены.
Количество десятичных знаков для цены ценной бумаги.
Positions
Список позиций.
Commission
Делегат для расчета комиссии.
InitDeposit
Начальный депозит.
IsRealtime
Показывает находится ли агент в режиме реальной торговли.
Symbol
Символ ценной бумаги.
CompressTo(...)
CompressToPriceRange(...)
CompressToVolume(...)
Decompress(...)
Сжатие и разжатие свечей.
GetBuyQueue(...)
GetSellQueue(...)
UpdateQueueData(...)
Получить очередь заявок.
GetTrades(...)
Получить список сделок.
RoundPrice(...)
RoundShares(...)
Округление входящей цены до минимального тика.
Округление входящего количества до минимального шага лота.
TSLab API – это набор готовых библиотек на платформе .NET, которые позволяют писать торговые скрипты и индикаторы для TSLab.
Если вы строите стратегии с помощью кубиков, то TSLab переводит вашу стратегию на язык C# и исполняет ее. И бывает, что возможностей кубиков недостаточно для реализации ваших задумок, тогда имеет смысл перейти на написание кода. Через код можно реализовать очень многое.
TSLab API позволяет писать стратегии, индикаторы, работать с графикой, создавать контрольную панель, работать с позициями, с тиками, со стаканом, с ордерами напрямую.
Можно писать стратегии на платформе .NET Framework от 4.7.2 и выше.
На .NET можно писать на языках C#, VB.NET. В примерах мы будем использовать C#.
В TSLab можно подключать код стратегий в виде файлов .cs и в виде библиотек .dll.
http://files.tslab.ru/docs/2.0/ru/api/ - документация API (описание классов и интерфейсов).
При выполнении скрипта можно писать свои логи. Для этого есть метод IContext.Log.
Пример скрипта, который просто выводит сообщение "Hello world" в лог.
MessageType.Info - это тип сообщения, он может быть Debug, Info, Warning, Error.
Данное сообщение выведется в общем логе программы (файл tslab.log):
14:00:01.00[10]INFO :100:Info:Script::Hello world
Также можно дополнительно вывести сообщение в окно программы:
Вывести дополнительную информацию передав список значений в словаре:
В лог файле (tslab.log) будет выведено:
14:00:02.00[10]INFO :100:Info:Script:(agent:ExampleLog):Hello world
Запускаем Visual Studio, выбираем Создание проекта.
Мы будем создавать обычную библиотеку на языке C#, поэтому выберем шаблон Библиотека классов (Майкрософт).
Далее в поле Имя проекта укажем название, например MyLib. Также можно указать расположение проекта.
Укажем папку C:\TSLabHandlers\. Потом нам надо будет подключать скрипты в TSLab из этой папки.
Выберем платформу .Net 8.0. Если студия не предлагает такой вариант, то необходимо установить компонент Разработка классических приложений .NET. Для этого нужно скачать установочный файл Visual Studio и выбрать этот компонент. Подробнее в статье Установка Visual Studio.
После этого студия создаст проект со стандартным шаблоном на C#.
Для того чтобы собрать проект нужно нажать F6, при этом студия сохранит изменения, соберет библиотеку и выложит ее в папку проекта …/bin/Debug.
TSLab позволяет делать отладку скриптов в реальном времени. Это очень удобно. Например, можно посмотреть какое значение переменной в определенном месте, или даже изменить это значение.
Для начала надо включить режим отладки в TSLab. Для этого открыть Настройки, перейти во вкладку Оптимизация скриптов, отметить галочку Отладка скриптов.
Нажать Ок и перезагрузить TSLab.
Теперь запустим Visual Studio, откроем наш скрипт, в данном случае это BuyScript из прошлого примера.
Когда TSLab запускает скрипт в обработку, он передает управление методу Execute нашего скрипта. Поэтому поставим точку остановки сразу на вход этого метода. Хотя точку можно ставить в любом месте метода, там где мы хотим чтобы студия остановила процесс работы скрипта и передала нам управление.
Теперь присоединимся к процессу TSLab, для этого в меню Debug выбрать Attach to Process... (или Ctrl+Alt+P). В поиске наберем 'tslab' и выберем процесс TSLabApp64.exe.
После этого студия присоединится к процессу TSLab и теперь, если перейти в TSLab и нажать выполнить, то студия передаст нам управление в выбранной точке остановки.
В данном режиме можно посмотреть значения текущих переменных (вкладка Locals). Во вкладке Watch можно написать свои выражения или поменять значения переменных.
Горячие клавиши при отладке:
F10 - следующий шаг
F11 - следующий шаг с заходом в метод
Shift + F11 - выход из метода на следующий шаг
F5 - продолжить до следующей точки остановки
Shift + F5 - прекратить отладку
В TSLab API есть стандартные часто используемые индикаторы. Их можно без труда использовать в своих скрипта. Эти индикаторы находятся в двух местах:
TSLab.Script.Helpers.Series - класс содержит статичные методы расчета индикаторов, например SMA, EMA, Highest, Lowest, ATR, BollingerBands и другие.
TSLab.Script.Handlers - пространство имен, в котором содержатся классы обработчиков, например MACD, ParabolicSAR, TrailStop. Все эти обработчики отображаются в визуальном редакторе TSLab в виде кубиков.
В классе Series содержатся некоторые индикаторы, например SMA, EMA, Highest, Lowest, ATR, BollingerBands и другие.
К примеру, если нужно посчитать индикатор SMA, то просто вызываем метод SMA и передаем первым параметром список цен по которым нужно считать (sec.ClosePrices), а вторым параметром будет период расчета:
Напишем скрипт, который выводит на график индикаторы SMA и EMA от цен закрытия:
Красная линия - это индикатор SMA, синяя линия - это EMA.
TSLab.Script.Handlers - пространство имен, в котором содержатся классы обработчиков, например MACD, ParabolicSAR, TrailStop. Все эти обработчики отображаются в визуальном редакторе TSLab в виде кубиков.
В отличии от индикаторов из Series, нужно сначала создать объект индикатора а потом вызвать метод Execute для расчета значений индикатора.
Исходные коды индикаторов:
Для примера напишем скрипт, который считает и выводит на график индикатор ParabolicSAR:
Еще для примера напишем скрипт, который покупает по рынку и выставляем трейл-стоп. Стоп будет считаться готовым классом TrailStop из TSLab.Script.Handlers:
Как видим, происходит сделка и сразу выставляется трейл-стоп (красная линия), которая подтягивается вслед за ценой:
Для кеширования результатов индикаторов существует методы IContext.GetData(...), которые могут работать с тремя типами списков: double, int, bool.
В прошлой статье мы рассчитывали индикатор SMA таким образом:
При проведении оптимизации расчет SMA с одинаковым периодом будет повторяться много раз. Чтобы убрать лишние расчеты и уменьшить время оптимизации существует кеширование. Предыдущий пример можно переписать так:
Метод 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 сразу вернет значение по этому ключу, при этом не будет вызываться делегат.
Кеш значений сбрасывается на каждом пересчете скрипта. В режиме оптимизации кеш не сбрасывается.
Пример скрипта с кеширированием двух скользящих средних:
Рассмотрим пример со сложным кешированием. К примеру, нам надо построить границу верхнего канала по максимальным ценам, а потом сгладить эту границу по SMA. Получается что расчет канала зависит от периода канала, а расчет SMA зависит от периода SMA и от периода канала. Поэтому при расчете SMA надо передавать два параметра, период SMA и период канала. Полный пример:
Помните, что если неверно задать параметры кеширования, то результаты оптимизации будут неверные.
Доступ ко всем позициям скрипта осуществляется через свойство ISecurity.Positions. ISecurity.Positions - это список позиций.
Для каждой позиции назначается название сигнала. В один момент может быть активна только одна позиция с одним названием.
Например, если создали позицию "LE" и ее не закрыли, то нельзя создать вторую позицию с таким же названием.
Для открытия позиций используются методы sec.Positions.BuyXXX(...) для покупки и sec.Positions.SellXXX(...) для продажи.
Примеры покупок:
Примеры продаж:
Часто используемый метод для получения позиции GetLastActiveForSignal(...), пример:
Метод ищет активную позицию с названием "LE" и ее возвращает, если нету позиций, то метод вернет null.
Также существуют другие методы для получения позиций, все они пишутся так: sec.Positions.GetXXX(...).
Для изменении размера позиции используются методы pos.ChangeAtXXX(...), при этом передается размер новой позиции.
Например, чтобы увеличить длинную позицию на 2 контракта по рынку, надо написать pos.ChangeAtMarket(i + 1, pos.Shares + 2, "LX"); Тут pos.Shares показывает текущее количество контрактов позиции, а когда мы передаем pos.Shares + 2, это означает, что надо увеличить позицию на 2 контракта.
Если же передать отрицательное значение, например -3, то позиция перевернется в короткую с тремя контрактами.
Примеры:
Для закрытия позиции используются методы pos.CloseAtXXX(...).
Примеры:
Важно заметить, что методы которые закрывают позицию по условию (CloseAtPrice, CloseAtProfit, CloseAtStop) должны выставляться на каждом баре пока не закроется позиция, иначе TSLab снимет ордер закрытия на бирже.
Некоторые методы позиции:
Для примера напишем скрипт, который будет покупать после растущей свечи по рынку и выставлять стоп 200 пунктов и тейк 400 пунктов:
Свойство ISecurity.FinInfo предоставляет текущие данные по бумаге. Например, можно посмотреть текущие цены bid/ask, цену открытия, открытый интерес, дату экспирации если есть, гарантийные обязательства и многое другое.
Полный список можно посмотреть .
Пример скрипта, который выводит в лог текущие цены bid и ask при каждом пересчете:
Свойство ISecurity.Bars предоставляет список свечей. Этот список состоит из объектов IDataBar, которые содержат время, цены, объем, открытый интерес.
Пример скрипта, который рассчитывает и отображает на графике медианную цену по барам:
Для удобства, интерфейс ISecurity предоставляет различные списки цен:
Пример скрипта, который отображает на графике максимальные и минимальные цены:
Подробное описание текстовых файлов источников можно посмотреть .
OpenPrices
Список цен открытия
ClosePrices
Список цен закрытия
HighPrices
Список максимумов
LowPrices
Список минимумов
Volumes
Список объемов
Локальный кеш - сохраняемые данные, которые доступны только текущему скрипту.
Глобальный кеш - сохраняемы данные, которые доступны всем скриптам и агентам.
Методы для работы с локальным кешем:
IContext.StoreObject(...) - Записать объект в кеш
IContext.LoadObject(...) - Загрузить объект из кеша.
Методы для работы с глобальным кешем:
IContext.StoreGlobalObject(...) - Записать объект в глобальный кеш.
IContext.LoadGlobalObject(...) - Загрузить объект из глобального кеша
Важно: периодически кеш данных очищается для освобождения места. Для того чтобы при очередном сжатии не удалились данные их нужно обернуть в класс NotClearableContainer.
Пример с глобальным кешем:
Примечание: при кешировании на диск идет сериализация объекта через BinaryFormatter. Поэтому все классы объектов, которые кешируются должны иметь атрибут [Serializable].
IValuesHandlerWithPrecalc - интерфейс класса скрипта, показывает, что обработчик работает с текущими значениями, но требует предварительной обработки.
Имеет следующие методы:
PreCalc - метод выполняется до основного расчета.
Execute - метод основного расчета.
PostCalc - метод выполняется после основного расчета.
Входящие данные
Перед классом указываются входящие данные индикатора через атрибут Input, пример: [InputsCount(2)] [Input(0, TemplateTypes.DOUBLE, false, "list1")] [Input(1, TemplateTypes.DOUBLE, false, "list2")]
Это означает, что индикатор принимает два списка данных с вещественными числами. Количество параметров может быть не ограничено.
Выходящие данные
[OutputsCount(1)] [OutputType(TemplateTypes.DOUBLE)]
Это означает, что индикатор выдает один список вещественных чисел.
Пример с потоковыми данными:
В этом примере, сначала выполняется метод PreCalc, в него передаются два списка данных list1 и list2. Метод ничего не возвращает. Потом поочередно запускается метод Execute для каждого бара, тут i - номер бара. Метод возвращает вещественное число. После этого запускается метод PostCalc, он ничего не возвращает.
Пример с не потоковыми данными
Можно указать что данные не потоковые, тогда значения будут передаваться поэлементно в метод Execute, пример: [InputsCount(2)] [Input(0, TemplateTypes.DOUBLE, true, "list1")] [Input(1, TemplateTypes.DOUBLE, true, "list2")]
Обратите внимание, что теперь в метод PreCalc ничего не передается.
Особенность индикатора с одним списком данных
Если у индикатора только один входящий список данных, то он может работать только с потоковыми данными и метода PostCalc не будет.
Потоковый индикатор работает со списками значений.
Пример, напишем индикатор, который принимает два списка и выводит один список со средними значениями:
Это "не официальная" возможность. Мы не афишировали возможность делать свои оптимизаторы.
В версии 1.2 при выборе оптимизатора мы попадаем в Name а затем в GetMetodParameters() Это нужно, чтобы представить параметры оптимизатора, как параметры стратегии.
В версии 2.0 Параметры оптимизатора должны появляться под параметрами блоков Пример
Побарный индикатор принимает на вход единичные значения и выдает одно значение. На вход так же приходит номер бара (переменная i).
Пример индикатора, который принимает два числа и выводит среднее значение:
2. Пример индикатора, который принимает Источник и одно число. Выводит close + число.
3. Пример индикатора, который имеет 2 перегрузки метода Execute и принимает Источник и одно или два числа. Выводит close + число1 + число2.
Выдается при использовании Фиктивного исполнения и всех типов заявок, кроме заявок "По рынку".
Например:
System.NotImplementedException: It doesn't possible.
at TSLab.ScriptEngine.PositionsList.OpenIfGreater
Используется фиктивное исполнение и условная заявка, Открыть, если больше. Фиктивное исполнение работает только с заявками "По рынку".
Используйте их, пожалуйста.
В TSLab у блоков есть ограничение, блок может выдавать только одно значение. Бывают ситуации, когда один индикатор должен рассчитывать несколько значений и все их нужно использовать в скрипте.
Например, мы хотим написать индикатор Боллинжера, причем в скрипте мы хотим использовать верхнюю линию, нижнюю линию и ширину канала. Для расчета этих значений достаточно рассчитать среднюю линию SMA и стандартное отклонение StDev.
Напишем универсальный блок MyBollinger, который может в зависимости от параметров выдавать верхнюю линию, нижнюю линию или ширину боллинжера. У блока будут параметры:
Period – это период расчета индикатора.
Coef – это коэффициент расчета индикатора
Type – это тип перечисления задающий способ расчета индикатора. Если Top, то выводит верхнюю линию. Если Bottom, то выводит нижнюю линию. Если Width, то выводит ширину канала.
Формулы такие: Верхняя линия: SMA + StDev * Coef. Нижняя линия: SMA - StDev * Coef. Ширина канала: верхняя линия - нижняя линия.
Расчеты индикаторов SMA и StDev мы сделаем через кеширование (Context.GetData). Это сократит количество одинаковых расчетов. Например, в скрипт добавим один блок выдающий верхнюю линию, один блок выдающий нижнюю линию и один блок выдающий ширину канала. При выполнении скрипта первый блок рассчитает значения SMA, StDev и запишет их в кеш. Потом второй и третий блоки уже не будет рассчитывать эти значения, они просто возьмут их из кеша.
Важно: кешировать индикаторы нужно с полной цепочкой зависимых параметров (подробнее тут). Для этого добавлен интерфейс INeedVariableParentNames, у него есть одно свойство VariableParentNames, которое содержит названия родительских блоков с параметрами. Это свойство добавим во все кеши рассчитываемых индикаторов, для того чтобы точно идентифицировать входящий список IList<double> values.
Для получения списка сделок существует метод ISecurity.GetTrades(...). Он имеет две реализации:
GetTrades(int barNum) - Получить список сделок по номеру свечи.
GetTrades(int firstBarIndex, int lastBarIndex) - Получить список сделок по нескольким свечам.
В обоих случаях методы возвращают список объектов ITrade.
Интерфейс ITrade
Date
Дата и время
TradeNo
Номер сделки
Direction
Направление сделки (Buy, Sell)
Price
Цена
Quantity
Количество
OpenInterest
Открытый интерес
Пример, получаем последние 5 сделок и выводи их в лог:
Информация из TSLab полностью совпадает с данными из терминала.
Пример вывода таблицы со своими данными в новом окне. Можно раскрашивать ячейки с данными.
Индикатор считает спред двух инструментов по формуле (K1 * sec1 - K2 * sec2). Возвращает новый инструмент. Показан пример работы метода sec.CloneAndReplaceBars(bars).
Блок 'Внешний скрипт' может принимать несколько инструментов. В этом случае класс скрипта нужно наследовать от интерфейса IExternalScriptMultiSec.
Существует два варианта подключения блоков.
Вариант 1:
Для каждого инструмента добавить блок 'Торгуемый инструмент'. Каждый блок соединить с блоком 'Внешний скрипт'. Таким образом можно подключить до 50 источников к внешнему скрипту.
Вариант 2:
Добавить блок 'Мульти-источник', в нем выбрать необходимые инструменты. Двойным кликом по мультиисточнику открывается форма, инструменты выбираются списком. Мульти-источник соединить с блоком 'Внешний скрипт'. В примере добавлены блоки 'Инструмент по номеру'. Этот блок извлекает инструмент по указанному номеру из мульти-источника и подает его на панель графика. Данный блок можно не добавлять если скрипт (c#) сам выводит графики.
Пример скрипта:
В метод Execute приходит массив инструментов (ISecurity[]). Выведем в лог список названий инструментов.
Результат:
C++/CLI — один из языков платформы .NET Framework — редко используется для разработки больших самостоятельных проектов. Его главное назначение — создание сборок для взаимодействия .NET с родным (неуправляемым) кодом.
В TSLab можно подключать скрипты написанные на C#, VB.NET. Или подключать библиотеки dll написанные на .NET (C#, VB.NET, C++/CLI).
Для примера, напишем библиотеку на C++/CLI и подключим в TSLab.
И так, создадим новый проект в Visual Studio 2019. Выберем тип: CLR Class Library на языке C++.
Сразу переключим проект на x64, добавим библиотеки TSLab.Script.dll, TSLab.Script.Handlers.dll, TSLab.DataSource.dll.
Пример скрипта возьмем из статьи Первый скрипт (API) написанный на C#, но мы его переделаем на C++. Создадим класс BuyScript, и напишем следующий код:
Теперь можно собрать проект и если нет ошибок, то студия создаст библиотеку \x64\Debug\TestLibC.dll ее и подключим в TSLab.
Как видим, скрипт подключился, на графике отобразились сделки.
При разработке скриптов приходится работать с большими массивами данных. Создание и удаление больших массивов трудозатратный процесс. Если в скрипте часто создаются большие массивы, то загрузка процессора может просесть, что сказывается на времени оптимизации.
Чтобы этого избежать, можно использовать внутренний пул массивов в TSLab. Это работает только для оптимизации.
Пул массивов - это коллекция массивов, которые находятся в памяти. Например, запускаем оптимизацию скрипта. В скрипте запрашиваем новый массив из пула. Пул ищет у себя подходящий свободный массив, по типу и длине данных. Если такого массива в пуле нет, то он создается. Далее скрипт помещает в этот массив свои данные, производит вычисления, выводит их на график если надо. После выполнения скрипта контекст данных освобождается, и все массивы из пула, которые были использованы в этом контексте, очищаются и помещаются обратно в пул. Далее, при проходе следующего итерации оптимизации, скрипт запрашивает массив из пула и действия повторяются. Таким образом, сокращается количество создаваемых массивов.
При освобождении контекста массив будет помещаться обратно в пул, только если он не закеширован.
Методы:
T[] IContext.GetArray<T>(int count) – получить массив с типом T и количеством элементов count из пула. Если в пуле нет такого массива, то он создается.
void ReleaseArray(Array array) – передать обратно массив в пул. При этом массив очищается. Метод ReleaseArray вызывается автоматически при освобождении контекста (IContext), после прохода оптимизации (если массив не был помещен в кеш).
Рассмотрим пример. Создадим массив с double числами, поместим в него среднюю цену бара и в конце выведем в лог хеш-код этого объекта.
Теперь проведем оптимизацию, только в настройках TSLab поставим 'Количество потоков оптимизации' равное 1. Это нужно чтобы проверить как часто у нас создается новый массив. Так как указали количество потоков 1, то сначала пройдет один проход итерации, потом второй и так далее, друг за другом. В логе мы видим разные значения хеш-кодов. Это означает, что при каждом проходе оптимизации создавался новый массив.
Теперь мы немного изменим наш пример, вместо создания массива будем получать его из пула: var arr = ctx.GetArray<double>(bars.Count);
Запускаем оптимизацию, снова с одним потоком. В логе мы видим одинаковый хеш-код. Это означает что во всех итерациях оптимизации использовался только один массив. Мы брали его из пула, использовали, при завершении итерации он помещался обратно в пул. При следующей итерации мы снова берем этот же массив из пула.
Что будет если в настройках поставить максимальное количество потоков и запустить оптимизацию на 10000 проходов? Оптимизация запустится на всех имеющихся потоках процессора. При каждой итерации будет браться массив из пула, обрабатываться и помещаться обратно в пул. Но так как потоков теперь у нас много, то теперь одного массива в пуле на всех не хватит. В этом случае будут созданы дополнительные массивы в пуле. И в конце оптимизации окажется что количество массивов в пуле равно количеству потоков. Хотя проходов у нас было 10000.
Во многих стандартных блоках TSLab используется пул массивов. Например блок расчета ATR:
В режиме самостоятельного управления заявками можно управлять заявками агента, выставлять заявку, снимать заявку. В этом режиме недоступны стандартные методы работы с позициями (BuyAtMarket, BuyAtPrice и т.д.). И соответственно у агента не будут работать стандартные опции для работы с позициями: Автооткрытие, автозакрытие, виртуальная позиция, ждать исполнения и другие. В этом режиме работать с заявками следует через интерфейс ISecurityRt.
Доступные свойства и методы интерфейса ISecurityRt: IsPortfolioReady - возвращает истину если инструмент готов к торговле BalanceQuantity - количество лотов по бумаге из таблицы Позиции EstimatedBalance - баланс счета на основе стоимости всех бумаг CurrencyBalance - количество свободных денег на счету, связанном с бумагой Orders - список исполненных и активных заявок по бумаге (только для данного агента) CancelledOrders - список отмененных заявок по бумаге (только для данного агента) HasActiveOrders - есть активные заявки или нет NewOrder(...) - выставить новую заявку CancelOrder(...) - отменить заявку ChangeOrder(...) - выставить новую заявку
Важно: - В свойстве скрипта нужно поставить галочку 'Отключить генерацию позиций' - Нужно запустить скрипт в режиме агента. - Чтобы агент начал выставлять заявки нужно в свойстве агента поставить галочки: 'исполнять входы сразу' и 'исполнять выходы сразу'.
Здравствуйте, уважаемые разработчики!
Прошу Вас объяснить мне назначение некоторых параметров в Атрибуте "HandlerParameter":
1) В "Третьей перегрузке" Атрибута "HandlerParameter" идут друг за другом параметры: bool isShown, string def, [string Default] и так далее.
В пояснении для "string def" указано: "Значение по умолчанию".
В пояснении для "[string Default]" указано: "Значение параметра по умолчанию - строка".
У меня вопрос: Если эти оба параметра "по умолчанию", то в чём между ними различия и для чего каждый из них нужен?
2) "[string Editor]" - это "Редактор параметра".
Вопрос: Что значит "Редактор параметра", для чего редактирование, где происходит редактирование, может это как-то связано с оптимизацией, то есть это на подобие "OptimProperty"?
3) Для чего нужны [string EditorMax] и [string EditorMin], а также [string Max] и [string Min]?
Предполагаю, что одна из этих пар устанавливает рамки, в которых должна происходить оптимизация параметра, но зачем тогда нужна вторая пара?
4) [string Step] - "Шаг параметра для значения - строка".
Правильно ли я понимаю, что это "Шаг изменения параметра при использовании Оптимизации"?
5) Параметр [bool isCalculable] - предполагаю, что правильный перевод "Вычисляется".
Что значит "Вычисляется? Для чего этот параметр?
=========
1. Согласно документации такой перегрузки нет .Внутри конкструктора HandlerParameter (bool isShown, string def) def присваивается Default
Атрибуты в C#.
В частности, различие между позиционными и именованными параметрами.
Синтаксис в декларации атрибутов позволяет одновременно указывать позиционные и именованные параметры, несмотря на то, что они относятся к одному члену класса.
То что показывает Visual Studio в квадратных скобках, это именованные параметры.
2. "[string Editor]" служит для задания визуального редактора свойства отличного от умолчания. Для этого также требуется поддержка внутри программы, поэтому на данный момент пользователи не могут воспользоваться этим атррибутом. 3. [string EditorMax] и [string EditorMin] служит для валидации вводимых значений. 4. [string Max] и [string Min]и [string Step] Мин, Макс и Шаг на закладке Оптимизация 5. [bool isCalculable]это значит что параметр может учавствовать в оптимизации, т.е. появляться на вкладке Оптимизация.
IContext.ScriptResults - Вычисляемые пользователем значения, которые отображаются на вкладке общих результатов скрипта и на вкладке результатов оптимизации. Это свойство имеет тип Dictionary. В котором, ключ - это название поля, а значение - это число double.
На контрольной панели можно размещать значения индикаторов, кнопки, текст.
Создать контрольную панель: var controlPane = ctx.CreateControlPane("Control", "Control", "My control");
Первый элемент "Control" – уникальный id элемента.
Второй элемент "Control" – название панели
Третий элемент "My control" – заголовок панели, если он не указан, то значение будет браться из названия панели.
Добавить элемент на контрольную панель: var pane = controlPane.AddControl("SMA", "", ControlParameterType.NumericUpDown, true, 0, 0, double.NaN, double.NaN, false, false, null, Period);
"SMA" - название элемента
"" - название свойства, если пустое, то будет браться название последней переменной, в данном случае Period.
ControlParameterType.NumericUpDown - тип элемента, в данном случае число с элементом управления.
Period - параметр скрипта OptimProperty
Пример: Добавим индикатор SMA и контрольную панель. На контрольную панель выведем значение индикатора. При изменении значения будет пересчитываться скрипт и меняться индикатор SMA.
Пример рисования различных фигур на графике.
Для того чтобы что то улучшить, это надо измерить. Поэтому хорошая практика замерять выполнение скрипта. В начале скрипта ставим: var sw = Stopwatch.StartNew(); В конце: ctx.Log($"Time: {sw.Elapsed}", MessageType.Info, true); После выполнения скрипта в логе будет написано время выполнения. Также можно замерять отдельные участки кода. Пример:
Это касается свойств в ISecurity: ClosePrices, OpenPrices, HighPrices, LowPrices, Volumes. Внутри них есть кеширование, из-за этого получение этих данных будет медленным. Поэтому рекомендуется получить все нужные данные перед торговым циклом и в цикле их использовать. var closePrices = sec.ClosePrices; var openPrices = sec.OpenPrices; var highPrices = sec.HighPrices; var lowPrices = sec.LowPrices; var volumes = sec.Volumes;
Пример:
По возможности создавать необходимые объекты до цикла, в котором они используются. Например, есть такой код, в котором используется время:
Создание TimeSpan лучше вынести до цикла:
Заметка: Пул потоков в TSLab это аналог из .NET Core.
Подробнее тут: .
У некоторых брокеров не транслируется информация о шаге цены при обновлении котировок (Событие UpdateQuote)
Соответственно и в АПИ, например так: tickPrice = (double)rtSymbol.FinInfo.StepPrice / rtSymbol.Tick;
вытащить шаг цены у ITInvest нельзя. У других брокеров именно такая строчка работать будет.
У FinInfo.StepPrice свойства такие же как у FinInfo.BuyDeposit,
BuyDeposit работать будет, StepPrice нет, его просто не присылает брокер. Проверить, что именно присылает брокер в котировках легко по окну Котировок. Вид - Котировки. По правой кнопке выберите интересующие инструменты. Если в таблице нет каких-то данных, то и в АПИ данных не будет.
В свойстве ctx.Runtime.LastRecalcReasons содержатся список причин пересчета скрипта. Их может быть несколько.
Пример как получить причину пересчета скрипта (выводится в лог):
Пример как получить событие открытия позиции (выводится в лог):
В настройках агента должна быть включена галочка События -> Открытие позиции, тогда скрипт будет пересчитываться при открытии позиции. Все возможные события перечислены в enum EventKind.
В API ISecurity есть методы:
IReadOnlyList<IQueueData> GetBuyQueue(int barNum);
IReadOnlyList<IQueueData> GetSellQueue(int barNum); Как работает UpdateQueueData()
Эта функция обнуляет кеш и заставляет заново запросить данные по стакану. Так сделано - чтобы снизить нагрузку. Получается, чтобы получить самую свежую информацию, нужно перед каждым вызовом GetBuyQueue/GetSellQueue делать вызов UpdateQueueData
стакан "фиксируется" на момент пересчета, чтобы гарантировать, что "слепок" стакана (обе его части) взят в момент, когда начался пересчет.
для того,чтобы обновить стакан во время пересчета и получить новый слепок сделали этот метод. С производительностью не связано.
Метод runtime.GetAllAgentRuntimeInfo() возвращает данные по всем агентам. Те же данные что в таблице Агенты.
В блок Внешний скрипт надо добавить TSLab.DataModel.dll и TSLab.ScriptEngine.dll.
Полный список настроек агента:
Взаимодействия через COM нет. Можно писать скрипты на api c#, агенты имеют доступ к своим сделками. Описание api: API
Close,Open,High,Low через Contex.GetData ?
или методы symbol.GetClosePrices(context) уже кешированы ?
===
Без разницы. Внутри методов GetClosePrices,GetOpenPrices,... идет обращение к context.GetData
ТСЛаб распознает интерполяцию строк в коде начиная с версии 2.1. В ранних версиях не работает, скрипт будет выдавать ошибку, поэтому используйте форматирование строк.
Пример интерполяции строк:
Аналогичная запись через форматирование:
Можно получить весь список инструментов, у каждого инструмента получить его характеристики, такие как шаг цены, размер лота и другие. Список баров по инструменту нельзя получить таким способом.
В скрипте можно получить параметры обращаясь к полям скрипта (OptimProperty). Либо через метод ctx.Runtime.GetParameters(), он отдает весь список параметров. Но он корректно работает при обычном прогоне скрипта. При оптимизации же будут параметры с неактуальными данными. Value всегда равно начальному значению параметра.
Если нужно получить параметры скрипта из кода другого индикатора, то можно поступить следующим образом: У контекста есть объект скрипта (ctx.ScriptObject через класс ContextExecutor), соответственно можно через рефлексию достать параметры.
Пример кода:
Метод для принудительный пересчет скрипта:
IContext.Runtime.Recalc(string recalcReason, IDataSourceSecurity dataSourceSecurity, bool inAnyCase = fals)
recalcReason - причина пересчета, любая строка. dataSourceSecurity - источник из-за которого запускается пересчет. Можно передать null. inAnyCase - выполнить пересчет в любом случае. Если указано false, то пересчет будет выполнен только если завершился текущий пересчет (используется в кубиках метронома). Если указано true, то пересчет будет выполнен сразу после текущего пересчета.
Пример вызова: ctx.Runtime.Recalc($"MyRecalc", null, true);
Пример, читается текущая причина пересчета скрипта, если скрипт запущен не через метод Recalc, то запускается Recalc, ждет 5 секунд и завершается. После завершения запускается новый пересчет, в нем будет указано что текущая причина пересчета "MyRecalc" и повторно не будет вызываться Recalc.
Нельзя сравнивать две double переменные. Об этом можно посмотреть в интернете.
================================== Здравствуйте. Столкнулся с проблемой в АПИ. Алгоритм должен проверять актуальность цены заявки и отменять её, если цена не актуальна. Вот реальный пример. Цена активной заявки в реальном времени равна 1332.8 Если выполняется такой код, всё работает правильно - заявка не отменяется, новая не выставляется //////////////////////////////////////////////////////////////////////////////////////////// double BuyPrice = 1332.8; foreach (IOrder MyOrder in SymbolRt.Orders) { if (MyOrder.IsActive) { if (MyOrder.Price == BuyPrice) return; // Выход else SymbolRt.CancelOrder(MyOrder); // Отмена заявки } } //Затем проверяются условия выставления новых заявок. if (,,,,,,,,,,,,,,,,) { SymbolRt.NewOrder(OrderType.Growth, true, BuyPrice, 1, "пример"); } //////////////////////////////////////////////////////////////////////////////////////////// Если выполняется такой код, заявка ВСЕГДА отменяется, а затем выставляется ТОЧНО такая же с той же ценой //////////////////////////////////////////////////////////////////////////////////////////// double BuyPrice = 1332.9 - 0.1; foreach (IOrder MyOrder in SymbolRt.Orders) { if (MyOrder.IsActive) { if (MyOrder.Price == BuyPrice) return; // Выход else SymbolRt.CancelOrder(MyOrder); // Отмена заявки } } //Затем проверяются условия выставления новых заявок. if (,,,,,,,,,,,,,,,,) { SymbolRt.NewOrder(OrderType.Growth, true, BuyPrice, 1, "пример"); } //////////////////////////////////////////////////////////////////////////////////////////// При этом, если переменную сделать таким образом, то всё работает правильно ///////////////////////////////////////////////// double BuyPrice = (1332.9 * 10 - 1) / 10 //////////////////////////////////////////////// ТО ЕСТЬ, почему-то возникает проблема, когда происходит вычитание и сложение десятичных дробей - после этого две переменные неправильно сравниваются
В коде можно указать проскальзывание при работе с позицией:
Выставить стоп с проскальзыванием 100: pos.CloseAtStop(i + 1, price, 100, "LX");
Выставить тейк с проскальзыванием 100: pos.CloseAtProfit(i + 1, price, 100, "LX");
Важно: В скрипте должно использоваться одно значение проскальзывания. Например, ставим стоп (CloseAtStop) и тейк (CloseAtProfit). В обоих случаях надо передать в эти методы одно значение проскальзывания. Если будут разные, то будет применено последнее для обоих заявок.
Если проскальзывание не указывать в коде, то будет применяться из настроек агента.
Зашифрованный контейнер может содержать внутри себя библиотеку dll и скрипт будет с ней работать.
Но одно условие, скрипт не должен вызывать статик поля или методы из библиотеки.
"сохраняю переменную в кэш:
int qty = 1; ctx.StoreObject(__key, qty);
при следующем пересчете скрипта загружаю:
object fv = ctx.LoadObject(__key); if (fv != null) volume = (int)fv; else ctx.Log("Volume not in cache!", MessageType.Warning);
И всегда мне печатается сообщение о том, что по данному ключу в кэше ничего нет. " ===
Нужно использовать класс NotClearableContainer и сохранять в кеше объекты, а не типы-значения, потому что получаются операции упаковки-преобразование и распаковки-преобразование.
Для того, чтобы посмотреть файл cs из кодогенератора программы, необходимо:
Откройте в программе: Файл -> Настройки программы -> Оптимизация скриптов -> Отладка скриптов
Создайте скрипт и скомпилируйте его, например, как в примере Видео ниже
Файл cs будет доступен в папке temp. В нее несложно попасть из программы.
Инструменты -> Папка с логами -> Подняться на один уровень выше, на одном уровне с папкой logs, папка temp.
Пример создания скрипта в визуальном редакторе программы и просмотр C # (CS file) этого скрипта из кодогенератора.
Для расчета данных скрипта можно задействовать стандартный алгоритм, используя класс Perfomance. Ниже приведен пример скрипта который, считает основные метрики и выводит их в лог. Для сокращения кода сам торговый алгоритм не приводится, вместо него поставьте любой свой или из примеров. В блок Внешний скрипт надо добавить TSLab.ScriptEngine.dll.
Для работы с очередью заявок в интерфейсе ISecurity есть следующие методы:
GetBuyQueue(int barNum) - Получить очередь заявок для покупок.
GetSellQueue(int barNum) - Получить очередь заявок для продаж.
UpdateQueueData() - Обновить закешированный стакан (не влияет на FinInfo.Ask / FinInfo.Bid).
Методы GetBuyQueue и GetSellQueue выдают список объектов IQueueData. На данный момент эти методы всегда возвращают текущую очередь, т.е. не зависят от передаваемого параметра barNum.
Интерфейс IQueueData
IsBuyPart
LastUpdate
Время последнего обновления
Price
Цена
Quantity
Количество
HasOwnOrderContains
Есть ли наши ордеры
Security
Доступ к инструменту
Пример, выведем данные очередей в лог:
Информация из TSLab совпадает с данными из терминала.
При написании скриптов на TSLab API можно определить параметры, которые можно будет настраивать из TSLab вкладка Оптимизация. Также эти параметры используются при оптимизации скрипта.
Например, скрипт строит индикатор SMA. Для его расчета необходимо значение периода по которому рассчитывается индикатор. Чтобы это значение не записывать в коде, мы можем добавить параметр в скрипт и использовать его в качестве расчета.
Эта строка описывает свойство с типом OptimProperty. Свойство содержит тип double. По-умолчанию будет значение 100, при оптимизации будут перебираться значения от 10 до 200 с шагом 1.
Существуют разные типы параметров:
Типы параметров
OptimProperty
Число с плавающей точкой (double)
IntOptimProperty
Целое число (int) (Только положительное!)
BoolOptimProperty
Логическое значение (bool)
DateTimeOptimProperty
Дата и время (DateTime)
EnumOptimProperty
Перечисление (Enum)
StringOptimProperty
Строка (String)
TimeSpanOptimProperty
Интервал времени (TimeSpan)
Напишем скрипт, который рассчитывает два значения SMA, быстрое и медленное. Периоды SMA будем передавать через параметры скрипта:
На вкладке Оптимизация мы видим наши параметры, там их можно редактировать.
Создадим первый скрипт, он будет покупать по рынку и выставлять стоп и тейк на расстоянии 0.5%.
Запустим Visual Studio, создадим новый проект MyLib если еще не создан. Как создавать проект смотрите в статье Создание проекта в Visual Studio.
Добавим в проект библиотеки TSLab. Они лежат в папке установки TSLab. Для этого:
Кликнем правой кнопкой мыши по References -> Add Reference…
Перейдем во вкладку Browse, кликнем по кнопке Browse…
В открывшемся диалоге перейдем в папку программы TSLab: - для TSLab 2.1: C:\Program Files (x86)\TSLab 2.0 - для TSLab 2.2: C:\Program Files\TSLab\TSLab 2.2
Выберем следующий файлы: TSLab.Script.dll, TSLab.Script.Handlers.dll, TSLab.DataSource.dll, TSLab.Utility.dll.
Также вы можете загрузить эти файлы в проект используя инструмент NuGet доступный в Visual Studio.
В обозревателе решений щелкните правой кнопкой мыши узел Ссылки и выберите пункт Управление пакетами NuGet.
Выберите nuget.org в качестве источника пакетов, перейдите на вкладку Обзор, выполните поиск по запросу TSLab, выберите необходимые пакеты в списке и нажмите кнопку Установить.
Добавим новый класс BuyScript, для этого правой кнопкой мыши щелкнуть по названию проекта, выбрать Add -> Class… и написать название класса BuyScript.cs.
В редакторе кода напишем следующий код:
Мы создали класс BuyScript от интерфейса IExternalScript. В этом интерфейсе описан метод Execute, который вызывается каждый раз при запуске скрипта в TSLab.
Метод имеет два параметра, IContext ctx и ISecurity sec.
IContext - содержит общий контекст скрипта, нужен для рисования графиков, кэширования, получения торговой статистики.
ISecurity - позволяет работать с инструментом, содержит свечи, тики, можно открывать/закрывать позиции.
В теле метода мы написали перебор по всем барам через цикл for. Методом sec.Positions.GetLastActiveForSignal получаем последнюю активную позицию и записываем ее в переменную longPos. Далее проверяем эту переменную, если позиции нету (longPos == null), то покупаем по рынку (sec.Positions.BuyAtMarket), если же позиция существует, то выставляем стоп для позиции (longPos.CloseAtStop) и тейк-профит (longPos.CloseAtProfit). Тут важно заметить что позицию мы открываем с названием сигнала "LE" и получаем позицию по этому же названию (sec.Positions.GetLastActiveForSignal("LE", i)). В один момент может быть открыта только одна позиция с одинаковым названием.
sec.Positions - доступ к позициям
sec.Positions.GetLastActiveForSignal("LE", i) - получить последнюю активную позицию с сигналом "LE".
sec.Positions.BuyAtMarket(i + 1, 1, "LE") - открыть позицию по рынку с сигналом "LE"
longPos.CloseAtStop - поставить стоп-лосс.
longPos.CloseAtProfit - поставить тейк-профит.
Нажмем F6, студия проверит ошибки и если их нету, то соберет проект.
Теперь перейдем к TSLab. Создадим новый скрипт, добавим источник, блок Внешний скрипт и подключим наш файл BuyScript.cs
Нажмем Сохранить и выполнить. Если все сделали правильно, то отобразится график со сделками.
Если в скрипте используются дополнительные библиотеки или классы, то их тоже надо добавить в TSLab в блок Внешний скрипт.
В TSLab можно подключать библиотеку dll вместо файла скрипта.