Сохранение параметров скрипта QLua(Lua) между запусками

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

Блоки кода

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

local a = {
   ['a']=10,
   ['d']={
      [1]=100,
      [2]='asdfg',
      [3]=false,
      [4]={
         ['a']=10,
         ['c']=true,
         ['b']='qwerty'
      }
   },
   ['c']=true,
   ['b']='qwerty'
}
return a

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

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

Сохранение параметров скрипта QLua(Lua) между запусками: 116 комментариев

  1. Здравствуйте. Пробую использовать загрузку и сохранение таблицы. При остановке скрипта выводится ошибка "bad argument #1 to 'for iterator' (table expected, got nil)" на строку "for key, val in pairs(a) do". Что может быть?

      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
        48
        49
        50
        51
        52
        53
        54
        55
        56
        57
        58
        59
        60
        61
        62
        63
        64
        65
        66
        67
        68
        69
        70
        71
        72
        73
        74
        75
        76
        77
        78
        79
        80
        
        function main()
           local FPath = getScriptPath()..'\\balance.txt'  -- Функция возвращает путь, по которому находится запускаемый скрипт, без завершающего обратного слеша («\»). Например, C:\QuikFront\Scripts.
           local NewParams = LoadTable(FPath)-- Загружает таблицу из файла
           CreateTable()-- Создает таблицу
        end
        function CreateTable()--- Функция создает таблицу
           -- Получает доступный id для создания
           t_id = AllocTable()      
           -- Добавляет 2 колонки
           AddColumn(t_id, 0, "Дата", true, QTABLE_INT_TYPE, 30)
           AddColumn(t_id, 1, "Средства", true, QTABLE_INT_TYPE, 30)
           -- Создаем
           t = CreateWindow(t_id)
           -- Даем заголовок   
           SetWindowCaption(t_id, "Баланс")
           -- Добавляет строку
           InsertRow(t_id, -1)
           SetWindowPos(t_id, 0, 0, 349, 263)-- Задаем позицию окна
        end 
        function OnStop()
         SaveTable(t_id, FPath)  -- Сохраняет таблицу в файл
        end;
         
        ------------------------------------------------------------------------------------
        ------------------------------------------------------------------------------------
         
        LoadTable = function(FPath)-- Загружает таблицу из файла
           local func, err = loadfile(FPath)
           if not func then
              message('Ошибка загрузки таблицы из файла: '..err)
              return nil
           else
              return func()
           end
        end
         
         
        SaveTable = function(Table, FPath)  -- Сохраняет таблицу в файл
         
           local Lines = {}
           local level = 0
         
           function Rec(a)
              local first = true 
              level = level + 1  
              local s = '' for i=1,level do s = '   '..s end
              for key, val in pairs(a) do         
                 if not first then Lines[#Lines] = Lines[#Lines]..',' end
                 local k = '[\''..key..'\']'
                 if type(key) == 'number' then k = '['..key..']' end
                 if type(val) ~= 'table' then 
                    if type(val) == 'string' then
                       val = '\''..val..'\''
                    else
                       val = tostring(val)
                    end
                    table.insert(Lines, s..k..'='..val)
                    first = false
                 else            
                    table.insert(Lines, s..k..'={')
                    first = false
                    Rec(val)            
                    table.insert(Lines, s..'}')
                    level = level - 1
                 end
              end      
           end   
         
           table.insert(Lines, 'local a = {')
           Rec(Table)   
           table.insert(Lines, '}')
           table.insert(Lines, 'return a')
         
           local f = io.open(FilePath, 'w')   
           for i=1,#Lines do
              f:write(Lines[i]..'\n') 
              f:flush() 
           end
           f:close()
        end

        В чем ошибка?

        1. Ваш фрагмент кода ошибку не может сгенерировать, событие OnStop не наступает и функция SaveTable не вызывается.
          Ну да ладно, вы в Onstop передаете вместо таблицы - t_id, которое имеет тип 'number'.
          А ошибка говорит про 'nil'.
          Для того, что бы сохранить пользовательскую таблицу в файл нужно считать с нее данные в lua-таблицу и уже ее сохранять

          1. Разве событие OnStop не наступает когда останавливаю скрипт? Чем отличается пользовательская таблица от Lua таблицы? И еще нужно таблицу преобразовать в строку а потом только сохранять в файл?

          2. Что преобразуют данные функции в строку?
            function table.val_to_str (v)
            function table.key_to_str (k)
            function table.tab_to_str(tbl)
            function table.arr_to_str(tbl, s)
            В чем разница function table.save_to_read(directory, tbl) от function table.save_to_load(directory, tbl)?
            Мне нужно использовать function table.save_to_load(directory, tbl)?

            1. "Разве событие OnStop не наступает когда останавливаю скрипт?" - наступает, но в вашем случае main не содержит бесконечного цикла и завершается сразу после запуска скрипта, без события OnStop, которое возникает при нажатии на кнопку "остановить" в окне доступных скриптов.

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

              Пользовательская таблица, это не lua, а визуальная форма у нее есть функции интерфейса, типа SetCell.
              Считываете из ячеек значения и присваиваете их в свою таблицу, например:

              1
              2
              3
              4
              5
              6
              7
              8
              9
              10
              11
              12
              13
              14
              15
              16
              17
              18
              19
              20
              
              local t = {}
              local rows,  cols = GetTableSize (t_id)
              for r = 1, rows do
                local row = {}
                for c = 1, cols do
                  row[c] = GetCell(t_id, r, c).image or ""
                end
                t[r] =  row
              end
              --сохранил
              table.save_to_read(FilePath, t)
              --считал
              local t = table.read(FilePath)
              -- и заполняете пользовательскую таблицу
              for r = 1, #t do
                local rows = t[r]
                for c = 1, #rows do
                  SetCell(t_id, r, c, rows[c])
                end
              end

              table.tab_to_str(tbl) лучше использовать для файлов, которые вы сами читать не будете, разве что в процессе отладки своего скрипта,
              которые так же будут считаны и преобразованы в lua-таблицу функцией table.read

              1
              2
              3
              4
              5
              6
              7
              8
              9
              10
              11
              12
              13
              14
              15
              16
              17
              18
              
              ["20201006_135000"]= {C= 116380.0,H= 116410.0,V= 326.0,O= 116380.0,L= 116340.0,T= {ms= 0,year= 2020,day= 6,min= 50,month= 10,week_day= 2,count= 1,sec= 0,hour= 13}},
              ["20201006_135100"]= {C= 116330.0,H= 116380.0,V= 309.0,O= 116370.0,L= 116320.0,T= {ms= 0,year= 2020,day= 6,min= 51,month= 10,week_day= 2,count= 1,sec= 0,hour= 13}},
              ["20201006_135200"]= {C= 116350.0,H= 116380.0,V= 91.0,O= 116350.0,L= 116340.0,T= {ms= 0,year= 2020,day= 6,min= 52,month= 10,week_day= 2,count= 1,sec= 0,hour= 13}},
               
              --вот так делается такая строка:
              local s = table.key_to_str("20201006_135000").."= "..table.val_to_str( {C= 116380.0,H= 116410.0} )
              --ее можно дописать в открытый файл режиме дозаписи "a+"
              local f = io.open(FilePath, "a+")
              f:write(s..",\n"); f:flush() -- f:close() - закрыть файл можно при остановке скрипта
               
              --или переписать файл целиком в режиме "w":
              local t = {}
              t[#t+1] = table.key_to_str("20201006_135000").."= "..table.val_to_str( {C= 116380.0,H= 116410.0} )
              t[#t+1] = table.key_to_str("20201006_135100").."= "..table.val_to_str( {C= 116330.0,H= 116380.0} )
               
              local f = io.open(FilePath, "w")
              local s = table.concat(t, ",\n") -- преобразуем массив t в строку
              f:write(s); f:flush(); f:close()
                  1. 1
                    2
                    3
                    4
                    5
                    6
                    7
                    8
                    9
                    10
                    11
                    12
                    13
                    14
                    15
                    16
                    17
                    18
                    19
                    
                    function main()
                    	CreateTable()-- Создает таблицу
                    	while IsRun do
                    		if IsWindowClosed(t_id) then --Функция возвращает «true», если окно с таблицеи «t_id» закрыто
                                break
                    		end
                    		local t = {}
                    		local rows,  cols = GetTableSize(t_id)
                    		message(tostring(cols))
                    		for r = 1, rows do
                    			local row = {}
                    			for c = 1, cols do
                    				row[c] = GetCell(t_id, r, c).image or ""
                    			end
                    			t[r] =  row
                    		end
                    		sleep(1);	
                    	end;	
                    end;

                    выводится ошибка "attempt to index a nil value" на "row[c] = GetCell(t_id, r, c).image or """. что не так?

                    1. Не знаю, у меня все ровно работает. Теги "LUA - LUA" работают с ошибкой. Попробуйте так

                      function table.val_to_str (v)
                      if "string" == type( v ) then
                      v = string.gsub( v, "\n", "\\n" )
                      if string.match( string.gsub(v,"[^'\"]",""), '^"+$' ) then
                      return "'" .. v .. "'"
                      end
                      return '"' .. string.gsub(v,'"', '\\"' ) .. '"'
                      end
                      return "table" == type( v ) and table.tab_to_str( v ) or tostring( v )
                      end

                      function table.key_to_str (k)
                      if "string" == type( k ) and string.match( k, "^[_%a][_%a%d]*$" ) then
                      return k
                      end
                      return "[" .. table.val_to_str( k ) .. "]"
                      end

                      function table.tab_to_str(tbl)
                      if type(tbl)~='table' then return table.val_to_str(tbl) end
                      local result, done = {}, {}
                      for k, v in ipairs( tbl ) do
                      table.insert( result, table.val_to_str( v ) )
                      done[ k ] = true
                      end
                      for k, v in pairs( tbl ) do
                      if not done[ k ] then
                      table.insert( result, table.key_to_str( k ) .. "=" .. table.val_to_str( v ) )
                      end
                      end
                      return "{" .. table.concat( result, "," ) .. "}"
                      end

                      function table.arr_to_str(tbl, s)
                      local result, done= {"{"}, {}
                      for i = 1, #tbl do
                      if type(tbl[i]) == "table" then
                      local str = table.concat(table.arr_to_str(tbl[i], s.." "), ",\n")
                      result[#result+1] = s..table.key_to_str(i).."= "..str:gsub("{,", "{")
                      else
                      result[#result+1] = s..table.key_to_str(i).."= "..table.val_to_str(tbl[i])
                      end
                      done[i] = true
                      end

                      local rnum = {}
                      for k, v in pairs(tbl) do
                      if not done[k] and tonumber(k) then
                      rnum[#rnum+1] = k
                      done[k] = true
                      end
                      end
                      if #rnum > 0 then
                      table.sort(rnum, function (a, b) return tonumber(a) 0 then
                      -- table.sort(rstr, function (a, b) return a < b end)
                      for i=1, #rstr do
                      local k = rstr[i]
                      if type(tbl[k]) == "table" then
                      local str = table.concat(table.arr_to_str(tbl[k], s.." "), ",\n")
                      result[#result+1]= s..table.key_to_str(k).."= "..str:gsub("{,", "{")
                      else
                      result[#result+1]= s..table.key_to_str(k).."= "..table.val_to_str(tbl[k])
                      end
                      end
                      end

                      result[#result + 1] = s:sub(1, #s-1).."}"

                      return result
                      end

                      function table.save_to_read(directory, tbl)
                      if type(tbl) ~= "table" then return end
                      local f= io.open(directory, 'w'); if not f then return end

                      local r = table.arr_to_str(tbl, "")
                      table.remove(r, 1); table.remove(r, #r)

                      f:write(table.concat(r,",\n")); f:flush(); f:close()
                      end

                      function table.save_to_load(directory, tbl)
                      if type(tbl) ~= "table" then return end
                      local f= io.open(directory, 'w'); if not f then return end

                      local r = table.arr_to_str(tbl, "")
                      table.remove(r, 1)

                      f:write("{\n"..table.concat(r,",\n")); f:flush(); f:close()
                      end

                      function table.read(directory)
                      --преобразует строку: "key=val," в таблицу
                      local f = io.open(directory, "r")
                      if not f then
                      sleep(1)
                      f = io.open(directory, "r")
                      if not f then return end
                      end

                      local str = "{"..f:read("*a").."}"; f:close()

                      local fn = loadstring("return "..str)
                      if type(fn) == "function" then
                      return fn()
                      end
                      end

                      function table.load(directory)
                      --преобразует строку: "{key=val,}" в таблицу
                      local f = io.open(directory, "r")
                      if not f then
                      sleep(1)
                      f = io.open(directory, "r")
                      if not f then return end
                      end

                      local str = f:read("*a"); f:close()

                      local fn = loadstring("return "..str)
                      if type(fn) == "function" then
                      return fn()
                      end
                      end

                      function table.msg(tbl, slp)
                      if type(tbl) ~= "table" then
                      tbl = {tostring(tbl)}
                      end
                      local p = getScriptPath().."\\logmsg.txt"
                      table.save_to_read(p, tbl)
                      os.execute("start /I "..p)

                      local slp = slp or 5000
                      sleep(slp)
                      end

                      function main()
                      CreateTable()-- Создает таблицу
                      while not exitflag do
                      --Функция возвращает «true», если окно с таблицеи «t_id» закрыто
                      if IsWindowClosed(t_id) then break end

                      local t = {}
                      local rows, cols = GetTableSize(t_id)
                      -- message(tostring(rows).." "..tostring(cols))
                      for r = 1, rows do
                      local row = {}
                      for c = 1, cols do
                      row[c] = GetCell(t_id, r, c).image or ""
                      end
                      t[r] = row
                      end
                      table.msg(t, 3000)
                      end

                      end
                      function CreateTable()--- Функция создает таблицу
                      -- Получает доступный id для создания
                      t_id = AllocTable()
                      -- Добавляет 2 колонки
                      AddColumn(t_id, 1, "Дата", true, QTABLE_INT_TYPE, 30)
                      AddColumn(t_id, 2, "Средства", true, QTABLE_INT_TYPE, 30)
                      -- Создаем
                      CreateWindow(t_id)
                      -- Даем заголовок
                      SetWindowCaption(t_id, "Баланс")
                      -- Добавляет строку
                      InsertRow(t_id, -1)
                      SetWindowPos(t_id, 0, 0, 349, 263)-- Задаем позицию окна
                      end
                      function OnStop()
                      exitflag = true
                      DestroyTable(t_id)
                      return 1000
                      end

                  2. Все равно та же ошибка lua:18: attempt to index a nil value

                    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
                    
                    function OnInit()
                    	st = 1
                    	poz=0
                    	plan=0 
                    end;
                     
                    function main()
                    	CreateTable()-- Создает таблицу
                    	while not exitflag do
                    		--Функция возвращает «true», если окно с таблицеи «t_id» закрыто
                    		if IsWindowClosed(t_id) then break end
                    		local t = {}
                    		local rows, cols = GetTableSize(t_id)
                    		--message(tostring(rows).." "..tostring(cols))
                    		for r = 1, rows do
                    			local row = {}
                    			for c = 1, cols do
                    				row[c] = GetCell(t_id, r, c).image or ""
                    			end
                    			t[r] = row
                    		end
                    		table.msg(t, 3000)
                    	end
                     
                    end
                     
                    function CreateTable()--- Функция создает таблицу   
                    	t_id = AllocTable()  -- Получает доступный id для создания      
                    	AddColumn(t_id, 0, "Дата", true, QTABLE_INT_TYPE, 30)     -- Добавляет 2 колонки
                    	AddColumn(t_id, 1, "Средства", true, QTABLE_INT_TYPE, 30) --
                    	CreateWindow(t_id)-- Создаем   
                    	SetWindowCaption(t_id, "Баланс")-- Даем заголовок     
                    	InsertRow(t_id, -1)-- Добавляет строку
                    	SetWindowPos(t_id, 0, 0, 349, 263)-- Задаем позицию окна
                    end 
                     
                    function OnFuturesLimitChange(fut_limit)
                    	--message("Плановые чистые позиции "..tostring(fut_limit.cbplplanned));
                    	plan = fut_limit.cbplplanned
                    end
                     
                    function OnFuturesClientHolding(fut_pos)
                    	--message("Текущие чистые позиции "..tostring(fut_pos.totalnet));-- NUMBER 
                    	poz = fut_pos.totalnet
                    	vr = os.date("%d %B %X")
                    	if (poz >= 0) and (plan ~= 0) then 
                    		--message(tostring(st))
                    		SetCell(t_id, st, 1, tostring(plan))
                    		SetCell(t_id, st, 0, tostring(vr))
                    		st = InsertRow(t_id, -1)
                    	end
                    end
                     
                    function OnStop()
                    	exitflag = true
                    	DestroyTable(t_id)
                    	table.save(t,"C:\\Users\\Александр\\Desktop\\трейдинг\\Скрипты\\balance.txt")
                    	return 1000	
                    end;
                    1. Посмотрите внимательнее на свой пример и мой.
                      у вас Дата имеет столбец с индексом = 0, а такого индекса не существует в пользовательской таблице.
                      SetCell(t_id, st, 0, tostring(vr)) - вот это никуда и ничего не присвоит.
                      создать строку таблицы нужно прежде, чем ее заполнить значениями:
                      st = InsertRow(t_id, -1)
                      SetCell(t_id, st, 1, tostring(vr)) - так будет получше, чем у вас

                      и вот так:
                      table.save(t,"C:\\Users\\Александр\\Desktop\\трейдинг\\Скрипты\\balance.txt")
                      не пойдет.
                      во первых, таблица "t" в OnStop не видна, т.к. она объявлена локально в другом блоке - в main.
                      во вторых, скрипты обязательно! нужно держать в папках, имена которых не содержат кириллицы и магических символов.

                  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
                    
                    function main()
                    	CreateTable()-- Создает таблицу	
                    	local t = table.read(getScriptPath().."\\logmsg.txt")
                    	-- и заполняете пользовательскую таблицу
                    	for r = 1, #t do
                    		local rows = t[r]
                    		for c = 1, #rows do
                    			SetCell(t_id, r, c, rows[c])
                    		end
                    	end
                     
                    	--[[while not exitflag do
                    		--Функция возвращает «true», если окно с таблицеи «t_id» закрыто
                    		if IsWindowClosed(t_id) then break end
                    		local t = {}
                    		local rows, cols = GetTableSize(t_id)
                    		--message(tostring(rows).." "..tostring(cols))
                    		for r = 1, rows do
                    		local row = {}
                    		for c = 1, cols do
                    		row[c] = GetCell(t_id, r, c).image or ""
                    		end
                    		t[r] = row
                    		end
                    		table.msg(t, 3000)
                    	end]]
                     
                    end
                    1. навскидку, у вас столбцы int_type
                      вставка значения в ячейку этого типа выглядит так:
                      SetCell(t_id, r, c, tostring(rows[c]), rows[c])

                    1. потому что у вас строка добавлена одна, при создании формы, а в логе наверное чутка поболее строк, судя по туму, что вы используете OnFuturesClientHolding(fut_pos).
                      Считывайте на запуске одноименною таблицу, формируете строки, считываете файл, вставляете данные.
                      Но нужно понимать, что в таблице "futures_client_holding" строки могут не совпадать со строками, которые в файле.
                      Нужна проверка на валидность, по коду инструмента и торговому счету.

                  4. Как считать из массива на сколько строк будет таблица? Сколько строк будет содержать "t"? Чтобы в новую создаваемую таблицу добавить нужное колличество строк

                    1
                    
                      local t = table.read(getScriptPath().."\\logmsg.txt")
                    1. 1
                      2
                      3
                      4
                      5
                      6
                      7
                      
                      for r = 1, #t do
                      	InsertRow(t_id, -1)
                      		local rows = t[r]
                      		for c = 1, #rows do
                      			SetCell(t_id, r, c, rows[c])
                      		end
                      	end
                    1. можно написать индикатор и добавить его на график или сделать еще таблицу с большим кол-вом строк/столбцов и заливать цветом нужные ячейки

  2. Какова роль в этой ситуации оператора fn = loadstring("return " ..fn)? Может всё дело в логике его работы?

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
    function read (filename)
       local f,err = io.open(filename,"r")
       if not f then
          return nil,err
       end
       local fn =  "{"..f:read("*a").."}"
       fn = loadstring("return " ..fn)
       f:close()
       if not fn then
          return nil, "Не правильный формат данных в файле\n"..filename
       end
       return fn()
    end
    1. Здравствуйте!
      строка 6: считываем в переменную fn весь файл формата:
      a=10, b={a=10},

      т.к. это строка, то добавляем ей фигурные скобки, но это пока не таблица луа.

      строка 7: создаем функцию fn, которая при выполнении вернет из строки таблицу луа
      строка 12: возвращаем результат выполнения функции fn(), т.е. таблицу:
      {a=10, b={a=10}}

      А какой переменной вы присвоите эту таблицу - ваша забота))

      1. Я уже давно переделал под другие нужды эти функции, и не могу найти, где последний раз их использовал.
        local fn = "{"..f:read("*a").."}" --вот это выражение у меня уже без фигурных скобок

        1. Спасибо большое за уделенное внимание и ценные советы! Буду дальше разбираться. Мощная штука, надо все понять))). Поплыл на регулярных выражениях(((. Сижу, копаю. С Вашего позволения еще потом могу обратиться с вопросом?

            1. Уважаемый Kalikazandr, здравствуйте! Прошу пояснить в части, касающейся регулярных выражений, которые вы применили в своем коде:

              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
              
              function val_to_str (v)
              	if "string" == type( v ) then
              		v = string.gsub( v, "\n", "\\n" ) - меняем \n на \\n: перевод строки в строчной переменной, "экранирующий" обратный слэш?
              		if string.match( string.gsub(v,"[^'\"]",""), '^"+$' ) then - "вычищаем" строчную переменную от апострофов в начале фразы (или что-то ещё)?; ищем совпадение по шаблону '^"+$' - его суть в силу неопытности не понял?
              			return "'" .. v .. "'"
              		end
              		return '"' .. string.gsub(v,'"', '\\"' ) .. '"' - не понял шаблон замены '\\"', зачем ? 
              	end
              	return "table" == type( v ) and  tab_to_str( v ) or tostring( v )
              end
               
              function key_to_str (k)
              	if "string" == type( k ) and string.match( k, "^[_%a][_%a%d]*$" ) then - "^[_%a][_%a%d]*$"? - буквенно-цифровая последовательность от начальной позиции до любой позиции во фразе с заполнением любыми символами до конца? - правильно?
              		return k
              	end
              	return "[" .. val_to_str( k ) .. "]"
              end
               
              function tab_to_str(tbl)
              	local result, done = {}, {}
              	for k, v in ipairs( tbl ) do
              		table.insert( result, val_to_str( v ) )
              		done[ k ] = true
              	end
              	for k, v in pairs( tbl ) do
              		if not done[ k ] then
              			table.insert( result, key_to_str ( k ) .. "=" .. val_to_str ( v ) )
              			end
              	end
              	return table.concat( result, ",\n" )
              end

              Заранее спасибо за уделенное внимание)!

              1. Фишка в том, что в луа таблице пара: ключ = значение - и ключ и значение могут быть чем угодно, соответственно, что бы правильно сделать сохранение (и, что главное, что бы потом все это дело считать правильно из файла) необходимо, проверить и заменить/дополнительно экранировать с начала переменной и до ее конца, обрезать лишние пробелы. Например:
                path={}
                path._Orders = 'C:\\lua_modules\\Orders.lua'
                или так
                path["C:\\lua_modules\\Orders.lua"] = {hint = "лог заявок",{sec_code="SBER",price = 100,qty=10,balance=10}}

                Выражение '^"+$' познаково с начала и до конца строки

                1. Не тратьте на это время. Научитесь сохранять, считывать.
                  Когда ваш алгоритм начнет приносить прибыль, то вам не понадобится знание всех тонкостей луа - за не надобностью. Пить пиво на пляже можно и без знания захватов для форматирования строки)) Я в свое время зря потратил на это время, и на самом деле уже и подзабыл - за не надобностью)))

                2. Уважаемый kalikazandr, добрый вечер! Учитывая ваш опыт, не подскажите, интересует такой вопрос. Необходимо для дела выделить цветом текст (число) и подчеркнуть его цветной двойной чертой. Можно конечно всё организовать через клетки таблицы (значения собраны в таблицу) - раскрашивать с помощью SetColor, но уж очень интересно - есть ли в QLua (Lua) такие или приблизительно такие средства для String'ов), чтобы не загромождать таблицу цветовыми схемами? Спасибо! С уважением, Алексей!

                  1. Здравствуйте, к сожалению такой способ мне не известен и спец-символов в луа таких нет. Возможно вам стоит посмотреть в сторону юникода и функции string.gsub (s, pattern, repl [, n]).
                    Но оно того стоит?))) По времени дешевле закрасить ячейку.

                    1. Спасибо большое за уделенное время! Похоже, вы правы, проще будет раскрасить ячейку, тем более можно расцветить не только фон ячейки, но и сам текст - прекрасная альтернатива. Попробовал поискать по string.gsub - применение символов Юникода дает богатые возможности, но есть много "НО", чтобы их учесть нужно многое предусмотреть, возможно даже от чего-то отказаться в реализации. Не хотелось бы.

      2. Добрый день, kalikazandr! Хочу спросить не по теме. Вы не сталкивались с необходимостью вывода в одну таблицу Quik'а данных в динамике (к примеру - объемы) по нескольким инструментам. Какие подводные камни могут быть? И возможно ли это в принципе без каких-либо временных коллизий и потерь? Суть в том, чтобы изменения данных по свече не в новую строку записывать, а в уже выделенную строку под неё. Свеча закрывается, данные перестают записываться в старую ячейку, добавляется строка или просто переход на новую, уже добавленную строку, данные начинают писаться туда, в соответствующую ячейку. Данные по разным инструментам задумка писать в одну строку - соответственно временному интервалу.

        1. День добрый! Нет проблем,
          если строки - это время - выбранный таймфрейм, то и с источника данных в нужную строку добавляйте все изменения по нужному параметру.

            1. День добрый!
              Сделайте таблицу формата:
              local curr_rows = {posix1 = 1, posix2 = 2, posixN = N},
              где posix это время в секундах, округленное до нужного таймфрейма(ТФ), а значения - ссылки на № строк в таблице.
              Тогда получив новое значение нужного параметра, округляете текущее время до нужного ТФ,
              из таблиц квик:
              local item = getItem("all_trades", getNumberOf("all_trades")-1)
              local posix = math.floor(os.time(item.datetime)/60)*60 -- округлили до 1 минуты (так можно <= М60)
              local row = curr_rows[posix] -- получили № строки, в которую нужно сделать запись.
              Если со свечек берете данные, то там уже время округлено до ТФ, просто в секунды перевести.

              1. Kalikazandr, добрый вечер! Подскажите, маленько подвис в задаче: опираясь на вашу идею пытаюсь в таблицу добавить строку ключ - тайм-фрейм, значение - номер строки в пользовательской таблице, вроде posix1=1 и т.д. Пытаюсь это сделать через table.insert / table.sinsert. Не получается. Синтаксис для добавления строки с пользовательским ключом (в моем случае - тайм-фрейм) мне найти и подобрать не удалось. Может вы подскажите направление, куда смотреть? С уважением!

                1. Добрый вечер!
                  table.insert служит для добавления элемента массива:
                  tab = {"a", 1, "C"} эквивалентно {[1] = "a", [2] = 1, [3] = "C"}
                  print(tab[2]) = > 1
                  table.insert(tab, 2, "Вася")
                  print(tab[2]) = > Вася
                  А у вас не массив, тем паче использовать потокобезопасный table.sinsert - плохая идея без нужды.

                  local curr_rows = {posix1 = 1, posix2 = 2, posixN = N} -- из примера выше не массив
                  При инициализации вам нужно достать текущий datetime свечи, например вот такой он будет на 15-й торговой минуте ТФ = 1М:
                  local datetime = {year = 2018, month = 1, day = 18, hour = 10, min = 15} -- 2018/01/18 10:15
                  local posix = os.time(datetime) -- 1516259700 создали будущий ключ строки
                  curr_rows[posix] = 15 -- где 15 - № строки в вашей таблице, соответствует 15 минуте
                  Когда появился новый бар, нужно:
                  1) Разово добавить в curr_rows ссылку на строку таблицы - curr_rows[1516259760] = 16
                  2) Разово добавить саму 16-ю строку в таблицу.
                  Если не разберетесь, пишите на почту, сделаю пример. Тут сайт переделывает луа-код как ему хочется и возникнут новые вопросы.

                  1. Kalikazandr, добрый день! Спасибо за уделенное время и силы). Вашу идею уловил. К table.sinsert я обратился именно из-за его особенности (потокобезопасности) для того, чтобы на границе тайм-фреймов (окончание предыдущего - начало следующего) скрипт не перепутал строку, в которую надо вставить данные. По скольку поток информации достаточно плотный и обновляется быстро, а инструмент не один (минимум - два), то возможна ситуация, когда на границе тайм-фрейма изменения придут по обоим инструментам с мизерной разницей и оба передадут сигнал в таблицу соответствия на добавление новой строки с одинаковой [тайм-фрейм] = № строки - конфликт, ведь уникальность не контролируется! Вся сложность для меня в том, чтобы в момент начала поступления информации по инструментам на новой "свече" в таблице соответствия уже была новая запись (для нового бара) - вот это пока я не смог осилить. Может я чего не понял, просто не все проверил практически... Кстати, а call-back появления новой "свечки" есть встроенный? Спасибо за предложение помощи! Если совсем упрусь - буду знать). А как можно узнать адрес Вашей электронной почты?

                    1. Привет!
                      table.sinsert нужна для обмена данными между потоками. В вашем случае поток один и хоть 300 инструментов будет у вас в таблице - пофик вообще.
                      проверку делаете при: первый кто увидел новый бар:
                      if not curr_rows[posix] then
                      row = InsertRow(t_id, -1)
                      curr_rows[posix] = row
                      end
                      И усё)

                    1. По простому так:
                      поток №1 - main
                      поток №2 - все что не в main выполняется (функции обратного вызова, например)

  3. Дмитрий, здравствуйте! Пытаюсь разобрать код, который в качестве оптимизации предложил kalikazandr, 20 мая 2016 года. Очень интересно, но в силу новизны для меня этого языка сложно полностью понять суть предложения. Для начала интересует функция считывания из файла настроек. Никак не могу получить тот самый элемент - NewParams.d[4].b с помощью кода предложенного kalikazandr. Можете провести аналогию между вашим и его вариантами кода? Где может быть тот самый нюанс?)

      1. Дмитрий, добрый вечер! Не подскажите, интересует такой вопрос. Необходимо для дела выделить цветом текст (число) и подчеркнуть его цветной двойной чертой. Можно конечно всё организовать через клетки таблицы (значения собраны в таблицу) - раскрашивать с помощью SetColor, но уж очень интересно - есть ли в QLua (Lua) такие или приблизительно такие средства для String'ов), чтобы не загромождать таблицу цветовыми схемами? Спасибо! С уважением, Алексей!

        1. Добрый вечер! Такого простого способа не знаю, думаю можно попробовать string.char https://quikluacsharp.ru/qlua-osnovy/funktsii-raboty-so-strokami-v-qlua-lua/ и посмотреть коды символов в гугле, есть готовые символы с подчеркиванием, метод так себе, но, возможно, задачу решит 🙂

          1. Спасибо большое! Посмотрел string.char, тяжеловато выглядит - может быть это опыт (т.е. его отсутсвие). Проще будет раскрасить ячейку, тем более можно расцветить не только фон ячейки, но и сам текст - прекрасная альтернатива. Применение символов Юникода дает богатые возможности (визуальные), но есть много "НО", чтобы их учесть нужно многое предусмотреть, возможно даже от чего-то отказаться в реализации. Не хотелось бы. Ещё раз спасибо!

    1. Добрый день! Выведет 10-ю строку файла:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      
      -- Пытается открыть файл текущего инструмента в режиме "чтения"
      TradesFile = io.open(TradesFilePath,"r")
      -- Встает в начало файла
      TradesFile:seek('set',0)
      -- Перебирает строки файла
      local Count = 0 -- Счетчик строк
      for line in TradesFile:lines() do
         Count = Count + 1      
         if Count == 10  then
            message(line)
            break
         end
      end
  4. Дмитрий, добрый день!
    Правильно ли я понимаю что строки:
    SaveTable = function(Table, FilePath) и
    LoadTable = function(FilePath) - просто описание функций и их можно заменить на обычные (привычные):
    function SaveTable (Table, FilePath) и
    function LoadTable (FilePath)?

      1. Возможно кому-то, наверно, и мне пригодится запись в статье "ПЕРЕМЕННЫЕ, МАССИВЫ И ФУНКЦИИ В QLUA (LUA)" о такой форме записи при определении функции ~
        MyFunc = function(Param1, Param2) ;
        Спасибо!

      1. Данный код сохраняет массив (таблицу lua) в файл, когда Вы вызываете функцию SaveTable и загружает таблицу из файла (если он существует), когда Вы вызываете функцию LoadTable, а какой массив Вы сохраните в файл это от Вас зависит, а не от скрипта.

            1. Стырила нижевыложенный код и вставила в скрипт Баланс Покупок и продаж в функцию Main.Все работает и сохраняется.Я даже технически не понимаю пока всего как и что,буду разбираться.Не посмотрите,насколько корректно получилось?

              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
              
               function main()
                 -- Создает таблицу
                 CreateTable();
               
                 -- Основной цикл
                 while IsRun do
                    sleep(100);
                 end;
               
                 Array5Min = {}
               
                 function val_to_str ( v )
                 if "string" == type( v ) then
                    v = string.gsub( v, "\n", "\\n" )
                    if string.match( string.gsub(v,"[^'\"]",""), '^"+$' ) then
                       return "'" .. v .. "'"
                    end
                    return '"' .. string.gsub(v,'"', '\\"' ) .. '"'
                 end
                 return "table" == type( v ) and  tab_to_str( v ) or tostring( v )
              end
              --
              function key_to_str ( k )
                 if "string" == type( k ) and string.match( k, "^[_%a][_%a%d]*$" ) then
                    return k
                 end
                 return "[" .. val_to_str( k ) .. "]"
              end
              function tab_to_str( tbl )
                 local result, done = {}
                 for k, v in ipairs( tbl ) do
                    table.insert( result, val_to_str( v ) )
                    done[ k ] = true
                 end
                 for k, v in pairs( tbl ) do
                    if not done[ k ] then
                       table.insert( result, key_to_str ( k ) .. "=" .. val_to_str ( v ) )
                    end
                 end
                 return table.concat( result, ",\n" )
              end
               
               
              function read (filename)
                 local f,err = io.open(filename,"r")
                 if not f then
                    return nil,err
                 end
                 local fn =  "{"..f:read("*a").."}"
                 fn = loadstring("return " ..fn)
                 f:close()
                 if not fn then
                    return nil, "Не правильный формат данных в файле\n"..filename
                 end
                 return fn()
              end
              end
              1. Что-то мне кажется Вы усложнили себе задачу. Вставьте просто функции SaveTable и LoadTable в конец своего скрипта, создайте какой-нибудь массив, в котором Вы будете хранить параметры, которые нужно сохранять между перезапусками. Добавьте в скрипт функцию OnStop, в ней вызывайте SaveTable, передавая туда Ваш массив на сохранение. И добавьте функцию OnInit, в ней вызывайте функцию LoadTable, которая возвратит Вам Ваш сохраненный массив. Вот и все.

  5. Привет.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    
    function OnInit() -- Функция вызывается терминалом QUIK перед вызовом функции main()
       local f = io.open(getScriptPath().."//Spread_N_62_74.txt","r+") -- Пытается открыть файл состояния в режиме "чтения/записи"
       if f ~= nil then -- Если файл существует, перебирает строки файла, считывает содержимое в соответствующие переменные
          local Count = 0 -- Счетчик строк
          for line in f:lines() do
             Count = Count + 1
             if     Count == 1  then SC_Open   = tonumber(line)
             elseif Count == 2  then SC_Num    = tonumber(line)
             elseif Count == 3  then SC_Volume = tonumber(line)
             elseif Count == 4  then SC_Price  = tonumber(line)
             elseif Count == 5  then SC_Close  = tonumber(line)
    .................. и т.д.

    Вопрос можно ли переменные SC_ записать в одну строку что то типа

    1
    
     if     Count == 1  then SC_Open   = tonumber(line) SC_Num    = tonumber(line) и т.д.

    и потом прочитать их?

      1. 1
        2
        3
        4
        
        function SaveCurrentState() -- Функция сохраняет текущее состояние в файл
           local f = io.open(getScriptPath().."//Spread_L.txt","w") -- Создает файл в режиме "записи"
           -- Записывает в файл текущее состояние
           f:write(SC_Open.."  "..SC_Num.."  "..SC_Volume.."  "..SC_Price.."  "..SC_Close.."  ")

        не знаю как записать

        1. Можешь и так записать, только в конце добавь символ конца строки

          1
          
          f:write(SC_Open..";"..SC_Num..";"..SC_Volume..";"..SC_Price..";"..SC_Close.."\n")

          А потом читай, примерно вот так

          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
          
          -- Читает из открытого файла существующие сделки в массив
          function ReadTradesHistoryFile()
             -- Встает в начало файла
             TradesFile:seek('set',0);
             -- Перебирает строки файла, считывает содержимое в массив сделок
             local Count = 0; -- Счетчик строк
             for line in TradesFile:lines() do
                Count = Count + 1;
                if Count > 1 and line ~= "" then
                   NewTrade = true;
                   Trades[Count-1] = {};
                   local i = 0; -- счетчик элементов строки
                   for str in line:gmatch("[^;^\n]+") do
                      i = i + 1;
                      if i == 3 then Trades[Count-1].Num = tonumber(str);
                      elseif i == 4 then Trades[Count-1].Date = tonumber(str);
                      elseif i == 5 then Trades[Count-1].Time = tonumber(str);
                      elseif i == 6 then Trades[Count-1].Operation = str;
                      elseif i == 7 then Trades[Count-1].Qty = tonumber(str);
                      elseif i == 8 then Trades[Count-1].Price = tonumber(str);
                      elseif i == 9 then Trades[Count-1].Hint = str; Trades[Count-1].OpenCount = Trades[Count-1].Qty;
                      end; 
                   end;
                end;
             end; 
          end;
                  1. 1
                    2
                    3
                    4
                    5
                    6
                    7
                    8
                    9
                    10
                    11
                    12
                    13
                    14
                    15
                    16
                    17
                    
                     function OnInit() -- Функция вызывается терминалом QUIK перед вызовом функции main()
                       local f = io.open(getScriptPath().."//Spread_N_62_74.txt","r+") -- Пытается открыть файл состояния в режиме "чтения/записи"
                       if f ~= nil then -- Если файл существует, перебирает строки файла, считывает содержимое в соответствующие переменные
                          local Count = 0 -- Счетчик строк
                          for line in f:lines() do
                             Count = Count + 1
                             if Count == 1 then
                                local i = 0; -- Счетчик элементов строки
                                for str in line:gmatch("[^;^\n]+") do
                                   i = i + 1;
                                   if     i == 1 then SC_Open = tonumber(str);
                                   elseif i == 2 then SC_Num = tonumber(str);
                                   elseif i == 3 then SC_Volume = tonumber(str);
                                   elseif i == 4 then SC_Price = tonumber(str);
                                   elseif i == 5 then SC_Close = tonumber(str);
                                   end
                                end
                    1. 1
                      
                        f:write(SC_Open..";"..SC_Num..";"..SC_Volume..";"..SC_Price..";"..SC_Close.."\n")

                      запись так

                    2. вставь между 5 и 6 строками
                      message(tostring(line))
                      а между 9 и 10 строками
                      message(tostring(str))
                      посмотри что считывается

                    3. Разобрался ошибка как всегда идиотская:
                      было

                      1
                      
                        local f = io.open(getScriptPath().."//Spread_N_62_74.txt","r+")

                      надо

                      1
                      
                        local f = io.open(getScriptPath().."//Spread_L.txt","r+")

                      а код правильно работает, спасибо

          1. Здравствуйте, Дмитрий!
            Попробовал данный код для хранения настроек в csv файле.
            Все работает, кроме одного:
            Если в ячейке пусто, то этот элемент между точками с запятой (пустая строка) просто игнорируется.((
            И пятый элемент становится четвертым и так далее.
            Это мне понадобилось при ручной правке csv файла в Экселе.

            1
            2
            3
            4
            5
            6
            7
            8
            9
            
             line='100;200;aaa;bbb;ccc' -- В этом случае все нормально.
            --line='100;200;aaa;;mmm'  -- В этом случае прозевывает четвертый элемент - пустую строку.
             
            vyh=''
            for str in line:gmatch("[^;^\n]+") do
            	vyh=vyh.."/"..tostring(str)
            end
             
            message(vyh)
              1. Нет, дело не в этом, вы не поняли, Дмитрий.

                Скрипт Lua сохраняет файл в csv с разделителями точка с запятой.

                Эксель нормально открывает этот файл.
                Если пользователь сотрет какие-то значения в ячейках, то Эксель сохранит их тоже нормально

                рассмотрим файл в одну строку и 4 ячейки:
                ААА;BBB;;DDD

                Так вот данный код проигнорирует 4 элемент и DDD станет третьим при разборе строки.

                    1. Пардон, искал по кусочку кода в Яндексе.

                      Я помню, что этот чудесный способ хранения таблиц в csv файле я позаимствовал у вас.

                      Просто я часто редактирую вечером эти файлы csv в Экселе, вот и столкнулся с данной неприятностью.

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

              --line='100;200;aaa;bbb;ccc' -- В этом случае все нормально.
              line='100;200;aaa;;mmm' -- В этом случае прозевывает пустую строку.

              line=string.gsub(line,";",";@") -- Добавил справа от кадо1 точки с запятой собаку.

              vyh=''
              for str in line:gmatch("[^;^\n]+") do
              str=string.gsub(str,'@','') -- Убрал все собаки из отделенного элемента.

              vyh=vyh.."/"..tostring(str)
              end

              message(vyh)

              1. Время вопроса (коментария) вижу, но может кому пригодится вариант попроще

                1
                2
                3
                4
                5
                6
                
                	s = '1;2;3;;4'
                	i = 1
                	for x,y in string.gmatch(s..';', "([^;]*);") do
                		message(i .." - '".. tostring(x) .."'")
                		i=i+1
                    end
  6. Добрый день, Дмитрий.
    Очень громоздкий и медленный подход вы избрали для сохранения таблицы в файл.
    a=10 и ['a']=10 одно и тоже.
    Не проще вот такие функции использовать?

    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
    
    function val_to_str ( v )
       if "string" == type( v ) then
          v = string.gsub( v, "\n", "\\n" )
          if string.match( string.gsub(v,"[^'\"]",""), '^"+$' ) then
             return "'" .. v .. "'"
          end
          return '"' .. string.gsub(v,'"', '\\"' ) .. '"'
       end
       return "table" == type( v ) and  tab_to_str( v ) or tostring( v )
    end
    --
    function key_to_str ( k )
       if "string" == type( k ) and string.match( k, "^[_%a][_%a%d]*$" ) then
          return k
       end
       return "[" .. val_to_str( k ) .. "]"
    end
    function tab_to_str( tbl )
       local result, done = {}, {}
       for k, v in ipairs( tbl ) do
          table.insert( result, val_to_str( v ) )
          done[ k ] = true
       end
       for k, v in pairs( tbl ) do
          if not done[ k ] then
             table.insert( result, key_to_str ( k ) .. "=" .. val_to_str ( v ) )
          end
       end
       return table.concat( result, ",\n" )
    end

    и чтение:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
    function read(filename)
       local f,err = io.open(filename,"r")
       if not f then
          return nil,err
       end
       local fn =  "{"..f:read("*a").."}"
       fn = loadstring("return " ..fn)
       f:close()
       if not fn then
          return nil, "Не правильный формат данных в файле\n"..filename
       end
       return fn()
    end
      1. Дело не в 10 или 20 строках или читабельности, а в практичности lua.
        Если грамотно использовать эти функции, то читабельность в файле будет лучше, чем у вас, а вот по скорости сохранения раз в 30 быстрее (поначалу лепил такой же примерно код как у вас), объем получаемого файла меньше на лишние скобки, "return a" и прочее не нужное барахло : )
        Согласитесь, если файл настроек велик или файл будет содержать хеш таблицу с данными за последние 30-40 торговых дней, то прирост в скорости будет заметен визуально. Для небольших файлов безусловно пойдет и ваш код.

        1. При навешивании assert на loadstring начинает сообщать о недопустимых символах у параметра local в сформированной строке?! Правда, считываемый файл формируется кодом Дмитрия, а не вашим. Может дело в этом?

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

    1. Уважаемый Kalikazandr, здравствуйте! Пытаюсь разобрать код, который вы в качестве оптимизации предложили 20 мая 2016 года по предмету промежуточного сохранения данных в период выключения. Мне это очень интересно, но в силу новизны для меня этого языка сложно полностью понять суть предложения. Для начала интересует функция считывания из файла настроек. Никак не могу получить тот самый элемент - NewParams.d[4].b с помощью вашего кода. Скрипт работает, а вот ожидаемого сообщения с информацией о содержимом указанного элемента нет. Можете поподробнее описать суть этого кода, провести аналогию, соответствие между вашим вариантом кода и вариантом Дмитрия? Какова роль в этой ситуации оператора fn = loadstring("return " ..fn)? Может всё дело в логике его работы?