Из QLua (Lua) в Excel (CSV)

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

csvФайл CSV-формата это обычный текстовый файл, с которым Excel и аналогичные программы могут работать как с таблицей. Каждая строка таблицы в этом файле записывается как новая строка со знаком переноса в конце, а значения полей разделены между собой каким-то символом, чаще ";". В самой первой строке такого файла можно (не обязательно) указать названия столбцов, так же через ";".

Ниже приведен пример создания такого файла и записи в него данных о совершенных сделках средствами QLua(Lua):

Код скрипта QLua

Скрипт создает файл следующего вида:
Таблица
Если у Вас появились какие-то вопросы, задайте их в комментариях под статьей !!!

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

Из QLua (Lua) в Excel (CSV): 86 комментариев

  1. Здравствуйте!
    Скажите пожалуйста, возможно ли на QLua дать команду Квику выгрузить таблицы по DDE в эксель?
    (параметры выгрузку уже заданы вручную в диалоговом окне Квика)
    Таблицы следующие:
    Обезличенные сделки,
    Клиентский портфель,
    Позиции по клиентским счетам,
    Таблица сделок,
    Таблица заявок,
    Таблица стоп-заявок

      1. Дмитрий, благодарю за ответ!

        Может быть существует другой способ программно дать команду квику выполнить ряд действий?
        Например, каждый день приходится руками исправлять список бумаг для выгрузки в Эксель таблицы обезличенных сделок (добавлять новые опционы и т.д.)

  2. Добрый день! Подскажите, пожалуйста, как при записи в файл числа с дробной частью заменить "." точку на "," запятую? Вариант: заменить в экселе не подходит, т.к. часть значений с точкой эксель в даты пытается превратить. Нужно как-то через код, либо в Квике где можно поменять "." точку на ","? Спасибо

  3. Дмитрий! Помогите разобраться:
    Не могу воткнуть последнюю сделку в CSV, ибо происходит она по закрытию скрипта на закрытие позиций в функции OnStop, в которой вызывается функция close_positions, но оттуда она уже не идет в функцию OnTrade(сужу по логам). Можно в принципе не выдумывать велосипед пытаясь ее послать куда надо, вопрос будет таков:
    Можно ли записать последнюю сделку по-другому в тот же файл, допустим сразу после исполнения сделки?
    Я бы и сам смог разобраться, если бы понял откуда взять переменную trade.trade_num откуда это берется?

              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
                
                 --Обработчик события сделки
                function OnTrade(trade) 
                	nord=trade["order_num"] --номер заявки
                	price=trade["price"] --цена сделки
                	--Запись номер робота номер 1
                	if ((id=="10" or id=="11") 
                	and p_seccode=="BRJ8" 
                	and p_account=="SPBFUTJRaGY") then
                	--Перебирает массив с номерами записанных сделок (в обратном порядке)
                	    for i=#TradeNumsALL1,1,-1 do
                		--Если данная сделка уже была записана, выходит из функции
                		if TradeNumsALL1[i] == trade.trade_num then return; end
                	    end
                	     -- Если мы здесь, значит сделка не была найдена в числе уже записанных
                	     -- Добавляет в массив номер новой сделки
                	     TradeNumsALL1[#TradeNumsALL1 + 1] = trade.trade_num;
                	     -- Вычисляет операцию сделки
                	     local Operation = "";
                	     if CheckBit(trade.flags, 2) == 1 then Operation = "Продажа"; else Operation = "Покупка"; end;
                	     -- Создает строку сделки для записи в файл ("Дата и время;Код класса;Код бумаги;Номер сделки;Номер заявки;Операция;Цена;Количество\n")
                	     local TradeLine = 	os.date("%c", os.time(trade.datetime))..";"..
                						trade.class_code..";"..
                						trade.sec_code..";"..
                						trade.trade_num..";"..
                						trade.order_num..";"..
                						Operation..";"..
                						trade.price..";"..
                						trade.qty..";"..
                						p_numberROBOT1.."\n";
                	     -- Записывает строку в файл
                	     CSV:write(TradeLine);
                	     -- Сохраняет изменения в файле
                	     CSV:flush();
                to_log("Совершена сделка: номер заявки "..tostring(nord).."; цена "..tostring(price)..": количество "..tostring(trade["qty"]))

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

                  1. Это чтобы он писал от разных роботов в разные массивы и из разных массивов писал в один файл, это нужно чтобы в файле в который идет запись указывался какой робот сделал эту сделку, я их пометил p_numberROBOT1 до 4, так же массивы.
                    Соответственно каждый массив проверяется на свои условия, в данном случае ((id=="10" or id=="11")
                    and p_seccode=="BRJ8"
                    and p_account=="SPBFUTJRaGY"

                    1. Вот так понимаю:

                      1
                      2
                      3
                      4
                      5
                      6
                      7
                      8
                      9
                      10
                      11
                      12
                      13
                      14
                      15
                      16
                      17
                      
                      SEC_CODE = 'BRJ8'
                       
                      RUN = true
                       
                      main = function()
                         while RUN do sleep(100) end
                      end
                       
                      OnStop = function() RUN = false end
                       
                      OnTrade = function(trade)
                         if trade.sec_code == SEC_CODE then
                            message('Наша сделка')
                         else
                            message('Не наша сделка')
                         end
                      end
                      1
                      2
                      3
                      4
                      5
                      6
                      7
                      8
                      9
                      10
                      11
                      12
                      13
                      14
                      15
                      16
                      17
                      
                      SEC_CODE = 'RIM8'
                       
                      RUN = true
                       
                      main = function()
                         while RUN do sleep(100) end
                      end
                       
                      OnStop = function() RUN = false end
                       
                      OnTrade = function(trade)
                         if trade.sec_code == SEC_CODE then
                            message('Наша сделка')
                         else
                            message('Не наша сделка')
                         end
                      end

                      А как у Вас не понимаю

                    2. точно... я врубился... очень глупая ошибка)) спасибо! не знаю что бы без Вас делал)
                      Я брал ее из шапки, когда как должен был подгружать.

  4. Здравствуйте!
    Работаю с двумя роботами оба должны писать в разные CSVешки, указал путь в функции OnInit до разных файлов в одной папке.
    В итоге пишет сделки в обе CSV и так делает каждый робот!
    Использую Ваш скрипт из примера выше для перегонки в CSV .
    Помогите понять отчего такое происходит?

      1. Здравствуйте, покажите функции OnInit обоих скриптов, ну и, желательно, те места, где они пишут в файл, ошибка однозначно какая-то банальная, просто не внимательно все проверили, потому что чудес в программировании не бывает, компьютер всегда делает только то, что Вы ему указали делать.

        1. Робот 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
          81
          82
          83
          84
          85
          86
          87
          88
          89
          90
          91
          92
          93
          94
          
          --Служебные переменные
          is_run = true
          TradeNumsSI = {}
          count = 0
          in_trade = false --признак того, что мы в позиции
          order_num = "" --номер заявки по открытой сделке
          stop_loss_num = "" --номер стоп заявик по открытой сделке
          direction=""  --последний тип операции
          last_price=0 --цена последней сделки по стратегии
          in_set_stop_loss = false --признак, что мы в данный момнет ожидаем результата выставления стоп лосса
           
           
          function OnInit()
           
          	--Создает, или открывает для чтения/добавления файл CSV в той же папке, где находится данный скрипт
          	CSV = io.open("C:\\Open_Broker_QUIK_Junior\\RLOG\\SiTrades1.csv", "a+")
           
          	--Встает в конец файла, получает номер позиции
          	local Position = CSV:seek("end",0)
           
          	     --Если файл еще пустой
          	    if Position == 0 then
          			--Создает строку с заголовками столбцов
          			local Header = "Дата и время;Код класса;Код бумаги;Номер сделки;Номер заявки;Операция;Цена;Количество\n"
          			--Добавляет строку заголовков в файл
          			CSV:write(Header)
          			--Сохраняет изменения в файле
          			CSV:flush()
          	    end
           
          end
          --Обработчик события сделки
          function OnTrade(trade) 
          	nord=trade["order_num"] --номер заявки
          	price=trade["price"] --цена сделки
          	to_log("Совершена сделка: номер заявки "..tostring(nord).."; цена "..tostring(price)..": количество "..tostring(trade["qty"]))
          	if nord==order_num then
          		qty=trade["qty"] --Количество бумаг в последней сделке в лотах 
          		if direction=="B" then
          			count=qty
          		else
          			count=-qty
          		end
          		send_stop_loss(direction,count,price)
          		last_price=price
          	end
           
          	--Перебирает массив с номерами записанных сделок (в обратном порядке)
          	for i=#TradeNumsSI,1,-1 do
          		--Если данная сделка уже была записана, выходит из функции
          		if TradeNumsSI[i] == trade.trade_num then return; end
          	end
          	-- Если мы здесь, значит сделка не была найдена в числе уже записанных
          	-- Добавляет в массив номер новой сделки
          	TradeNumsSI[#TradeNumsSI + 1] = trade.trade_num;
          	-- Вычисляет операцию сделки
          	local Operation = "";
          	if CheckBit(trade.flags, 2) == 1 then Operation = "Продажа"; else Operation = "Покупка"; end;
          	-- Создает строку сделки для записи в файл ("Дата и время;Код класса;Код бумаги;Номер сделки;Номер заявки;Операция;Цена;Количество\n")
          	local TradeLine = 	os.date("%c", os.time(trade.datetime))..";"..
          						trade.class_code..";"..
          						trade.sec_code..";"..
          						trade.trade_num..";"..
          						trade.order_num..";"..
          						Operation..";"..
          						trade.price..";"..
          						trade.qty.."\n";
          	-- Записывает строку в файл
          	CSV:write(TradeLine);
          	-- Сохраняет изменения в файле
          	CSV:flush();
           
          end
           
          function CheckBit(flags, bit)
             -- Проверяет, что переданные аргументы являются числами
             if type(flags) ~= "number" then error("Ошибка!!! Checkbit: 1-й аргумент не число!"); end;
             if type(bit) ~= "number" then error("Ошибка!!! Checkbit: 2-й аргумент не число!"); end;
             local RevBitsStr  = ""; -- Перевернутое (задом наперед) строковое представление двоичного представления переданного десятичного числа (flags)
             local Fmod = 0; -- Остаток от деления 
             local Go = true; -- Флаг работы цикла
             while Go do
                Fmod = math.fmod(flags, 2); -- Остаток от деления
                flags = math.floor(flags/2); -- Оставляет для следующей итерации цикла только целую часть от деления           
                RevBitsStr = RevBitsStr ..tostring(Fmod); -- Добавляет справа остаток от деления
                if flags == 0 then Go = false; end; -- Если был последний бит, завершает цикл
             end;
             -- Возвращает значение бита
             local Result = RevBitsStr :sub(bit+1,bit+1);
             if Result == "0" then return 0;     
             elseif Result == "1" then return 1;
             else return nil;
             end;
          end;

          Робот 2

          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
          81
          82
          83
          84
          85
          86
          87
          88
          89
          90
          91
          92
          93
          94
          
           --Служебные переменные
          is_run = true
          TradeNums = {}
          count = 0
          in_trade = false --признак того, что мы в позиции
          order_num = "" --номер заявки по открытой сделке
          stop_loss_num = "" --номер стоп заявик по открытой сделке
          direction=""  --последний тип операции
          last_price=0 --цена последней сделки по стратегии
          in_set_stop_loss = false --признак, что мы в данный момнет ожидаем результата выставления стоп лосса
           
           
          function OnInit()
           
          	--Создает, или открывает для чтения/добавления файл CSV в той же папке, где находится данный скрипт
          	CSV = io.open("C:\\Open_Broker_QUIK_Junior\\RLOG\\Trades.csv", "a+")
           
          	--Встает в конец файла, получает номер позиции
          	local Position = CSV:seek("end",0)
           
          	     --Если файл еще пустой
          	    if Position == 0 then
          			--Создает строку с заголовками столбцов
          			local Header = "Дата и время;Код класса;Код бумаги;Номер сделки;Номер заявки;Операция;Цена;Количество\n"
          			--Добавляет строку заголовков в файл
          			CSV:write(Header)
          			--Сохраняет изменения в файле
          			CSV:flush()
          	    end
           
          end
           
          function OnTrade(trade) 
          	nord=trade["order_num"] --номер заявки
          	price=trade["price"] --цена сделки
          	to_log("Совершена сделка: номер заявки "..tostring(nord).."; цена "..tostring(price)..": количество "..tostring(trade["qty"]))
          	if nord==order_num then
          		qty=trade["qty"] --Количество бумаг в последней сделке в лотах 
          		if direction=="B" then
          			count=qty
          		else
          			count=-qty
          		end
          		send_stop_loss(direction,count,price)
          		last_price=price
          	end
           
          	--Перебирает массив с номерами записанных сделок (в обратном порядке)
          	for i=#TradeNums,1,-1 do
          		--Если данная сделка уже была записана, выходит из функции
          		if TradeNums[i] == trade.trade_num then return; end
          	end
          	-- Если мы здесь, значит сделка не была найдена в числе уже записанных
          	-- Добавляет в массив номер новой сделки
          	TradeNums[#TradeNums + 1] = trade.trade_num;
          	-- Вычисляет операцию сделки
          	local Operation = "";
          	if CheckBit(trade.flags, 2) == 1 then Operation = "Продажа"; else Operation = "Покупка"; end;
          	-- Создает строку сделки для записи в файл ("Дата и время;Код класса;Код бумаги;Номер сделки;Номер заявки;Операция;Цена;Количество\n")
          	local TradeLine = 	os.date("%c", os.time(trade.datetime))..";"..
          						trade.class_code..";"..
          						trade.sec_code..";"..
          						trade.trade_num..";"..
          						trade.order_num..";"..
          						Operation..";"..
          						trade.price..";"..
          						trade.qty.."\n";
          	-- Записывает строку в файл
          	CSV:write(TradeLine);
          	-- Сохраняет изменения в файле
          	CSV:flush();
           
          end
           
          function CheckBit(flags, bit)
             -- Проверяет, что переданные аргументы являются числами
             if type(flags) ~= "number" then error("Ошибка!!! Checkbit: 1-й аргумент не число!"); end;
             if type(bit) ~= "number" then error("Ошибка!!! Checkbit: 2-й аргумент не число!"); end;
             local RevBitsStr  = ""; -- Перевернутое (задом наперед) строковое представление двоичного представления переданного десятичного числа (flags)
             local Fmod = 0; -- Остаток от деления 
             local Go = true; -- Флаг работы цикла
             while Go do
                Fmod = math.fmod(flags, 2); -- Остаток от деления
                flags = math.floor(flags/2); -- Оставляет для следующей итерации цикла только целую часть от деления           
                RevBitsStr = RevBitsStr ..tostring(Fmod); -- Добавляет справа остаток от деления
                if flags == 0 then Go = false; end; -- Если был последний бит, завершает цикл
             end;
             -- Возвращает значение бита
             local Result = RevBitsStr :sub(bit+1,bit+1);
             if Result == "0" then return 0;     
             elseif Result == "1" then return 1;
             else return nil;
             end;
          end;
                1. и вот снова беда!
                  Сделал целых 2!! проверки одна на инструмент другая по ИД.
                  По инструменту работает проверка, но у меня теперь уже 4 робота, 2 по SI 2 по BR, от брент роботов это помогло, но 2 SI робота теперь пишут все в один файл, хотя указаны для них в функции OnInit разные файлы. В итоге в двух CSV по SI сделки с них обоих.
                  Возможно я как то не правильно проверку по id делаю, подскажите, пожалуйста, в чем я не прав тут.

                  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
                  
                   --Перебирает массив с номерами записанных сделок (в обратном порядке)
                  	for i=#TradeNumsSI2,1,-1 do
                  		--Если данная сделка уже была записана, выходит из функции
                  		if TradeNumsSI2[i] == trade.trade_num then return; end
                  	end
                  	-- Если мы здесь, значит сделка не была найдена в числе уже записанных
                  	-- Добавляет в массив номер новой сделки
                  	if id=="12" or id=="13" then -- та самая не работающая проверка
                  	if p_seccode=="SiH8" then -- работающая проверка
                  	TradeNumsSI2[#TradeNumsSI2 + 1] = trade.trade_num;
                  	-- Вычисляет операцию сделки
                  	local Operation = "";
                  	if CheckBit(trade.flags, 2) == 1 then Operation = "Продажа"; else Operation = "Покупка"; end;
                  	-- Создает строку сделки для записи в файл ("Дата и время;Код класса;Код бумаги;Номер сделки;Номер заявки;Операция;Цена;Количество\n")
                  	local TradeLine = 	os.date("%c", os.time(trade.datetime))..";"..
                  						trade.class_code..";"..
                  						trade.sec_code..";"..
                  						trade.trade_num..";"..
                  						trade.order_num..";"..
                  						Operation..";"..
                  						trade.price..";"..
                  						trade.qty.."\n";
                  	-- Записывает строку в файл
                  	CSV:write(TradeLine);
                  	-- Сохраняет изменения в файле
                  	CSV:flush();
                  	end
                  	end
                  end
                    1. сек-код правильно считается так бы и брент писал, задан он в шапке робота как и в вашем МА роботе для примера, впрочем так все делают вроде)
                      насчет id:

                      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
                      
                       function OnTransReply(trans_reply)
                      	id=tostring(trans_reply["trans_id"]) --Пользовательский идентификатор транзакции 
                       
                      	to_log("Обработка транзакции "..id)
                       
                      	--если это наша транзакция, обработаем ее
                      	if id==p_TRANS_ID then
                      		nord=trans_reply["order_num"] --Номер заявки 
                       
                      		to_log("Обработка транзакции номер заявки "..nord)
                       
                      		--если заявка выставилась - запоминаем ее номер, иначе считаем, что мы не в сделке
                      		if nord==nil or nord==0 or nord=="0" then
                      			message("Заявка не выставилась ",1)
                      			in_trade=false
                      		else
                      			order_num=nord
                      		end
                      	end
                       
                      	if id==p_TRANS_ID_STOP then
                      		message("Сообщение транзакции стоп ордера "..trans_reply["result_msg"],1)
                      		nord=trans_reply["order_num"] --Номер заявки 
                       
                      		--если заявка выставилась - запоминаем ее номер, иначе считаем, что мы закончили выставлять стоп заявку
                      		if nord==nil then
                      			message("Стоп заявка не выставилась ",1)
                      			in_set_stop_loss=false
                      			stop_loss_num=""
                      		else
                      			stop_loss_num=nord
                      		end
                      	end 
                      end

                      p_TRANS_ID p_и TRANS_ID_STOP также в шапке указаны под значениями, чтобы робот не путал свои транзакции с другими.

                    1. нет, во всех роботах разные поставил конечно же в одном 2 и 3, в другом 4 и 5 и т. д.

                    2. впрочем я пока что нашел альтернативу: указывать в проверке счет ибо он разный - помогло. Хотя почему не хочет по ИД различать я не могу в толк взять, все равно интересно, так что если есть мысли буду благодарен за помощь!

                    1. Да все ID расписаны как надо в таблице сделок.

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

                    1. А можете дать пример, если Вам не сложно?

                  1. У меня нет такого готового примера, и если я Вам дам такой пример, Вы хуже научитесь, разберитесь в этом вопросе самостоятельно, эффект будет значительно выше, особенно Вы это заметите когда в следующий раз столкнетесь с подобной задачей, все, что Вам для этого нужно, это внимательно изучить эту статью, а затем потестировать в отдельном скрипте как это все работает, не пытайтесь сразу пихать в робота то, в чем Вы еще не разобрались.
                    https://quikluacsharp.ru/quik-qlua/poluchenie-v-qlua-lua-dannyh-iz-grafikov-i-indikatorov/

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

                  2. Вроде бы ведь все просто:

                    1
                    2
                    3
                    4
                    5
                    6
                    7
                    8
                    
                    -- Поза закрылась
                    -- Сохранили в переменной индекс
                    CloseIndex = DS:Size()
                    ...
                    -- В следующий раз смотрите
                    -- Работать только если индекс больше
                    if DS:Size() > CloseIndex then
                    ...

                    А как еще это можно сделать? Логика-то ведь простая 🙂

                    1. С утра или после дисконекта, индексы запомненные, ни о чём не скажут.
                      А вот время свечи всегда уникально:
                      local bar_posix = 0
                      local CloseIndex = 0
                      local size = ds:Size()
                      -- Поза закрылась
                      bar_posix = os.time(ds:T(size))

                      Совсем от индекса не нужно отказываться, просто после дисконекта/включения ищем индекс бара с:
                      if bar_posix == os.time(ds:T(size)) then
                      CloseIndex = ds:Size()
                      ...

    1. Привет!
      1. Самая ужасная ошибка - это использование функции CheckBit в функциях обратного вызова т.к. в ней есть цикл и на время его выполнения терминал курит нервно в сторонке и копит данные. Ну и иже с ним вот этот цикл: for i=#TradeNumsSI,1,-1 do - зачем?

      2. local Position = CSV:seek("end",0) зачем? У вас автоматом при открытии файла 'a+' добавит в конец файла строку.

      "--Перебирает массив с номерами записанных сделок (в обратном порядке) " - это тоже решение не "фонтан":
      TradeNumsSI = {}
      OrderNumsSI = {}
      function OnOrder(order)
      local order_num = order.order_num
      OrderNumsSI[order_num] = order.qty -- запомнили кол-во контрактов в заявке
      -- если заявка снята, то нужно поправить: OrderNumsSI[order_num] = order.qty - order.balance
      end

      function OnTrade(trade)
      local trade_num = trade.trade_num
      -- Всегда! сначала проверяем только то, что точно не даст возможности дальнейшего выполнения кода в блоке
      if not trade_num or TradeNumsSI[trade_num] then return end
      TradeNumsSI[trade_num] = true -- больше не будем считать эту сделку
      local order_num = trade.order_num
      OrderNumsSI[order_num] = OrderNumsSI[order_num] - trade.qty
      if OrderNumsSI[order_num] == 0 then -- вот и всё - сделок по этой заявке больше не будет совсем, можно и в файл сохранить
      ... some code
      end
      end

      ЗЫ: и вообще, Дмитрий, функцию CheckBit ее собратьев с циклами ожидающими, нужно удалить с сайта, дабы не вводить в заблуждение народ )))
      Например, на эти:
      function OrderStatus(flags)
      if bit.band(flags, 0x1) ~= 0 then return "A"
      else
      if bit.band(flags, 0x2) ~= 0 then return "K"
      else return "F"
      end
      end
      end
      function Operation(flags) -- применима к "orders", "stop_orders", "trades", "all_trades"
      if bit.band(flags, 0x4) ~= 0 then return "S" -- лучше использовать не букву"S", а цифру: -1
      else return "B" -- лучше использовать не "B", а цифру: 1
      end
      end
      local direction = Operation(flags)
      local posa = qty * direction
      Как-то так, примерно.

          1. Да в квике пофик по идее, пакеты событий не чаще 10 раз в секунду идут, если пинг нормальный.
            Актуально в майн сносить только события обезличенных сделок. Факториал же не считаешь в OnTrade ))

        1. Пожалуйста, про gsub можете здесь посмотреть: https://quikluacsharp.ru/qlua-osnovy/funktsii-raboty-so-strokami-v-qlua-lua/
          Это шаблон поиска '[\.]+', о том как они строятся можете почитать погуглив "регулярные выражения"
          Можно проще сделать:

          1
          2
          
          price = 103.39
          price = string.gsub(tostring(price),'%.', ',')

          tostring преобразует число в строку, gsub производит поиск в строке и все совпадающие с шаблоном поиска элементы заменяет шаблоном замены, в данном случае точки на запятые. Так как символ точка в регулярных выражениях означает "любой символ", то для поиска именно точки нужно его экранировать, для этого в Lua используется символ "%"

        1. Это нужно в Excel после открытия файла уже настраивать автоматическую ширину столбца, может быть можно в Excel сделать предустановленные настройки для открываемых файлов CSV, но я не знаю как это сделать. Вообще, CSV файл это обычный текстовый файл, в котором хранятся только значения ячеек и не хранится никакой информации для Excel о том, как его правильно отображать.

  5. Здравствуйте. А есть возможность реализовать вывод инфо по закрытым сделкам? Т.е. дата/время, цена, объем открытия, и тут же в строке дата/время и цена закрытия? Что-то вроде отчета брокера.
    Если рассмотреть лот размером 1, то можно просто запоминать данные открытия и при закрытии экспортировать их вместе с текущими.
    Когда же лот равен 2 и выше, то он часто исполняется частями, а если еще и сделка была открыта позавчера, а закрыта сегодня, то тогда такой метод не подойдет.

    1. Здравствуйте, можете просто сформировать строку из нужных Вам данных и таким же образом записать ее в файл.Данные можно брать из соответствующих таблиц квика, а если Вам нужно отслеживать и вчерашние сделки, то тут нужен комплексный подход, не только писать в файл, но и читать из него.

      1. Да, я это понимаю. Тут вопрос не сколько в программировании, сколько по торговле. Я не могу сообразить, как из таблиц понять и формализовать, что пришла сделка, закрывающая предыдущую. Конечно, если они друг за другом идут (открылся/закрылся), то понятно. У вас опыта больше, как я думаю, вот и интересуюсь.
        Пробую аккумулировать совершенные сделки в массиве и, как есть у вас один пример, перебирать и взаимоубирать встречные, но пока нет уверенности, что будет правильно, пока проверяю.
        Как вы думаете, как правильно перебрать много сделок и из них собрать строки для выгрузки по закрытым?

        1. Скорее всего нужно в функции OnTrade мониторить новые сделки, когда появилась новая сделка, смотрите таблицу ограничений по клиентским счетам(фьючерсы), в ней есть поле текущая чистая позиция (totalnet), по изменению этого значения будет понятно что за сделка прошла, только там нужно посмотреть, я сейчас не помню, возможно в момент срабатывания функции OnTrade значение totalnet еще не поменяется, тогда, наверное, не нужно обрабатывать данную сделку, а подождать следующего вызова OnTrade, тогда уже, наверное, totalnet поменяется, OnTrade по несколько раз вызывается для одной и той же сделки. В общем, нужно опытным путем проверять.
          Для справки:
          функции обратного вызова https://quikluacsharp.ru/qlua-osnovy/funktsii-obratnogo-vyzova-vstroennye-v-qlua/
          получение данных из таблиц Quik https://quikluacsharp.ru/quik-qlua/poluchenie-dannyh-iz-tablits-quik-v-qlua-lua/

          1. Да, так можно. Только если позиция набиралась частями (разные объемы, цены) и закрывалась через несколько дней, то так тоже не отследить.
            Конечно, однозначно нужно хранить все совершенные сделки и при уменьшении чистых позиций производить анализ. Только не могу сообразить, чем общим у открытой и закрытой сделки оперировать. При отправке транзакции добавляю комментарий, что упрощает работу, но т.к. сделка наливается и исполняется частями, то не ясно, какую с какой сделкой связывать.
            В любом случае, спасибо за совет, буду тогда думать дальше.

              1. А не, не понесло 🙂 Вы имеете в виду, когда наливается заявка, то номер заявки один на всех, да, это так, но не это интересно. У сделки есть продажа со своим номером заявки и есть закрывающая ее покупка, со своим номером заявки. И они разные.
                Если сделка продажа в 10 лотов, а потом выставляется ТП для покупки 10 лотов, то ордер ТП имеет уже совсем другой, не связанный с первой сделкой продажей, номер заявки. Как потом в таблицах понять, что это закрывающая сделка? Ну и нужно помнить, что наливается частями, что немного усложняет ситуацию.
                Выходит нужно смотреть по цене и разнонаправленности. Ну или вести массив с ключом ценой открываемой сделки и собирать туда все данные. Ладно, буду думать дальше.

  6. Здравствуйте, Дмитрий!
    Как понимаю, в Qlua не поддерживается "getFileLen" или "getFileLength", кок в таком случае считать и использовать значение из конкретной ячейки внешнего файла? Или ячейки заданного столбца в последней значимой строке?

    1. Здравствуйте, в документации есть вот такая функция:

      1
      2
      3
      4
      5
      6
      
      function fsize (file)
         local current = file:seek()      -- get current position
         local size = file:seek("end")    -- get file size
         file:seek("set", current)        -- restore position
         return size
       end

      можете ее использовать для получения размера файла.
      Чтобы получать значения ячеек, нужно учитывать то, что CSV это обычный текстовый файл, в котором есть строки, а значения в строке разделяются символом ";", обычно.
      Вот пример работы с ним:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      
      FilePath = 'C:\\MyCSVFile.csv'
      -- Открывает файл
      local CSVFile = io.open(FilePath,"r+");
      local RowNum = 0 -- номер строки
      local CellNum = 0 -- номер ячейки
      -- Перебирает все строки
      for line in CSVFile:lines() do
         RowNum = RowNum + 1
         -- Перебирает все ячейки строки
         CellNum = 0
         for str in line:gmatch("[^;^\n]+") do
            CellNum = CellNum + 1
            -- Выводит значение ячейки в виде строки "2,5: какое-то значение"
            message(RowNum..','..CellNum..': '..tostring(str))
         end
      end
      1. Спасибо! То есть получить индекс (порядковый номер) последней строки можно только через цикл? И получается можно использовать обычный *.txt с разделителем, типа ";" ?

        1. Всегда пожалуйста!
          Возможно есть какой-то другой способ узнать количество строк, но я его не знаю, к сожалению. Можете использовать любое расширение файла, Lua, в данном случае, все равно будет обращаться с ним, как с .txt

          1. Дмитрий, в приведенном выше скрипте, цикл for str in line:gmatch("[^;^\n]+") do продолжает суммировать кол-во столбцов не зависимо от значения line, то есть если в файле две строки и пять столбцов, то увидим message(RowNum..','..CellNum..': '..tostring(str)) "2,10 какое-то значение"

                1. Что у Вас в файле записано?
                  Я сейчас создал на диске С текстовый файл следующего содержания:
                  1;2;3;4;5
                  6;7;8;9;10
                  переименовал его в MyCSVFile.csv,
                  запустил скрипт, и он вывел:
                  1,1: 1
                  1,2: 2
                  1,3: 3
                  1,4: 4
                  1,5: 5
                  2,1: 6
                  2,2: 7
                  2,3: 8
                  2,4: 9
                  2,5: 10

                  Как и ожидалось!

  7. Спасибо, очень полезная статейка.

    Простенько и со вкусом можно записывать свои сделки, заявки и все что душа пожелает, даже не нажимая кнопок.))

          1. Здравствуйте, если нужно работать с полноценными таблицами Excel, то нужна LuaCom, а если с CSV работать, то это обычный текстовый формат, который можно читать стандартными средствами lua.
            Вот ссылка на скачивание с оф.сайта: http://files.luaforge.net/releases/luacom/luacom/1.4/luacom-1.4-luabinaries.zip
            Вот документация на английском: http://files.luaforge.net/releases/luacom/luacom/1.4/luacom-1.4-doc.pdf

            1. Спасибо буду разбираться.
              Цитата - "если с CSV работать, то это обычный текстовый формат, который можно читать стандартными средствами lua.", у Вас были статьи про то как это делать?

              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
                
                FPath = getScriptPath()..'\\Test.csv'
                F = nil
                 
                function main ()
                   -- Создает файл в папке скрипта
                   F = io.open(FPath, 'w')
                   -- Пишет в файл 2 строки
                   F:write('1;2;3;4;5\n')
                   F:write('6;7;8;9;10\n')
                   F:flush()
                   -- Закрывает файл
                   F:close()
                 
                   -- Открывает файл в режиме чтения/записи
                   F = io.open(FPath, 'r+')
                   -- Читает все строки
                   for line in F:lines() do
                      -- Получает значения столбцов
                      local v1, v2, v3, v4, v5 = line:match('(%d+);(%d+);(%d+);(%d+);(%d+)')
                      -- Выводит их в сообщении
                      message(v1..' '..v2..' '..v3..' '..v4..' '..v5)
                   end
                   -- Закрывает файл
                   F:close()
                end