nikolz (Все сообщения пользователя)

Выбрать дату в календаре…Выбрать дату в календаре

Страницы: 1 2 3 4 5 6 7 8 9 10 11 ... 95 След.
Баги программы, Баги программы
 
Цитата
flash написал:
Добрый день, уважаемые разработчики. Обнаружил баг при передаче неполного стакана по DDE. Версия QUIK 13.1.1.15, брокер БКС.

В QUIK стакан выглядит так:



При передаче этого стакана по DDE приходит следующее:
Код
  Rows x Cols: 9x3
CellType: String. Value = Покупка         |  CellType: String. Value = Цена            |  CellType: String. Value = Продажа
CellType: String. Value = (empty string)  |  CellType: String. Value = (empty string)  |  CellType: String. Value = (empty string)
CellType: String. Value = (empty string)  |  CellType: String. Value = (empty string)  |  CellType: String. Value = (empty string)
CellType: String. Value = (empty string)  |  CellType: String. Value = (empty string)  |  CellType: String. Value = (empty string)
CellType: String. Value = (empty string)  |  CellType: String. Value = (empty string)  |  CellType: String. Value = (empty string)
CellType: String. Value = (empty string)  |  CellType: String. Value = (empty string)  |  CellType: String. Value = (empty string)
CellType: String. Value = (empty string)  |  CellType: String. Value = (empty string)  |  CellType: String. Value = (empty string)
CellType: String. Value = (empty string)  |  CellType: String. Value = (empty string)  |  CellType: String. Value = (empty string)
CellType: String. Value = (empty string)  |  CellType: Double. Value =  10300            |  CellType: Double. Value =  1   

Тут видно что в стакане на скрине всего 8 заполненных строк + одна строка заголовка, остальные строки пустые. При передаче по DDE приходит 8 + 1 строка, т.е. количество строк правильное, и видимо было намерение передать именно 8 заполненных строк, не передавая пустые. Но передаются все строки с начала стакана подряд, включая пустые, заполненная строка в итоге передана только одна, остальные данные, таким образом, не передаются.
У вас где-то ошибка.
вот результат вывода стакана по DDE
Описание полей в методе on_trans_reply
 
Цитата
VPM написал:
Дествительно не подумал и повелся на маркетинговые мифы о "Си-скоростях". Любое значение Lua — это сложная структура TValue, и побитовые операторы в Lua 5.4 действительно не являются прямыми ассемблерными инструкциями ЦП.

nikolz,  Спасибо за замечание и системное погружение в исходный Си-код языка. Это позволяет строить более осознанную архитектуру, понимая такт процессора.

Вывод Lua = Таблица, Lua ~= Ассемблер.

Несмотря на то, что на уровне исполнения это вызов функций ВМ, оператор & в Lua 5.4.1 работает быстрее, чем вызов старой внешней библиотеки bit.band(flags, 0x4). Следовательно вывод что встроенный оператор & всё же предпочтительнее для Квика сохраняется и это главное!
Согласен.  
Описание полей в методе on_trans_reply
 
Цитата
VPM написал:
Поскольку поддержка отмалчивается, проведу разбор самостоятельно, для себя и таких как я (хотя и слабо разбираюсь в этом), а что делать?

1. Опечатка в тексте руководства ARQA Technologies очевидна, авторы справки перепутали номера битов и шестнадцатеричные (HEX) маски. Авторы опечатались в HEX-записи. Они хотели написать бит 20 (маска 0x100000) и бит 21 (маска 0x200000). Но почему то написали 0x21 и 0x22?
Видимо механически скопировали номера битов, забыв перевести их в степени двойки и ошибочно записав номера битов 20 и 21 как 0x20 / 0x21, а затем сдвинули индексы?
Доверять HEX-значениям 0x21 и 0x22 из этой справки нельзя — они полностью неверны.

2. Ловушка Истины или почему 0 в Lua — это true? Это главная концептуальная ловушка для программистов, переходящих на Lua с языков Си, C++ или Python.
В отличие от Си/Python, где 0 — это false, любое другое число — это true.
В Lua (всех версий), логическим ложным значением (false) признаются исключительно два ключевых слова: false и nil. Всё остальное, включая число 0, пустую строку "" и пустую таблицу {}, интерпретатором Lua всегда признается истиной (true)!

Так как в терминал QUIK v11+ при работе на чистом Lua 5.4.1, встроенная библиотека bit (которая была внешней .dll в старых версиях) является устаревшей. Появилась возможность использовать родные поразрядные Си-операторы Lua 5.4.1, которые работают на уровне процессора и не тратят время на вызовы функций.
Собствено на этом можно и закончить. Хотя про это много уже писалось повторюсь. Вот золотой стандарт проверки флагов, который гарантирует отсутствие ложных срабатываний (как памятка):

Вариант 1. Проверка через родной оператор Lua 5.4.1 (&) — Рекомендуемый HFT-выбор. Оператор & (побитовое И) возвращает значение маски (например, 4) или 0. Проверяем строго через ~= 0:
-- Проверяем Бит №2 заявки/сделки (маска 2^2 = 4, то есть 0x4)
-- На Мосбирже: flags & 0x4 ~= 0 означает "Сделка на ПРОДАЖУ"
local is_sell = (flags & 0x4) ~= 0
if is_sell then
   -- Блок выполнится строго если бит присутствует (результат операции равен 4)
end

Вариант 2. Проверка через встроенную функцию bit.test в старых скриптах. Если используете функцию bit.test, она сама под капотом делает сравнение и возвращает чистый булев тип (true или false). Здесь оператор ~= 0 не нужен:
-- В функцию передается не маска, а чистый НОМЕР БИТА (начиная с 0)
local is_sell = bit.test(flags, 2) -- Возвращает true или false
if is_sell then
   -- Код безопасен, так как is_sell — это честный boolean
end
поясню почему в луа все значения true кроме nil,  
Это связано с тем что все данные в луа содержат в неявном виде свой тип
Поэтому  если значение типа не равно нулю то это true а если равно нулю то это false  
У nil значение типа равно нулю.
--------------------------------------
Утверждение :
"Появилась возможность использовать родные поразрядные Си-операторы Lua 5.4.1, которые работают на уровне процессора и не тратят время на вызовы функций."   ошибочно.
Так как значения переменных это не значения СИ а структуры, то для применения бинарных операций  вызываются  функции в соответствии с сохраненным типом переменных.
Поэтому бинарные логические и сдвиговые операции на символьном уровне записываются как в СИ но на уровне исполнения это вызов функций.
----------------------------------  
Блок перерасчёта данных индикатора, Актуально для арбитражных индикаторов и аналогичных, использующих несколько инструментов в своём расчёте.
 
Для наглядности покажите на графиках без и с пересчетом иначе непонятно зачем это городить.  
QUIK и жор памяти, Проблема с памятью скриптов Qlua
 
Цитата
Ziveleos написал:
Цитата
Кот написал:
for iord=lastact,N do
   local tab=getItem('orders',iord); -- получаем заявку i
  if tab~=nil then if tab.account==acc then if bit.band(tab.flags,0x1)==0x1 then
    ...  
