Получение данных из таблиц QUIK в QLua(Lua)

Автор записи: Дмитрий (Admin)
1 звезда2 звезды3 звезды4 звезды5 звезд (Голосов 11, среднее: 3,91 из 5)
Загрузка...

QUIK-Qlua-poluchenie-dannyh Для получения данных из таблиц терминала QUIK удобно пользоваться 3-мя функциями: getItem(), getNumberOf() и getParamEx().

Пример:

-- Перебирает строки таблицы "Позиции по клиентским счетам (фьючерсы)", ищет Текущие чистые позиции по инструменту "RIH5"
for i = 0,getNumberOf("FUTURES_CLIENT_HOLDING") - 1 do
   -- ЕСЛИ строка по нужному инструменту И чистая позиция не равна нулю ТО
   if getItem("FUTURES_CLIENT_HOLDING",i).sec_code == "RIH5" and getItem("FUTURES_CLIENT_HOLDING",i).totalnet ~= 0 then
      -- ЕСЛИ текущая чистая позиция > 0, ТО открыта длинная позиция (BUY)
      if getItem("FUTURES_CLIENT_HOLDING",i).totalnet > 0 then 
         IsBuy = true;
         BuyVol = getItem("FUTURES_CLIENT_HOLDING",i).totalnet;	-- Количество лотов в позиции BUY				
      else   -- ИНАЧЕ открыта короткая позиция (SELL)
         IsSell = true;
         SellVol = math.abs(getItem("FUTURES_CLIENT_HOLDING",i).totalnet); -- Количество лотов в позиции SELL
      end;
   end;
end;

Далее перечислены таблицы, их идентификаторы и поля, к которым можно обращаться:

Фирмы:
Классы:
Инструменты:
Торговые счета:
Коды клиентов:
Обезличенные сделки (Таблица всех сделок):
Денежные позиции:
Заявки:
Позиции по клиентским счетам (фьючерсы):
Лимиты по фьючерсам:
Лимиты по денежным средствам:
Лимиты по бумагам:
Сделки:
Стоп-заявки:
Заявки на внебиржевые сделки:
Сделки для исполнения:
Отчеты по сделкам для исполнения:
Текущие позиции по бумагам:
Текущие позиции по клиентским счетам:
Обязательства и требования по деньгам:
Обязательства и требования по активам:
Для получения значений всех параметров биржевой информации из таблицы "Текущие торги" существует специальная функция
getParamEx (STRING class_code, STRING sec_code, STRING param_name),
которая принимает 3 параметра:
   - Код класса
   - Код инструмента
   - Имя параметра из таблицы "Текущие торги".

Функция возвращает таблицу Lua со следующими полями:
   param_type    STRING Тип данных параметра, используемый в таблице "Текущие торги". Возможные значения: 
      «1» - DOUBLE ,
      «2» - LONG, 
      «3» - CHAR, 
      «4» - перечислимый тип, 
      «5» - время, 
      «6» - дата 
 
   param_value   STRING Значение параметра. Для param_type = 3 значение параметра равно «0», в остальных случаях – числовое представление. Для перечислимых типов значение равно порядковому значению перечисления  

   param_image   STRING Строковое значение параметра, аналогичное его представлению в таблице. В строковом представлении учитываются разделители разрядов, разделители целой и дробной части. Для перечислимых типов выводятся соответствующие им строковые значения

Пример использования:

Status =  tonumber(getParamEx("SPBFUT",  "RIM5", "STATUS").param_value);
-- Выводит сообщение о текущем состоянии
if Status == 1 then message("RIM5 торгуется"); else message("RIM5 не торгуется"); end;
Список возможных идентификаторов параметров, передаваемых в функцию getParamEx()

Если у Вас появились какие-то вопросы, задайте их в комментариях под статьей !!!

Добавить комментарий

