Т.к. в индикаторе необходимо при инициализации указывать количество возвращаемых линий, то написана такая конструкция в функции 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
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
Если было выведено при инициализации 100 линий (т.е. когда происходит первый вызов OnCalculate), то больше не показывает индикатор. Далее линии вроде имеют значение, но не видны.
Приходится выводить всегда столько линий, сколько определено, накладывая одну на другую, чтобы они слились в одну для пользователя. По мере поступления новых линий они будут тогда уже распределятся по уровням цены.
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 возвращает значение.
Как написал выше, пришлось выводить ненулевые значения для каждой линии, накладывая одну на другую. Так работает.
Nikolay, В том то и дело, что Вы привели не полную версию кода, в связи с чем о многом приходится догадываться. Выводить значения всех линий вовсе не обязательно. ниже пример который явно это показывает:
Скрытый текст
Код
Settings = {
Name = "*TEST",
line = {{Name = "line 1", Color = RGB(255, 0, 0)},
{Name = "line 2", Color = RGB(0, 255, 0)}
}
}
function Init()
return 2
end
function OnCalculate(Index)
if (Index >= 10) and (Index < 20) then
return 4320
elseif (Index >= 20) then
return 4320, 4325
end
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
А вот сделав такие изменения в цикл вывода строк, все работает.
Код
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
маска-пленка с лифтинг-эффектом Новейшая разработка - маска с коллоидным золотом для безоперационной подтяжки кожи лица и шеи. Реанимирует выработку коллагена, разглаживает морщины и подтягивает кожу, а также чистит поры и удаляет черные точки. 100% натуральный состав.
Ошибка, описанная в данном инциденте, связано с некорректной работой ПО терминала QUIK и будет исправлена в одной из очередных версий программы. Приносим извинения за причиненные неудобства.
Описанная в данном инциденте проблема была устранена в версии 7.23.0 терминала QUIK. Данная версия была разослана всем брокерам системы QUIK в рамках стандартной процедуры обновления версии 23.11.2018. По поводу получения обновления рекомендуем вам обратиться к своему брокеру.