end; end; end;
end;

Если сначала найти индексы всех интересующих заявок, и в цикле извлекать только их, то памяти может потребоваться меньше.

Код
     local   function   Fnd (f,a)
     return  f  &   1   =  =   1   and  a  =  =  acc
   end 
   local  aOrd  =   SearchItems ( "orders" , lastact, N, Fnd,  "flags, account" )  or  {}
  
   for  _, iord  in  ipairs(aOrd)  do 
     local  tab  =   getItem ( "orders" ,iord)
     --... 
   end 

  
в начале активные заявки ищутся один раз  что не критично для дальнейших расчетов
Но если потом выставляются новые заявки то вопрос именно в том как они находятся  
Если их искать по таблице заявок QUIK то все будет ровно так как написал автор темы
------------------------
А чтобы так не было надо делать свою таблицу активных заявок
Сначала ее заполняем по таблице заявок QUIK
а потом работаем с ней по колбеку заявок
QUIK и жор памяти, Проблема с памятью скриптов Qlua
 
Цитата
Кот написал:
Цитата
Код :

for iord=lastact,N do
    local tab=getItem('orders',iord); -- получаем заявку i
  if tab~=nil then if tab.account==acc then if bit.band(tab.flags,0x1)==0x1 then
     ...
   end; end; end;
end;  

Перебирал большое число заявок (все) и засорял память, хоть и срабатывал сравнительно редко.
До сих пор не понимаю как в qlua сделать локальную переменную массива (таблицы), чтобы при её обработке в цикле не возникало жора памяти?!
в версии 5.3 и 5.4 есть логические операции
вместо этого:
if bit.band(tab.flags,0x1)==0x1
можно писать это:
if  tab.flags&1==1
---------------------------------------  
измените алгоритм программы.
Создайте массив активных заявок.
И не надо будет много перебирать
QUIK и жор памяти, Проблема с памятью скриптов Qlua
 
Предположу ,что проблема в разном объеме в VMLUA свободной памяти.
Поэтому сборщик мусора работает по-разному.
QUIK и жор памяти, Проблема с памятью скриптов Qlua
 
Цитата
Кот написал:
Цитата
nikolz написал:

Посмотрите сколько памяти занимает каждый вариант QUIK  
Тот который не перегружает занимает 1200 мБ, другой, который перегружает 1870 мБ.

Пока что создаётся впечатление, что это работа OnTrnsReply(reply), так как перегрузка памяти идёт именно в моменты активной торговли другими системами.

Соответственно встаёт вопрос - как с этим бороться? Может быть стоит прописать reply= { } чтобы чистить память сразу из Коллбэка?
Потому что никакой сложной обработки у меня нет, фактически все завершается первой строчкой  :  if  reply.account~=Account then return; end;
Да и в остальном коде только поиски по тексту ошибки. Мегабайтам там уходить некуда!  
посмотрите на что Вы подписаны и сделайте число инструментов  в подписке одинаковым  
Сделайте одинаковое число открытых графиков
QUIK и жор памяти, Проблема с памятью скриптов Qlua
 
Цитата
Кот написал:
Случилось у меня такое интересное наблюдение :

Торговый робот на одной копии QUIK у одного брокера работает нормально, создаёт таблицу данных (43 мБ) и в течении всего времени работы поддерживает выделение памяти на этом уровне.
Тот же самый работ на другой копии QUIK (той же версии 8.13.1.6), но у другого брокера испытывает скачки памяти (в таблицах Qlua) с интервалом приблизительно в 3-5 секунд : память растёт от 43мБ до 86 мБ, после чего опять возвращается к уровню 43мБ.

Никакой разницы в роботах или версиях QUIK нет. Все тяжелые вычисления проводятся с намного большей периодичностью (меньшей частотой), приблизительно раз в 30-60 секунд и в обязательном порядке сопровождаются вызовом сборщика мусора. В промежутках между вычислениями сборщик мусора не вызывается, а вся нагрузка это не частые запросы типа Poll (опрос) к котировкам и таблицам заявок. Среди колбэков только легковесные OnOrder(), OnTrnsReply() и всё. При этом на обоих экземплярах QUIK ведётся торговля другими торговыми алгоритмами приблизительно с одинаковой интенсивностью, то есть все обработчики вызываются одинаково регулярно и их выполнение точно не занимает в памяти 40 мБ.

Подскажите, в чём может быть проблема?!  
Посмотрите сколько памяти занимает каждый вариант QUIK  
Работа колбеков
 
Цитата
Nikolay написал:
Цитата
nikolz написал:
Но колбек вызывается перед записью в таблицу обезличенных сделок.
Если читать из таблицы то будет вызов колбека, запись в таблицу, и потом чтение из нее.
Здесь есть сомнения, т.к. колбек на таблицу - это событие на изменение данных, которые приходят в пакетах. Что произойдет первым - это вопрос, т.к. я не раз ловил в логах ситуацию когда запись в таблице появлялась ранее чем колбек. Но даже если это такт, то не очень понятно, что это изменяет. Я бы сказал, наоборот, наличие такого частого колбека приведет к падению производительности. Организовать процедуру чтения порций с опросом числа записей в таблице - довольно банальная задача. Сотни тысяч записей записываются в файл за пару секунд. А далее, по мере поступления уже новых, просто их обрабатываем. Зачем для этого колбек в потоке терминала - не ясно. Сама идея событийной модели - это абстракция, за которой все равно будет код, проверяющий что событие наступило. Поэтому я предпочитаю сам это делать, с необходимой мне частотой.
Когда только сделали этот механизм тоже спорил с автором сей разработки об алгоритме решения  
Но автор так написал роман, а мы лишь читатели.
Использование мессенджера MAX для обмена данными в QUIK, Использование мессенджер для обмена для отправки сообщений и управление торговым приводом в QUIK
 
Еще один вариант.
Особо не отлаживал.
Выкладываю как есть:
Код
-- max_bridge.lua
-- Двусторонний мост QUIK <-> MAX Messenger
-- Lua 5.3/5.4: native bitwise, //, math.tointeger, <const>
-- Очереди: T={{},0,0} — массив + jw/jr, сброс в push

local MAX = {}

--========== КОНФИГУРАЦИЯ ==========--
MAX.TOKEN       = "ВАШ_ТОКЕН"
MAX.API_URL     = "https://platform-api2.max.ru"
MAX.CHAT_ID     = nil
MAX.LAST_MARKER = 0

MAX.LAST_SEND   = 0.0
MAX.LAST_POLL   = 0.0
MAX.ERR_COUNT   = 0
MAX.HTTP        = nil

local luacom_ok, luacom = pcall(require, "luacom")

local MSG_GAP    <const> = 2.0
local POLL_GAP   <const> = 2.0
local ONIDLE_RET <const> = 200

local F_ACTIVE <const> = 1
local F_SELL   <const> = 4

--========== ОЧЕРЕДИ ==========--
-- T = { buf, jr, jw }
-- T[1] — массив данных
-- T[2] — указатель чтения (jr)
-- T[3] — указатель записи (jw)
-- При jr == jw (очередь пуста) — сброс в 0, следующая запись в buf[1]

