В Lua, все же, прототипная модель ООП (https://ru.wikipedia.org/wiki/Прототипное_программирование). Если быть точным, в Lua все построено на таблицах. Поэтому думать о расходе памяти и оптимизировать код надо всегда (слабые таблицы). Поэтому Классы в Lua решают больше задачу создания некой сущности на основе шаблона. Что бывает удобно. Но все то же можно сделать и без оного, придется просто сделать больше переменных.
Anton написал: Впрочем, можете квик не насиловать, она даже под консольным луа падает на тестовом скрипте из поставки, откуда очевидно, что дело не в квике. Доходит вот докуда Скрытый текст
Дальше мне рыть лень, это уже работа какая-то получается )
Да, увидел. Уже подумалось, что проще консольную программку написать, чем библиотеки насиловать. У меня почта так отпраляется и читается. Надежней.
Nikolay написал: Я не смотрел в код. Надо посмотреть значит, да.
Заодно и в мейкфайл посмотрите, там кое-что генерируется из луа с помощью luac, получается *.lo, а потом с помощью bin2c из него получается *.loh. Так вот в мастере luacom5.lo уже лежит готовый. Очевидно, мейкфайл его пропустит (оставит как есть), а как есть он, сдается мне, 32-битный.
Это как раза было поправлено. Иначе ошибки линковки.
Nikolay написал: плюс что-то с кодировкой символов.
Таки дело не в бобине, взгляните в сорцы, файл tUtil.cpp, строки 111, 128, 171, 175. Возможно, список неполный.
Компилировал VS через nmake. Я не смотрел в код. Надо посмотреть значит, да. Правда то же, скомпилированное под Квик 7, работает, а у него utf тоже нет.
Меня попросили собрать luaCom для Квик 8. Собрал. Но тесты показывают, что Квик 8.0-8.4 падает, плюс что-то с кодировкой символов. Очень похоже на то, что было с luasql(ODBC).
Создается таблица и перехватывается нажатие клавиш внутри таблицы по их коду. Для стирания можно перехватывать BackSpace. Для плавного увеличения-уменьшения значения можно перехватить клавиши стрелки и т.д.
Есть параметр TIME - время последней сделки. А дата? TRADE_DATE_CODE - это дата прошлого торгового дня или дата дня где были сделки или дата текущего нового торгового дня, раз он уже наступил?
Да, можно взять время последнего любого бара, но проблема как раз в том, что бары при заказе не сразу поступают с сервера, надо подождать. Вот сравнивая дату-время сделки и дату-время бара хочется понять, что все бары загружены. Но если даты сделки нет, то гарантии, что это сделка прошлой сессии нет, может это сделка позапрошлой и т.д.
Эксперимент с функцией CreateDataSource выявил интересную особенность. Если отправить туда интервал, отличный от заданного в константах, то нет ошибки.
Вот я отправил число 615 вместо интервала.
Get DataSource: SBER|QJSIM, интервал: 615 InitSec DS all bars: 2, last bar: 2019.12.03 10:15:00
Также возвращается и для 619. Это фича или все же баг. Я вполне ожидаю, что мне вернется ошибка, чтобы как-то отреагировать.
Nikolay написал: Если кому еще необходимо. Я бился над сборкой socket и ssl под x64. Вроде собрал
Незаработали они у меня потому что собрал ты их строго под Луа 5.1, может будешь так добр что соберешь все это еще и под Луа 5.3?
А зачем под Lua 5.3? В Квике 5.1. Все это только ради Квика и собиралось. В этом весь смысл. Если под 5.3 и для сторонних скриптов, то, кажется, сборки есть готовые под 5.3.
Если кому еще необходимо. Я бился над сборкой socket и ssl под x64. Вроде собрал, но как-то нестабильно оно работало. В итоге нашел готовые сборки, более стабильно работающие. А вот lCurl пока не удалось собрать под x64.
Возвращаюсь к вопросу. Тех. поддержка ответила (Падение Quik 8.0.3.14 (CQ02527163)), что ошибка в файле alltrade.dat. Но это явно не оно. Т.к. удаление файла не приводит к другому поведению. Квик 8 стабильно падает без дампов. Сделал простой тестовый скрипт, стабильно работающий в 7-ой версии с 32 битной библиотекой, но приводящий к падению Квика 8 с 64 битной. Причем падает непостоянно в одном месте, то при выборке из базы данных, то, что более странно, при вызове getQuoteLevel2.
Если под некорретным поведением подразумевается ошибка, которая здесь не указана, то предположу, что есть проблема вывода в таблицу, т.к. при остановке скрипта окно таблицы закрывается, а OnStop еще не вызван. В main нет ни одной проверки, что таблица существует и она не закрыта, вот и падает при попытке вывести в закрытую таблицу. Также sleep лучше повысить до 50 мс (т.к. используется вывод в таблицу), иначе Квик начнет процессор нагружать.
Для этой же задачи был переписан скрипт автологина, использующий w32 библиотеку, который при обрыве связи, нажимает на список выбора сервера в форме логина/пароля.
Дополнительные сведения 2: f9888e0a087b444ad5df05a65c8fed35
Дополнительные сведения 3: 9540
Дополнительные сведения 4: 954017ff240795f447e29bd8d973bef5
Но далее все интереснее. При возникновении ошибки, я попробовал запустить отладку приложения. Получил сообщение об ошибке в ntdll.dll Закрыл сообщение и запустил продолжить выполнение приложения. Вернулся в окно Квика и он продолжил работать как будто ничего не произошло. Скрипт работает.
Далее останавливаю скрипт и получаю еще раз эту же ошибку. Опять в отладе нажимаю продолжить и возвращаюсь в окно терминала, он работает, выведя сообщение:
Вернулся к вопросу. Скомпилировал драйвер ODBC, т.к. он мне больше всего нужен. Что пришлось менять: В файле ls_odbc.c пришлось заменить определение окружения на _WIN64. Пришлось заменить тип SQLINTEGER на SQLLEN, т.к. в библиотеке ODBC 64 другие типы.
Также надо удалить все файлы *.obj, т.к. они собраны для x86.
После библиотека собралась. Даже запустился скрипт, работающий под x86.
Но, пришлось переписать код обхода курсора на итератор. Прямой обход приводит к ошибке. Но даже это не долго живет, Квик падает без слов через секунд 10-20 работы.
SQL Profiler показывает, что запросы идут нормально, ошибок нет. В те несколько секунд, что успевает отработать скрипт, данные стакана выводятся в окно исправно. Но падает Квик всегда.
Что может быть, драйвер или все же Квик? Этот же драйвер под 7 Квик работает исправно.
Возможно, как писали в одной из веток, надо еще и lua5.1.dll самому собирать. Есть подозрение, что некорректно обрабатываются таблицы в луа стеке.
Добрый день. Я пробовал скомпилировать через make файл, но лезут ошибки линковки целевой платформы. Хотя делалось все под окружением x64. Остается вариант сконвертировать проект VS6 под VS2015-2017 и собирать, меняя зависимости. Он есть в исходниках. Но напрямую он не конвертируется, слишком старая версия. Видел собщение, что надо делать через VS2010, т.к. более поздние версии не конвертируют проект.
Добрый день. Я бился с подключением SSL библиотеки. Не подключалась никак из готовых библиотек. На стороне тоже все работало. В конечном итоге пришлось просто пересобрать конкретную версию библиотеки, которая в итоге заработала. Так что советую собрать luacurl через luarocks именно под lua 5.1.
Еще один момент важный, такого рода библиотеки в Квике работают, если они лежат в папке с библиотекой lua5.1, т.е. в папке установки Квика. В чем здесь особенность Квика не знаю, но пришлось так сделать, иначе никак не запускалось.
Проблему решил. Пришлось скомпилировать самому luaSec 0.8-1 под lua 5.1, используя Open SSL. Файлы libeay32 и ssleay32 обязаны быть рядом с файлом lua 5.1, т.е. в папке установки Quik.
У меня отправка почты совершается за секунду, две. Не более. Никаких задержек.
Теперь можно приступать к использованию библиотеки для интересных вещей, типа чтения Twitter и т.д.
А у меня такой строчки нету и все работает, короче у меня все прописано вот так:
Ну я хочу использовать эту библиотеку не для отправки почты. Для почты я написал на C# програмульку на 10 строк, вызываю ее из Квика и все. А вот для других целей хочется запустить. Ясно, что дело в путях, т.к. в другом месте работает. Будем копать...
Анатолий написал: Сложно так сказать не видя что у тебя в скрипте и в папках квика, покажи тогда свой кусок скрипта где обьявляются пути
package.cpath = ... package.path = ...
и идет подключение библиотек командой require
Так а что здесь такого при подключении... Я же говорю, что этот же скрипт ZeroBraneStudio в работает. Отличие в том, что у него свои пути к библиотекам. А здесь такие. Попытка поставить пути из ZeroBraneStudio для Квика приводит к аналогичной ошибке.
Nikolay написал: Я тоже подозреваю, что с интерпретатором lua в Квике не все ладно. У меня также из ZeroBraneStudio все работает.
С интерпретатором луа в квике все ладно, просто там есть нюансы, у меня были все эти проблемы и я их решил, помоему ты всетаки не все скопировал из окна ошибок, полностью все оттуда скопируй и вставь сюда, или лучше сделай скрин и выложи
Я скопировал все, кроме полного пути до файла ssl.dll. Больше ошибок нет. Выше по сообщениям этой темы были аналогичные сообщения.Если бы интерпретатор луа был чист, то типовая библиотека strict не приводила бы к падению терминала.
Анатолий написал: В момент выполнения строки registry = ctx: ctx= SSL context: 0D72EE88 тип - userdata s = SSL connection: 0D848F00 тип - userdatasock = tcp{client}:0D846E88 setfd=nilВообщем мне кажется что это связано с квиковским интерпретатором луа, т.к. операция registry это сохранение любых значений C кода в специальной общей таблице для всех выполняемых С кодов, вот тут в чем о и проблема, т.к. в других интерпретаторах, том же Zerobrane, такой проблемы нет
Я тоже подозреваю, что с интерпретатором lua в Квике не все ладно. У меня также из ZeroBraneStudio все работает. Также не все библиотеки можно подключить в Квике, для примера strict - Квик падает без слов. Также обнаружил, что внутри фунции SearchItems (в функции проверки) у меня почему-то проверка nil как false не срабатывает. Приходится явно писать ~= nil
Nikolay написал: Я подключил все библиотеки, socket подключается нормально. Но при подключении ssl, не находит ssl.core. ssl.lua, собственно, и пытается его включить local core = require("ssl.core")Т.е. нужна папка \ssl а в ней файл core.dll
Скопируй из квика из окна "Ошибки выполнения скрипта" то что там пишется при твоей попытке выполнить скрипт и выложи это сюда"
Ну так и пишется, что не может загрузить модуль. Все пути прописаны, библиотека рабочая, взята из поставки ZeroBraneStudio. Из нее все прекрасно работает. При этом просто socket загружается нормально.
Код
error loading module 'ssl.core' from file 'ssl.dll':
Не найден указанный модуль.
А тоже самое из ZeroBraneStudio работает. ssl.core - ошибку не дает, письма отправляются. А Квик ошибку выдает на local core = require("ssl.core") в ssl.lua
Я подключил все библиотеки, socket подключается нормально. Но при подключении ssl, не находит ssl.core. ssl.lua, собственно, и пытается его включить local core = require("ssl.core") Т.е. нужна папка \ssl а в ней файл core.dll
Попробовал взять сборку Телеграм бота, что здесь выкладывали, аналогичная проблема. Похоже какая-то библиотека все же отсутствует. Кто-то встречал похожую ошибку? Я нашел упоминания, то это связано с libeay32 и ssleay32, но они есть, путь к ним прописан. Плюс пробовал класть их в разные папки.
Что касается "стандартных стратегий", то я не верю в них, т.к. они основаны на предположеннии, что тенденция прошлого сохранятся и в будущем, что, естественно, не так. Да, есть решения, позволяющие минимизировать риски, но рынок меняет свои характеристики так, что требуется пересчет оптимальных параметров стратегии. Что, в свою очередь, приводит к проблеме переоптимизации. А это замкнутый круг.
Я же больше верю в стратегии с элементами количественнго анализа, основанные на потоке реальных данных. При этом, естественно, стратегия строится на выявлении закономерностей на истории, но не путем усреднения характеристик движения цены (скользящие, осцилляторы и др.), а на анализе абсолютных значений движения цены и их отношений друг к другу. Свечи же - это искусственное загрубление характеристик. Скажем, почему ТФ - минута, а если я хочу 25 секунд. Кто решил, что всем хватит минуты? Ведь, если хоть немнго сдвинуть ТФ, то картина может кардинально измениться, сменятся максимумы-минимумы, открытия-закрытия. Поэтому более важна характеристика сколько цена прошла за отведенный период. Период, при этом, не привязан к ТФ, хоть, зачастую, им и является.
Для примера, безумная стратегия: если цена в 13:00 выше открытия дня (или недели), то покупаем и наоборот Закрытие сделки по рассчитанному профиту или через 45 минут, что раньше. Это выдуманный пример выявленной закономерности. Такая стратегия живет месяц-два, а то и меньше. Также выявляются и другие закономерности вплоть до тикового уровня. При этом также находятся и закономерности типа гармонических фигур любого перирода, их последовательности, отношений.
Все это требует вычислительной мощности и постоянного анализа данных. Это, естественно, мало кто может себе позволить.
Спасибо Николай, но видимо так я и не дождался этой доработки. Пока ждал данный функционал был разработан уже, причем в веб интерфейсе, и ровно так как я его описывал год назад http://bit.ly/2TKazri
Николай так скажите, а Вы какого подхода придерживаетесь при работе с стратегиями, если "не очень верю в торговлю по инерционным индикаторам" немного поподробней??
На нашем рынке, данный виртуальный режим реализован в TigerTrade и SBPro. Второй правда только на срочном рынке, но думаю, что в основном это и надо только на срочке. Данные платформы имеют демо, где можно все посмотреть.
Реализация данного режима - это запуск экземпляра приложения с последовательным чтением данных из истории (не только сохраненной, но и загрузка с сервера). Что важно, реализовано это все с поддержкой склейки контрактов. Также важно, что даные режимы для ручной торговли. Т.е. сделки на истории вы будете совершать руками.
Я для себя сделал робот с возможностью вирутальной торговли, который позволяет совершать сделки, ориентируясь на текущий стакан. Что, естественно, имеет некие допущения, т.к. при вирутальной торговле объем из стакана не исчезает. Поэтому можно говорить, что данное мое решение допустимо рассматривать только для малых объемов.
А для истории написан тестер алгоритмов. Сделать в Квике ручную торговлю на истории пока невозможно, но, с дургой стороны, это не такой уж большой недостаток получается, если можно симулироать торговлю в реале.
Единственное замечание - я не очень верю в торговлю по инерционным индикаторам, поэтому решение по написанию конструктора стратегии псевдоязыком (что реализовано в MT5, WealthLab и др.) видится сомнительным занятием. Но здесь, как говориться, вопрос про деньги. Если бы платформа Квик была платной для конечного пользователя, то возможно можно бы реализовать такую функциональность для завлечения новых "инвесторов", заявив, что теперь наша платформа позволяет обучаться "трейдингу" и вы легко можете написать своего робота.
Здесь, скорее всего, проблема скорости выполнения цикла. У меня есть сложный скрипт, выполняющий много расчетов в main. В нем все работает без задержек. А простейшие скрипты, которые делают мало действий в main, не обнолвяют таблицу. Возможно, идет переполнение стека вызовов обновления окна.
Хотя разработчки выше писали, что проблема не воспроизводится.
А вот сделав такие изменения в цикл вывода строк, все работает.
Код
for i=1,lines do
outlines[i] = {index = index-shift+bars, val = maxPrice}
if sortedProfile[i]~=nil then
sortedProfile[i].vol=math.floor(sortedProfile[i].vol/MAXV*bars)
if sortedProfile[i].vol>0 then
outlines[i].index = index-shift+sortedProfile[i].vol
outlines[i].val = sortedProfile[i].price
end
end
SetValue(index-shift, i+1, outlines[i].val)
SetValue(outlines[i].index, i+1, outlines[i].val)
--WriteLog('line '..tostring(i).." price "..tostring(GetValue(index-shift, i)).." - "..tostring(GetValue(outlines[i].index, i)).." vol "..tostring(outlines[i].index-index+shift))
end
Код
--logfile=io.open(getWorkingFolder().."\\LuaIndicators\\priceAvgProfile.txt", "w")
Settings={}
Settings.period = 150
Settings.shift = 100
Settings.Name = "*priceAvgProfile"
Settings.weeks = 0 -- 1 - текущая, отрицательное число - сколько прошлых недель, включая текущую
Settings.fixShift = 1 -- 1 - всегда смещено на указанное количество shift, если 0, то будет смещено на дату начала неделеи расчета
Settings.showMaxLine = 1
---------------------------------------------------------------------------------------
lines = 100
scale = 2
min_price_step = 1
function Init()
Settings.line = {}
Settings.line[1] = {}
Settings.line[1] = {Name = 'maxVol', Color = RGB(255, 128, 64), Type = TYPE_LINE, Width = 2}
for i = 1, lines do
Settings.line[i+1] = {}
Settings.line[i+1] = {Color = RGB(185, 185, 185), Type = TYPE_LINE, Width = 2}
end
myFFF = FFF()
return lines
end
function OnCalculate(index)
if index == 1 then
DSInfo = getDataSourceInfo()
min_price_step = getParamEx(DSInfo.class_code, DSInfo.sec_code, "SEC_PRICE_STEP").param_value
scale = getSecurityInfo(DSInfo.class_code, DSInfo.sec_code).scale
end
return myFFF(index, Settings)
end
---------------------------------------------------------------------------------------
function FFF()
local cacheL={}
local cacheH={}
local cacheC={}
local weeksBegin={}
local maxPriceLine={}
local outlines = {}
local calculated_buffer={}
return function(ind, Fsettings)
local period = Fsettings.period or 150
local shift = Fsettings.shift or 150
local weeks = Fsettings.weeks or 0
local fixShift = Fsettings.fixShift or 0
local showMaxLine = Fsettings.showMaxLine or 0
local bars = 50
shift = math.max(bars+1, shift)
local index = ind
if index == 1 then
maxPriceLine = {}
weeksBegin = {}
cacheL = {}
cacheL[index] = 0
cacheH = {}
cacheH[index] = 0
cacheC = {}
cacheC[index] = 0
calculated_buffer = {}
outlines = {}
return nil
end
------------------------------
--maxPriceLine[index] = maxPriceLine[index-1]
cacheL[index] = cacheL[index-1]
cacheH[index] = cacheH[index-1]
cacheC[index] = cacheC[index-1]
if not CandleExist(index) then
return maxPriceLine[index]
end
cacheH[index] = H(index)
cacheL[index] = L(index)
cacheC[index] = C(index)
if T(index).week_day<T(index-1).week_day or T(index).year>T(index-1).year then
weeksBegin[#weeksBegin+1] = index
end
if index < Size() then return nil end
if calculated_buffer[index] ~= nil then
return maxPriceLine[index]
end
if showMaxLine==1 then
SetValue(index-shift-1, 1, nil)
SetValue(index-shift, 1, nil)
end
for i=1,#outlines do
SetValue(index-shift-1, i+1, nil)
SetValue(index-shift, i+1, nil)
SetValue(outlines[i].index, i+1, nil)
outlines[i].index = index-shift
outlines[i].val = nil
end
local beginIndex = index-period
if weeks == 1 then
beginIndex = weeksBegin[#weeksBegin] or beginIndex
end
if weeks < 0 then
beginIndex = weeksBegin[#weeksBegin+weeks] or beginIndex
end
if fixShift==0 then
shift = math.max(bars+1, index-beginIndex)
end
--WriteLog('weeks '..tostring(weeks)..' last '..tostring(weeksBegin[#weeksBegin])..' beginIndex '..tostring(beginIndex))
local maxPrice = math.max(unpack(cacheH,math.max(beginIndex, 1),index))
local minPrice = math.min(unpack(cacheL,math.max(beginIndex, 1),index))
----------------------------------------
local priceProfile = {}
--local clasterStep = math.max((maxPrice - minPrice)/lines, min_price_step)
--local clasterStep = (maxPrice - minPrice)/lines
local clasterStep = 10*min_price_step
--WriteLog('minPrice '..tostring(minPrice)..' maxPrice '..tostring(maxPrice)..' clasterStep '..tostring(clasterStep))
for i = 0, (index-beginIndex) do
if CandleExist(index-i) then
local barSteps = math.max(math.ceil((H(index-i) - L(index-i))/clasterStep),1)
for j=0,barSteps-1 do
local clasterPrice = math.floor((L(index-i) + j*clasterStep)/clasterStep)*clasterStep
local clasterIndex = clasterPrice*math.pow(10, scale)
if priceProfile[clasterIndex] == nil then
priceProfile[clasterIndex] = {price = clasterPrice, vol = 0}
end
priceProfile[clasterIndex].vol = priceProfile[clasterIndex].vol + V(index-i)/barSteps
end
end
end
--------------------
local MAXV = 0
local maxPrice = 0
local maxCount = 0
local sortedProfile = {}
for i, profileItem in pairs(priceProfile) do
MAXV=math.max(MAXV,profileItem.vol)
if MAXV == profileItem.vol then
maxPrice=profileItem.price
end
maxCount = maxCount + 1
sortedProfile[maxCount] = {price = profileItem.price, vol = profileItem.vol}
end
--WriteLog('maxV '..tostring(MAXV)..' tblMax '..tostring(sortedProfile[1].vol))
if maxPrice == 0 then
maxPrice = O(index)
end
table.sort(sortedProfile, function(a,b) return (a['vol'] or 0) > (b['vol'] or 0) end)
---------------------
for i=1,lines do
outlines[i] = {index = index-shift+bars, val = maxPrice}
if sortedProfile[i]~=nil then
sortedProfile[i].vol=math.floor(sortedProfile[i].vol/MAXV*bars)
if sortedProfile[i].vol>0 then
outlines[i].index = index-shift+sortedProfile[i].vol
outlines[i].val = sortedProfile[i].price
end
end
SetValue(index-shift, i+1, outlines[i].val)
SetValue(outlines[i].index, i+1, outlines[i].val)
--WriteLog('line '..tostring(i).." price "..tostring(GetValue(index-shift, i)).." - "..tostring(GetValue(outlines[i].index, i)).." vol "..tostring(outlines[i].index-index+shift))
end
if showMaxLine==1 then
SetValue(index-shift, 1, maxPrice)
maxPriceLine[index] = maxPrice
end
calculated_buffer[index] = true
return maxPriceLine[index]
end
end
function WriteLog(text)
logfile:write(tostring(os.date("%c",os.time())).." "..text.."\n");
logfile:flush();
LASTLOGSTRING = text;
end
Я привел весь код индикатора. Больше кода в нем нет.
Вы хотите увидеть ка формируется табоица algoResults? Но она получается из dll. Это простая таблица в которой накопленный объем по цене. Далее они просто выводятся.
В Вашем примере, данные не получаются онлайн. Вы их вывели по данным прошлых свечек. А значит, при первом вызове OnCalculate, все данные для линий получены. При установке индикатора OnCalculate вызывается же два раза. Хотя зачем?
Делаю предположение, что это как-то связано.
Вот, для примера, испорченный индикатор горизонтальных объемов. Он реализует такой же алгоритм, получая данные из свечек, а не из dll. В таком варианте он не работает как надо:
Код
--logfile=io.open(getWorkingFolder().."\\LuaIndicators\\priceAvgProfile.txt", "w")
Settings={}
Settings.period = 150
Settings.shift = 100
Settings.Name = "*priceAvgProfile"
Settings.weeks = 0 -- 1 - текущая, отрицательное число - сколько прошлых недель, включая текущую
Settings.fixShift = 1 -- 1 - всегда смещено на указанное количество shift, если 0, то будет смещено на дату начала неделеи расчета
Settings.showMaxLine = 1
---------------------------------------------------------------------------------------
lines = 100
scale = 2
min_price_step = 1
function Init()
Settings.line = {}
Settings.line[1] = {}
Settings.line[1] = {Name = 'maxVol', Color = RGB(255, 128, 64), Type = TYPE_LINE, Width = 2}
for i = 1, lines do
Settings.line[i+1] = {}
Settings.line[i+1] = {Color = RGB(185, 185, 185), Type = TYPE_LINE, Width = 2}
end
myFFF = FFF()
return lines
end
function OnCalculate(index)
if index == 1 then
DSInfo = getDataSourceInfo()
min_price_step = getParamEx(DSInfo.class_code, DSInfo.sec_code, "SEC_PRICE_STEP").param_value
scale = getSecurityInfo(DSInfo.class_code, DSInfo.sec_code).scale
end
return myFFF(index, Settings)
end
---------------------------------------------------------------------------------------
function FFF()
local cacheL={}
local cacheH={}
local cacheC={}
local weeksBegin={}
local maxPriceLine={}
local outlines = {}
local calculated_buffer={}
return function(ind, Fsettings)
local period = Fsettings.period or 150
local shift = Fsettings.shift or 150
local weeks = Fsettings.weeks or 0
local fixShift = Fsettings.fixShift or 0
local showMaxLine = Fsettings.showMaxLine or 0
local bars = 50
shift = math.max(bars+1, shift)
local index = ind
if index == 1 then
maxPriceLine = {}
weeksBegin = {}
cacheL = {}
cacheL[index] = 0
cacheH = {}
cacheH[index] = 0
cacheC = {}
cacheC[index] = 0
calculated_buffer = {}
outlines = {}
return nil
end
------------------------------
--maxPriceLine[index] = maxPriceLine[index-1]
cacheL[index] = cacheL[index-1]
cacheH[index] = cacheH[index-1]
cacheC[index] = cacheC[index-1]
if not CandleExist(index) then
return maxPriceLine[index]
end
cacheH[index] = H(index)
cacheL[index] = L(index)
cacheC[index] = C(index)
if T(index).week_day<T(index-1).week_day or T(index).year>T(index-1).year then
weeksBegin[#weeksBegin+1] = index
end
if index < Size() then return nil end
if calculated_buffer[index] ~= nil then
return maxPriceLine[index]
end
if showMaxLine==1 then
SetValue(index-shift-1, 1, nil)
SetValue(index-shift, 1, nil)
end
for i=1,#outlines do
SetValue(index-shift-1, i+1, nil)
SetValue(index-shift, i+1, nil)
SetValue(outlines[i].index, i+1, nil)
outlines[i].index = index-shift
outlines[i].val = nil
end
local beginIndex = index-period
if weeks == 1 then
beginIndex = weeksBegin[#weeksBegin] or beginIndex
end
if weeks < 0 then
beginIndex = weeksBegin[#weeksBegin+weeks] or beginIndex
end
if fixShift==0 then
shift = math.max(bars+1, index-beginIndex)
end
--WriteLog('weeks '..tostring(weeks)..' last '..tostring(weeksBegin[#weeksBegin])..' beginIndex '..tostring(beginIndex))
local maxPrice = math.max(unpack(cacheH,math.max(beginIndex, 1),index))
local minPrice = math.min(unpack(cacheL,math.max(beginIndex, 1),index))
----------------------------------------
local priceProfile = {}
--local clasterStep = math.max((maxPrice - minPrice)/lines, min_price_step)
--local clasterStep = (maxPrice - minPrice)/lines
local clasterStep = 10*min_price_step
--WriteLog('minPrice '..tostring(minPrice)..' maxPrice '..tostring(maxPrice)..' clasterStep '..tostring(clasterStep))
for i = 0, (index-beginIndex) do
if CandleExist(index-i) then
local barSteps = math.max(math.ceil((H(index-i) - L(index-i))/clasterStep),1)
for j=0,barSteps-1 do
local clasterPrice = math.floor((L(index-i) + j*clasterStep)/clasterStep)*clasterStep
local clasterIndex = clasterPrice*math.pow(10, scale)
if priceProfile[clasterIndex] == nil then
priceProfile[clasterIndex] = {price = clasterPrice, vol = 0}
end
priceProfile[clasterIndex].vol = priceProfile[clasterIndex].vol + V(index-i)/barSteps
end
end
end
--------------------
local MAXV = 0
local maxPrice = 0
local maxCount = 0
local sortedProfile = {}
for i, profileItem in pairs(priceProfile) do
MAXV=math.max(MAXV,profileItem.vol)
if MAXV == profileItem.vol then
maxPrice=profileItem.price
end
maxCount = maxCount + 1
sortedProfile[maxCount] = {price = profileItem.price, vol = profileItem.vol}
end
--WriteLog('maxV '..tostring(MAXV)..' tblMax '..tostring(sortedProfile[1].vol))
if maxPrice == 0 then
maxPrice = O(index)
end
table.sort(sortedProfile, function(a,b) return (a['vol'] or 0) > (b['vol'] or 0) end)
---------------------
for i=1,lines do
if sortedProfile[i]~=nil then
outlines[i] = {index = index-shift+bars, val = maxPrice}
sortedProfile[i].vol=math.floor(sortedProfile[i].vol/MAXV*bars)
if sortedProfile[i].vol>0 then
outlines[i].index = index-shift+sortedProfile[i].vol
outlines[i].val = sortedProfile[i].price
end
SetValue(index-shift, i+1, outlines[i].val)
SetValue(outlines[i].index, i+1, outlines[i].val)
end
--WriteLog('line '..tostring(i).." price "..tostring(GetValue(index-shift, i)).." - "..tostring(GetValue(outlines[i].index, i)).." vol "..tostring(outlines[i].index-index+shift))
end
if showMaxLine==1 then
SetValue(index-shift, 1, maxPrice)
maxPriceLine[index] = maxPrice
end
calculated_buffer[index] = true
return maxPriceLine[index]
end
end
function WriteLog(text)
logfile:write(tostring(os.date("%c",os.time())).." "..text.."\n");
logfile:flush();
LASTLOGSTRING = text;
end
lines = 150
min_price_step = 1
scale = 2
function Init()
Settings.line = {}
for i = 1, lines do
Settings.line[i] = {}
Settings.line[i] = {Color = RGB(185, 185, 185), Type = TYPE_LINE, Width = 2}
end
algoF = getResults()
return lines
end
Проблема именно в том, что если выводить линии не все сразу, они почему-то потом не показываются, а только те, что имели значение при запуске. Далее, по мере поступления новых данных (цена же двигается и торовый диапазон расширяется) в таблицу algoResults, надо выводить новые линии, но они не показываются. Именно визуально не видны, но при этом, что очень странно, GetValue возвращает значение.
Как написал выше, пришлось выводить ненулевые значения для каждой линии, накладывая одну на другую. Так работает.
Приходится выводить всегда столько линий, сколько определено, накладывая одну на другую, чтобы они слились в одну для пользователя. По мере поступления новых линий они будут тогда уже распределятся по уровням цены.
Если было выведено при инициализации 100 линий (т.е. когда происходит первый вызов OnCalculate), то больше не показывает индикатор. Далее линии вроде имеют значение, но не видны.
Sergey Gorokhov написал: Здравствуйте, Приведите полную версию кода.
Здравствуйте.
Код простой.
Код
require("StaticVar")
Settings ={
Name = "*priceProfile",
shift = 150,
ChartId = "Sheet11"
}
lines = 150
min_price_step = 1
scale = 2
function Init()
Settings.line = {}
for i = 1, lines do
Settings.line[i] = {}
Settings.line[i] = {Color = RGB(185, 185, 185), Type = TYPE_LINE, Width = 2}
end
algoF = getResults()
return lines
end
function getResults()
local outlines = {}
local priceProfile = {}
local tmpOutlines = {}
return function(index, Fsettings)
local shift = Fsettings.shift or 150
local bars = 50
if index == 1 then
outlines = {}
tmpOutlines = {}
for i=1,lines do --необходимо вывести все линии хотя бы один раз, чтобы потом они отображались без проблем
tmpOutlines[i] = O(Size())
end
return unpack(tmpOutlines)
end
if index == 2 then
for i=1,lines do
SetValue(1,i,nil)
end
return nil
end
if index == Size() then
stv.UseNameSpace(Fsettings.ChartId)
algoResults = stv.GetVar('priceProfile')
priceProfile = {}
if algoResults ~= nil and type(algoResults) == "table" then
for i=1,#outlines do
SetValue(index-shift-1, i, nil)
SetValue(index-shift, i, nil)
SetValue(outlines[i].index, i, nil)
outlines[i].index = index-shift
outlines[i].val = nil
end
local MAXV = 0
local maxCount = 0
for i, profileItem in pairs(algoResults) do
MAXV=math.max(MAXV,profileItem.vol)
maxCount = maxCount + 1
priceProfile[maxCount] = {price = profileItem.price, vol = profileItem.vol}
end
table.sort(priceProfile, function(a,b) return (a['vol'] or 0) > (b['vol'] or 0) end)
for i=1,math.min(#priceProfile, lines) do
if priceProfile[i]~=nil then
outlines[i] = {index = index-shift, val = nil}
priceProfile[i].vol=math.floor(priceProfile[i].vol/MAXV*bars)
if priceProfile[i].vol>0 then
outlines[i].index = index-shift+priceProfile[i].vol
outlines[i].val = priceProfile[i].price
end
SetValue(index-shift, i, outlines[i].val)
SetValue(outlines[i].index, i, outlines[i].val)
end
end
end
end
return nil
end
end
Т.к. в индикаторе необходимо при инициализации указывать количество возвращаемых линий, то написана такая конструкция в функции init:
Код
for i = 1, lines do
Settings.line[i] = {}
Settings.line[i] = {Color = RGB(185, 185, 185), Type = TYPE_LINE, Width = 2}
end
Алгоритм вводит линии по мере наполнения массива линий. Сначала их может быть 20, потом уже 100 и т.д. Для оптимизации линии выводятся только с не пустым значением.
Все бы хорошо, но заметил такую особенность (версия 7.19), если не вывести хотя бы один раз все линиий с не пустым значением, то потом они не отображаются на графике, хотя функция GetValue возвращает значение. Я долго не мог понять почему у меня линии не показываются с течением времени, а если заново инициализировать индикатор (через параметры графика), то все на месте.
Пришлось сделать такой странный костыль:
Код
if index == 1 then
tmpOutlines = {}
for i=1,lines do --необходимо вывести все линии хотя бы один раз, чтобы потом они отображались без проблем
tmpOutlines[i] = O(Size())
end
return unpack(tmpOutlines)
end
if index == 2 then
for i=1,lines do
SetValue(1,i,nil)
end
return nil
end