вторник, 24 мая 2011 г.

Диагностика тормозов при разработке торговых систем

Не так давно столкнулась с тем, что задача робота подвисала время от времени при отправке заявок в систему. Стала вылавливать эту "заразу" простым дедовским методом — писать в лог время до и после участков кода, которые вызывали подозрение (в том плане, что могли тормозить).

В результате у меня появилось такое огромное количество участков кода типа:
DateTimeToString(strDT,'h:m:s.z',ServerDT);
S := Format ('MTS12: Time: Offer FindOrderIdByPriceAndDirection end: %s',[strDT]);
R.fATLibLog(PChar(S),4);

что я потом запарилась анализировать лог. Было вообще непонятно: проблема крылась за пределами тех участков, в которых я ее подозревала.

Но ответ был найден — тормоза случались при поиске SecIdx инструмента. Видимо, такое их огромное количество.

А решение проблемы с тормозами — введение в структуру задачи поля TaskSecIdx (индекс инструмента), которое вычисляется один раз и на следующих итерациях при отправлении заявки не пересчитывается. В рамках торговой сессии это делать можно спокойно.
___

Чтобы быть в курсе обновлений блога, подпишитесь на RSS.

среда, 20 апреля 2011 г.

RoundTrip — простой модуль для QuikOrdersDOM для измерения времени раундтрипов

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

Итак, модуль RoundTrip написан на SDK для QuikOredersDOM. Он предназначен для измерения раундтрипа (проверки скорости канала связи с брокером). Программа отправляет заявку (не по рынку, чтобы она не исполнялась), дожидается подтверждения о выставлении (проверяет, чтобы статус стал OS_ACTIVE, OS_EXECUTED или OS_REJECTED), потом через таймаут снимает ее и дожидается подтверждения о снятии (чтобы статус стал OS_CANCELED, OS_EXECUTED или OS_REJECTED).

Измерение раундтрипа (roundtrip)

Раундтрипы особенно важны для скальперов и интрадей-трейдеров, когда скорость выставления заявки — чуть ли не главный козырь стратегии.

Как запустить модуль RoundTrip?


Запустить его очень просто (как, в принципе, и любой модуль в QuikOrdersDOM). Для этого его надо, в первую очередь, скачать, положить в папку QuikOrdersDOM/lib. Потом в запущенном приводе перейти на вкладку "Настройки" и выбрать dll в поле "Библиотека автотрейдинга". Кто одновременно запускает несколько разных модулей — пользуйтесь менеджером задач для QuikOrdersDOM (модуль TaskManager).

Настройки работы программы RoundTrip, которые можно задать через интерфейс


Максимальное количество транзакций


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

Задержка между транзакциями


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

Еще параметры:
- инструмент, по которому мы будем выставлять заявки (его название и класс);
- тип сделки (покупка или продажа);
- отступ от центральной цены, чтобы выставлять заявки (нам же не надо, чтобы они исполнялись).

После того, как тестирование завершится, можно будет вывести отчет на экран, нажав кнопку "Отчет". Автоматически этот отчет будет сохранен в файл в той же папке, в которой лежит dll модуля. Название файлов с отчетами будет формироваться по шаблону:

roundtrip_report_ddmmyy_hhmmss.txt

Где ddmmyy_hhmmss — время окончания тестирования.

Так вы сможете вернуться к результатам проведения тестов в любое время.

Вот пример. Запускаю модуль на 10 транзакций. Выставляются и снимаются заявки, всего 5 штук.
Работа модуля для измерения раундтрипа (roundtrip) в QuikordersDOM
Результат:
Всего сделано 10 транзакций: выставлений заявок = 5, снятий заявок = 5
Максимальный roundtrip = 2468
Среднее время выставления заявки = 1281,40
Среднее время снятия заявки = 1355,80
Общее среднее время = 1318,60


Время в результатах указывается в мс.

Ссылка для скачивания модуля RoundTrip.