local function setQ(T, X)
    local jw = T[3]
    if T[2] == jw then
        jw = 0
        T[2] = 0
    end
    jw = jw + 1
    T[1][jw] = X
    T[3] = jw
end

local function getQ(T)
    local jr = T[2]
    if T[3] > jr then
        jr = jr + 1
        T[2] = jr
        return T[1][jr]
    end
end

local function qsize(T)
    return T[3] - T[2]
end

MAX.INCOMING = {{}, 0, 0}
MAX.OUTGOING = {{}, 0, 0}

--========== ТАБЛИЦЫ КОДИРОВАНИЯ/ДЕКОДИРОВАНИЯ ==========--

local escape_map <const> = {
    [92]  = '\\\\',  -- \  > \\
    [34]  = '\\"',   -- "  > \"
    [10]  = '\\n',   -- \n > \n
    [13]  = '\\r',   -- \r > \r
    [9]   = '\\t',   -- \t > \t
}

local unescape_map <const> = {
    [110] = '\n',    -- n  > \n
    [114] = '\r',    -- r  > \r
    [116] = '\t',    -- t  > \t
    [34]  = '"',     -- "  > "
    [92]  = '\\',    -- \  > \
    [47]  = '/',     -- /  > /
}

--========== КОДИРОВАНИЕ (escape) ==========--

local function json_escape(s)
    local res, j = {}, 0
    for i = 1, #s do
        local b = s:byte(i)
        local rep = escape_map[b]
        if rep then
            j = j + 1
            res[j] = rep
        else
            j = j + 1
            res[j] = s:sub(i, i)
        end
    end
    return table.concat(res)
end

--========== ДЕКОДИРОВАНИЕ (JSON-парсер) ==========--

local J = { s = "", p = 1 }

local j_skip, j_val
j_skip = function()
    while J.p <= #J.s do
        local c = J.s:byte(J.p)
        if c == 32 or c == 9 or c == 10 or c == 13 then J.p = J.p + 1
        else break end
    end
end

