1

Тема: Подсчёт спреда

Помогите пожалуйста с написанием скрипта на ЛУА, который бы вычислял среднесуточный спред по тем или иным бумагам.
Работать он должен по такому алгоритму:

1. Задаётся список отслеживаемых бумаг. Например, "GAZP, RASP, LKOH, TGKE"
2. Раз в промежуток времени (например 10 минут) по ним подсчитывается спред по формуле (ask-bid)*100/bid и записывается в файл.
3. По мере накапливания значений в файле (или единожды в конце дня) по каждой бумаге считается среднее арифметическое её спреда и выводится куда-либо, например в соседний файл.

Буду очень признателен за любую помощь!

2

Re: Подсчёт спреда

Уточните, пожалуйста:

"Раз в промежуток времени (например 10 минут) по ним подсчитывается спред" - в смысле интересуют лишь те значение, какие были 1 раз в эти 10 минут? т.е. просто в какой-то момент взяли спред и его запомнили? Или все эти 10 минут спред тоже должен как-то усредняться по всем значениям вообще? не понятно из описания.

3

Re: Подсчёт спреда

admin пишет:

Уточните, пожалуйста:

"Раз в промежуток времени (например 10 минут) по ним подсчитывается спред" - в смысле интересуют лишь те значение, какие были 1 раз в эти 10 минут? т.е. просто в какой-то момент взяли спред и его запомнили? Или все эти 10 минут спред тоже должен как-то усредняться по всем значениям вообще? не понятно из описания.

Внутри этого времени подсчёт не ведётся.
Раз в 10 минут делается срез. И так весь торговый день.
10 минут - это опция, время можно увеличивать или уменьшать.

4 (2013-01-14 22:45:28 отредактировано Kosmonavt)

Re: Подсчёт спреда

Kosmonavt пишет:

по каждой бумаге считается среднее арифметическое её спреда и выводится куда-либо, например в соседний файл.

или в таблицу в самом квике.

Также было бы здорово научить этот скрипт отслеживать экстремальные значения:
-самый широкий спред за торговый день
-самый узкий спред за торговый день.

5

Re: Подсчёт спреда

Понял.
Если никто не опередит - завтра к вечеру, хорошо?

Я про файл. С интерфейсом - немного отдельная песня. Стабильного нет, а глючный вам зачем? )

6

Re: Подсчёт спреда

Спасибо!
Вместе мы победим этот рынок! smile

7

Re: Подсчёт спреда

Выложу то что есть, чуь позже несколько дополню.

Вначале файла константы, их необходимо задать подходящие вам.

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

Ограничения и условия использования:

  • задается бумага и класс, но все тикеры должны быть разные;

  • стаканы для инструментов, по которым осуществляется мониторинг, должны быть открыты; если тскан не открыт - спред не мониторится и не логируется;

  • если стакан пуст или в нем есть только спрос или только предложение - спред по такому инструменту не логируется.

Сохраняем текст в файл spread-logger-1.lua и запускаем. Выходные данные будут записываться в файл spread-logger-1.lua.log в том же каталоге, откуда запущен скрипт.

SEC = {}
SEC["LKOH"] = "EQBR"
SEC["SBER"] = "EQBR"
SEC["MSNG"] = "EQBR"

PERIOD = 10*60  -- период чтения спреда, в секундах

-----
-- Т.к. в ТЗ был задан довольно большой промежуток между расчетами - 
-- выходной файл каждый раз открывается и закрывается.
-- Если период обновления будет выбран существенно меньшим (секунда, например) -
-- тогда есть смысл выходной файл постоянно держать открытым.
-----

spreads = {}

function ReadLogFile()
  nnn = 0
  message("Readed " .. tostring(nnn) .. " record(s)")
end

function OnInit(path)
  scriptPath = path
  logFileName = scriptPath .. ".log"
  is_run = true
  message("Start " .. logFileName)
  ReadLogFile()
end

function OnStop()
  is_run = false
end

function OutSpreadToLog()
  local file = io.open(logFileName, "a+t")

  local i,sec
  for sec,class in pairs(SEC) do 

    local qt = getQuoteLevel2(class, sec)
    if ((qt.bid_count+0 > 0) and (qt.offer_count+0 > 0)) then
      local bid = qt.bid[qt.bid_count+0].price
      local offer = qt.offer[1].price
      local p_spread = (offer - bid) / bid * 100

      local key = sec .. ":" .. class
      local elem = spreads[key]
      if elem == nil then
        spreads[key] = { Count = 1, Spread = p_spread, Avr = p_spread, Min = p_spread, Max = p_spread }
        elem = spreads[key]
      else
        elem.Spread = p_spread
        elem.Avr = (elem.Avr * elem.Count + p_spread) / (elem.Count + 1)
        elem.Count = elem.Count + 1
        if elem.Min > p_spread then  elem.Min = p_spread  end
        if elem.Max < p_spread then  elem.Max = p_spread  end
      end

      file:write("[" .. tostring(elem.Count) .. "] " .. sec .. ":" .. class .. "=" .. tostring(p_spread) .. " avr=" .. tostring(elem.Avr) .. " min=" .. tostring(elem.Min) .. " max=" .. tostring(elem.Max) .. "\n")
    end

  end

  file:write("---\n")
  file:close()
