<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
	<channel>
		<title>Форум QUIK [тема: Нюансы в работе OnTrade]</title>
		<link>http://forum.quik.ru</link>
		<description>Новое в теме Нюансы в работе OnTrade форума  на сайте Форум QUIK [forum.quik.ru]</description>
		<language>ru</language>
		<docs>http://backend.userland.com/rss2</docs>
		<pubDate>Fri, 01 May 2026 16:37:47 +0300</pubDate>
		<item>
			<title>Нюансы в работе OnTrade</title>
			<description><![CDATA[<b><a href="http://forum.quik.ru/messages/forum10/message53673/topic6317/">Нюансы в работе OnTrade</a></b> в форуме <a href="http://forum.quik.ru/forum10/">Программирование на языке Lua</a>. <br />
			УФ! Кажется, я разобрался, как наиболее оптимально отслеживать заявки. Я уже говорил, что не хотелось бы связываться ни с прерываниями, ни с ковырянием по таблицам (по крайней мере, без крайней необходимости). В результате пришлось использовать и то, и другое, хотя и по минимуму: единственным прерыванием осталось OnTrade, единственной таблицей - Orders. Функциональность должна остаться неизменной: обеспечить возможность надёжной торговли в режиме &quot;кентавра&quot; (торговать может и сам скрипт, и пользователь).<br /><br />Структуру паспорта я несколько упростил: теперь это просто массив сделок с тремя полями<br />&#91;i&#93;&#91;&quot;Orders&quot;&#93;&#91;j&#93;&#91;0&#93; - ID заявки в торговой системе<br />&#91;i&#93;&#91;&quot;Orders&quot;&#93;&#91;j&#93;&#91;1&#93; - ID сделки<br />&#91;i&#93;&#91;&quot;Orders&quot;&#93;&#91;j&#93;&#91;2&#93; - количество лотов в заявке (остаток для незакрытых заявок), где i - ID тикера, j - ID сделки (натуральное число), а нулевая строка этой таблицы предназначена для самого паспорта сделок/заявок:<br />&#91;i&#93;&#91;&quot;Orders&quot;&#93;&#91;0&#93;&#91;0&#93; - размер массива сделок (число строк, оно же ID последней строки)<br />&#91;i&#93;&#91;&quot;Orders&quot;&#93;&#91;0&#93;&#91;1&#93; - значение счётчика прерываний по таймеру, после которого можно снимать заявки (снимаются сразу все незакрытые)<br />&#91;i&#93;&#91;&quot;Orders&quot;&#93;&#91;0&#93;&#91;2&#93; - не используется<br /><br />Работа алгоритма:<br /><br />1. При подаче заявки по какому-либо тикеру через скрипт мы формируем транзакцию и подаём заявку &quot;по ненадёжному протоколу&quot; - никаких OnTransReply или OnOrder, контролируем только возвращаемое значение sendTransaction (функция должна возвращать пустую строку). Если так оно и есть, заносим новую строку в таблицу сделок тикера, заполняя её следующим образом:<br />&#91;i&#93;&#91;&quot;Orders&quot;&#93;&#91;j&#93;&#91;0&#93; - ID транзакции (ID заявки пока неизвестен)<br />&#91;i&#93;&#91;&quot;Orders&quot;&#93;&#91;j&#93;&#91;1&#93; - 0 (сделок пока нет)<br />&#91;i&#93;&#91;&quot;Orders&quot;&#93;&#91;j&#93;&#91;2&#93; - количество лотов в заявке (продажа или покупка - неважно, это нам знать не обязательно).<br />Разумеется, поправляем (инкрементируем) поле &#91;i&#93;&#91;&quot;Orders&quot;&#93;&#91;0&#93;&#91;0&#93;, а в &#91;i&#93;&#91;&quot;Orders&quot;&#93;&#91;0&#93;&#91;1&#93; добавляем C+10 - текущее значение счётчика прерываний по таймеру (у меня это 15-секундные тики, так что снимать заявки можно будет через 2.5 минуты - за это время гарантированно должны придти все &quot;лишние&quot; прерывания OnTrade).<br /><br />2. По приходу прерывания OnTrade в цикле по сделкам ищем совпадение &quot;ID заявки + ID сделки&quot;, при совпадении игнорируем это прерывание. При такой организации мы отловим все повторные OnTrade, даже если они будут приходить &quot;крест-накрест собачьим шагом&quot;.<br /><br />3. Если в том же цикле нарываемся на совпадение ID транзакции с полем ID заявки при нулевом поле &quot;ID сделки&quot; (дополнительный контроль), то это первая сделка по нашей заявке: перебиваем туда реальные ID заявки и ID сделки, а из поля &quot;количество лотов&quot; вычитаем то, что нам пришло под &quot;qty&quot; (что при покупке, что при продаже), то есть там остаётся число ещё не реализованных по заявке лотов. Насколько я понимаю, то же значение должно быть и в пришедшем поле balance, но на него у меня особой надежды нет, так что я просто в том же цикле забираю значение поля &#91;i&#93;&#91;&quot;Orders&quot;&#93;&#91;j&#93;&#91;2&#93; (количество оставшихся лотов по заявке) из тех строк, у которых поле &#91;i&#93;&#91;&quot;Orders&quot;&#93;&#91;j&#93;&#91;0&#93; совпало с ID заявки (сделки заносятся последовательно, так что значение на выходе получим верное).<br /><br />4. Если мы нашли хотя бы одну строку с кодом этой заявки, остаток лотов не может быть нулевым (в противном случае, заявка была бы исполнена, и нового OnTrade придти не могло). Заносим новую строку в таблицу (поправляя количество строк и значение счётчика) записывая туда ID заявки и ID сделки, а в поле &quot;количество&quot; найденное значение остатка минус пришедшее значение qty.<br /><br />5. Если мы ничего не нашли (остаток равен нулю), значит, это сделка по &quot;левой&quot; заявке, созданной юзером в обход скрипта. Забавно, но в этом случае можно вообще ничего не заносить в таблицу - нам по барабану, исполнена они или нет (нам же её не снимать), так что мы просто корректируем количество лотов в портфеле и добавляем (убавляем, если это покупка) нужное количество наличности (поле value).<br /><br />6. Если в обработчике прерываний по таймеру значение счётчика превышено, пришло время снимать заявки (все сразу). Идём в цикле по сделкам тикера и смотрим: остаток нулевой - убиваем строку (кажется, для этого достаточно присвоить &#91;i&#93;&#91;&quot;Orders&quot;&#93;&#91;j&#93;=nil). Если нет, а &quot;выше&quot; есть строка с тем же номером заявки, делаем то же самое. Если это последняя строка, а номер заявки совпал, значит, это частично исполненная заявка, которую нужно убивать через KILL_ORDER с последующим удалением строки и правкой количества денег и лотов.<br /><br />7. А вот (самое неприятное) если номер заявки не совпадает, значит, там сидит номер транзакции на заявку, по которой так и не пришло ни одного прерывания. Здесь-то и приходится лезть в таблицу orders, ловить там строку с нужным trans_id, смотреть по флагам, не снял ли её юзер и, если нет, убивать заявку по тамошнему order_num и снова корректировать деньги и лоты.<br /><br />Ох, чую, неделю придётся отлаживать эту хрень! <br />
			<i>11.03.2021 15:05:48, Владимир.</i>]]></description>
			<link>http://forum.quik.ru/messages/forum10/message53673/topic6317/</link>
			<guid>http://forum.quik.ru/messages/forum10/message53673/topic6317/</guid>
			<pubDate>Thu, 11 Mar 2021 15:05:48 +0300</pubDate>
			<category>Программирование на языке Lua</category>
		</item>
		<item>
			<title>Нюансы в работе OnTrade</title>
			<description><![CDATA[<b><a href="http://forum.quik.ru/messages/forum10/message53520/topic6317/">Нюансы в работе OnTrade</a></b> в форуме <a href="http://forum.quik.ru/forum10/">Программирование на языке Lua</a>. <br />
			<a class="blog-p-user-name" id="bp_NNn04tkn" href="/user/14644/" bx-tooltip-user-id="14644">Артем</a>, Да ведь коллбеки и есть &quot;информирование что что-то поменялось&quot; - произошла сделка. И что такое &quot;табличные данные&quot;? И почему писать именно в стек? Я и так могу их отсортировать, как бы я их ни писал, только смысла нет: из множества прерываний по одной сделке лишь одно (любое, но лучше первое) следует обработать, а остальные отбросить. Алгоритмически это реализуется тривиально: если ID заявки и ID сделки совпадают с данными ранее пришедшего прерывания, новые данные отбрасываются. Не удаляются, а даже не записываются. Ну и, наконец, я не &quot;опираюсь на последовательность вызова коллбеков&quot; - я вообще считаю, что более одного колбека на одно событие есть маразм, грубейшая ошибка в программном обеспечении, которая, к сожалению, не исправляется годами. Так что вся эта конструкция создана лишь для компенсации этого глюка - в противном случае код упрощается до неприличия, даже по сравнению с моим первоначальным вариантом - не надо даже проверять на совпадение айдишек. <br />
			<i>07.03.2021 20:32:05, Владимир.</i>]]></description>
			<link>http://forum.quik.ru/messages/forum10/message53520/topic6317/</link>
			<guid>http://forum.quik.ru/messages/forum10/message53520/topic6317/</guid>
			<pubDate>Sun, 07 Mar 2021 20:32:05 +0300</pubDate>
			<category>Программирование на языке Lua</category>
		</item>
		<item>
			<title>Нюансы в работе OnTrade</title>
			<description><![CDATA[<b><a href="http://forum.quik.ru/messages/forum10/message53519/topic6317/">Нюансы в работе OnTrade</a></b> в форуме <a href="http://forum.quik.ru/forum10/">Программирование на языке Lua</a>. <br />
			Правильный подход в асинхронных программах это каждый раз запрашивать таблицу и пользоваться только табличными данными - колбеки это только для информирования что что-то поменялось. По поводу пачек колбеков: пишите их в стек а не исполняйте все сразу. В стеке можно их отсортировать по времени и удалить &quot;дублирующие&quot; инстанции. Как и в любом другом API, последовательность вызова колбеков не &nbsp;определена и в лучшем случае имеется &quot;типичная последовательность&quot; &nbsp;опираться на которую не следует. <br />
			<i>07.03.2021 20:11:59, Артем.</i>]]></description>
			<link>http://forum.quik.ru/messages/forum10/message53519/topic6317/</link>
			<guid>http://forum.quik.ru/messages/forum10/message53519/topic6317/</guid>
			<pubDate>Sun, 07 Mar 2021 20:11:59 +0300</pubDate>
			<category>Программирование на языке Lua</category>
		</item>
		<item>
			<title>Нюансы в работе OnTrade</title>
			<description><![CDATA[<b><a href="http://forum.quik.ru/messages/forum10/message53514/topic6317/">Нюансы в работе OnTrade</a></b> в форуме <a href="http://forum.quik.ru/forum10/">Программирование на языке Lua</a>. <br />
			Последние недели две я вылизываю код своего робота (перфекционист хренов! <img src="http://forum.quik.ru/upload/main/smiles/5/icon_smile.png" border="0" data-code=":smile:" data-definition="SD" alt=":smile:" style="width:16px;height:16px;" title="С улыбкой" class="bx-smile" />), и на данный момент у меня осталась непричёсанной только функция OnTrade. Функция довольно неприятная: как известно, прерывания OnTrade (как и OnOrder) приходят пачками, и нет никаких признаков, что проблема эта будет когда-нибудь решена. Самое противное, что прерывания эти приходят не только пачками, но и вразнобой, т.е возможен последовательный приход прерываний по одной и той же заявке order_num, но с разными кодами сделки trade_num, например: trade_num1, trade_num1, trade_num2, trade_num2, trade_num1, то есть первое прерывание trade_num1 мы должны обработать, второе - игнорировать, третье (другая сделка по той же заявке) - обработать, четвёртое - игнорировать, пятое (предыдущая сделка по той же заявке) - тоже игнорировать. Когда я писал обработчик, я предположил, что такой ситуации быть не может &quot;потому, что не может быть никогда&quot;. Увы, я ошибся. <img src="http://forum.quik.ru/upload/main/smiles/5/icon_sad.png" border="0" data-code=":sad:" data-definition="SD" alt=":sad:" style="width:16px;height:16px;" title="Печально" class="bx-smile" /><br /><br />Очевидно, что снимать даже исполненные заявки просто так нельзя - обязательно напоремся на повторные прерывания с тем же кодом. Я и держал у себя айдишки заявок и сделок до конца сессии - всё равно они по окончанию снимаются автоматически. А чтобы не было вышеописанных глюков, поставил заглушку &quot;1 заявка - 1 лот&quot;. Ни то, ни другое меня более не устраивает. От прерывания OnOrder (и его потенциальных глюков) я отказался с самого начала, но OnTrade хотелось бы сохранить - не в таблице же сделок ковыряться (тем более, там своих глюков наверняка предостаточно). Поэтому алгоритм торговли я сейчас вижу примерно так:<br /><br />1. Обо всех &quot;своих&quot; заявках (либо сделанных самостоятельно, либо совершённых пользователем вручную через сервис контекстного меню) скрипт, конечно, знает, но ведь юзер может торговать и в обход скрипта, через стаканы! Поскольку скрипт ведёт учёт состояния портфеля, он должен знать и об этих сделках, и узнаёт он о них именно через OnTrade. При этом он способен определить, какая именно это сделка: своя или &quot;левая&quot;, но для &quot;левых&quot; заявок он не знает, какой она величины (разве что получит статус &quot;заявка исполнена&quot;).<br /><br />2. Пользователь может не только подать заявку, но и снять её, причём не только свою, но и сделанную скриптом. О таких &quot;подлянках&quot; скрипт не может узнать в принципе (если отказаться от OnOrder и не ползать по таблицам).<br /><br />3. В момент подачи заявки на покупку скрипт резервирует необходимое количество соответствующей валюты, но если заявку подаёт пользователь, такого резервирования нет, и потому он закрывает заявку не из резерва, а из свободной наличности.<br /><br />4. Через некоторое время (скажем, 3-5 минут) скрипт должен принудительно снимать заявки. Собственно, закрытые заявки (здесь уже наверняка пришли все возможные прерывания) не снимаются - просто редактируется паспорт состояния соответствующего тикера, а вот активные (они могут быть только свои, заявки юзера скрипт снимать не имеет права) нужно убивать через KILL_ORDER.<br /><br />Примерная структура паспорта (i-го тикера), касающаяся заявок/сделок:<br />&#91;i&#93;&#91;&quot;Orders&quot;&#93; - сам паспорт (таблица Lua, то бишь дерево)<br />&#91;i&#93;&#91;&quot;Orders&quot;&#93;&#91;&quot;C&quot;&#93; - значение счётчика прерываний по таймеру, после которого можно снимать заявки (пока кажется разумным иметь общее для всех заявок, в противном случае нужно это поле воткнуть в паспорт заявки)<br />&#91;i&#93;&#91;&quot;Orders&quot;&#93;&#91;&quot;N&quot;&#93; - количество (незакрытых) заявок<br />&#91;i&#93;&#91;&quot;Orders&quot;&#93;&#91;j&#93; - массив паспортов заявок (я люблю C, так что нумерация с нуля).<br />&#91;i&#93;&#91;&quot;Orders&quot;&#93;&#91;j&#93;&#91;&quot;ID&quot;&#93; - ID заявки в торговой системе<br />&#91;i&#93;&#91;&quot;Orders&quot;&#93;&#91;j&#93;&#91;&quot;n&quot;&#93; - количество лотов в заявке (для &quot;левых&quot; заявок 0)<br />&#91;i&#93;&#91;&quot;Orders&quot;&#93;&#91;j&#93;&#91;&quot;N&quot;&#93; - количество сделок по j-й заявке<br />&#91;i&#93;&#91;&quot;Orders&quot;&#93;&#91;j&#93;&#91;k&#93; - массив паспортов сделок (нумерация с нуля) с (кажется) единственным значением в паспорте: &#91;i&#93;&#91;&quot;Orders&quot;&#93;&#91;j&#93;&#91;k&#93; - ID сделки (чтобы игнорировать &quot;лишние&quot; прерывания)<br /><br />Что-то громоздко получается... Где наврал? <br />
			<i>07.03.2021 16:29:57, Владимир.</i>]]></description>
			<link>http://forum.quik.ru/messages/forum10/message53514/topic6317/</link>
			<guid>http://forum.quik.ru/messages/forum10/message53514/topic6317/</guid>
			<pubDate>Sun, 07 Mar 2021 16:29:57 +0300</pubDate>
			<category>Программирование на языке Lua</category>
		</item>
	</channel>
</rss>