j_val = function()
    j_skip()
    local c = J.s:byte(J.p)
    if c == 34 then
        J.p = J.p + 1
        local buf, i = {}, 0
        while J.p <= #J.s do
            local ch = J.s:byte(J.p)
            if ch == 92 then
                local n = J.s:byte(J.p + 1)
                local rep = unescape_map[n]
                if rep then
                    buf[i + 1] = rep
                else
                    buf[i + 1] = J.s:sub(J.p + 1, J.p + 1)
                end
                i = i + 1
                J.p = J.p + 2
            elseif ch == 34 then
                J.p = J.p + 1
                return table.concat(buf)
            else
                buf[i + 1] = J.s:sub(J.p, J.p)
                i = i + 1
                J.p = J.p + 1
            end
        end
        return table.concat(buf)
    elseif c == 123 then
        J.p = J.p + 1; j_skip()
        local obj = {}
        if J.s:byte(J.p) == 125 then J.p = J.p + 1 return obj end
        while true do
            j_skip()
            local key = j_val()
            j_skip()
            if J.s:byte(J.p) == 58 then J.p = J.p + 1 end
            obj[key] = j_val()
            j_skip()
            local sep = J.s:byte(J.p)
            if sep == 44 then J.p = J.p + 1
            elseif sep == 125 then J.p = J.p + 1 break
            else break end
        end
        return obj
    elseif c == 91 then
        J.p = J.p + 1; j_skip()
        local arr = {}
        if J.s:byte(J.p) == 93 then J.p = J.p + 1 return arr end
        while true do
            arr[#arr + 1] = j_val()
            j_skip()
            local sep = J.s:byte(J.p)
            if sep == 44 then J.p = J.p + 1
            elseif sep == 93 then J.p = J.p + 1 break
            else break end
        end
        return arr
    else
        local start = J.p
        while J.p <= #J.s do
            local b = J.s:byte(J.p)
            if b == 32 or b == 9 or b == 10 or b == 13
               or b == 44 or b == 125 or b == 93 then break end
            J.p = J.p + 1
        end
        local v = J.s:sub(start, J.p - 1)
        if v == "true" then return true
        elseif v == "false" then return false
        elseif v == "null" then return nil
        else return tonumber(v) or v end
    end
end

local function json_parse(str)
    J.s = str; J.p = 1
    return j_val()
end

--========== HTTP ==========--

local function http(method, url, body)
    if not MAX.HTTP then return nil, 0 end
    MAX.HTTP:Open(method, url, false)
    MAX.HTTP:SetRequestHeader("Authorization", MAX.TOKEN)
    MAX.HTTP:SetRequestHeader("Accept", "application/json")
    if body then
        MAX.HTTP:SetRequestHeader("Content-Type", "application/json")
        MAX.HTTP:Send(body)
    else
        MAX.HTTP:Send()
    end
    return MAX.HTTP.ResponseText, MAX.HTTP.Status
end

local function raw_send(msg, chat_id, nFormat)
    chat_id = chat_id or MAX.CHAT_ID
    if not chat_id then return false end
    chat_id = math.tointeger(chat_id) or chat_id

    local fmt = ""
    if nFormat == 1 then fmt = ',"format":"html"'
    elseif nFormat == 2 then fmt = ',"format":"markdown"' end
    local body = '{"text":"' .. json_escape(msg) .. '"' .. fmt .. '}'
    local url  = MAX.API_URL .. "/messages?chat_id=" .. tostring(chat_id)
    local resp, status = http("POST", url, body)
    if status == 200 then
        MAX.ERR_COUNT = 0
        return true
    end
    MAX.ERR_COUNT = MAX.ERR_COUNT + 1
    if MAX.ERR_COUNT <= 3 then
        message("MAX: " .. tostring(status) .. " " .. tostring(resp):sub(1, 150), 3)
    end
    return false
end

--========== ПУБЛИЧНЫЙ API ==========--

function SendToMAX(msg, chat_id, nFormat)
    setQ(MAX.OUTGOING, { m = msg, c = chat_id, f = nFormat or 0 })
end

--========== POLLING ==========--

local function poll()
    local url = MAX.API_URL .. "/updates?marker=" .. tostring(MAX.LAST_MARKER)
               .. "&timeout=0"
    local resp, status = http("GET", url, nil)
    if status ~= 200 or not resp then
        MAX.ERR_COUNT = MAX.ERR_COUNT + 1
        return
    end
    MAX.ERR_COUNT = 0
    local data = json_parse(resp)
    if not data then return end

    if data.marker then MAX.LAST_MARKER = data.marker end
    if not data.updates then return end

    for _, upd in ipairs(data.updates) do
        local utype = upd.update_type

        if utype == "bot_started" then
            if upd.chat and upd.chat.chat_id then
                MAX.CHAT_ID = math.tointeger(upd.chat.chat_id) or upd.chat.chat_id
            end
            message("MAX: chat_id = " .. tostring(MAX.CHAT_ID))
            SendToMAX("? Связь установлена. /help — список команд", nil, 2)

        elseif utype == "message_created" then
            local msg = upd.message
            if not msg then goto next_upd end
            if msg.recipient and msg.recipient.chat_id then
                MAX.CHAT_ID = math.tointeger(msg.recipient.chat_id) or msg.recipient.chat_id
            end
            local text = msg.body and msg.body.text or ""
            setQ(MAX.INCOMING, text)
            message("MAX < " .. text)
        end
        ::next_upd::
    end
end

--========== ДИСПЕТЧЕР ==========--

local function dispatch()
    local now = os.clock()
    if now - MAX.LAST_SEND < MSG_GAP then return end

    -- Приоритет: исходящие
    if qsize(MAX.OUTGOING) > 0 then
        local item = getQ(MAX.OUTGOING)
        if raw_send(item.m, item.c, item.f) then
            MAX.LAST_SEND = os.clock()
        end
        return
    end

    -- Затем: подтверждение входящего
    if qsize(MAX.INCOMING) > 0 then
        local text = getQ(MAX.INCOMING)
        local reply = MAX.build_reply(text)
        if raw_send(reply, nil, 2) then
            MAX.LAST_SEND = os.clock()
        end
        return
    end
end

--========== ОБРАБОТКА КОМАНД ==========--

function MAX.build_reply(text)
    if not text then return "/help — список команд" end
    local cmd = text:lower():gsub("^%s+", ""):gsub("%s+$", "")

    if cmd == "/help" or cmd == "помощь" then
        return "Команды:\n/status — состояние\n/positions — позиции\n/orders — заявки\n/trades — последние сделки\n/help — список"

    elseif cmd == "/status" or cmd == "статус" then
        return "QUIK работает. " .. os.date("%Y-%m-%d %H:%M:%S")

    elseif cmd == "/positions" or cmd == "позиции" then
        local lines, n = {}, 0
        for i = 0, getNumberOf("FUTURES_CLIENT_HOLDING") - 1 do
            local p = getItem("FUTURES_CLIENT_HOLDING", i)
            if p and p.openqty ~= 0 then
                n = n + 1
                local qty = math.abs(math.tointeger(p.openqty) or tonumber(p.openqty) or 0)
                local vm  = tonumber(p.varmargin) or 0
                lines[n] = string.format("%s **%s** %d шт, вм %.2f",
                    p.openqty > 0 and "Лонг" or "Шорт",
                    tostring(p.sec_code), qty, vm)
            end
        end
        return n == 0 and "Нет позиций." or table.concat(lines, "\n")

    elseif cmd == "/orders" or cmd == "заявки" then
        local lines, n = {}, 0
        for i = 0, getNumberOf("ORDERS") - 1 do
            local o = getItem("ORDERS", i)
            if o and (o.flags & F_ACTIVE) ~= 0 then
                n = n + 1
                local qty = math.tointeger(o.qty) or tonumber(o.qty) or 0
                local price = tonumber(o.price) or 0
                lines[n] = string.format("%s **%s** %d @ %.2f",
                    (o.flags & F_SELL) ~= 0 and "Прод" or "Покуп",
                    tostring(o.sec_code), qty, price)
            end
        end
        return n == 0 and "Нет заявок." or table.concat(lines, "\n")

    elseif cmd == "/trades" or cmd == "сделки" then
        local lines = {}
        local total = getNumberOf("TRADES")
        local start = total > 5 and total - 5 or 0
        for i = start, total - 1 do
            local t = getItem("TRADES", i)
            if t then
                local qty = math.tointeger(t.qty) or tonumber(t.qty) or 0
                local price = tonumber(t.price) or 0
                lines[#lines + 1] = string.format("%s **%s** %d @ %.2f",
                    (t.flags & F_SELL) ~= 0 and "Прод" or "Покуп",
                    tostring(t.sec_code), qty, price)
            end
        end
        return #lines == 0 and "Сделок нет."
             or "Последние:\n" .. table.concat(lines, "\n")

    else
        return "Получено: " .. text .. "\n/help — список команд"
    end
end

--========== CALLBACKS QUIK ==========--

function OnInit()
    if not luacom_ok then
        message("MAX Bridge: luacom не найден!", 3)
        return
    end
    MAX.HTTP = luacom.CreateObject("WinHttp.WinHttpRequest.5.1")
    if MAX.HTTP then
        MAX.HTTP:SetTimeouts(3000, 3000, 5000, 5000)
        message("MAX Bridge: запущен. Lua " .. _VERSION .. ". Ожидание /start...")
    else
        message("MAX Bridge: WinHttpRequest не создан!", 3)
    end
end

function OnIdle()
    if not MAX.HTTP then return 1000 end

    local now = os.clock()

    local interval = POLL_GAP
    if MAX.ERR_COUNT > 3 then
        interval = POLL_GAP * (MAX.ERR_COUNT < 10 and MAX.ERR_COUNT or 10)
    end
    if now - MAX.LAST_POLL >= interval then
        MAX.LAST_POLL = now
        poll()
    end

    dispatch()

    return ONIDLE_RET
end

function OnStop()
    local out_n = qsize(MAX.OUTGOING)
    local inc_n = qsize(MAX.INCOMING)
    if MAX.CHAT_ID then
        if out_n + inc_n > 0 then
            raw_send(string.format(
                "?? MAX Bridge остановлен. В очередях: %d исх, %d вх.",
                out_n, inc_n), nil, 0)
        else
            raw_send("?? MAX Bridge остановлен.", nil, 0)
        end
    end
    MAX.HTTP = nil
    return 1000
end

function OnTrade(trade)
    local is_sell = (trade.flags & F_SELL) ~= 0
    local qty   = math.tointeger(trade.qty) or tonumber(trade.qty) or 0
    local price = tonumber(trade.price) or 0
    SendToMAX(string.format("?? %s **%s** %d @ %.2f",
        is_sell and "Прод" or "Покуп",
        tostring(trade.sec_code), qty, price), nil, 2)
end
--[[
Инструкция: мост QUIK - MAX
Шаг 1. Создание бота в MAX
Откройте MAX на смартфоне, найдите бота Masterbot (или через бизнес-кабинет , если есть юрлицо/ИП).
Создайте нового бота, получите токен — длинная строка вида f9LHodD0cOIq...0BhMw0gm_6f1-DBY.
Напишите боту команду /start — это нужно, чтобы бот «увидел» вас и скрипт позже сможет подхватить ваш chat_id.

Шаг 2. Установка скрипта в QUIK
Откройте скрипт в любом текстовом редакторе (Блокнот, Notepad++).
Найдите строку MAX.TOKEN = "ВАШ_ТОКЕН" и вставьте свой токен.
Сохраните файл, например, max_bridge.lua.
В QUIK: меню Сервис > Lua-скрипты (или Ctrl+I).
Нажмите Добавить, выберите max_bridge.lua.
Нажмите Запустить.
В окне сообщений QUIK должно появиться: MAX Bridge: запущен. Lua 5.x. Ожидание /start...

Шаг 3. Подключение смартфона
Откройте MAX на телефоне, найдите своего бота.
Отправьте ему команду /start.
Скрипт перехватит событие bot_started, запомнит ваш chat_id и пришлёт ответ: ? Связь установлена. /help — список команд.
Всё — двусторонняя связь работает.

Шаг 4. Команды с телефона
Отправляйте боту в MAX:

Команда   Что делает
/help   Список всех команд
/status   Состояние QUIK, дата/время
/positions   Открытые фьючерсные позиции
/orders   Активные заявки
/trades   Последние 5 сделок
Шаг 5. Отправка сообщений из QUIK
Скрипт регистрирует глобальную функцию SendToMAX().
Любой другой Lua-скрипт в QUIK может вызывать её для отправки уведомлений на телефон:

lua
-- Простой текст (без разметки)
SendToMAX("Сигнал: RTS пробил 100000")

-- Текст с разметкой Markdown (формат 2)
SendToMAX("**Внимание!** Цена `GAZP` упала ниже 200", nil, 2)

-- Текст с разметкой HTML (формат 1)
SendToMAX("<b>Внимание!</b> Цена GAZP упала ниже 200", nil, 1)

-- Отправка в конкретный чат (если chat_id известен)
SendToMAX("Личное сообщение", 117000000474, 0)
Форматы:

0 — без разметки (по умолчанию)
1 — HTML (<b>жирный</b>, <i>курсив</i>)
2 — Markdown (**жирный**, _курсив_, `моноширинный`)
Все сообщения попадают в очередь и отправляются не чаще одного в 2 секунды — лимит MAX не нарушается.

Шаг 6. Автоматические уведомления
Скрипт уже содержит обработчик OnTrade — при каждой сделке в QUIK на телефон автоматически уходит уведомление:

text
?? Прод **RIZ5** 5 @ 105250.00
?? Покуп **GAZP** 100 @ 198.50
Чтобы добавить свои триггеры, допишите в скрипт или в отдельный файл:

lua
-- Пример: уведомление при изменении цены
function OnQuote(class_code, sec_code)
    if sec_code == "RIZ5" then
        local ql = getQuoteLevel2(class_code, sec_code)
        if ql and #ql.bid > 0 then
            local bid_price = tonumber(ql.bid[#ql.bid].price)
            if bid_price and bid_price > 105000 then
                SendToMAX("RIZ5 выше 105000! Текущая bid: " .. bid_price, nil, 2)
            end
        end
    end
end

Шаг 7. Остановка
В окне Lua-скрипты нажмите Остановить.
Скрипт отправит в MAX сообщение: ?? MAX Bridge остановлен. (или с количеством неотправленных сообщений в очередях).
COM-объект освобождается, связь закрывается.

Типичные проблемы
Скрипт запущен, но /start не приходит:

Проверьте, что токен вставлен правильно, без пробелов.
Проверьте интернет-соединение на ПК.
В окне сообщений QUIK посмотрите ошибки — если WinHttpRequest не создан, возможно, COM заблокирован антивитором.
Сообщения приходят с задержкой:

Это нормально — rate limit 2 секунды. Если в очереди 5 сообщений, последнее уйдёт через 10 секунд.
Если задержка больше минуты — проверьте, не вырос ли ERR_COUNT (ошибки сети). При ошибках polling замедляется.
luacom не найден:

В некоторых сборках QUIK библиотека luacom не установлена по умолчанию.
Скачайте luacom.dll и положите в папку QUIK рядом с остальными .dll.
Кириллица в сообщениях ломается:

Скрипт отправляет в кодировке UTF-8.
Если QUIK отдаёт строки в Windows-1251 (что типично), кириллица может прийти кривой.
В этом случае нужно конвертировать текст перед отправкой — скажите, добавлю функцию перекодировки.
Если торгуете акциями, а не фьючерсами — замените FUTURES_CLIENT_HOLDING на нужную таблицу в build_reply для команды /positions.
--]]

Квик перестал запускаться, Диалоговое окно "Прекращена работа программы "Рабочее место информационно-торговой системы QUIK""
 
Цитата
vyachnik написал:
Цитата
Polina Anisova написал:
vyachnik , здравствуйте.

Просим выполнить следующие рекомендации и сообщить результат:

Удалите временные файлы *.dat и *.log, кроме файлов: scripts.dat, portfolio.dat, alert.dat, qbclists.dat. Проверьте работу программы.

Исключите загрузку текущего файла настроек окон *.wnd. Проверьте работу программы.

Обновите используемые скрипты и внешние модули от сторонних разработчиков если это возможно или отключите их, удалив файл scripts.dat перед запуском программы. Проверьте работу программы.
все было нормально, а сегодня опять ошибка, но уже после авторизации, приложение попросило заменить несколько инструментов, я их заменил, произошла замена и та же ошибка - "Прекращена работа программы...";
запустил второй раз, авторизацию прошел, опять просит замену инструментов - "Истек срок использования 9 инструментов, заменить их?" - отвечаю Нет, а оно еще раз показывает это окно повторно - опять отвечаю Нет, дальше будем смотреть, пока приложение работает нормально, скрин ошибки:
Подозреваю, что проблема в железе.
Давно комп от пыли чистили?
Проверьте температуру CPU и работу вентиляторов
Использование мессенджера MAX для обмена данными в QUIK, Использование мессенджер для обмена для отправки сообщений и управление торговым приводом в QUIK
 
Цитата
Евгений написал:
Поскольку , эта тема вроде нигде не поднималась , опишу свой опыт , может быть будет кому то полезно.
Для информирования и  контроля за работой торгового привода у меня был поднят небольшой   саит на локальном компе с QUIK ,и меня все  устраивало,
Сейчас эта схема практически утратила смысл из за почти постоянного отключения мобильного интернета. Я решил  посмотреть  , что можно использовать взамен.  Нормально работали только саиты из белого списка и MAX.  Поскольку других вариантов  не было , выбор пал на MAX ,
Код
Странно, если мобильный интернет не работает то и МАХ не должен работать.
скорее всего не работал DNS cервер, поэтому не могли найти IP адрес
----------------------------
из моего опыта .
Кратко.
можно не делать сайт ,
а создать сервер на локальном компьютере там же где квик  
Потом сообщить на смартфон IP адрес своего локального компа ,
у которого есть выход в интернет.
Будете работать напрямую со своим сервером.
------------------------------
Еще варианты прокси или VPN  можно бесплатные
Вопросы про OnParam
 
Возможно это связано со сменой сервера,
т к   OnCleanUp вызывается  терминалом  
при смене сервера QUIK внутри торговой сессии.
Что происходит при экспирации опциона с точки зрения скрипта?
 
Цитата
Nikolay написал:
OnFuturesClientHolding - срабатывает на любые изменения в таблице, как и любой другой колбек в контексте Квика. Уж так сделано...

Сделки не должно быть, второго контрагента же нет.  
А Вы не ошибаетесь? На каждый CALL есть свой PUT. Т е если кто-то купил ,то должен быть тот, кто продал
Квик перестал запускаться, Диалоговое окно "Прекращена работа программы "Рабочее место информационно-торговой системы QUIK""
 
Попробуйте сделать так  
Если будет ошибка посмотрите объем занятой памяти.
Потом закройте все приложения и снова запустите QUIK
Посмотрите объем занятой памяти.
Напишите что получилось
Коннектор на Lua 5.1 переписать под Lua 5.4 &, Lua 5.1 -Lua 5.4
 
Для декомпиляции не нужна отладочная информация в скрипте
Декомпилируется байт-код.
Коннектор на Lua 5.1 переписать под Lua 5.4 &, Lua 5.1 -Lua 5.4
 
Цитата
Виктор Волков написал:
luac)
возможно, но успех зависит от сложности  скрипта.
Иногда проще написать заново  
Закрывается DataSource при остановке скрипта?
 
Закрывается DataSource при остановке скрипта?
 
Код
class="QJSIM"
sec="SBER"
local run = true
function OnStop()
  run = nil
  local x=ds:Close()
  message(tostring(ds)..","..tostring(x),1)
end

function main()
  ds = assert(CreateDataSource(class, sec, INTERVAL_M5))
  while run do sleep(500) end
end


Демо Quik - даёт истрические данные текущего дня?, Получение исторических ланных в Демо версии?
 
Цитата
Виктор Волков написал:
используется связка     Скрипт на Python  (библиотека  обёртка  QuikPy) , коннектор на Lua (QuikSharp) , Демо Квик...
может  это пригодится:  https://pypi.org/project/moex-store/
Робот, торгующий опционами
 
и еще...
мое мнение:
-------------------------
Суть в том что
когда торгуем фьючерсом то при покупке и продаже возникает обязанность.
когда же продаем опцион,  то как во фьючерсах возникает обязанность ,
но когда покупаем опцион , то возникает лишь  право.
----------------------
Таким образом , продажа опциона как и торговля фьючерсом создает обязанность(обязательство).
Но в случае с опционом величина риска ничем не ограничена.
-----------------------
Что же касается фьючерсов, то в них риск больше по сравнению с акциями только потому,
что во фьючерсах всегда есть плечо и срок жизни фьючерса ограничен, если он не вечный.
-----------------------
Поэтому риск во фьючерсах больше, чем в акциях.
Робот, торгующий опционами
 
Вот мнение ИИ вполне компетентное и обоснованное:
Прямой и короткий ответ:
Нет, классических биржевых опционов, базовым активом которых выступает именно бессрочный (вечный) фьючерс, практически не существует.

Однако есть очень важные нюансы и альтернативы, которые работают точно так же или даже лучше.

Вот почему так и какие есть варианты:

1. Техническая причина: Дата экспирации

Это главное противоречие. Опцион всегда имеет дату истечения (экспирации). Его стоимость зависит от времени, оставшегося до этой даты (тета-распад).

Бессрочный фьючерс (Perpetual Swap) создан так, чтобы никогда не истекать. Он привязан к спотовой цене через механизм ставки финансирования.

Привязать срочный контракт (опцион) к бессрочному инструменту так, чтобы получился ликвидный биржевой продукт, технически очень сложно. Биржам это не нужно, потому что уже существуют отличные аналоги.

2. Что используют вместо опционов на вечный фьючерс?

Трейдеры, которые хотят торговать волатильностью или хеджировать позиции по "вечникам", используют два инструмента:

А) Опционы на спотовый актив

Это 90% рынка крипто-опционов (Deribit, OKX, Binance Options).

  • У вас открыт вечный фьючерс на BTC.

  • Вы покупаете пут-опцион на спотовый BTC.

  • Результат: Хедж работает идеально, потому что цена вечного фьючерса благодаря финансированию всегда стремится к спотовой цене. Расхождение минимально.

Б) Опционы на срочные (квартальные) фьючерсы