Чтобы уменьшить погрешность измерений, можно на испытательный период поставить в настройках QuikOrdersDOM параметр CycleInterval поменьше (по умолчанию он, вроде, равен 500).

// Интервал опроса памяти QUIK в мс (очень маленькие интервалы сильно увеличивают загрузку процессора)
CycleInterval=50


Очень интересно было бы посмотреть на результаты тестов, запущенных на квиках разных брокеров. Делитесь в комментариях (или в своих блогах, а потом оставляйте в комментах ссылку) :)

** Если меня кто-нибудь читает ;DDD
___

Чтобы быть в курсе обновлений блога, подпишитесь на RSS. Если материал оказался интересным — поделитесь им с друзьями :)

вторник, 19 апреля 2011 г.

Не забудьте обновиться!

Сегодня два часа ломала голову над тем, почему робот перестал работать! Запускала и так, и сяк... в частности, функция fATLibSendCustomOrder упорно ничего, кроме ноля возвращать не хотела. Проблема решилась обновлением QuikOrdersDOM до последней версии 2.0.4.6. (вышла не так давно, но я не придала этому особого значения и продолжала работать со старой). Будьте бдительны! %D
___

Чтобы быть в курсе обновлений блога, подпишитесь на RSS.

пятница, 1 апреля 2011 г.

Ограничение на переставление заявок в Quik Junior

Хотя сегодня и пятница, но что-то какая-то напряжная пятница. День дурака принес с собой проблемы на хостинге всех сайтов, над которыми я работаю. Поэтому 2 часа потратила на переписку с техподдержкой (а вернее — на ожидание ответа от нее). В каком-то подвешенном состоянии находилась, ничего делать не могла.

Но сейчас, вроде, — тьфу-тьфу-тьфу — работа восстановилась (хотя кое-какие проблемы время от времени вылазят), можно облегченно вздохнуть и написать пост на этот блог :)

Хочу поделиться интересным наблюдением при работе с тестовым Quik Junior. Если тестировать на нем маркетмейкерского робота, который часто переставляет заявки в зависимости от изменения рыночной ситуации, то 10 переставлений проходят на "ура", а 11-ое отклоняется системой. В лог идет примерно такая запись:
01.04.2011 11:22:07 [470] [FORTS] В операции отказано: Превышен лимит операций от указанного клиента.

Причем повторяется это периодически, без каких-либо сбоев. То есть 10 удачних переставлений — 2 реджекта (я переставляю сразу пару заявок — бид и оффер) — 10 удачных переставлений и так далее.

Сначала у меня чуть мозги не закипели, когда пыталась найти причину этого где-нибудь в ошибке в своем коде. Даже заказала тестовый аккаунт в другом месте. И только в результате опытов поняла, что дело в ограничениях Quik Junior-а.

Может, мой опыт кому-нибудь поможет меньше ломать голову над этой проблемой, когда столкнется с ней.

___

Чтобы быть в курсе обновлений блога, подпишитесь на RSS.

пятница, 18 марта 2011 г.

Как найти индекс заявки по ее ID в qSDK

Поиск индекса выставленной в торговый терминал заявки по Id — зачем он вообще нужен?

Когда мы выставляем заявку с помощью функции fATLibSendCustomOrder, она соответственно возвращает нам Id выставленной заявки.

BidOrderId := R.fATLibSendCustomOrder(SecIdx,OD,BidPrice,Size,Account,aAdditional);


Дальше нам желательно отслеживать состояние заявки. Это делается с помощью функции R.fATLibGetOrderInfo. Только вот в качестве параметра туда надо передавать не Id, а индекс в списке QuikOrdersDOM.

R.fATLibGetOrderInfo(tmpOrderIndex,@tmpOrderInfo);


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

