Не могу реализовать память через CSV файл

Страницы: 1
RSS
Не могу реализовать память через CSV файл
 
Всем добрый вечер. Мне 14, я пишу своему отцу пару скриптов, чтобы ему было удобнее работать в терминале. В этом скрипте должна быть реализована стратегия FiBo, вроде бы она довольно известная.
Суть в том что при увеличении цены срабатывает функция CheckLine, где я сравниваю строки в csv файле, состоящем из цен, по которым я покупал когда либо инструмент.
При покупке он должен записывать туда цену, при продаже вычеркивать одну из цен, ориентируясь на которую он продал.
Казус в том, что ничего из прошлой написанной мною строки он не делает. Память по просту не работает!
Я пишу скрипты на Qlua недолго, иногда использую ИИ (Извините меня все, я сам против вайбкодинга, но мне батя узкие сроки ставит). Помогите пожалуйста!!!!
Если есть любые другие замечания пишите.

Сам скрипт:

function OnInit()
--Настройки, изменять при надобности.--
class = "QJSIM" -- Тип интсрумента
sec = "SBER" -- Код интсрумента
account = "NL0011100043" -- Номер аккаунта, тут всё понятно
LotSize = 1 -- Кол-во лотов в заявке.
step = 0.02 -- Шаг цены.
limits = 10 -- Лимит торговли

tid = 0 -- Не трогать!
trz_comment = "FIBO-2 --> " -- Не трогать.
price = nil -- Не трогать.
uprice = nil
dprice = nil
SellPrice = nil
TradeNums = {}
Trades[]

CSV = io.open(getScriptPath().."/FiboTrades.csv", "a+");
secondfile = "fibobuys.csv"
end

function CheckLine(number)
current_line = 0
same = 0;
ss = 0
local CheckFile = io.open(secondfile, "r");
for line in CheckFile:lines() do
if tonumber(line) == number then same = same+1; end
if tonumber(line) <= number then LimitSpot('S', number); break end;
                   current_line = current_line + 1
end
elseif same == 0 and climits ~= limits then LimitSpot('B', number) end

CheckFile:close();
end

function LimitSpot(operation, SpotPrice)
tid = tid+1
transaction={
["TRANS_ID"]=tostring(tid),
["ACTION"]="NEW_ORDER",
["CLASSCODE"]=class,
["SECCODE"]=sec,
["OPERATION"]=operation,
["QUANTITY"]=tostring(LotSize),
["PRICE"]=tostring(SpotPrice),
["ACCOUNT"]=tostring(account),
["EXECUTION_CONDITION"]="PUT_IN_QUEUE", -- тип заявки лимитная
}
res = sendTransaction(transaction)
if res ~= '' then
message(trz_comment..'Transaction Error! '..res)
else
message(trz_comment..'Transaction Success! ID = '..tid)
end
end

function math_round( roundIn , roundDig ) -- первый аргумент число для округления, второй - количество знаков после запятой
    local mul = 10^roundDig
    return ( math.floor( ( roundIn * mul ) + 0.5 )/mul )
end

function main()
message(trz_comment..'Start.')
climits = 0
do_main = true

while do_main do
sleep(1000)
end
end

function OnParam(class1, sec1)
if class1 == class and sec1 == sec then
price = math_round(getParamEx(class,  sec, "last").param_value, 2)
if uprice ~= nil then
if price <= dprice then
dprice = dprice-step
uprice = uprice-step
message(trz_comment..'Price-- -> '..price)
else if price >= uprice then
CheckLine(price);
dprice = dprice+step
uprice = uprice+step
message(trz_comment.."Price++ -> "..price)
end
end
end
end
end

function OnTrade(trade)
if trade.sec_code == sec and trade.class_code == class then
if uprice == nil then
uprice = trade.price + step
dprice = trade.price - step
message(trz_comment..'Grid placed success!')
end
t = bit.band(tonumber(trade['flags']),4) -- 1 = sell 0 = buy
if t == 0 then message(trz_comment.."buy "..trade.price); Op = "Buy"; climits = climits + 1;
wr = io.open(secondfile, 'a+')
wr:write(trade.price.."\n");
wr:flush();
wr:close();

else message(trz_comment.."Sell "..trade.price); climits = climits - 1; Op = "Sell"; SellPrice = nil;
remove_line_from_csv(secondfile, current_line)
end


for i=#TradeNums,1,-1 do
-- Если данная сделка уже была записана, выходит из функции
if TradeNums[i] == trade.trade_num then return; end;
end;