end

function main()
  while is_run do

    OutSpreadToLog()

    local n = PERIOD
    while (n > 0) and is_run do
      sleep(1000)
      n = n - 1
    end

  end
end

Строки выходного файла сейчас сохраняются в таком формате:

[КОЛИЧЕСТВО] КОД_БУМАГИ:КЛАСС=СПРЕД avr=УСРЕДНЕННОЕ min=МИНИМУМ max=МАКСИМУМ

Вопросы, предложения, претензии - пишите, не стесняйтесь.

8

Re: Подсчёт спреда

Спасибо!
Это то что нужно! smile

9

Re: Подсчёт спреда

Возможность корректного рестарта скрипта внутри дня (со считыванием ранее накопленных данных из файла) актуальна?
Визуализация (табличка) - нужна? (в каком виде)

10 (2013-01-15 21:15:29 отредактировано Kosmonavt)

Re: Подсчёт спреда

admin пишет:

Возможность корректного рестарта скрипта внутри дня (со считыванием ранее накопленных данных из файла) актуальна?
Визуализация (табличка) - нужна? (в каком виде)

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

11

Re: Подсчёт спреда

Пока дополнил только возможностью заданя бумаг с разными классами (но тикеры все должны быть различными, т.е. не пересекаться в разных классах!), и изменил формат вывода логов, добавил туда минимум и максимум. Остальное уже завтра, сорри.

Текст программы в сообщении выше (исправлен).

12

Re: Подсчёт спреда

Здравствуйте!
Помогите пожалуйста разобраться с округлением.
Числа которые получаются в ходе работы данного кода (такие как avr=1.3561960970447) хотел бы округлять до 5 знака после запятой. Округлять по правилам арифметики, смотря какой остаток. То есть math.floor (x) и math.ceil (x) не годятся.

13

Re: Подсчёт спреда

http://quik2dde.ru/viewtopic.php?id=31
Вот тут пример есть

14

Re: Подсчёт спреда

admin пишет:

      file:write("[" .. tostring(elem.Count) .. "] " .. sec .. ":" .. class .. "=" .. tostring(p_spread) .. " avr=" .. tostring(elem.Avr) .. " min=" .. tostring(elem.Min) .. " max=" .. tostring(elem.Max) .. "\n")
Строки выходного файла сейчас сохраняются в таком формате:

[КОЛИЧЕСТВО] КОД_БУМАГИ:КЛАСС=СПРЕД avr=УСРЕДНЕННОЕ min=МИНИМУМ max=МАКСИМУМ

Я радикально поменял эту строку. Теперь данные выдаются так:
AVR_MAGN=0.12
AVR_TGKE=0.53
AVR_MRKK=0.72
и так далее.

Ни нумерации, ни текущего, ни максимального больше нет. Только среднее.

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

15

Re: Подсчёт спреда

И ещё вопрос.
При нынешней реализации поддерживает ли он корректный рестарт лога при перезапуске?

16

Re: Подсчёт спреда

Рестарт с чтенем из лога так и не сделан. Надо подумать что для этго нужно, достаточно ли имеющихся данных.

(после раздумий)

Для корректного рестарта с лога необходима информация:
1) Время записи спреда (чтобы можно было соблудать периодичность, если это критично)
2) Тикер инструмента и класс
3) Количество сохраненных спредов (сколько было записей по этому инструменту)
4) Текущее насчитанное среднее

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

Подойдет?

17 (2013-01-29 22:52:42 отредактировано Kosmonavt)

Re: Подсчёт спреда

Да, вполне.
Спасибо!
Правильно ли я понял, что строчку с выводом данных я могу менять хоть в хвост, хоть в гриву?
Вот эту:

file:write("[" .. tostring(elem.Count) .. "] " .. sec .. ":" .. class .. "=" .. tostring(p_spread).....................

18

Re: Подсчёт спреда

Kosmonavt пишет:

Правильно ли я понял, что строчку с выводом данных я могу менять хоть в хвост, хоть в гриву?
Вот эту:

file:write("[" .. tostring(elem.Count) .. "] " .. sec .. ":" .. class .. "=" .. tostring(p_spread).....................

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

19

Re: Подсчёт спреда

Чтобы этот робот обращался не к стакану, а к бид и аск в ТТП, достаточно ли внести в него такие правки? Я поставил знак коммента -- на тех строках, которые задействованы в обращении к ненужным теперь стаканам.

--    local qt = getQuoteLevel2(class, sec) - убираем вызов к стакану
--    if ((qt.bid_count+0 > 0) and (qt.offer_count+0 > 0)) then - убираем проверку условия
--    local bid = qt.bid[qt.bid_count+0].price - убираем получение бида из стакана
--    local offer = qt.offer[1].price - убираем получение аска из стакана

      
      local bid = getParamEx(class,  sec, "bid")
      local offer = getParamEx(class,  sec, "offer")
      
      local p_spread = (offer - bid) / bid * 100

      local key = sec .. ":" .. class
      local elem = spreads[key]
      if elem == nil then
        spreads[key] = { Count = 1, Spread = p_spread, Avr = p_spread, Min = p_spread, Max = p_spread }
        elem = spreads[key]
      else
        elem.Spread = p_spread
        elem.Avr = (elem.Avr * elem.Count + p_spread) / (elem.Count + 1)
        elem.Count = elem.Count + 1
        if elem.Min > p_spread then  elem.Min = p_spread  end
        if elem.Max < p_spread then  elem.Max = p_spread  end
      end

      file:write("AVR_".. sec .. "=" .. tostring(elem.Avr).. " " .. "\n")
      file:write("[" .. tostring(elem.Count) .. "] " .. sec .. ":" .. class .. "=" .. tostring(p_spread) .. " avr=" .. tostring(elem.Avr) .. " min=" .. tostring(elem.Min) .. " max=" .. tostring(elem.Max) .. "\n")

--    end - убираем финал проверки условия

20

Re: Подсчёт спреда

Kosmonavt,

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

21

Re: Подсчёт спреда

появляется ошибка:
attempt to perfom arithmetic on local "offer" (a table value)

как с этим бороться?
Добавленные строчки я стянул из документации, там они есть в чистом виде.
Почему offer стал табличным значением?

22 (2013-02-11 18:53:25 отредактировано sam)

Re: Подсчёт спреда

Kosmonavt пишет:

появляется ошибка:
attempt to perfom arithmetic on local "offer" (a table value)

как с этим бороться?
Добавленные строчки я стянул из документации, там они есть в чистом виде.
Почему offer стал табличным значением?

у вас offer -  это таблица(скорее всего как массив с параметрами qty,price), посмотрите описание функции getParamEx

23

Re: Подсчёт спреда

тогда по ходу должно быть вот так

      local tablebid = getParamEx(class,  sec, "bid")
      local bid=tablebid.param_value
      
      local tableoffer = getParamEx(class,  sec, "offer")
      local offer=tableoffer.param_value

24

Re: Подсчёт спреда

Kosmonavt,

да, именно так. Вы и сами догадались )

25

Re: Подсчёт спреда

У меня инструменты заданы в таком порядке:

SEC["APTK"] = "EQNL"
SEC["AVAZP"] = "EQNL"
SEC["BSPB"] = "EQNL"
SEC["GAZP"] = "EQNE"
SEC["HYDR"] = "EQBR"
SEC["KMAZ"] = "EQBS"
SEC["LKOH"] = "EQBR"
SEC["LSNG"] = "EQNL"
SEC["MAGN"] = "EQNL"
SEC["MRKK"] = "EQNL"
SEC["MSRS"] = "EQBR"
SEC["OGKB"] = "EQBR"
SEC["OPIN"] = "EQNL"
SEC["RASP"] = "EQNL"
SEC["RTKM"] = "EQBR"
SEC["SBER"] = "EQBR"
SEC["SNGS"] = "EQNL"
SEC["SNGSP"] = "EQNL"
SEC["SYNG"] = "EQNL"
SEC["TGKA"] = "EQBR"
SEC["TGKE"] = "EQNL"
SEC["TGKF"] = "EQNL"
SEC["TRMK"] = "EQBR"
SEC["URKA"] = "EQBR"
SEC["VTBR"] = "EQNL"
SEC["VZRZ"] = "EQBS"




Почему в итоговом файле они идут вразнобой?

AVR_APTK=0.2068627406292
AVR_OGKB=0.16652977902568
AVR_VZRZ=0.32100360912737
AVR_RASP=0.11919033845793
AVR_URKA=0.058398588662547
AVR_LKOH=0.024594674588463
AVR_AVAZP=0.27888853122047
AVR_SNGSP=0.05923011085541
AVR_LSNG=0.70180301215612
AVR_TGKE=0.53887543384184
AVR_TGKA=0.32988383264019
AVR_TRMK=0.44494026965039
AVR_MSRS=0.86604488170569
AVR_TGKF=0.5589009518343
AVR_OPIN=1.5116186775737
AVR_MAGN=0.068115489441876
AVR_HYDR=0.029428833146793
AVR_VTBR=0.027005381309969
AVR_SYNG=0.8848156772472
AVR_RTKM=0.066976460702493
AVR_SBER=0.013652757755115
AVR_BSPB=0.14493702671554
AVR_SNGS=0.049061792735469
AVR_KMAZ=0.34151044356869
AVR_GAZP=-0.00053292169463214

Как сделать так, чтобы они шли в том порядке, который я им задал?