function FindOrderIndexById ( aInitRec  : TLibInitialRecord;
aId : Cardinal) : integer;
var
Res,
i : integer;
cnt : Cardinal;
tmpOrderInfo : TOrderInfo;
begin
Result := -1;
cnt := aInitRec.fATLibGetOrdersCount;
if (cnt = 0) or (cnt = $FFFFFFFF) then exit;
for i := cnt-1 downto 0 do
begin
Res := aInitRec.fATLibGetOrderInfo(i,@tmpOrderInfo);
if Res = 0 then
if tmpOrderInfo.Id = aId then
begin
Result := i;
exit;
end;
end;
end;


Еще один ньюанс, о котором бы хотелось написать (авось кому пригодится). Для торговых роботов, совершающих большое количество сделок (маркетмейкерских, скальперских), лучше хранить "активные" заявки в отдельном списке. Так вам проще и быстрее будет отслеживать их состояние. Причем хранить не Id, не индекс, а запись, в которой указаны и то, и другое (при необходимости — еще какие-нибудь параметры, например, время попытки снятия).

В ходе проверки состояния заявок будете удалять исполненные (tmpOrderInfo.State = OS_CREATED), переставлять отклоненные и т.д., в зависимости от вашего алгоритма.

Напомню, что у заявки могут быть 8 статусов:
OS_CREATED    : byte = 0; // создана
OS_SENT : byte = 1; // отправлена в терминал
OS_ACTIVE : byte = 2; // активна (подтверждено из терминала)
OS_EXECUTED : byte = 3; // исполнена
OS_CANCELED : byte = 4; // отменена
OS_CANCELING : byte = 5; // отправлена транзакция на отмену, но ещё не
// отменена
OS_INDEFINITE : byte = 6; // неопределенный, после загрузки заявок
OS_REJECTED : byte = 7; // отклонена системой

___

Чтобы быть в курсе обновлений блога, подпишитесь на RSS.

пятница, 4 марта 2011 г.

Новая версия TradeProcessor 1.0.0.1 — всем, кто любит SDK :)

На Ttools.ru была проанонсирована новая версия модуля TradeProcessor. Самое примечательное в ней — появление SDK. Сейчас можно использовать готовые функции вычисления дельты (инструмента или позиции), а так же работать с задачами в своих приложениях: создавать, запускать и т.д.

Использование функций интуитивно понятно и для тех, кто уже программировал на QuikOrdersDOM SDK, разобраться с ними не составит труда. В принципе, потом по их использованию тоже можно написать статью-мануал с примерами, если будет кому-нибудь интересно :) Сейчас просто не до этого: работы много, да и приоритеты в отношении материалов для этого бложика уже распределены (первым делом надо взять себя в руки и закончить начатую серию статей про программирование тестировщика раундтрипа).
___

Чтобы быть в курсе обновлений блога, подпишитесь на RSS.

Если материал оказался интересным — поделитесь ссылкой с друзьями :)

среда, 2 февраля 2011 г.

Хорошая новость — QuikOrdersDOM+ со скидкой 10%!

Сообщаю хорошую и — надеюсь — полезную новость! Этот блог стал партнером ttools.ru. Что это значит? Это значит, что, воспользовавшись реферальной ссылкой, вы можете купить расширенную версию QuikOrdersDOM (с дополнительными полезными функциями) со скидкой 10%!

Подробнее обо всем написала на странице, посвященной этому радостному событию %D
___

Чтобы быть в курсе обновлений блога, подпишитесь на RSS.

вторник, 1 февраля 2011 г.

Что такое roundtrip (раундтрип)? Пишем приложение для определения значения раундтрипа

Roundtrip — так называется у трейдеров время, которое затрачивается на выставление заявки и получение информации о том, что заявка зарегистрирована в системе (ну или отклонена по каким-то причинам).

На примере вычисления раундтрипа я хочу показать по пунктам, как начинать работу с QuikOrdersDOM SDK. Все от А до Я: создание проекта, описание структуры, некоторые функции, которые понадобятся для реализации задачи.

Программу будем писать на Delphi.

