<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
	<title type="html"><![CDATA[QUIK -> DDE &mdash; Советы по написанию Lua-роботов от профессионалов]]></title>
	<link rel="self" href="https://quik2dde.ru/extern.php?action=feed&amp;tid=286&amp;type=atom" />
	<updated>2023-01-06T18:55:03Z</updated>
	<generator>PunBB</generator>
	<id>https://quik2dde.ru/viewtopic.php?id=286</id>
		<entry>
			<title type="html"><![CDATA[Re: Советы по написанию Lua-роботов от профессионалов]]></title>
			<link rel="alternate" href="https://quik2dde.ru/viewtopic.php?pid=4089#p4089" />
			<content type="html"><![CDATA[<div class="quotebox"><cite>kalikazandr пишет:</cite><blockquote><p>4. с генерацией Trans_id и UID терминала не стоит заморачиваться вообще. Есть поле brokerref(комментарий), которое делаем так:<br /><strong>clientcode..&quot;//&quot;..botname..id</strong>, нужно понимать, что коментарий составное 20-ти символьное поле и для фондовой секции арка удосужилась разделить его &quot;//&quot; а не &quot;/&quot;, т.е. украла 1 символ, а для срочки вообще пофик, можно любой разделяющий символ.</p></blockquote></div><p>Насколько я понял, символ не крали: в полном формате в sendTransaction в поле &#039;CLIENT_CODE&#039; идёт код клиента, а через косую его субсчёт, потом через косую может идти комментарий. Если заявка делается не с субсчёта, то вместо него пусто, а косая остаётся, чтобы комметарий не принять за субсчёт. Отсюда и две косые подряд.</p>]]></content>
			<author>
				<name><![CDATA[Serge1234]]></name>
				<uri>https://quik2dde.ru/profile.php?id=3387</uri>
			</author>
			<updated>2023-01-06T18:55:03Z</updated>
			<id>https://quik2dde.ru/viewtopic.php?pid=4089#p4089</id>
		</entry>
		<entry>
			<title type="html"><![CDATA[Re: Советы по написанию Lua-роботов от профессионалов]]></title>
			<link rel="alternate" href="https://quik2dde.ru/viewtopic.php?pid=2341#p2341" />
			<content type="html"><![CDATA[<div class="quotebox"><cite>swerg пишет:</cite><blockquote><p><strong>kalikazandr</strong>, спасибо вам!!<br />Это в самом деле очень ценные знания, за которыми явно куча практического опыта.</p></blockquote></div><p>Н.з.ч) практика, да.<br />Алгоритмы сложные пишу по 10-20 &quot;умных&quot; заявок на открытие/закрытие/реверс, боты одной и разных стратегий общаются с собой через StaticVar. Один раз написал блок контроля заявок и не думаю - а что же делать с конкретной заявкой - они все для ботов обезличены и имеют встроенные алгоритмы поведения: поиск сигнала - сопровождение позиции.</p><p>Например бот(Si66000), который работает на страйке 66000, транслирует в общее пространство имен волатильность этого страйка, т.к. это его страйк и это центральный страйк, а бот(Si65500) ничего не знает о коде инструмента на 66000 страйке, но знает текущую волатильность на центральном страйке, а также открытую позицию пут/кол/фьюч на центральном страйке, ну и вообще всю инфу об остальных ботах-страйках, а так же инфу ботов других базовых активов.</p><p>Так же половину алгоритма я скидываю в таймеры qvcl - это закрытие позиций по времени, которые не требуют скорости.<br />Связка потоков квик-main-timer позволяет одновременно работать 500 роботам без сколько-то видимой нагрузки на ЦП</p><p>запускаемый бот выглядит вот так:<br /></p><div class="codebox"><pre><code># steps.lua
strike = 66000
base = &quot;Si&quot;

package.path=package.path..&quot;;C:/Creator/lib/?.lua;C:/Creator/Strategy/steps/?.lua&quot;
package.cpath=package.cpath..&quot;;C:/Creator/lib/?.dll&quot;
--package.loaded.steps = nil -- раскоментировать если была ошибка загрузки торгового модуля
dofile(&quot;C:/Creator/smart_forts.lua&quot;) -- блок контроля &quot;умной заявки&quot;, содержит main

local steps = require&#039;mysteps&#039;
function Robot()
    while working do
        steps:init()
        Trade()
        while start do
            steps:process()
            Trade()
        end
        Trade()
    end
end</code></pre></div><p>Клонируем, меняем страйк и базовый актив - бот готов)</p>]]></content>
			<author>
				<name><![CDATA[kalikazandr]]></name>
				<uri>https://quik2dde.ru/profile.php?id=208</uri>
			</author>
			<updated>2019-02-27T12:06:31Z</updated>
			<id>https://quik2dde.ru/viewtopic.php?pid=2341#p2341</id>
		</entry>
		<entry>
			<title type="html"><![CDATA[Re: Советы по написанию Lua-роботов от профессионалов]]></title>
			<link rel="alternate" href="https://quik2dde.ru/viewtopic.php?pid=2339#p2339" />
			<content type="html"><![CDATA[<p><strong>kalikazandr</strong>, спасибо вам!!<br />Это в самом деле очень ценные знания, за которыми явно куча практического опыта.</p>]]></content>
			<author>
				<name><![CDATA[swerg]]></name>
				<uri>https://quik2dde.ru/profile.php?id=78</uri>
			</author>
			<updated>2019-02-26T06:19:27Z</updated>
			<id>https://quik2dde.ru/viewtopic.php?pid=2339#p2339</id>
		</entry>
		<entry>
			<title type="html"><![CDATA[Re: Советы по написанию Lua-роботов от профессионалов]]></title>
			<link rel="alternate" href="https://quik2dde.ru/viewtopic.php?pid=2338#p2338" />
			<content type="html"><![CDATA[<p>Кстати, хочу отметить, что OnOrder не вызывается терминалом по событию получения данных о заявке терминалом. У них там sleep и очередь. Т.е. если в main делать опрос на предмет новой записи в таблице заявок/сделок и др., то в main вы увидите событие в 99% случаев раньше на 15 мс, чем будет вызван соответствующий callback в вашем алгоритме. <br />Единственное не удобство - это отжим времени у торгового алгоритма, за счет ожидания полной загрузки данных в таблицу иначе можно поймать nil или пол цены(без дробной части, например).</p><p>А вот отсутствие TransReply - тут недавно совсем арке писал про это - пяткой в грудь себя били - &quot;у нас всегда все TransReply приходят - это ты м*дак&quot;.</p>]]></content>
			<author>
				<name><![CDATA[kalikazandr]]></name>
				<uri>https://quik2dde.ru/profile.php?id=208</uri>
			</author>
			<updated>2019-02-22T16:20:58Z</updated>
			<id>https://quik2dde.ru/viewtopic.php?pid=2338#p2338</id>
		</entry>
		<entry>
			<title type="html"><![CDATA[Re: Советы по написанию Lua-роботов от профессионалов]]></title>
			<link rel="alternate" href="https://quik2dde.ru/viewtopic.php?pid=2337#p2337" />
			<content type="html"><![CDATA[<p>как то все сложно. </p><p>1. <strong>OnTransReply</strong> обрабатываем только ошибки - не пришла и хрен с ней, значит ошибки нет.<br />Если ошибка не связана с состоянием сессии то желательно бота выключить, т.к. где-то у вас ошибка алгоритма, которую нужно править при выключенном боте. Если таки ошибка - &quot;сессия не идет&quot;, то лучше не пытаться выставить заявку заново со старой ценой и объемом - обнулить сигнал и получить новый после начала сессии.<br />2. <strong>OnOrder</strong> - делаем таблицу Orders = {order_num = true}, - если еще такого номера заявки в Orders нет - запоминаем и return. Если № есть - передаем в <strong>main</strong> для обработки.<br />&nbsp; &nbsp; 2.1 active/status - состояния заявки:<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;0/&quot;O&quot;/&quot;K&quot;/&quot;F&quot; - нет заявки ожидание/снята/исполнена;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;1/&quot;O&quot; - транзакция новой заявки оправлена на сервер квик;<br />&nbsp; &nbsp; &nbsp; &nbsp; -1/&quot;A&quot; - транзакция снятия заявки отправлена на сервер квик;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;2/&quot;A&quot; - заявка зарегистрирована в ТС - активна.<br />В алгоритме проверяем <strong>if math.abs</strong>(active) == 1 <strong>then return end</strong> - транзакция в пути.<br />3. <strong>OnTrade</strong> - не нужна, есть функция быстрого поиска (сделки ищем по № заявки), если исполненное кол-во лотов в заявке не равняется кол-ву лотов в сделках - курить, пока не будет равенства, иначе квик не пересчитает лимиты, особенно критично для заявок на закрытие позиции. Заодно можно посчитать avg_price.<br />4. с генерацией Trans_id и UID терминала не стоит заморачиваться вообще. Есть поле brokerref(комментарий), которое делаем так:<br /><strong>clientcode..&quot;//&quot;..botname..id</strong>, нужно понимать, что коментарий составное 20-ти символьное поле и для фондовой секции арка удосужилась разделить его &quot;//&quot; а не &quot;/&quot;, т.е. украла 1 символ, а для срочки вообще пофик, можно любой разделяющий символ.<br />botname - имя стратегии; id - что душе угодно, 1,2,3... вполне себе подойдет.<br />Если бот видит, что brokerref не его - вот и фильтр, на все случаи.<br />5. Сделать таблицу CloseOrders = {order_num = true,} На старте бота делать обход таблицы заявок на предмет поиска не активных заявок, их запоминать и не принимать по ним OnOrder, все не активные заявки в ходе торговли помещать туда. Чистить CloseOrders по мере необходимости.<br />6. Сделать таблицу ActiveOrders = {[brokerref] = order}, где order содержит первоначальное состояние отправленной заявки в ТС и потом его сравнивать с изменениями этой заявки.<br />7. Если нет ответа на транзакцию выставления/снятия заявки, то ни в коем случае нельзя пытаться ее перевыставить. Лучше ожидать ответ до посинения и в итоге его получить и обработать должным образом. Если у бота чешется и очень срочно хочется/нужно купить/продать - то создать другую заявку, а старую не забывать, она не обязательно потерялась в сети, просто подлый брокер занимается махинацией и держит события от ТС на прокси, а в нужный момент отправит события, связанные с этой заявкой клиенту. Только не факт, что и по этой заявке ответ придет сразу, что чревато, т.к. депо может внезапно кончится, а цена приобретения инструмента неожиданно будет на десяток - другой процентов хуже - здравствуйте Дядя Коля. Краями, можно попытаться выставить заявку по минимально/максимально возможной цене 1-м лотом, и если по ней ответ придет, тогда можно считать, что прошлая транзакция сдохла в сети, но и это не 100% гарантия. Надежнее всего звонить брокеру, и спрашивать, а где же где же заявка с вот таким trans_id, по инструменту и время отправки и если ее нет, то перезапустить бота.</p><p>Все.</p>]]></content>
			<author>
				<name><![CDATA[kalikazandr]]></name>
				<uri>https://quik2dde.ru/profile.php?id=208</uri>
			</author>
			<updated>2019-02-22T16:01:36Z</updated>
			<id>https://quik2dde.ru/viewtopic.php?pid=2337#p2337</id>
		</entry>
		<entry>
			<title type="html"><![CDATA[Re: Советы по написанию Lua-роботов от профессионалов]]></title>
			<link rel="alternate" href="https://quik2dde.ru/viewtopic.php?pid=2336#p2336" />
			<content type="html"><![CDATA[<p>.</p>]]></content>
			<author>
				<name><![CDATA[swerg]]></name>
				<uri>https://quik2dde.ru/profile.php?id=78</uri>
			</author>
			<updated>2019-02-21T07:03:04Z</updated>
			<id>https://quik2dde.ru/viewtopic.php?pid=2336#p2336</id>
		</entry>
		<entry>
			<title type="html"><![CDATA[Советы по написанию Lua-роботов от профессионалов]]></title>
			<link rel="alternate" href="https://quik2dde.ru/viewtopic.php?pid=2335#p2335" />
			<content type="html"><![CDATA[<p>На форуме QUIK наткнулся на совершенно шикарный список советов про создание скриптов на Lua от пользователя <strong>_sk_</strong><br />Скопирую сюда.</p><h5>Что надо продумать при создании торгового робота на Lua</h5><p>Порядок OnTransReply -&gt; OnTrade -&gt; OnOrder обычно соблюдается, но может нарушаться. Программу надо писать так, чтобы она работала всегда. Вы ведь не хотите получить внезапно ошибку и убыток из-за неё.</p><p>Многократные ответы OnTrade() появились в 7-й версии. В повторных ответах могут добавиться некоторые поля.</p><p>Функция sendTransaction() может успешно выполниться, а OnTransReply() не придёт. Такое бывает, но очень редко. Однако, программу надо писать так, чтобы она работала всегда.</p><p>С асинхронностью нужно разобраться сразу и навсегда, чтобы не словить внезапных глюков. Один из способов такой: при получении коллбэка передавать данные через очередь из потока коллбэков в поток main(). В потоке main() периодически проверять очередь и обрабатывать данные, которые из неё приходят.</p><p>Проблему можно решить без участия команды QUIK. Надо прочитать документацию, разработать модель, запрограммировать её. Форум поможет при решении более тонких вопросов.</p><p>Основные шаги реализации примерно такие.</p><p>1) Нужны статусы заявок типа WAITING (ещё не отправлена), EXECUTING (отправлена на биржу), STOPPING (снимается, но не вся информация дошла), STOPPED (снята), EXECUTED (исполнена полностью), KILL_REJECTED (kill-заявка отвергнута), ERROR (ошибка).</p><p>2) Нужны таймауты для борьбы с неопознанными &quot;посторонними&quot; заявками (от других скриптов), а также заявками, для которых не пришёл OnTransReply().</p><p>3) Нужно следить за UID экземпляра QUIK, чтобы фильтровать &quot;чужие&quot; заявки.</p><p>4) Нужна функция генерации уникальных номеров транзакций, обеспечивающих непересекающиеся множества transId для разных скриптов.</p><p>5) Нужна таблица актуальных limit-заявок, куда заявка попадает при успешном вызове sendTransaction(), а удаляется при полном или частичном исполнении и ошибках.</p><p>6) Нужна таблица актуальных kill-заявок, куда заявка попадает при попытке снять limit-заявку, а удаляется при ошибках и исполнении.</p><p>7) Нужна таблица ответов о сделках на актуальные limit-заявки.</p><p>8) Нужен фильтр событий от &quot;чужих&quot; OnTrade(), например, работающий по комментарию к заявке.</p><p>9) В limit-заявке нужно помнить volume, volumeTraded, volumeLeft. Обычно volumeTraded + volumeLeft == volume, но иногда при снятии частично исполненной заявки становится понятно, чему равен volumeLeft и надо дождаться событий OnTrade(), которые ещё пока не пришли. При приходе OnTrade() нужно отбрасывать дубликаты (в 7-й версии) и корректировать volumeTraded, volumeLeft. Как только volumeLeft == 0, так удалять заявку из таблицы актуальных вместе со всеми связанными с ней kill-заявками и ответами на них.</p><p>10) При попытке снять limit-заявку иногда надо ждать, чтобы понять, какой order_num у отправленной заявки, поскольку ещё мог не придти ответ OnTransReply().</p><p>11) Периодически проверять, не стали ли известны order_num для лимитных заявок, для которых есть kill-заявки.</p><p>12) Периодически проверять, не стало ли известно, каким лимитным заявкам соответствуют OnTrade(), которые пришли раньше, чем OnTransReply().</p><p>13) Периодически разбираться с тем, сработали ли отправленные kill-заявки.</p><p>14) Удалять старые &quot;неопознанные&quot; сделки.</p><p>15) Реагировать на OnTransReply(), связывая transId скрипта и order_num биржи для limit-заявок. Понимать, как сработали kill-заявки. Проверять ждущие kill-заявки, если они есть.</p><p>16) Реагировать на OnTrade(), игнорируя дубликаты, логгируя соответствующие сделки.</p><p>17) Реагировать на OnOrder(), связывая transId скрипта и order_num биржи для limit-заявок. Понимать, что для некоторых уже неактивных заявок не дошли OnTrade() и логгировать сообщения об ошибках.</p><p>18) Периодически обрабатывать kill-заявки, чтобы понять, какие из них остались без OnTransReply(). Посылать новые, если что.</p><p>19) Периодически обрабатывать limit-заявки, чтобы понять, для каких из них получились кросс-сделки, а какие остались без OnTransReply(). Проверять, limit-заявки, которые почему-то неактивны, а volumeLeft не равен 0, и логгировать ошибки.</p><p>Как-то так.</p>]]></content>
			<author>
				<name><![CDATA[swerg]]></name>
				<uri>https://quik2dde.ru/profile.php?id=78</uri>
			</author>
			<updated>2019-02-21T07:01:08Z</updated>
			<id>https://quik2dde.ru/viewtopic.php?pid=2335#p2335</id>
		</entry>
</feed>
