10:15:01 Заявка на продажу TID=43851120
10:15:02 Продажа по заявке TID=43851120
10:15:03 Дубль заявки TID=43851120
10:15:03 Дубль заявки TID=43851120
10:15:23 Удаляем паспорт заявки TID=43851120
17:26:02 Не нашли - левая сделка TID=43851120
И это, собственно, ерунда - скрипт информацию о сделке скрипт всё-таки получил, дубли отфильтровал (двумя способами), но портфель и кошелёк изменились так, как надо. А вот потери сделок из-за глюков в данных, получаемых в OnTrade, реально ДОСТАЛИ! Даже специально написанный для компенсации этих глюков нечёткий поиск не всегда помогает! Решил пойти на крайние меры: задублировал ID транзакции ещё и в поле COMMENT, которое рассчитываю получить в коллбеке в поле brokerref. Надеюсь, хоть одно из двух полей хоть в одном из трёх или четырёх прерываниях на одно событие будет всё-таки передано правильно, господа разработчики?
Третий или четвёртый раз (только в этом году или, скорее всего, с конца февраля) наблюдаю такую вот картину:
Заголовки и номера строк видны, а содержимое ячеек как корова языком слизнула. Поскольку у меня там в разных ячейках разный не только цвет фона, но и цвет текста, остаётся предположить, что Квик либо рисует текст строго цветом фона либо на рисует его вообще. Насколько я успел заметить, появляется эта прелесть тогда, когда (возможно, в момент прорисовки таблицы) приходит прерывание OnTrade (не уверен, просто гипотеза). В любом случае, я сильно сомневаюсь, чтобы подобное был способен сотворить МОЙ код
Последние недели две я вылизываю код своего робота (перфекционист хренов! ), и на данный момент у меня осталась непричёсанной только функция OnTrade. Функция довольно неприятная: как известно, прерывания OnTrade (как и OnOrder) приходят пачками, и нет никаких признаков, что проблема эта будет когда-нибудь решена. Самое противное, что прерывания эти приходят не только пачками, но и вразнобой, т.е возможен последовательный приход прерываний по одной и той же заявке order_num, но с разными кодами сделки trade_num, например: trade_num1, trade_num1, trade_num2, trade_num2, trade_num1, то есть первое прерывание trade_num1 мы должны обработать, второе - игнорировать, третье (другая сделка по той же заявке) - обработать, четвёртое - игнорировать, пятое (предыдущая сделка по той же заявке) - тоже игнорировать. Когда я писал обработчик, я предположил, что такой ситуации быть не может "потому, что не может быть никогда". Увы, я ошибся.
Очевидно, что снимать даже исполненные заявки просто так нельзя - обязательно напоремся на повторные прерывания с тем же кодом. Я и держал у себя айдишки заявок и сделок до конца сессии - всё равно они по окончанию снимаются автоматически. А чтобы не было вышеописанных глюков, поставил заглушку "1 заявка - 1 лот". Ни то, ни другое меня более не устраивает. От прерывания OnOrder (и его потенциальных глюков) я отказался с самого начала, но OnTrade хотелось бы сохранить - не в таблице же сделок ковыряться (тем более, там своих глюков наверняка предостаточно). Поэтому алгоритм торговли я сейчас вижу примерно так:
1. Обо всех "своих" заявках (либо сделанных самостоятельно, либо совершённых пользователем вручную через сервис контекстного меню) скрипт, конечно, знает, но ведь юзер может торговать и в обход скрипта, через стаканы! Поскольку скрипт ведёт учёт состояния портфеля, он должен знать и об этих сделках, и узнаёт он о них именно через OnTrade. При этом он способен определить, какая именно это сделка: своя или "левая", но для "левых" заявок он не знает, какой она величины (разве что получит статус "заявка исполнена").
2. Пользователь может не только подать заявку, но и снять её, причём не только свою, но и сделанную скриптом. О таких "подлянках" скрипт не может узнать в принципе (если отказаться от OnOrder и не ползать по таблицам).
3. В момент подачи заявки на покупку скрипт резервирует необходимое количество соответствующей валюты, но если заявку подаёт пользователь, такого резервирования нет, и потому он закрывает заявку не из резерва, а из свободной наличности.
4. Через некоторое время (скажем, 3-5 минут) скрипт должен принудительно снимать заявки. Собственно, закрытые заявки (здесь уже наверняка пришли все возможные прерывания) не снимаются - просто редактируется паспорт состояния соответствующего тикера, а вот активные (они могут быть только свои, заявки юзера скрипт снимать не имеет права) нужно убивать через KILL_ORDER.
Примерная структура паспорта (i-го тикера), касающаяся заявок/сделок: [i]["Orders"] - сам паспорт (таблица Lua, то бишь дерево) [i]["Orders"]["C"] - значение счётчика прерываний по таймеру, после которого можно снимать заявки (пока кажется разумным иметь общее для всех заявок, в противном случае нужно это поле воткнуть в паспорт заявки) [i]["Orders"]["N"] - количество (незакрытых) заявок [i]["Orders"][j] - массив паспортов заявок (я люблю C, так что нумерация с нуля). [i]["Orders"][j]["ID"] - ID заявки в торговой системе [i]["Orders"][j]["n"] - количество лотов в заявке (для "левых" заявок 0) [i]["Orders"][j]["N"] - количество сделок по j-й заявке [i]["Orders"][j][k] - массив паспортов сделок (нумерация с нуля) с (кажется) единственным значением в паспорте: [i]["Orders"][j][k] - ID сделки (чтобы игнорировать "лишние" прерывания)
Столкнулся с забавным явлением: у меня робот торгует, ориентируясь только на LAST, а в контекстном меню я пришпилил две кнопки "купить" и "продать" (для ручной торговли через скрипт), и там я повесил уже не LAST, а BID и OFFER (чтобы сделка исполнялась почти мгновенно, но не по рыночной цене). Всё бы хорошо, но сегодня я вдруг увидел там нули: LAST показывает нормальную цену последней сделки (впрочем, она её показывает даже при работе без Инета), а BID и OFFER обнулились. Начал разбираться - оказалось, что нули она показывает не для всех тикеров, а только для тех, по которым торги в данный момент не ведутся (вечерняя сессия на Мосбирже, когда после 19:00 торгуются только "голубые фишки"). Я не раз нарывался на диагностику "Торги по этому финансовому инструменту сейчас не ведутся" когда я или робот хотели что-то купить/продать "не вовремя". А теперь - вот он, индикатор: при подаче заявки буду проверять на 0 эти параметры и не торговать "чем попало". Не знаю, кто и как это делает (если делает), но этот индикатор мне кажется самым удобным. Рекомендую!
Господа разработчики! Что за хрень? Я пытаюсь продать СВОИ акции, они у меня ЕСТЬ, они принесли мне прибыль, которую я собираюсь зафиксировать. Какой, в задницу, может быть "шорт"? Раз пять уже такое случалось, но. как правило, со второй или третьей попытки акции всё-таки удавалось продать. Но на этот раз Квик с упорством носорога зудит: "Данный инструмент запрещен для операции шорт". Это глюк в программе или неизвестный мне доселе способ воровства моих денег?
Я имею в виду "режим кентавра", когда робот торгует параллельно с юзером. Как обрабатываются возможные конфликты? Мои соображения такие.
0. Главный здесь юзер, у него на торговлю нет никаких ограничений.
1. Скрипт должен быть в куре всех действий юзера (и своих, разумеется) и ловить их по прерываниям OnTrade и OnOrder.
2. Я не уверен, что OnTrade вообще нужно обрабатывать - ведь OnOrder в этом случае всё равно должен придти - по нему, похоже, и можно всё посчитать.
3. Скрипт я вижу "одноразовым" - он не должен посылать заявок по данному тикеру, если не обработаны все предыдущие (пофиг, от робота или от юзера).
4. Сами заявки хранятся у брокера, а не в скрипте и даже не в Квике. С другой стороны, подавляющее большинство заявок (по крайней мере, МОИХ заявок), должны бы обрабатываться немедленно (я выставляю цену покупки или продажи соответствующим образом), и потому о них можно забыть сразу после исполнения, т.е. хранить именно в теле скрипта - от первого до последнего OnOrder.
Одним словом, я не знаю, как всё это дело лучше организовать. А кто-нибудь знает?
Если я ничего не напутал, возможности для организации диалога с пользователем (меню, чекбоксы, редактируемые поля и т.п.) практически отсутствуют. Между тем, скрипт в моём понимании есть не только (и даже не столько) торговый робот, сколько помощник юзера, обеспечивающий ему условия для комфортной торговли. Иными словами, он должен а) снабжать пользователя необходимой актуальной информацией; б) выполнять его команды; в) самостоятельно совершать сделки в определённых юзером рамках; г) быть в курсе действий пользователя, если тот торгует самостоятельно (режим "кентавра").
Возможности редактирования полей (например, указание цены покупки/продажи) или там чекбоксов я не нашёл, от слова "совсем". Короче, весь диалог, видимо, приходится строить только на "таблицах Lua". В использовании клавиатуры для диалога (например, стрелки, клавиши Enter, Escape) я пока не вижу особой необходимости, а вот мышку задействовать хотелось бы.
Сначала я создал собственную таблицу, похожую на таблицу текущих торгов, но содержащую интересующие меня сведения (текущая цена, информация о моих сделках, подсчёт текущей прибыли/убытка по каждому тикеру и т.п.), стал подкрашивать разные поля для наглядности. Всё бы хорошо, но таблица получилась довольно громоздкой, и я стал выдавать её в трёх режимах: а) все тикеры, которые у меня есть или которые я хотел бы приобрести при случае; б) все тикеры, по которым у меня есть покупки; в) все тикеры, которые, по мнению скрипта, заслуживают моего внимания в данный момент (самый компактный вид).
Переключение режимов я организовал по двойному клику по левому столбцу (который с номерами строк и который в некоторых случаях было бы очень неплохо убрать, но я не умею), повесив на таблицу свой обработчик с помощью SetTableNotificationCallback(T,E), примерно так:
Код
function E(T,m,p1,p2) -- реакция на события от юзера в таблице
if m == QTABLE_LBUTTONDBLCLK then
if p2==-1 then -- клик по левому столбцу
if p1==0 then w();message("Файл результатов записан!");
else iB=B[iB];end; -- сменился тип выдачи, сообщаем юзеру
end; -- конец условия "клик по левому столбцу"
end; -- конец условия "двойной клик мыши"
end; -- конец функции E()
А в мейне проинициализировал: iB=1; -- тип выдачи: 1 - нужные, 2 - свои, 3 - все B={2,3,1}; -- переключатель по типу выдачи (левый столбец) В качестве кнопки "сохранить результаты" приспособил заголовок левого столбца (в который тоже не хило бы записать что-то типа "Save", но я не умею).
С помощью Lua "и какой-то матери" эта конструкция заработала вполне удовлетворительно, но это, так сказать, "групповуха", команды для ВСЕЙ таблицы. А вот при клике по ЯЧЕЙКЕ (с кодом тикера) хотелось бы иметь уже всплывающее меню, специфицирующее действия конкретно по этому тикеру, со своим обработчиком, примерно так: "Купить" - приказ немедленно купить; "Продать" - приказ немедленно продать; "Auto" - разрешить скрипту самостоятельно совершать сделки с этим тикером; "Вручную" - сделки по этому тикеру выполняет пользователь, и только он.
Проинициализировал такую таблицу прямо в мейн (как и таблицу визуализации результатов), а в обработчике повесил на клик по ячейке с кодом тикера соответствующий CreateWindow, и тут началась АМБУЛА.
Окно-то прорисовывается, но ПУСТОЕ. Подумал: я же в обработчике его вызываю! Стал там только устанавливать флаг необходимости прорисовки окна меню, а в цикле (в котором sleep) вставил: if ft==1 then CreateWindow(t);ft=0;end; Пофиг - окно снова рисуется, и снова пустое (заголовков и номеров строк тоже нет). Помню, там какая-то последовательность операторов была важна (уже забыл, какая) - может, в этом дело? Или прямо в цикле надо набивать таблицу значениями. Короче, мне это дело надоело, отложил до завтра, а здесь хотел бы послушать соображения на тему организации диалога. Например, неплохо было бы иметь аналогичную таблицу (вызываемую, видимо, по горячей клавише) с настройками работы скрипта прямо в процессе его исполнения.
Начал я потихоньку оформлять свой первый "боевой" скрипт - "уж совсем была бы наша победа, но тут припёрся очередной Мальчиш-Кибальчиш": после остановки скрипта теряется управление. Локализация места ошибки привела вот к такому коду:
Код
function main()
f=true;-- признак работы скрипта (false - завершение)
c=0; -- малый счётчик прерываний
C=0; -- большой счётчик прерываний
... -- идёт начальная инициализация данных (вместо onInit)
while f do -- бесконечный цикл до остановки скрипта
sleep(1500); -- раз в 1.5 секунды запускаем утилиту опроса
small(); -- текущих данных и вывода их юзеру
end; -- конец бесконечного цикла
... -- идёт запись результатов в файл
message("Скрипт остановлен!");
end;
function small()-- обработчик прерывания по таймеру
local i=0; -- индекс текущего тикера
local j=0; -- значение последней цены
c=c+1; -- счётчик "мелких" прерываний
if c==10 then c=0; big();end; -- прошло 15 секунд, включаем анализ
while i<N do -- цикл по тикерам (опрос текущих курсов)
if a[i][9]~= 0 then -- если тикер присутствует в таблице
j=getParamEx(a[i][1][0],a[i][0],"LAST").param_value;
if j~=a[i][2] then -- если курс изменился
a[i][2]=j; -- запоминаем новое значение курса
SetCell(T,a[i][9],3,tostring(d0(a[i][2])));
end; -- конец условия "курс изменился"
end; -- конец условия "тикер присутствует в таблице"
i=i+1;-- переходим к следующему тикеру
end; -- конец цикла по тикерам
end
function big() -- более серьёзный обработчик
... -- идёт некий анализ данных
end
function d0(s) -- обрезка концевых нулей после запятой
s=tonumber(s); -- для числовых переменных
if s==math.floor(s) then s=math.floor(s) end
return s; -- возвращаем огрызок
end
function OnStop()-- вызов по нажатию кнопки "Остановка скрипта"
f=false; -- команда на прекращение бесконечного цикла
end
Если закомментировать вызов SetCell (а он прекрасно работает, таблица значениями заполняется), то по остановке и файл результатов записывается, и сообщение "Скрипт остановлен!" выскакивает. А "на нет и суда нет" - ни файла, ни месседжа! Насколько я понимаю, какая-то скотина, связанная с SetCell, не возвращает управление в main. Что делать? Ну не писать же в onStop окончание main!
Я тут заметил, что на некоторых тикерах кто-то (явно скрипт) "дрочит" заявками вблизи последней сделки, примерно раз в секунду перекидывая заявки в сотню-другую лотов "туда-сюда-обратно", имитируя какое-то движение по тикеру. Лично я бы за такое наказывал. Скажем, задержка хотя бы в 5-10 секунд на юзера (хотя бы по одному и тому же тикеру) эти трепыхания обрубила бы на корню (или, по крайней мере, замедлила).