На биржах вроде Deribit или CME торгуются опционы, где базовый актив — не вечный, а классический фьючерс с экспирацией (март, июнь, сентябрь, декабрь).

  • Вы покупаете опцион, который при экспирации превращается в срочный фьючерс.

  • Это сложнее, и здесь нужно следить за базисом (разницей между ценой фьючерса и спота), но профессиональные трейдеры активно этим пользуются.

3. Экзотика: "Вечные опционы" (Perpetual Options)

Справедливости ради, существуют экспериментальные проекты, которые пытаются создать именно бессрочные опционы (например, исследование Paradigm о "Perpetual Options" или Everlasting Options).

  • Суть идеи: У такого опциона нет экспирации. Вместо этого периодически (например, раз в сутки) происходит клиринг, где прибыль начисляется автоматически, а если опцион "вне денег" — у держателя списывается аналог "ставки финансирования" (тета-сбор).

  • Реальность: На данный момент (2024-2025) это больше теоретические модели De-Fi протоколов. Как массовый ликвидный биржевой инструмент на CEX (централизованных биржах) "вечные опционы" пока не прижились.

Итог

Если вы торгуете вечные фьючерсы и хотите купить опцион для страховки позиции, вам не нужен специальный опцион именно на вечный фьючерс.

  1. Открывайте терминал опционов (например, Deribit).

  2. Выбирайте базовый актив — спот (BTC/USD) или срочный фьючерс.

  3. Покупайте пут или колл.