-- Если мы здесь, значит сделка не была найдена в числе уже записанных
-- Добавляет в массив номер новой сделки
TradeNums[#TradeNums + 1] = trade.trade_num;
-- Вычисляет операцию сделки
-- Создает строку сделки для записи в файл ("Дата и время;Код класса;Код бумаги;Номер сделки;Номер заявки;Операция;Цена;Количество\n")
local TradeLine = os.date("%c", os.time(trade.datetime))..";"..
trade.class_code..";"..
trade.sec_code..";"..
trade.trade_num..";"..
trade.order_num..";"..
Op..";"..
trade.price..";"..
trade.qty.."\n";
-- Записывает строку в файл
CSV:write(TradeLine);
-- Сохраняет изменения в файле
CSV:flush();
end
end

function remove_line_from_csv(filename, line_to_remove)
 local input_file = io.open(filename, "r")
 if not input_file then
   print("Ошибка открытия файла для чтения")
   return false
 end

 local temp_filename = filename .. ".tmp"
 local output_file = io.open(temp_filename, "w")
 if not output_file then
   print("Ошибка открытия временного файла для записи")
   input_file:close()
   return false
 end

 local line_number = 1
 for line in input_file:lines() do
   if line_number ~= line_to_remove then
     output_file:write(line .. "\n")
   end
   line_number = line_number + 1
 end

 input_file:close()
 output_file:close()
 -- Удаляем старый файл и переименовываем временный
os.remove(filename)
 os.rename(temp_filename, filename)
 message("deleted")
end
 
Главная причина, почему Ваш скрипт "ничего не помнит" — критические синтаксические ошибки, из-за которых Lua-интерпретатор в QUIK просто ломается и не может корректно выполнить код.

Главные ошибки в Вашем коде:
1. Синтаксический слом в CheckLine: Конструкция end elseif написана с ошибкой. В Lua нет elseif после end. Из-за этого функция вообще не компилировалась. Сломанный массив - строка Trades[] в OnInit — это некорректный синтаксис Lua. Выдаст ошибку.
2. Потеря переменной current_line. В функции OnTrade вы вызываете remove_line_from_csv(secondfile, current_line). Но current_line была локальной переменной внутри CheckLine. В OnTrade она всегда равна nil или старой глобальной пустышке. Скрипт не понимал, какую строку удалять.
3. Бесконечный цикл без выхода. В main() запущен while do_main do sleep(1000) end, но флаг do_main нигде не меняется на false. Скрипт зависнет навсегда при остановке.
4. Конкуренция за файлы. Файл secondfile открывается в режиме "r" (чтение), но при удалении строки Вы пытаетесь стереть его через os.remove прямо во время работы, что в Windows часто блокируется, если хэндл не освобожден или пересекается с другими функциями.

Попробуйте так. Исправленный скрипт. Этот вариант исправляет логику работы с файлом покупок (fibobuys.csv). Вот что изменилось.
Исправлен синтаксис.
Удален сломанный elseif в CheckLine и пустой массив Trades[].
Скрипт теперь корректно запускается терминалом QUIK.
Добавлена функция поиска строки (find_line_by_price). Теперь скрипт при продаже сканирует файл fibobuys.csv, находит точный номер строки, где лежит цена этой покупки, и передает этот индекс в функцию удаления. Если точная цена не найдена (например, из-за проскальзывания), он удаляет первую строку (принцип FIFO).
Безопасная перезапись файла. Вместо рискованного удаления файла через os.remove (который часто блокируется Windows, если файл занят QUIK), скрипт выкачивает строки в массив, очищает файл через режим "w" и записывает обратно только нужные данные.
Добавлен корректный OnStop. Теперь при остановке скрипта кнопкой в QUIK цикл в main останавливается корректно, а хэндлы файлов закрываются.

Скрипт все еще остается учебным и требуется доработать логику. Да и архитектуру тоже. Используйте код предварительно проверив, выкладываю как есть просто исправленный Ваш вариант. Удачи!
Код
-- Настройки торговой стратегии
local class = "QJSIM" 
local sec = "SBER" 
local account = "NL0011100043" 
local LotSize = 1 
local step = 0.02 --> Нужно проверить не уверен что так?
local limits = 10 

-- Служебные переменные
local tid = 0 
local trz_comment = "FIBO-2 --> " 
local uprice = nil
local dprice = nil
local TradeNums = {}
local do_main = true
local climits = 0

-- Пути к файлам
local csv_path = getScriptPath().."/FiboTrades.csv"
local secondfile = getScriptPath().."/fibobuys.csv"
local CSV = nil

function OnInit()
    CSV = io.open(csv_path, "a+")
end

-- Округление
function math_round(roundIn, roundDig)
    local mul = 10^roundDig
    return math.floor((roundIn * mul) + 0.5) / mul
end

-- Отправка транзакции -->  Проверьте параметры?
function LimitSpot(operation, SpotPrice)
    tid = tid + 1
    local transaction = {
        ["TRANS_ID"]   = tostring(tid),
        ["ACTION"]     = "NEW_ORDER",
        ["CLASSCODE"]  = class,
        ["SECCODE"]    = sec,
        ["OPERATION"]  = operation,
        ["QUANTITY"]   = tostring(LotSize),
        ["PRICE"]      = tostring(SpotPrice),
        ["ACCOUNT"]    = tostring(account),
        ["EXECUTION_CONDITION"] = "PUT_IN_QUEUE",
    }
    local res = sendTransaction(transaction)
    if res ~= '' then
        message(trz_comment..'Transaction Error! '..res)
    else
        message(trz_comment..'Transaction Success! ID = '..tid)
    end
end

-- Проверка файла цен для продажи или покупки
function CheckLine(number)
    local current_line = 1
    local same = 0
    local is_sold = false
    
    local CheckFile = io.open(secondfile, "r")
    if CheckFile then
        for line in CheckFile:lines() do
            local val = tonumber(line)
            if val then
                if val == number then same = same + 1 end
                -- Если цена в файле ниже или равна текущей — продаем по этой фиксации
                if val <= number then 
                    LimitSpot('S', number)
                    is_sold = true
                    break 
                end
            end
            current_line = current_line + 1
        end
        CheckFile:close()
    end

    -- Если совпадений нет, лимит не исчерпан и мы не продали — покупаем
    if not is_sold and same == 0 and climits < limits then 
        LimitSpot('B', number) 
    end
end

-- Удаление строки из CSV (безопасное переписывание)
function remove_line_from_csv(filename, line_to_remove)
    local lines = {}
    local input_file = io.open(filename, "r")
    
    if input_file then
        for line in input_file:lines() do
            lines[#lines + 1] = line
        end
        input_file:close()
    end

    local output_file = io.open(filename, "w")
    if not output_file then return end

    for i, line in ipairs(lines) do
        if i ~= line_to_remove then
            output_file:write(line .. "\n")
        end
    end
    output_file:close()
    message(trz_comment.."Строка "..line_to_remove.." удалена из базы покупок.")
end

-- Функция поиска строки по цене для удаления при OnTrade
function find_line_by_price(filename, price_to_find)
    local input_file = io.open(filename, "r")
    if not input_file then return nil end
    
    local line_number = 1
    for line in input_file:lines() do
        if tonumber(line) == price_to_find then
            input_file:close()
            return line_number
        end
        line_number = line_number + 1
    end
    input_file:close()
    return nil
end

function main()
    message(trz_comment..'Start.')
    while do_main do
        sleep(1000)
    end
end

function OnStop()
    do_main = false
    if CSV then CSV:close() end
    return 1000
end

function OnParam(class1, sec1)
    if class1 == class and sec1 == sec then
        local last_param = getParamEx(class, sec, "last")
        if not last_param or last_param.param_value == "" then return end
        
        local price = math_round(tonumber(last_param.param_value), 2)
        
        if uprice ~= nil then
            if price <= dprice then
                dprice = dprice - step
                uprice = uprice - step
                message(trz_comment..'Price-- -> '..price)
            elseif price >= uprice then
                CheckLine(price)
                dprice = dprice + step
                uprice = uprice + step
                message(trz_comment.."Price++ -> "..price)
            end
        end
    end
end

function OnTrade(trade)
    if trade.sec_code == sec and trade.class_code == class then
        -- Инициализация сетки по первой сделке
        if uprice == nil then
            uprice = trade.price + step
            dprice = trade.price - step
            message(trz_comment..'Grid placed success!')
        end

        -- Проверка на дубликаты сделок
        for i = #TradeNums, 1, -1 do
            if TradeNums[i] == trade.trade_num then return end
        end
        TradeNums[#TradeNums + 1] = trade.trade_num

        local Op = ""
        local t = bit.band(tonumber(trade.flags), 4) -- 4 = sell, 0 = buy
        
        if t == 0 then 
            -- ЛОГИКА ПОКУПКИ
            Op = "Buy"
            climits = climits + 1
            message(trz_comment.."buy "..trade.price)
            
            local wr = io.open(secondfile, 'a+')
            if wr then
                wr:write(trade.price.."\n")
                wr:flush()
                wr:close()
            end
        else 
            -- ЛОГИКА ПРОДАЖИ
            Op = "Sell"
            climits = climits - 1
            message(trz_comment.."Sell "..trade.price)
            
            -- Ищем строку с ценой, которая послужила триггером для продажи
            local line_idx = find_line_by_price(secondfile, trade.price)
            if line_idx then
                remove_line_from_csv(secondfile, line_idx)
            else
                -- Если точной цены нет, удаляем первую попавшуюся строку (FIFO)
                remove_line_from_csv(secondfile, 1)
            end
        end

        -- Логирование в общий файл FiboTrades.csv
        if CSV then
            local TradeLine = os.date("%x %X", os.time(trade.datetime))..";"..
            trade.class_code..";"..
            trade.sec_code..";"..
            trade.trade_num..";"..
            trade.order_num..";"..
            Op..";"..
            trade.price..";"..
            trade.qty.."\n"
            
            CSV:write(TradeLine)
            CSV:flush()
        end
    end
end
Страницы: 1
Читают тему
Наверх