вторник, 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.