Разница в цене между вечным фьючерсом и этим базовым активом будет ничтожной (обычно в пределах ставки финансирования за пару часов), поэтому защита работает на 100%.

Робот, торгующий опционами
 
Цитата
Сергей Че написал:
Цитата
Сергей Че написал:
Написал письмо Мосбирже. Жду официального ответа
Из письма следует что опционов на вечный фьючер на московской бирже нет.
а голое мнение,
что такие опционы бывают ничем не подкреплено
и вполне может быть ошибочным
Робот, торгующий опционами
 
Цитата
Сергей Че написал:
Цитата
nikolz написал:
Это самый рискованный инструмент для торговли.  

Опцион - это самый доходный инструмент из имеющихся, но не самый рискованный.
Самый рискованный - это как раз фьючерс.
Если не продавать, а только покупать опционы, то твой маскимальный убыток - это деньги, которые ты заплатил за опционы.
Во фьючерсах же максимальный убыток не ограничен, и можно просто обнулиться (если не следить за позицией), сколько бы у тебя денег ни было на счету.
Все верно Вы лишь подтвердили то что я сказал. если не будете продавать  опционы Т е  вы кастрируете саму торговлю опционами то опционы станут менее рискованными/
Но тогда уж если вообще не будете торговать на бирже то совсем риска не будет
-------------------------
Поэтому без кастрации опционы самый рискованный ,
если кастрировали - то фьючерс .
================================
Итоговый вердикт от ИИ:
  1. По уровню риска для новичка: Фьючерс опаснее опциона (в роли покупателя).

  2. Самый опасный инструмент в мире: Продажа непокрытых (голых) опционов. Там можно потерять сумму, значительно превышающую весь ваш капитал.

Поэтому: если хотите ограничить потери известной суммой, работайте с покупкой опционов. Если готовы к быстрым и линейным прибылям/убыткам — фьючерсы. Никогда не продавайте опционы, если до конца не понимаете «греков» и маржинальные требования.