Создание нового проекта Delphi для работы с QuikOrdersDOM



Проще всего создать новый проект на основе заготовки, которую нам предлагает автор qSDK.

В архиве с приводом, который можно скачать на сайте, есть папка src, а в ней — несколько поддиректорий с примерами проектов. Создаем себе рабочую папку (я назвала ее RoundTrip) и копируем туда файлы из каталога ATLibSample.

Открываем проект в Delphi и прересохраняем его под названием RoundTrip.

пятница, 28 января 2011 г.

Модуль для сравнения локального и серверного времени

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

Что делает модуль TimeDelay?


Этот модуль рисует график разницы между локальным и серверным временем (в мс). Значения могут быть какие угодно (будут зависеть от настроек времени на вашем компьютере), но главное — чтобы "диапазон" (временной коридор) был ровным, без всяких "иголок".

Вычисление разницы между значениями времени происходит раз в секунду.

Вот так выглядит форма модуля:

На графике представлена статистика, накопленная мной за 40 минут на демо-сервере Quik. Что можно сказать, посмотрев на картинку? Я запустила сбор статистики одновременно с запуском Quik. И в самом начале видно, что сервер подтормаживал (по сравнению с выравнявшимся в дальнейшем коридором). Это можно объяснить тем, что он скачивал архивные данные о сделках (с 3-х ночи).

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

Вы можете в любой момент остановить работу модуля или сохранить в виде jpg-картинки ваш график. Интересно было бы посмотреть на графики у разных брокеров.

Как запустить модуль в приводе QuikOrdersDOM?


Для начала вам надо его скачать, положить в папку QuikOrdersDOM/lib (или другое удобное место). А потом добавить его в поле "Библиотека автотрейдинга" на вкладке "Настройки".

Если у вас уже запущен менеджер задач (TaskManager.dll), то добавьте библиотеку к списку запускаемых модулей:

пятница, 21 января 2011 г.

Локальное и серверное время Quik: на что следует обратить внимание при разработке роботов на QuikOrdersDOM SDK

Сегодня речь пойдет о времени. Не о том, что его постоянно не хватает :), а о том, как правильно работать с временем при разработке своих автоматических торговых систем с помощью QuikOrdersDOM SDK (qSDK).

Для чего может понадобиться время? Например, в случае, если вы выставляете задержку между заявками (можно, конечно, в циклах, но в миллисекундах как-то красивее, на мой взгляд). Или для "отсрочки" запуска задачи. Или для остановки задачи на время клирингов.

В любом случае, правильно работать с временем сервера Quik. Но перед началом торговли все-таки смотрите, чтобы часы на вашем компьютере были синхронизированы и подведены.

В QuikOrdersDOM SDK есть 2 функции работы со временем:

fATLibGetServerDate : TATLibGetServerDT; – функция получения текущей даты
fATLibGetServerTime : TATLibGetServerDT; – функция получения текущего времени


Пишем небольшую процедурку для определения текущего времени из Quik:

function GetServerDT(aInitRec : TLibInitialRecord; var aNow : TDateTime) : Cardinal;
var
dDT, tDT :TDateTime;
begin
Result := 1;
if aInitRec.fATLibIsOnline <> 1 then exit;
if (aInitRec.fATLibGetServerDate(dDT) = 0) and (aInitRec.fATLibGetServerTime(tDT) = 0) then
begin
aNow := dDT + tDT;
Result := 0;
end;
end;

После этого, не забывая проверять возвращаемое значение, время вычисляем так:

// время последней сделки
if GetServerDT(R, aNow) = 0 then
aHedgerTask.LastOrderTick := aNow
else
aHedgerTask.LastOrderTick := Now;

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

среда, 19 января 2011 г.

Как перевести деньги на счет в банке без комиссии?

Связной банкВчера озадачилась вопросом, как оптимально перевести деньги на счет, и нашла решение.