Получение данных из таблиц QUIK в QLua(Lua): 474 комментария

  1. Добрый день. Какой функцией (как можно проще) узнать цену по акции и фьючерсу, если робот будет совершать сделки по рынку. А может есть функция чтобы узнать среднюю цену?

    1. BID,OFFER,WAPRICE. могут еще понадобиться нижний/верхний лимиты цен, так как на фортсе нет рыночных заявок и их нужно эмулировать лимитками: PRICEMAX и PRICEMIN

      1. Я не правильно вопрос задал я покупаю по рынку следующим скриптом, а в ответном сообщение цена = 0, т.к. по рынку куплена. Я думал может использовать какую то функцию, чтобы брать цену из таблицы сделок. Но честно говоря вообще запутался в этих функция, мне надо потом еще использовать полученную цену, если не сложно посоветуйте функцию или может пример использования. Может есть какая то функция показывающая цену как акции так и фьючерса?

        -- Код класса
        class_code = "TQBR" -- НАДО МЕНЯТЬ В ЗАВИСИМОСТИ ЧТО ХОЧЕШЬ КУПИТЬ ПРОДАТЬ
        -- Код бумаги
        sec_code = "HYDR" -- ВЫБИРАЕШЬ ИНСТРУМЕНТ
        -- Номер счета
        account = "L01+00000F00" -- БЕРЕТСЯ ИЗ ОКНА ВЫСТАВЛЕНИЯ ЗАЯВКИ

        -- Классы фьючерсов и опционов
        FUT_OPT_CLASSES = "FUTUX, OPTUX, SPBOPT, SPBFUT"

        -- Уникальное число берется из текущей полной даты и времени.
        -- Используется для получения случайного числа
        RANDOM_SEED = tonumber(os.date("%Y%m%d%H%M%S"))

        function main()
        -- Вызываем функцию отправки рыночной заявки на покупку. 1 контракт
        local trans_id, trans_msg = send_market("B", 1) -- ВЫБИРАЕМ SELL ИЛИ BUY
        -- Выводим результат в виде сообщения
        message("trans_id: " .. tostring(trans_id) .. "; trans_msg: " .. tostring(trans_msg))
        end

        -- Функция для отравки рыночной заявки
        function send_market(direction, volume, comment)
        -- отправка рыночной заявки
        -- все параметры кроме кода клиента и коментария должны быть не nil
        -- если код клиента nil - подлставляем счет
        -- Данная функция возвращает 2 параметра
        -- 1. ID присвоенный транзакции либо nil если транзакция отвергнута на уровне сервера Квик
        -- 2. Ответное сообщение сервера Квик либо строку с параметрами транзакции
        if (class_code == nil or sec_code == nil or direction == nil or volume == nil or account == nil) then
        return nil, "Can`t send order. Nil parameters."
        end

        -- Получаем случайное уникальное число для id
        local trans_id = random_max()
        -- Таблица параметров транзацкии
        local transaction={
        ["TRANS_ID"] = tostring(trans_id),
        ["ACTION"] = "NEW_ORDER",
        ["CLASSCODE"] = class_code,
        ["SECCODE"] = sec_code,
        ["OPERATION"] = direction,
        ["TYPE"] = "M",
        ["QUANTITY"] = string.format("%d", tostring(volume)),
        ["ACCOUNT"] = account
        }
        -- Если нет кода клиента, то вместо него подставляем номер счета
        if client_code == nil then
        transaction.client_code = account
        else
        transaction.client_code = client_code
        end

        -- Если это заявка для Фьючерсов
        if string.find(FUT_OPT_CLASSES, class_code) ~= nil then
        local sign = 0
        -- Для покупки
        if direction == "B" then
        sign = 1
        -- Получаем максимальную цену
        transaction.price = getParamEx(class_code, sec_code, "pricemax").param_value
        -- Если текущая цена 0
        if transaction.price == 0 then
        -- Берем цену предложения и добавляем 10 шагов
        transaction.price = getParamEx(class_code, sec_code, "offer").param_value + 10 * getParamEx(class_code, sec_code, "SEC_PRICE_STEP").param_value
        end
        else
        sign = -1
        -- Для продажи берем минимальную цену
        transaction.price = getParamEx(class_code, sec_code, "pricemin").param_value
        -- Если текущая цена 0
        if transaction.price == 0 then
        -- Берем цену Bid и отнимаем 10 шагов
        transaction.price = getParamEx(class_code, sec_code, "bid").param_value - 10 * getParamEx(class_code, sec_code, "SEC_PRICE_STEP").param_value
        end
        end
        -- Если цена так и не установилась
        if transaction.price == 0 then
        -- То пытаемся отнять/прибавть 10 шагов к последней известной цене
        transaction.price = getParamEx(class_code, sec_code, "last").param_value + sign * 10 * getParamEx(class_code, sec_code, "SEC_PRICE_STEP").param_value
        end
        -- Форматируем цену
        transaction.price = to_price(sec_code, transaction.price, class_code)
        else
        transaction.price = "0"
        end

        -- Если комментарий не указан
        if comment ~= nil then
        transaction.client_code = string.sub(transaction.client_code .. '/' .. tostring(comment), 0, 20)
        else
        transaction.client_code = string.sub(transaction.client_code, 0, 20)
        end

        -- Отправляем транзацкию
        local res = sendTransaction(transaction)
        -- Если результат не пустой
        if res ~= "" then
        return nil, res
        else
        local msg =
        "Market order sended sucesfully. Class = " .. class_code ..
        " Sec = " .. sec_code ..
        " Dir = " .. direction ..
        " Vol = " .. volume ..
        " Acc = " .. account ..
        " Trans_id = " .. trans_id ..
        " Price = " .. transaction.price

        return trans_id, msg
        end
        end

        function random_max()
        -- не принимает параметры и возвращает от 0 до 2147483647 (макс. полож. 32 битное число) подходит нам для транзакций
        local res = (16807*(RANDOM_SEED or 137137))%2147483647
        RANDOM_SEED = res
        return res
        end

        function to_price(security, value, class)
        -- преобразования значения value к цене инструмента правильного ФОРМАТА (обрезаем лишнии знаки после разделителя)
        -- Возвращает строку
        if (security == nil or value == nil) then return nil end
        local scale = getSecurityInfo(class or getSecurityInfo("", security).class_code, security).scale
        return string.format("%."..string.format("%d", scale).."f", tonumber(value))
        end

        1. к сожалению, конкретная цена исполнения не может быть известна заранее, это свойство рыночной заявки "исполнять по любым ценам". вы можете посылать лимитную заявку с признаком "снять остаток", и тогда цена, которую вы указываете, будет "оценкой снизу": сделки будут по этой указанной цене или лучше. либо вы можете обрабатывать колбэк OnTrade и накапливать сделки по заявке, после чего по этим сделкам можно легко вычислить цену, которая получилась. другого способа нет.

          1. Мне не надо заранее цену знать, я потом не могу узнать и понять какая функция самая простая OnTrade (в ней столько параметров и еще какие битовые флаги!!!, она показывает и фьючи и акции?) или может для акций предпочтительней выбрать OnDepoLimit (нет примера толкового) а для фьючерсов OnFuturesClientHolding (примеров тоже нет). Хочется сделать больше универсальности и как можно проще без изысков лишних.

            1. Я не очень понимаю, что вы подразумеваете под " узнать цену по акции и фьючерсу". Вы написали три камента, но яснее не стало. 🙂 Вы хотите получить среднюю цену исполнения каждой заявки? Или среднюю цену позиции по инструменту? Если ваш робот ставит заявку "по рынку" (или лимитку. или не робот, а вы ставите руками, или если кто-то, у кого есть доступ к вашему счету через другой квик, и по этой заявке возникает сделка), то сделки попадут в колбэк OnTrade (и по акциям, и по фьючам и по любым доступным инструментам). Далее, агрегируя приходящие сделки по номеру заявки, вы можете посчитать среднюю цену исполнения по любой новой заявке, а так же среднюю цену позиции, если агрегировать по инструменту. В OnTrade попадают только новые сделки, которые совершены, когда скрипт уже запущен. Чтобы посчитать что-то по прошлым данным, то вам нужно пролистать таблицу Trade. Формат строк в таблице Trade и в колбэке OnTrade - одинаковый, что упрощает задачу.

              1. Подскажите какие параметры нужно вписать в этот OnTrade? чтобы он выдал цену сделки к примеру по сберу. class_code, sec_code, trans_id? Никак не могу добиться цены((( Ни одной толковой книжки нет, купил курсы какие то тупые с арифметикой.

                1. не вы эту функцию вызываете. вы ее в коде объявляете, и дальше квик эту функцию вызывает. отвечая на ваш вопрос:

                  1
                  2
                  3
                  4
                  5
                  
                  function OnTrade(trade)
                    if trade.sec_code == "SBER" then 
                      message(tostring(trade.price), 0)
                    end
                  end

                  вы объявляете функцию в скрипте, и ждете, пока квик ее вызовет.

                    1. Добрый день. Скажите какой функцией, таблицей или еще чем добиться, что это г.... ontrade ОДНУ среднюю цену

                      1
                      2
                      3
                      4
                      5
                      6
                      7
                      8
                      9
                      10
                      11
                      12
                      13
                      14
                      15
                      16
                      17
                      18
                      19
                      20
                      21
                      22
                      23
                      24
                      25
                      26
                      27
                      28
                      29
                      30
                      31
                      32
                      33
                      34
                      35
                      36
                      37
                      38
                      39
                      
                      is_run = true				
                      Sec_code = "SBER"			
                      Table1 = {}
                      Table2 = {}
                       
                       
                      function main()				
                          while is_run do
                              sleep(50)
                          end;
                      end
                       
                      function OnStop()			
                          is_run = false
                      end
                       
                      function OnTrade(trade)
                      local itog = 0;
                      local posl = 0;
                      local sredcen = 0;
                      local i = 0
                       
                        if trade.sec_code == Sec_code then 
                          	i = i + 1 
                      		Table1[i] = tostring(trade.price)
                      		Table2[i] = tostring(trade.qty)
                       
                      sleep(10)
                       
                      	for n = 1, #Table1, 1 do
                      	itog = Table1[n]*Table2[n] + itog
                      	posl = posl + Table2[n]
                      	end
                       
                      sredcen = itog/posl
                      message((sredcen), 1)	
                       
                        end
                      end
                    2. не может быть одна средняя цена. предположим, вы хлотите продать 3 чего-то по цене 1. а в стакане стоят покупки 1 по цене 2, 1 по цене 3 и 1 по цене 4.

                      вы отправляете заявку. средняя цена у вас будет (3 * 1 )/3 = 1, потому, что у вас есть только ваша транзакция. дальше вам приедет сделка 1 по 4, и средняя цена станет (2 * 1 + 1 * 4) / 3 = 2. дальше придет вторая сделка и средняя цена станет (1 * 1 + 1 * 3 + 1* 4) / 3 = 2,666. дальше придет третья сдека и вы получите (0*1 + 1*2 + 1*3 + 1*4)/3 = 3

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

                      1
                      2
                      3
                      4
                      5
                      6
                      7
                      8
                      9
                      10
                      11
                      12
                      13
                      14
                      15
                      16
                      17
                      18
                      19
                      20
                      21
                      22
                      23
                      24
                      25
                      26
                      27
                      28
                      29
                      30
                      31
                      32
                      33
                      34
                      35
                      36
                      37
                      38
                      39
                      40
                      41
                      42
                      43
                      44
                      45
                      46
                      47
                      48
                      49
                      50
                      51
                      52
                      53
                      54
                      55
                      56
                      57
                      58
                      59
                      60
                      61
                      
                      order_holder = {
                          by_trans_id = {}
                      }
                      setmetatable(order_holder, {
                          __index = {
                              OnOrder = function (self, order)
                                  local holded_order = self[order.order_num]
                                  if holded_order ~= nil then
                                      for k, v in pairs(order) do
                                          holded_order[k] = v
                                      end
                                  else 
                                      self[order.order_num] = order
                                  end
                              end,
                              OnTrade = function (self, trade)
                                  local order = self[trade.order_num] 
                                  if order == nil then 
                                      order = self.by_trans_id[trade.trans_id] or {order_num = trade.order_num}
                                      self[trade.order_num] = order
                                  end
                                  order.trades = order.trades or {}
                                  local trades = order.trades
                                  if trades[trade.trade_num] == nil then
                                     trades.total_qty = (trades.total_qty or 0) + trade.qty
                                     trades.total_vol = (trades.total_vol or 0) + trade.qty * trade.price
                                  end
                                  trades[trade.trade_num] = trade    
                              end,
                              GetMeanPrice = function (self, order_num)
                                  local trades = (self[order_num] or {}).trades or {}
                                  trades.total_qty = trades.total_qty or 0
                                  if trades.total_qty > 0 then
                                      local order = self[order_num] or {qty = 0, price = 0}
                                      local order_balance_qty = math.max(0, order.qty - trades.total_qty)
                                      trades.total_vol = trades.total_vol or 0
                                      return ((order_balance_qty * order.price) + trades.total_vol) / (trades.total_qty + order_balance_qty)
                                  end 
                                  return (self[order_num] or {}).price or 0
                              end
                          }
                      })
                       
                      function OnOrder(order)
                          order_holder:OnOrder(order)
                      end
                       
                      function OnTrade(trade)
                          order_holder:OnTrade(trade)
                          message('Mean expected price for order #'..tostring(trade.order_num)..' is: '..tostring(order_holder:GetMeanPrice(trade.order_num)), 1)
                      end
                       
                      function main()
                          while not exitflag do
                              sleep(10)
                          end
                      end
                       
                      function OnStop() 
                          exitflag = true 
                      end
  2. Здравствуйте, Дмитрий.
    Я новичок и программлю всего чуть больше месяца. Как старший товарищ , пожалуйста, подскажите.
    В скрипте делаю запрос таблицы getParamEx, откуда забираю 10 строк. Пример:

    last = tonumber(getParamEx(Class,Emit,"LAST").param_value)
    BuyDep = tonumber(getParamEx(Class,Emit,"BUYDEPO").param_value)
    SellDep = tonumber(getParamEx(Class,Emit,"SELLDEPO").param_value)
    step = tonumber(getParamEx(Class,Emit,"SEC_PRICE_STEP").param_value)
    и т.д.

    Можно ли как-нибудь упростить запрос? (Возможно я ошибаюсь, но, по-моему к каждой строке идет "индивидуальный запрос", что не есть ГУД!) Что-нибудь как с отправкой транзакций.
    transaction = {
    ["ACCOUNT"]=MyAccount,
    ["CLASSCODE"]=Class,
    ["SECCODE"]=Emit,
    ...
    Например:
    getParamEx(Class,Emit) = {
    tonumber["LAST".param_value]=last,
    tonumber["BUYDEPO".param_value]=BuyDep,
    и.т.д.
    То есть чтобы необходимая таблица "всасывалась за 1 раз".
    И второй вопрос: кроме 10 глобальных переменных param_value есть 2 исключения:

    одно: ..."SHORTNAME").param_image)

    и одно local... - STATUS
    (В крайнем случае с 2 последними исключениями можно смириться ).

    1. 1. в квике нет встроенного способа "упростить запрос". но можно сделать небольшой класс, который упрощает синтаксис, предоставляя доступ к параметрам инструмента как к lua-таблице. делается это примерно так:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      
      params_fetcher = {}
      setmetatable(params_fetcher, {
          __call = function(self, aclass_code, asec_code)
              local tbl = {class_code = aclass_code, sec_code = asec_code}
              setmetatable(tbl, {
                  __index = function(self, key)
                      local res
                      local paramtable = getParamEx(self.class_code, self.sec_code, key)
                      if paramtable ~= nil and paramtable.result == '1' then 
                          if paramtable.param_type == '1' or paramtable.param_type == '2' then
                              res = tonumber(paramtable.param_value)
                          else
                              res = paramtable.param_value
                          end
                      end
      --                if res ~= nil then rawset(self, key, res) end
                      return res
                  end
              })
              return tbl
          end
      })
       
      function main()
          local tmp = params_fetcher('TQBR', 'SBER')
          message('TQBR@SBER.LAST == ' .. tostring(tmp.LAST))
      end

      да, все равно, при каждом доступе к параметру tmp.LAST, или любому другому, будет неявно вызываться getParamEx, но можно сделать "кэширование" при помощи rawset(), тогда вызов будет делаться один раз. просто раскомментарьте строку, содержащую rawset(). это полезно, например, если вы внутри одной функции 10 раз обращаетесь к значению LAST и хотите при этом, чтобы доступ к значению был быстрым. меняться это значение не будет.

      2. все эти исключения можно обработать в предлагаемом метаметоде __index(). там уже есть обработка значения поля param_type, можете добавить туда любую логику.

      1. вот так можно улучшить:

        1
        2
        
            __call = function(self, aclass_code, asec_code, acached)
                local tbl = {class_code = aclass_code, sec_code = asec_code, cached = (acached == true)}

        и

        1
        
                        if res ~= nil and self.cached then rawset(self, key, res) end

        , тогда при использовании вот так:

        1
        2
        3
        
            local tmp = params_fetcher('TQBR', 'SBER')
            message('TQBR@SBER.LAST == ' .. tostring(tmp.LAST))
            message('TQBR@SBER.LAST == ' .. tostring(tmp.LAST))

        функция getParamEx() вызовется ДВА раза, а если так:

        1
        2
        3
        
            local tmp = params_fetcher('TQBR', 'SBER', true)
            message('TQBR@SBER.LAST == ' .. tostring(tmp.LAST))
            message('TQBR@SBER.LAST == ' .. tostring(tmp.LAST))

        то ОДИН раз. при доступе к параметру LAST во второй раз будет взято значение из кэша.

      2. toxa.
        Премного благодарен, за оперативный и исчерпывающий ответ.
        Только вот проще не стало. В getParamEx размещаются помимо "динамических" (LAST, BID, OFFER) и "статические" данные с уменьшением по степени статичности (SEC_PRICE_STEP, DAYS_TO_MAT_DATE, PRICEMAX, PRICEMIN, BUYDEPO, SELLDEPO). Идея объединения всех getParamEx в одну кучу изначально базировалась на последующей возможного выноса этой таблицы за пределы основной математической функции робота.
        Писать всего робота в одной функции - не хотелось, а распихивать по разным блокам getParamEx получается слишком путано и мудрёно да и наверное не правильно. В настоящий момент в моём скрипте получается, что ресурсы системы неоправданно тратятся на частый запрос статики, а со столь нужной динамикой - тормоза. Созданная "визуализационная" таблица по LAST в сравнении с таблицей "Текущие торги" QUIKa показывает задержку видимую на глаз в районе 0.5-1 сек. Возможно перерисовка тормозит.
        Не пинайте меня сильно это мой первый робот, да и не совсем мой. Попытался стартовый курс обучения QLUA перекрутить на сторону мне необходимую для создания робота. Акелло промахнулся! Признаю.
        Вопросы по предложенному блоку начиная со второго:
        Вы писали ...__index(). там уже есть обработка значения поля param_type... DOUBLE и LONG в вашем случае заменить на param_image? Я пытаюсь по строкам понять что куда дописывать :)))
        1. вопрос:
        Метатаблица params_fetcher, как я понимаю, "собирает" все имеющиеся данные в getParamEx, self- параметр, а ключ - значение?
        В майне ТэЭмПэШка задаёт инструмент, а строковый tmp.LAST и содержит необходимую для меня цену последней сделки?

        Остальные данные с getParamEx мне в Майне tostringИТЬ:
        Задавать типа params_fetcher.SEC_PRICE_STEP = step
        или SPBFUT@SRH0.SEC_PRICE_STEP = step. (это вроде для вывода сообщения!?)

        Еще раз благодарю за помощи в возможно разделении на "динамические" и "статические" данные. Статику действительно можно "послать на ...Камчатку", т.к. нужна 1 - 3 раз за сессию"

    2. там не виден ваш ответ, из-за символа "собака", очевидно, включился режим премодерации. попробую объяснить.

      нет, метатаблица ничего не "собирает". в метатаблице могут быть определены метаметоды, которые вызываются в определенных случаях. так, метаметод __index() вызывается только в том случае, если в таблице нет значения. то есть, если написать a={[5]=10}, то для tmp = a[5] метаметод __index() НЕ БУДЕТ вызван, в для tmp = a[6] будет, так как в а нет ключа 6.

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

      1
      2
      3
      
      tmp.SEC_PRICE_STEP = tmp.SEC_PRICE_STEP
      message(tostring(tmp.SEC_PRICE_STEP))
      message(tostring(tmp.SEC_PRICE_STEP))

      это выглядит диковато, но не лишено смысла. в начале кода tmp не содержит пару 'SEC_PRICE_STEP': , это приводит к вызову метаметода __index(), получению параметра SEC_PRICE_STEP при помощи getParamEx(). дальше, в левой части первой строки мы добавляем ключ 'SEC_PRICE_STEP': . и на второй и третьей строке при получении значения по ключу SEC_PRICE_STEP метаметод __index() не вызывается, а просто возвращается имеющееся значение.

      что касается типов параметров. функция getParamEx() возвращает таблицу со строками: "результат вызова", "тип параметра", "значение", "значение в читаемом виде". в метаметоде мы смотрим "результат вызова", если он "успех", то смотрим "тип параметра", если он "целое число" или "дробное число", то мы возвращаем "значение" приведенное к числовому типу LUA. иначе мы возвращаем строку. мне было лень для примера писать обработку ВСЕХ случаев, например, преобразование даты/времени из строки в unixtime, для строковых параметров брать не "значение", а "значение в читаемом виде" и прочее, о чем вы пишите.

      таким образом tmp.LAST вернет нам NUMBER, tmp.SHORTNAME строку STRING, а tmp.ERRORPARAM вернет nil, так как параметра ERRORPARAM не существует и "результат вызова" = "ошибка". преобразовывать типы внутри лучше, так как это повышает читаемость кода. лучше написать tmp.BID - tmp.OFFER чем tonumber(getPatamEx('TQBR', 'SBER', 'BID').param_value) - tonumber(getPatamEx('TQBR', 'SBER', 'OFFER').param_value)

      1. собака SRH0.SEC_PRICE_STEP = step. (это вроде для вывода сообщения!?)

        Еще раз благодарю за помощи в возможно разделении на "динамические" и "статические" данные. Статику действительно можно "послать на ...Камчатку", т.к. нужна 1 - 3 раз за сессию"

        1. Ещё раз благодарю. Робот РИСКОпил вроде работает (убытки пилит) :))) С Вашим блоком замены toxa, я обязательно в течении ближайших пары дней разберусь. Если появятся вопросы можно будет обратиться?

          1. вот окончательный вариант. что непонятно - пишите.

            1
            2
            3
            4
            5
            6
            7
            8
            9
            10
            11
            12
            13
            14
            15
            16
            17
            18
            19
            20
            21
            22
            23
            24
            25
            26
            27
            28
            29
            30
            31
            32
            33
            34
            35
            36
            37
            38
            39
            40
            41
            42
            43
            44
            45
            46
            47
            
            params_fetcher = {}
            setmetatable(params_fetcher, {
                __call = function(self, aclass_code, asec_code, acached)
                    local tbl = {class_code = aclass_code, sec_code = asec_code, cached = (acached == true)}
                    setmetatable(tbl, {
                        __index = function(self, key)
                            local res
                            local paramtable = getParamEx(self.class_code, self.sec_code, key)
                            if paramtable ~= nil and paramtable.result == '1' then 
                                if paramtable.param_type == '1' or paramtable.param_type == '2' then
                                    res = tonumber(paramtable.param_value)
                                else
                                    res = paramtable.param_image
                                end
                            end
                            if res ~= nil and self.cached then rawset(self, key, res) end
                            return res
                        end
                    })
                    return tbl
                end
            })
             
            sec_params = {}
            setmetatable(sec_params, {
                __index = function(self, class_code)
                    local codes = {}
                    setmetatable(codes, {
                        __index  = function(self, sec_code)
                          local sec_table = params_fetcher(class_code, sec_code)
                          rawset(self, sec_code, sec_table)
                          return sec_table
                        end
                    })
                    rawset(self, class_code, codes)
                    return codes
                end
            })
             
            function main()
                -- example 1:
                message('TQBR@SBER.LAST == ' .. tostring(sec_params.TQBR.SBER.LAST))
                -- example 2:
                local tqbr_sber = sec_params.TQBR.SBER
                tqbr_sber.cached = true -- enable parameter caching, getParamEx() is called only once per parameter
                message('TQBR@SBER.SHORTNAME == ' .. tostring(tqbr_sber.SHORTNAME))
            end