глобалки колбэки и многопоточность
 
Цитата
andrey2185 написал:
на сколько безопасно изменения глобалки из колбэка и чтение из main? есть ли вероятность , что мы упадём нахер?

ISRUN = false

LAST_ALLTRADE_NUM = 0

function OnAllTrade(alltrade)
 LAST_ALLTRADE_NUM = alltrade.trade_num
end

function main()
 x = 0
 while ISRUN do
   if LAST_ALLTRADE_NUM > x then
     x = LAST_ALLTRADE_NUM
     -- ...
   else
     sleep(1)
   end
 end
end
колбеки  в том же стейте что и глобалки  Т е они никогда не упадут
проблемы в многопоточности не с чтением данных а с записью новых в момент чтения.
Поэтому с чтением в main глобалок  тоже нет проблем  
Полагаю что там задействованы еще и критические секции.
----------------------------
Но в любом случае туда , куда указали,точно не упадет.
Работа колбеков
 
Цитата
Nikolay написал:
Альтернатива - чтение таблицы обезличенных сделок (тики) и это будет самое точное (читать не через колбеки точно), но проблема что её не все брокеры выдают, поэтому это решение нельзя использовать как базовое.
Но колбек вызывается перед записью в таблицу обезличенных сделок.
Если читать из таблицы то будет вызов колбека, запись в таблицу, и потом чтение из нее.
limit_kind в таблице depo_limit
 
для фьючерсов ставлю NUMBER type=0
limit_kind в таблице depo_limit
 
  • limit_kind — это число, кодирующее режим расчётов:

    • 0 — T+0;
    • 1 — T+1;
    • 2 — T+2;
    • 365 — Tx (плановая позиция с максимальной датой расчётов).
limit_kind в таблице depo_limit
 
ставьте 0 или 1 или 2 или 3
limit_kind режим расчётов по сделке:
  • 0 — Т+0 (расчёт в день сделки);
  • 1 — Т+1 (расчёт на следующий рабочий день);
  • 2 — Т+2 и так далее.
limit_kind в таблице depo_limit
 
Цитата
Сергей Че написал:
Цитата
nikolz написал:
 
Цитата
Сергей Че  написал:
limit_kind
  limit_kind>=0
 

Очень информативно! (нет!)
Если не можете дать чёткий ответ, лучше не писать.
Я ответил вам про параметр NUMBER limit_kind
что непонятно?  
Работа колбеков
 
Цитата
TGB написал:
4. Коды скрипта разделяются между потоками, использующими его. В выбранном варианте трансляции исходников Lua активирована синхронизация, обеспечивающая использование кода LUa только одним потоком, но на разных стеках. Сишные функции (не использующие управление память Lua) при их вызовах выполняются в потоке их вызова, и при этом разрешается выполнение потока на другом стеке. После звершения сишной функции, его поток блокируется до тех пор пока в потоке, исполняемом код Lua не будет вызвана сишная функция.
Тут у Вас ошибка в понимании. Коды не надо разделять или синхронизировать  Они могут исполнятся потоками одновременно .
Разделяется обращение к памяти данных    Это еще называется синхронизация потоков. Т е синхронизация потоков это не про коды а про данные.
limit_kind в таблице depo_limit
 
Цитата
Сергей Че написал:
limit_kind
limit_kind>=0
Работа колбеков
 
Цитата
Nikolay написал:
Тест был направлен не на измерение скорости работы скрипта, а на влияние работы скрипта на работу терминала, его интерфейса в первую очередь. Какой смысл показать, да ещё на демо, что main работает быстро.
У меня на демо тоже всё прекрасно. Переключаешься на реальный и во время  открытия рынка - терминал вкладки переключает по 30 секунд.  Была бы возможность инструментально измерить работу самого терминала - так и вопросов не было бы.
Вы не поняли мой тест.
Тест проверяет быстродействие выполнения колбеков, включая обработку очереди .
Проблема возврата предыдущей версии, Вопрос к разработчикам.
 
Добрый день,
Так как есть подозрения , что тормоза терминала связаны с версией , то решил проверить это установив старую версию 8.7
Но установить ее по сохраненной версии в  backup не удается
--------------------------
копирую все файлы из архива
Удаляю все log
Запускаю info.exe
Появляется окно терминала
Исчезает окно терминала
Что не так?
Работа колбеков
 
Цитата
Nikolay написал:
Оставил скрипты тестовые с колбеками запущенными. В итоге:

задержка данных при обмене с сервером = 11.109
средняя задержка данных = 26.384.
Максимальная задержка данных = 175.063

Как только остановил скрипты - побежал счётчик полученных пакетов, отставших записей в информационном окне. И пока данные бегут, то терминал и без скриптов не особо спешит.
Вывод -тормозят Ваши скрипты.
Измерьте время их исполнения.
------------------
Кроме того, тормозит сервер брокера . И это не зависит от скриптов.
Последнее время  у Сбера видны задержки в выставлении заявок
Т е уходит заявка из терминала а в стакане она появляется с заметной задержкой визуально.  
Так как задержка реакция нашего глаза  не менее 0.2 сек то можно говорить что заявка выставляется с задержкой сотни миллисекунд.
------------------------  
Объяснить это можно лишь задержкой на стороне сервера.
Два варианта
1) Умышленная задержка на стороне сервера.
2) Большие очереди заявок.
-------------------------------------  
Но так как эта задержка существует и при спокойном рынке, то скорее всего это 1 вариант
 
Работа колбеков
 
Цитата
Nikolay написал:
Вот прямо сейчас смотрю на один из терминалов - банально время сервера в строке состояния изменяется скачками, замирая на длительный период. И так каждый раз при старте, что, в теории, нормально, т.к. приходят пакеты. Но не 15 минут же. На терминалах других брокеров это же занимает минуту, 5-ять иногда. И в это время отчетливо видно как с трудом совершаются любые операции с интерфейсом.

Аналогично и в периоды высокой ликвидности. В это время транзакции могут исполнятся минуты - это ожидаемо, но почему опять интерфейс также "умирает".

Я готов это списать на влияние работы скриптов. Но тогда хотелось бы понять, что именно и какие методы оказывают максимальное влияние. Иначе делаешь банальный скрипт, а эффект как будто модель на 100b считаешь.
О скачках времени сервера читайте здесь:
https://forum.quik.ru/forum1/topic9514/
Работа колбеков
 
сделал свой тест на демо сервере
---------------
Так как скрипт рабочий и большой, то расскажу основные моменты, чтобы было понятно , что и как
--------------------
У меня все колбеки сделаны одинаков  таким образом:
Код
OnOr der=function(t) setQ(4,t);   end
Функция setQ  помещает таблицу параметров колбека t и ключ колбека в очередь.
В этой функции я добавил в параметры значение точного счетчика Windows   Этот счетчик считает с квантом 0.1 мкс
--------------------------------
В функции main  я в цикле обрабатываю элементы очереди
В данном тесте я из значения точного счетчика вычитаю  сохраненное его значения при записи в очередь
Результат пишу в лог файл
-------------------------------------------
Таким образом в лог файле я получаю время задержки от вызова любого колбека до начала обработки параметров этого колбека в main
Т е фактически это время задержки от момента записи в очередь до момента начала обработки элемента из очереди
Код
t(мкс)=12.7
t(мкс)=8.6
t(мкс)=8.6
t(мкс)=3.1
t(мкс)=6.2
...
t(мкс)=59.1
t(мкс)=60.7
t(мкс)=60.0
t(мкс)=59.7
...
t(мкс)=32.9
t(мкс)=25.0
t(мкс)=14.1
t(мкс)=12.7

