Как отследить в Lua ситуацию, что торговля по определённым (или всем) инструментам запрещена?
В терминале кнопки для выставления транзакций заблокированы, т. е. терминал знает, что торговля по этому инструменту запрещена. Как определить это в Lua?
sendTransaction возвращает ошибку 'Указанная транзакция по указанному классу не найдена: "CETS".' тестирую на транзакции:
Скрытый текст
TYPE = L OPERATION = S ACTION = NEW_ORDER CLASSCODE = CETS TRANS_ID = 739621792 SECCODE = USD000000TOD QUANTITY = 1 CLIENT_CODE = //R4319 PRICE = 69.9150 ACCOUNT = ACCOUNT
Андрей написал: Знаю что Вы очень заняты, и у Вас очень много важных дел. Тем не менее сохраняю надежду на вашу доброту и милость.
Почему бы Вам не добавить в качестве скриптового языка PHP. А не этот недоязык LUA, который чисто по недоразумению стал таким популярным.
Тем более что готовые решения уже есть нужно только DLL откомпилировать. Работы на пару недель всего то.
Вы можете либо сами добавить поддержку PHP, либо заказать разработку DLL, которая будет связывать Lua и PHP специально под свои нужды у профессионального разработчика. Благо, QLua позволяет подгружать любые DLL.
Задача добавления самого PHP нецелесообразна потому, что (Q)Lua уже предоставляет достаточную гибкость в разработке. То, что Вы не знаете Lua -- это не проблема Lua. Разводить зоопарк поддерживаемых языков в QUIK тоже особого смысла нет. В QUIK'е даже отказались от дальнейших улучшений QPILE, вожможно, по этой же причине.
QLua API бы ещё чуть-чуть доработать... но это уже лирика.
s_mike@rambler.ru написал: Меня же интересует, в частности, вложенность натив луа функций, в том числе рекурсии. Рекурсия в 20 уровней - это слишком грустно.
То, что LUA_MINSTACK = 20 ничего не значит, т.к. LUA_MINSTACK - это минимальный выделенный размер стека Lua на момент вызова пользовательской Си-функции.
Согласно https://www.lua.org/source/5.0/llimits.h.html Количество вложенных в друг друга функций (и Си-функций и Lua-функций) в Lua - это LUA_MAXCALLS = 4096, из них может быть только LUA_MAXCCALLS = 200 вызовов пользовательских Си-функций (всякие SearchItems и т.п.)
помогите исправить ошибку attempt to index field(a nil value), при запуске скрипта если стакан не полный то вылезает ошибка attempt to index field(a nil value)
Сейчас я работаю с данными при помощи Subscribe_Level_II_Quotes и Unsubscribe_Level_II_Quotes. При этом подписка на данные переносится на следующий день. (т.е. я не отписаваюсь в обработчике OnDisconnected)
В какой момент нужно проверять, что таблица всех инструментов обновилась (добавились новые, удалились старые)? В момент начала выполнения обработчика OnConnected таблица инструментов уже обновлена?
Что делать с подписками по Subscribe_Level_II_Quotes на удаленные инструменты?
Робот у меня может непрерывно работать в нескольких месяцев.
Нет это не одно и тоже. Это два совершенна разных продукта, но выполняющие одну роль. Грубо говоря, если не вдаваться в подробности, то FIX Client Connector это урезанная версия QUIK FIX Adapter. В частности одной из "урезанных" функций является отсутствие xml схем. Это представлена по той ссылке которую Вы привели:
Ок. А где мне .xml-схему для FIX Client Connector найти? У брокера запрашивать отдельно?
для Вас QUIK FIX Adapter и FIX Client Connector - это одно и то же? Мы с Вами об одном инструменте говорим?
в дистрибутиве, который получил я не было ни одного .xml-файла, получается мне его у брокера нужно отдельно запрашивать? (ниже список файлов, которые у меня уже есть)
Я имею ввиду примеры FIX сообщений, чтобы можно было написать простого работающего робота, который, например, получал список инструментов и выдавал ордер на покупку какого-нибудь из них по фиксированной цене и объёму, заданному в коде, чтобы затем расширять функционал, зная, что базовые функции уже работают и если проблемы и будут, то не в коде, а в конфигурации.
_sk_, Не так сложно написать отдельную .dll, которая будет создавать отдельный Lua-поток и запускать его в новом системном потоке. НО! Этот созданный поток нужно будет аккуратно завершать перед выходом из main, т. к. Quik о нём ничего знать не будет. Вот поэтому вопрос: действительно ли нужен такой функционал?
Ну, если DS на стеке, что ж Вы его в аргументах вызова lua_pcall не учитываете? И порядок элементов на стеке у Вас неправильный. Должно быть { Size, DS } а не { DS, Size }
Stanislav Tvorogov, можно ли к транзакции на удаление ордера указать комментарий? Если да, то как это сделать? CLIENT_CODE = "//comment" не работает: в broker_ref приходит пустая строка.
getSecurityInfo возрващает поле min_price_step с типом number. Некоторые инструменты имеют min_price_step 0.05. Число 0.05 не имеет точного представления в типе number (double).
Код
local info = getSecurityInfo("SPBFUT", "MMM6");
message(string.format("%.20f", info.min_price_step), 1); -- см. 3 последние цифры
Отсюда возникают вопросы: 1. Планируется ли менять тип min_price_step с number на string. 2. Регламентировано ли (и где) максимальное число значащих цифр после запятой для min_price_step. 3. Те же вопросы для любой переменной типа number c вещественной частью, возвращаемой через QLua API (пока столкнулся только с min_price_step).
Почему это важно: Например, проверка на кратность цены вида ↓ работать не будет
Вопрос в тему: На практике кто-нибудь сталкивался с тем, что сумма всех qty > запрашиваемого объёма. Я имею ввиду, всегда ли гарантировано хватает точности Lua-типа number (или Си типа double) для хранения значения объёма (и проведения с этим значением поэтапного вычитания - вычисление невыполненного объёма)?
У вас Index внутри цикла может принять значение 0, следовательно в вызов getItem вы передадите -1, что неправильно. Советую заменить условие на строгое Index > 0 или заменить получение последнего индекса
Код
Index = getNumberOf("stop_orders")-1
или использовать for с обратным шагом
Код
function main()
local counter = 0;
local key;
for i=getNumberOf("stop_orders")-1,0,-1 do
local item = getItem("stop_orders", i)
if item.sec_code = "SiM6" and item.withdraw_time == 0 then
key = item.order_num
counter = counter + 1
if counter >= 2 then
message("ERROR",3)
end
end
end
end
dj.lexus, Читайте внимательно документацию. У Вас сейчас есть всё для того (и книга и google), чтобы писать правильный код. Также отладочной печатью в контрольных точках можно понять, после какой операции "падает" программа, а с помощью отладочной печати значений lua_gettop(L) - что происходит со стеком Lua во время выполнения. Или наймите программиста / консультанта, который сделает это за Вас.
Кстати, учтите, если Вы будете вызывать Ваши Си функции из разных потоков - поток main и поток callback'ов - то Вам нужно будет обеспечивать синхронный доступ к Вашим глобальным переменным. Можно с помощью std::mutex / std::recursive_mutex.
dj.lexus написал: Получается, нужно знать индекс элемента, который требуется положить в стек. Как же сохранить ссылку на таблицу, чтобы она "не потерялась" до конца работы программы? Или не перепуталась со ссылкой на другой источник данных.
Если между вызовами Си функций из Lua, то можно сохранить это значение в LUA_REGISTRYINDEX - это специальная таблица, видимая только из Си и хранящая важную информацию (удалять из неё что-то не своё точно не надо). См. API
dj.lexus написал: Видимо, я не до конца понимаю организацию стека Lua.
Рекомендую к прочтению книгу "Roberto Ierusalimschy - Programming in Lua - 2013". В своё время потратил на неё неделю, и не жалею.
Цитата
dj.lexus написал: Правильно ли я понял, что таблица DS существует, пока указатель на неё хранится в стеке?
Немного подкорректирую Ваш вопрос:
Цитата
Правильно ли я понял, что значение DS существует, пока оно хранится в стеке?
Да. Это правильно.
Само значение, кроме того, можно скопировать (например, в качестве значения одного из ключей другой таблицы; lua_pushvalue как раз копирует значение). Таблицы на стеке хранятся в виде ссылок на содержимое таблиц, т.е. при таком копировании сами элементы таблицы не копируются - копируется только ссылка на таблицу.
За освобождение памяти выделенной под Lua-таблицы, userdata и строки отвечает сборщик мусора. Освобожение памяти обычно происходит позже удаления всех ссылкок на таблицу / userdata / string.
Кстати, а Вы уверены, что DS - это таблица? Что говорит
Код
message(type(DS),1);
?
Если DS - это таблица, то QLUA API в этом месте сделано немного кривовато, т. к. DS можно бы было сразу сделать userdata, чтобы Си код обработки методов DS выглядел лаконичней и чуть производительней.
int myfunction(lua_State* L) {
// DS = CreateDataSource("SPBFUT", "RIM6", INTERVAL_M1); // Работает
lua_getglobal(L, "CreateDataSource");
lua_pushstring(L, "SPBFUT");
lua_pushstring(L, "RIM6");
lua_getglobal(L, "INTERVAL_M1");
lua_call(L, 3, 1);
// результат на стеке
int DS = lua_gettop(L);
// C = DS:C(1); // Не получается
lua_getfield(L, DS, "C");
lua_pushvalue(L, DS);
lua_pushnumber(L, 1);
lua_call(L, 2, 1);
// результат на стеке
int C = lua_gettop(L);
// DS:Close(); // Не получается
lua_getfield(L, DS, "Close");
lua_pushvalue(L, DS);
lua_call(L, 1, 0);
// В Lua
// obj:func(param);
// это то же самое что
// obj.func(obj, param);
// как-то так
return 0;
}
Вячеслав написал: в созданном системном потоке в рамках main_lua_thread вызывается Lua-функция main. Без установки обработчиков ошибок это выглядит так: lua_getglobal(main_lua_thread, "main"); lua_pcall(main_lua_thread, 1, 0, 0);
Нет функции редактирования, поэтому исправляюсь: lua_pcall(main_lua_thread, 0, 0, 0);
Из книги "Roberto Ierusalimschy - Programming in Lua - 2013"
Цитата
The io.read function reads strings from the current input file. Its arguments control what to read: “*a” reads the whole file “*l” reads the next line (without newline) “*L” reads the next line (with newline) “*n” reads a number num reads a string with up to num characters
Код
-- открываете файл
local f = io.open("myfile.txt", "r"); -- или "rb" если хочется читать в "бинарном" режиме
-- читаете
message(io.read("*l"),1)
-- во-первых, Lua-функции могут возвращать несколько значений. К этому привыкаешь не сразу.
function f()
return 1, "test", 5;
end
-- во-вторых все результаты одной функции можно перенаправить в другую
local f = io.open("log.log", "w+");
f:write(f())
-- выведет "1test5" ( то же самое, что и вызов io:write(1,"test",5); )
-- в-третьих, для обработки ошибок связанных с открытием файлов и не только, в Lua есть специальная функция assert
local f = assert(io.open("log.log", "w+"));
-- её алгоритм эквивалентен следующему:
function assert_alg(success, message)
if not success then
error(message);
end
end
-- также assert удобно использовать для проверки правильности программы
assert(qty > 0, "Объём должен быть больше 0");
-- в случае, если условие ложно, выведелся ошибка "Объём должен быть больше 0"
тот самый написал: проще и безопасней, в таком случае, передавать параметры в строковом типе и если параметра нет - ставить "" - т.е. ничего не ставить. присваивая же "nil" в LUA. Может получится так, что сборщик мусора - совсем избавится от этого параметра (честно говоря, давно не проверял - но, уже писалось кажется об этом).
Не не не не. Старатель знает, что пишет. И сборщик мусора тут не при чём. Любая Lua-таблица может быть проиндексирована любым значением в качестве индекса (кроме nil). В результате вы получите nil в случае, если ключ в таблице отсутствует или значение ключа. Поэтому код
Код
function OnTrade(trade)
message(tostring(trade.aadfadfafdaf), 1);
end
не затрагивает сборщик мусора и абсолютно безопасен, хоть и бессмысленен.
Вячеслав написал: Да, создаются 2 системных потока, да, в каждом из них выполняется lua_pcall
Всегда, при разговоре на эту тему - разделяйте понятия: системный поток - это НЕ LUA-поток, а поток ОС LUA-поток - это не поток ОС. LUA-поток - сродни корутинам.
В самой LUA - никакие системные потоки - не создаются
Ок, разделяю: Есть системный поток (основной поток процесса info.exe). При запуске Lua-скрипта:
создаётся lua_State *global = luaL_newstate();
выполняется код Lua-скрипта (по-простому - всё что вне декларций функций)
вызывается OnInit(имя_файла_скрипта)
создаётся новый Lua поток lua_State *main_lua_thread = lua_newthread(global);
созданный Lua-поток помещается в LUA_REGISTRYINDEX (luaL_ref), чтобы он не разрушался, и чтобы иметь возможность вызывать callback'и в основном Lua-потоке global.
создаётся новый системный поток
в созданном системном потоке в рамках main_lua_thread вызывается Lua-функция main. Без установки обработчиков ошибок это выглядит так: lua_getglobal(main_lua_thread, "main"); lua_pcall(main_lua_thread, 1, 0, 0);
в процессе работы всё просто. Когда терминал хочет вызвать callback, он выполняет: lua_getglobal(global, "OnQuote"); lua_push... lua_pcall(global, *кол-во аргументов*, 0, *обработчик ошибок*); в случае ошибки, предполагаю, что терминал принудительно останавливает новый системный поток и останавливает выполнение скрипта. В этом случае возможна утечка памяти/ресурсов (ресурсов, например, если были загружены .dll с помощью require).
когда в новом системном потоке происходит выход из ф-ции lua_pcall, новый системный поток завершается
выполняется закрытие lua_close(global). main_lua_thread Lua-поток разрушится сам, т.к. он хранится в REGISTRYINDEX в global lua_State.
Цитата
тот самый написал: 1. по понятным причинам, не ручаюсь на 100% но, код работы с коллбеками, завязан на отдельный LUA-поток в контексте одной виртуальной машины. Другой поток (и физический и LUA-поток) - это, ещё один коллбек - функция "main". Таким образом, никаких проблем там - уже давно не возникает.
Можно ещё разок. Я не понял, что Вы имеете ввиду.
Цитата
тот самый написал: 2. любой доступ к глобальным переменным - обёрнут критическими секциями. Это уже было сказано Михаилом Булычевым. Строго говоря, такие вопросы Вам - и надо ему задавать, а не "бодаться" с другими.
Случайно нет ссылки на то, что этот человек сказал и кто этот человек?
Цитата
Николай Камынин написал: все решается с помощью одного event, функций sinsert и sremove и использованием глобальных переменных.
Мне сложно Вам отвечать, т.к., возможно, Вы либо не понимаете, как устроен многопоточный доступ к Си данным lua_State, либо, в чём отличие Event (созданного с помощью CreateEvent) от критической секции (мьютекса).
имеется ввиду, что функции из lapi.c вызывают внутренние функции в библиотеке Lua, и их же вызывает luaV_execute напрямую. Например, lua_settable вызывает luaV_settable, которую напрямую (через макрос) вызывает luaV_execute.