Надо было перевести деньги на брокерский счет в "Уралсибе". Посмотрела тарифы в нескольких местах:
- непосредственно "Уралсиб" — 1,3%
- "Банк Москвы" — 1,5%
- "ВТБ24" — 1,5%
- "АМТ" — не помню точно, но максимум размер комиссии 1000 руб. (мне надо было перевести 100 тысяч, так что для меня в пересчете выходил 1%) - в этом банке у меня зарплатная карта, но онлайн-банком я не пользуюсь почему-то, не сложилось.
- "МАСТЕР-банк" — 1%

А из ситуации вышла так: открыла счет и завела карту в "Связном банке". Это молодой банк и пока они набирают клиентскую базу за счет оооочень выгодных условий (0% комиссии с платежей, частичный возврат потраченных сумм на карту в виде бонусов, безпроцентное снятие денег с карты в любом банкомате(!!!)).

Счет (и карта) привязываются к телефону.

Завела вчера карту, положила деньги — и сегодня они уже на моем брокерском счету. Очень удобно!

Для кого вопрос актуален — пользуйтесь возможностью, пока они процент не установили (сейчас там 2% — если переводите в банке через оператора и 0% — если через интернет-банк). О пользовании этой картой как кредитной речь, конечно, не идет (гы-гы, там плата за кредит — 48%... может, это для кого-то нормально, но я кредитными картами никогда не пользовалась и пользоваться не собираюсь).

суббота, 15 января 2011 г.

Срочка на демке не фурычит

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

Конечно, мое безделье чисто условное. Ведь кроме программирования торговых роботов у меня в жизни еще много всяких важных дел :)) Вчера завела себе аккаунт на Livejournal.com. Встречайте — Программиздка ;) Что-то поперло меня на социализацию. Заодним и имена себе мажорные застолбила и здесь, и на ЖЖ — тематические, инструментальные ;) Буду там собирать друзей, а писать, наверное, все-таки здесь (что ни говори, платформа Blogger-а мне как-то больше по душе). Буду рада знакомствам, добавляйтесь во френды!

четверг, 13 января 2011 г.

Проверка возвращаемых результатов функций QuikOrdersDOM SDK

При написании своих торговых роботов с помощью QuikOrdersDOM SDK никогда не забывайте проверять результат, возвращаемый функциями! Будьте предельно внимательны, не повторяйте моих ошибок! %D

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

Приведу пример. Вот, допустим, функция нахождения лучшей цены спроса:


function GetBestSellPrice(aInitRec : TLibInitialRecord; aSecInfo : PSecInfo) : double;
var
cnt : integer;
tmpQuotes : PAtQuoteArray;
tmpPBestBuy,
tmpPBestSell : PATQuote;
begin
Result := -1;
cnt:=aInitRec.fATLibGetQuotesCount(aSecInfo^.Idx);
GetMem (tmpQuotes,cnt*sizeOf(TATQuote));
if aInitRec.fATLibGetQuotes(aSecInfo^.Idx,cnt,
tmpPBestBuy,tmpPBestSell,tmpQuotes)<>0 then exit;

if (tmpPBestSell<>nil) then Result := tmpPBestSell.Price;

FreeMem (tmpQuotes,cnt*sizeOf(TATQuote));
end;


Сначала я забыла проверить, что fATLibGetQuotes возвращает 0 (т.е. функция успешно отрабатывает). Это привело к тому, что время от времени вылезал непонятный баг с определением цены.

Обратите внимание, что проверка на nil указателя возвращенного tmpPBestSell в этом случае оказалась бесполезной (она была, но не спасала от ошибочного определения цены).

Самое противное в таких ошибках — их несистематичность. То есть прога может отработать несколько часов подряд без сбоев или сбой повторится раза 3 за минуту после запуска. Это связано с механизмом — QuikOrdersDOM работает напрямую с памятью. А проверка результатов, возвращаемых функциями, и их грамотная обработка — полностью защитит вас от непредвиденных ситуаций и вернет работу робота под контроль.