Т е время задержки примерно 0.00006 сек
Работа колбеков
 
Цитата
TGB написал:
Цитата
nikolz написал:
что не так?
   Наверное, из-за того, что в таблице:
Код
   local  sec_list       =  {
    {'SPBFUT',  'MMU6' },
    {'SPBFUT',  'SiU6' },
    {'SPBFUT',  'RIU6' },
    {'SPBFUT',  'USDRUBF' },
    {'SPBFUT',  'CNYRUBF' },
    {'TQBR',  'SBER' },
    {'TQBR',  'GAZP' }
}
  

  классы заданы для реального рынка.
возможно  Вы правы пока не проверял.
Но тогда это прикольно. По крайней мере для меня.
-------------------------------
Полагаю ,что правильный скрипт запускается при любой погоде.
-------------------------
А эти тупо молчат нет никаких сообщений об ошибках или о нелетной погоде .  
Работа колбеков
 
Цитата
Nikolay написал:
Ок. Чтобы показать влияние на работу интерфейса терминала еще сделал такой тест.

Терминал свежий, установлен не более двух недель назад. Брокер ВТБ. Остановил все скрипты, даже автологин.

В тестируемом скрипте добавил запоминание последних значений, чтобы выводились в окно только измененные данные.
Лог через PrintDbgStr. В колбеках вывода в лог нет, только в main.

Запускаем три экземпляра скрипта и смотрим на реакцию терминала.

Один скрипт - почти нет влияния.
Два - уже ТТТ "подвисла" и данные выводятся не так быстро.
Три - всё встало, данные появляются только если "пнуть" таблицу

В архиве скрипты, лог с таймингом работы main, запись видео работы терминала.

https://disk.360.yandex.ru/d/P7xB_poL0QlKfA Код тестового скрипта - по факту, он ничего не делает, кроме обработки колбеков, запроса данных через методы QLUA, и вывода данных в таблицу. Никаких расчётов, даже файловые операции убраны.
Код
Взял скрипты   На демо версии тесты не запускаются  

что не так?
Периодические паузы в получении данных
 
лекарство только одно - стучать брокеру по ...
Периодические паузы в получении данных
 
Я про это писал и выкладывал скрипт, который это обнаруживает и пишет в лог файлы
Работа колбеков
 
Цитата
Nikolay написал:
Э... что же тогда большое.

У нас есть колбеки - настоятельно рекомендуемые к использованию. Есть окно скрипта - единственный способ вывести данные.
Банальная задача - вывести данные, полученные через getParamEx. Также, для какой-то нагрузки - читаем 7 стаканов, при этом даже не разбирая их, а банально беря верхние значения, т.е. просто запрос данных.

И выводим это в таблицу. Всё. Ни транзакций, ни расчетов, ни запросов данных графиков и потока данных - только, по факту, запрос данных ТТТ из терминала.

Проверяем как это терминал переваривает, если таких скриптов несколько. Такое на 7-ой версии терминала даже не вызывало вопросов.
Да стаканы читать тяжело. Но выводить все в таблицы если это робот то это избыточно.
---------------------
Относительно индикатора регрессии считаю в скользящем окне. Этот индикатор лучше чем мувинги.
Работа колбеков
 
что-то тест уж больно большой. Трудно проверить что же он все таки проверяет
Влияние колбэков на производительность
 
У вас все таки  колбеки не пустые
Код
if alltrade.class_code ~= class or alltrade.sec_code ~= sec then return end
либо поставьте измерение времени этого оператора и вычитайте либо действительно поставьте пустой колбек
Иначе эксперимент не чистый
Работа колбеков
 
Для себя сделал вывод.
Разработчики еще 25 лет назад честно написали QUIK - это для подачи заявок брокеру вместо телефона.
Они честно сказали и честно это соблюдают.
-------------------------------
QUIK - не для написания роботв а для подачи заявок и получения информации с биржи
-------------------------------
Роботов надо писать в отдельных приложениях
или хотя бы в отдельных потоках  с одноразовым вызовом колбеков и по возможности без графиков
Работа колбеков
 
Цитата
Nikolay написал:
Цитата
nikolz написал:
можете сделать вывод времени входа и выхода для колбеков и для  main  c помощью точного счетчик Windows?
Если без излишеств, просто через os.clock, то сами колбеки работаю микросекунды. main уже зависит о того, что делает, но там тоже не что-то существенное.

Я пока наблюдаю странное - подключаешь несколько библиотек на 1000 функций - и привет. Собственно это в тесте и сделано, чтобы проверить влияние этого.
Я, конечно, понимаю, что Lua - не тот язык в котором обычно пишутся модули на тысячи строк, но как-то я не встречал упоминаний о влиянии этого на производительность. Хотя если вспомнить World of Warcraft, то ему не мешает.

Пришлось разбираться, т.к. в последних версиях терминала стало всё очень медленно если запущены "большие" скрипты, банально вкладки переключаются со скрипом.
Пальцем в небо - что-то сделали с GC. У меня функции использую локальные переменные, а функций много. В итоге GC надо работать. Как он работает в Квике не знаю, но надеюсь, что независимо от основного потока.

Впрочем, приведённый нагрузочный тест не делает ничего такого, чтобы сильно потреблять память, а падение производительности есть.
Наблюдал такой прикол  ставишь индикатора регрессии на большой истории и привет ждешь старта квик очень долго.  На демо все быстро потому что там история маленькая.
Задать время снятия заявки программно при выставлении sendtransaction, Задать время снятия заявки программно при выставлении sendtransaction
 
Цитата
Игорь_С написал:
Попробовал, к сожалению не работает. Заявка выставляется без указанных параметров.
Может быть поддержка подскажет?
проверьте формат параметров. Все параметры  транзакции должны быть строками.  Часто вместо строк ставят числа
‘Вы уже работаете в системе’ + ‘Соединение установить не удалось’, Проблема с соединением
 
Цитата
Игорь С написал:
Когда выдаются сообщения ‘Вы уже работаете в системе’ + ‘Соединение установить не удалось’, команда TELNET  с адресом сервера и номером порта выполняется без ошибок (как описано в инструкции). История продолжается. Вероятно, придется смириться и терпеть. Всем спасибо!
если включено автоматическое восстановление связи то просто ждите и все восстановится минуты через 3-5
Работа колбеков
 
можете сделать вывод времени входа и выхода для колбеков и для  main  c помощью точного счетчик Windows?
Страницы: 1 2 3 4 5 6 7 8 9 10 11 ... 95 След.
Наверх