Не пойму, в чем проблема.. Выдает ошибку attempt to compare number with nil когда индикатор растет или падает и не обнаруживает пиков\впадин При успешном обнаружении пика\впадины выдает текстовое сообщение как положено Код:
require("w32") Settings = { Tiker = "\210\232\234\229\240", Name = "Kalman SndMsg", Mode = 6, KF = 50, line = { { Name = "Kalman", Color = RGB(0, 255, 0), Type = TYPE_LINE, Width = 2 } } } function Init() return 1 end i = 0 value = 0 count = 0 function OnCalculate(_ARG_0_) if _ARG_0_ == 1 then kalman ={} Distance = 0 Error = 0 value = dValue(1, Settings.Mode) Velocity = 0 i = 0 count = Size() end if _ARG_0_ > 1 then if _ARG_0_ > i then i = _ARG_0_ Distance = dValue(i, Settings.Mode) - value Error = value + Distance * math.sqrt(Settings.KF / 1000) Velocity = Velocity + Distance * (Settings.KF / 1000) value = Error + Velocity kalman[i] = value
if kalman[i-1] < kalman[i-2] and kalman[i-2] >= kalman[i-3] then if _ARG_0_ >= count then message(Settings.Tiker .. ": DOWN trend", 1) w32.MessageBeep(w32.MB_OK) count = _ARG_0_ end end
if kalman[i-1] > kalman[i-2] and kalman[i-2] <= kalman[i-3] then if _ARG_0_ >= count then message(Settings.Tiker .. ": UP trend", 1) w32.MessageBeep(w32.MB_OK) count = _ARG_0_ end end
end end return kalman[i] end
function dValue(_ARG_0_, _ARG_1_) if (_ARG_1_ or 0) == 1 then return O(_ARG_0_) elseif (_ARG_1_ or 0) == 2 then return H(_ARG_0_) elseif (_ARG_1_ or 0) == 3 then return L(_ARG_0_) elseif (_ARG_1_ or 0) == 0 then return C(_ARG_0_) elseif (_ARG_1_ or 0) == 4 then return (H(_ARG_0_) + L(_ARG_0_)) / 2 elseif (_ARG_1_ or 0) == 5 then return (H(_ARG_0_) + L(_ARG_0_) + C(_ARG_0_)) / 3 elseif (_ARG_1_ or 0) == 6 then return (H(_ARG_0_) + L(_ARG_0_) + 2 * C(_ARG_0_)) / 4 else return C(_ARG_0_) end end
Как минимум, уже ошибка в том что Вы берете значения kalman[i-1], kalman[i-2], kalman[i-3] тогда когда их может не быть. раз i это номер свечки, следует добавить условие if i>4 then и только после этого продолжать код.
Странно именно то, что когда пик или впадина образуется, то сообщение выводится корректно и ошибки нет. Ошибка возникает только когда условие if kalman[i-1] < kalman[i-2] and kalman[i-2] >= kalman[i-3] then или if kalman[i-1] > kalman[i-2] and kalman[i-2] <= kalman[i-3] then не выполняется.
torque написал: Но ведь выше в начале цикла есть присвоение переменной i номера текущего бара.
ну да, а если будет когда появится второй бар?
_ARG_0_ = 2 ...
if _ARG_0_ > 1 then if _ARG_0_ > i then i = _ARG_0_ kalman[i] = value if kalman[i-1] < kalman[i-2] and kalman[i-2] >= kalman[i-3] then
чему будет равно kalman[i-2]? а kalman[i-3]?
Цитата
torque написал: По поводу >=4 попробовал, ничего не изменилось
Надо не "пробовать", а исправлять и если не помогло смотреть дальше. Что попадает в kalman и что в i, перед возникновением ошибки. И действительно ли на предыдущих итерациях заполнялись нужные параметры kalman[i-1], kalman[i-2], kalman[i-3]
Добавьте логирование этих данных, например через message.
Roman Koledin написал: простите таже проблема можете помочь её исправить вод код
Settings = { Name = "*Kijun-sen", line = {{ Name = "Kijun-sen", Color = RGB(0, 0, 255), Type = TYPE_LINE, Width = 2 }} }
function Init() return 1 end
local kijun_period = 26 -- Период Kijun-sen (можно изменить)
function OnCalculate(index) -- Проверяем, достаточно ли данных для расчета if index < kijun_period - 1 then return nil end
-- Инициализация переменных для экстремумов local max_high = H(index - kijun_period + 1) local min_low = L(index - kijun_period + 1)
-- Поиск максимума и минимума за период for i = index - kijun_period + 2, index do local current_high = H(i) local current_low = L(i)
if current_high > max_high then max_high = current_high end
if current_low < min_low then min_low = current_low end end
-- Расчет Kijun-sen return (max_high + min_low) / 2 end
попробуйте так:
Код
Settings = {
Name = "*Kijun-sen",
line = {{
Name = "Kijun-sen",
Color = RGB(0, 0, 255),
Type = TYPE_LINE,
Width = 2,
kijun_period = 26 -- Период Kijun-sen (можно изменить)
}}
}
function Init() return 1 end
function OnCalculate(i)
if i==1 then
max_high = H(1)
min_low = L(1)
else
if i%Settings.kijun_period==0 then max_high = H(i); min_low = L(i) end
current_high = H(i)
current_low = L(i)
-- Поиск максимума и минимума за период
if current_high > max_high then max_high = current_high end
if current_low < min_low then min_low = current_low end
-- Расчет Kijun-sen
end
return (max_high + min_low) / 2
end
nikolz написал: if i%Settings.kijun_period==0 then max_high = H(i); min_low = L(i) end
ошибка, так как Settings.kijun_period = nil. 2. Вместо скользящей вами предлагается "прыгающая" . Начальные значения каждого периода берутся в качестве экстремумов. Но может быть вы предлагаете свой прыгающий индикатор? ----------- Ниже выложен код реализации индикатора Kijun-sen приблизительно в 3,5 раза более эффективный по времени выполнения, чем то, что выложил Roman Koledin:
Код
Settings = {
Name = "*Kijun-sen_opt",
kijun_period = 26, -- Период Kijun-sen (можно изменить)
line = {{
Name = "Kijun-sen_opt",
Color = RGB(0, 0, 200),
Type = TYPE_LINE,
Width = 2
}}
}
function Init() return 1 end
local kijun_period = Settings.kijun_period
function OnChangeSettings()
kijun_period = Settings.kijun_period
end
---
local TT
local N_C = 500
function OnCalculate(index)
-- -- Вычисление времени обработки свеч --
-- if index == kijun_period + 1 then
-- TT = os.clock()
-- end
-- if index == N_C + kijun_period then
-- message('Kijun-sen. Время обработки ' .. N_C .. ' свечей (млс.) = ' .. (os.clock() - TT) * 1000)
-- end
if index==1 then
max_high = H(1)
min_low = L(1)
else
current_high = H(index)
current_low = L(index)
--------
if current_high > max_high then -- Пришел максимальный
max_high = current_high
else
if index > kijun_period then
-- Ушел из скользящего периода максимальный --
if H(index - kijun_period) >= max_high then -- поиск максимального
max_high = current_high
for j = index - kijun_period + 1, index - 1 do
current_high = H(j)
if current_high > max_high then max_high = current_high end
end
end
end
end
---
if current_low < min_low then -- Пришел минимальный
min_low = current_low
else
if index > kijun_period then
-- Ушел из скользящего периода минимальный --
if L(index - kijun_period) <= min_low then -- поиск минимального
min_low = current_low
for j = index - kijun_period + 1, index - 1 do
current_low = L(j)
if current_low < min_low then min_low = current_low end
end
end
end
end
end
return (max_high + min_low) / 2
end
nikolz написал: if i%Settings.kijun_period==0 then max_high = H(i); min_low = L(i) end
ошибка, так как Settings.kijun_period = nil. 2. Вместо скользящей вами предлагается "прыгающая" :: . Начальные значения каждого периода берутся в качестве экстремумов. Но может быть вы предлагаете свой прыгающий индикатор? ----------- Ниже выложен код реализации индикатора Kijun-sen приблизительно в 3,5 раза более эффективный по времени выполнения, чем то, что выложил Roman Koledin:
Код
Settings = {
Name = "*Kijun-sen_opt" ,
kijun_period = 26 , -- Период Kijun-sen (можно изменить)
line = {{
Name = "Kijun-sen_opt" ,
Color = RGB ( 0 , 0 , 200 ),
Type = TYPE_LINE,
Width = 2
}}
}
function Init () return 1 end
local kijun_period = Settings.kijun_period
function OnChangeSettings ()
kijun_period = Settings.kijun_period
end
---
local TT
local N_C = 500
function OnCalculate (index)
-- -- Вычисление времени обработки свеч --
-- if index == kijun_period + 1 then
-- TT = os.clock()
-- end
-- if index == N_C + kijun_period then
-- message('Kijun-sen. Время обработки ' .. N_C .. ' свечей (млс.) = ' .. (os.clock() - TT) * 1000)
-- end
if index = = 1 then
max_high = H( 1 )
min_low = L( 1 )
else
current_high = H(index)
current_low = L(index)
--------
if current_high > max_high then -- Пришел максимальный
max_high = current_high
else
if index > kijun_period then
-- Ушел из скользящего периода максимальный --
if H(index - kijun_period) > = max_high then -- поиск максимального
max_high = current_high
for j = index - kijun_period + 1 , index - 1 do
current_high = H(j)
if current_high > max_high then max_high = current_high end
end
end
end
end
---
if current_low < min_low then -- Пришел минимальный
min_low = current_low
else
if index > kijun_period then
-- Ушел из скользящего периода минимальный --
if L(index - kijun_period) < = min_low then -- поиск минимального
min_low = current_low
for j = index - kijun_period + 1 , index - 1 do
current_low = L(j)
if current_low < min_low then min_low = current_low end
end
end
end
end
end
return (max_high + min_low) / 2
end
нет не проверял. Да именно прыгающий. Я же написал - попробуйте это. Как другой вариант, который работает быстрее.
Settings = {
Name = "*Kijun-sen",
kijun_period = 26, -- Период Kijun-sen (можно изменить)
line = {{
Name = "Kijun-sen",
Color = RGB(0, 0, 255),
Type = TYPE_LINE,
Width = 2,
}}
}
function Init() return 1 end
function OnCalculate(i)
high = H(i) low = L(i)
if high and low then
if i==1 then
max = H(1) min = L(1)
else
if i%Settings.kijun_period==0 then max = high; min =low end
if high > max then max = high end
if low < min then min = low end
return (max + min)/2;
end
end
end
Settings = {
Name = "*Kijun-sen",
kijun_period = 26, -- Период Kijun-sen (можно изменить)
line = {{
Name = "Kijun-sen",
Color = RGB(0,255,255),
Type = TYPE_LINE,
Width = 2,
}}
}
function Init() return 1 end
function OnCalculate(i)
if H(i) and L(i) then
local j=i-Settings.kijun_period; if j<1 then j=1; end
max=H(i); min=L(i);
while i>j do
local H,L=H(j),L(j)
if H and H>max then max = H end
if L and L<min then min =L end
j=j+1 end
return (max + min)/2;
end
end
Если уж уделяете столько времени этому вопросу, то, наверно, стоит сделать устойчивый вариант. Ошибка же была о сравнении nil с числом. А во всех предлагаемых реализациях нет проверок на наличие бара, полученных значений перед арифметикой, перехвата ошибок, чтобы Квик не умирал от потока сообщений об ошибках на графиках с 60 тыс барами и т.д. Даже если отбросить саму реализацию, то это все не рабочие решения.
Оптимизированный вариант обычный, считает тоже самое, но быстрее:
Код
Settings = {
Name = "*Kijun-sen",
kijun_period = 15,
line = {{
Name = "Kijun-sen",
Color = RGB(255,255,255),
Type = TYPE_LINE,
Width = 2,
}}
}
function Init() return 1 end
function OnCalculate(i)
local Hi,Li=H(i),L(i);
if Hi and Li then
if max==nil then max=Hi end
if min==nil then min=Li end
if Hi>max or min>Li then
local j=i-Settings.kijun_period; if j<1 then j=1; end
max=Hi; min=Li;
while i>j do
local Hj,Lj=H(j),L(j)
if Hj and Hj>max then max = Hj end
if Lj and Lj<min then min =Lj end
j=j+1 end
end
x=(max + min)/2;
end
return x;
end
пардон, последний вариант считает иначе, поэтому остается этот:
Код
Settings = {
Name = "*Kijun-sen",
kijun_period = 15,
line = {{
Name = "Kijun-sen",
Color = RGB(255,255,255),
Type = TYPE_LINE,
Width = 2,
}}
}
function Init() return 1 end
function OnCalculate(i)
local Hi,Li=H(i),L(i);
if Hi and Li then
local j=i-Settings.kijun_period; if j<1 then j=1; end
max=Hi; min=Li;
while i>j do
Hi,Li=H(j),L(j)
if Hi and Hi>max then max = Hi end
if Li and Li<min then min =Li end
j=j+1 end
x=(max + min)/2;
end
return x
end
Оптимизировал алгоритм вычислений, чтобы считал быстрее. Выкладываю для всех желающих:
Код
Settings = {Name = "*Kijun-sen",kijun_period = 6,}
function OnCalculate(i)
Hi=H(i) or H1; Li=L(i) or L1; x1=x;
if i1>i then
ma=Hi; mi=Li; jma=i; jmi=i;
end
if Hi and Li then
local j=i-Settings.kijun_period; if j<1 then j=1; end
if j>jma or j>jmi then
ma=Hi; mi=Li; jma=i; jmi=i;
while i>j do
Hi,Li=H(j),L(j)
if Hi and Hi>ma then ma = Hi jma=j; end
if Li and Li<mi then mi =Li jmi=j;end
j=j+1
end
else
if Hi>ma then ma=Hi; jma=i; end
if mi>Li then mi=Li; jmi=i; end
end
x=(ma + mi)/2; H1,L1,i1=Hi,Li,i;
end
return x1
end
function OnChangeSettings()
i1=0;jma=0; jmi=0; H1=0; L1=0;ma=0;mi=0;
end
function Init()
OnChangeSettings()
Settings.line = {{ Name=Settings.Name, Color=RGB(32,255,128), Type=TYPE_LINE, Width = 2,}}
return #Settings.line end
Можете попробовать использовать ранее выложенный мной, но слегка модифицированный код индикатора Kijun-sen, который работает в ~20 раз быстрее, чем чем то, что выложил Roman Koledin:
Код
Settings = {
Name = "*Kijun-sen",
kijun_period = 26, -- Период Kijun-sen (можно изменить)
line = {{
Name = "Kijun-sen",
Color = RGB(0, 0, 200),
Type = TYPE_LINE,
Width = 2
}}
}
local kijun_period = Settings.kijun_period
local QueueH, QueueL = {}, {}
local const_L = 999999999
function Init()
QueueH, QueueL = {}, {}
for i = 0, kijun_period - 1 do
QueueH[i] = 0; QueueL[i] = const_L
end
return 1
end
function OnChangeSettings()
kijun_period = Settings.kijun_period
QueueH, QueueL = {}, {}
for i = 0, kijun_period - 1 do
QueueH[i] = 0; QueueL[i] = const_L
end
end
---
local TT
local N_C = 500
local max_high, min_low
function OnCalculate(index)
-- -- Вычисление времени обработки свеч (500 свечкй за ~4 млс. в 5 раз быстрее моего же *Kijun-sen_opt)--
-- if index == kijun_period + 1 then
-- TT = os.clock()
-- end
-- if index == N_C + kijun_period then
-- message('Kijun-sen. Время обработки ' .. N_C .. ' свечей (млс.) = ' .. (os.clock() - TT) * 1000)
-- end
-- -----------------------------------
if index == 1 then
max_high = H(1) or 0
min_low = L(1) or const_L
QueueH[1], QueueL[1] = max_high, min_low
else
current_high = H(index) or 0
current_low = L(index) or const_L
local ind = index % kijun_period -- место в векторе --
local QueueH_end
local QueueL_end
if index > kijun_period then -- сохранение уходящих значений --
QueueH_end = QueueH[ind]
QueueL_end = QueueL[ind]
end
QueueH[ind], QueueL[ind] = current_high, current_low -- сохранение текущих в векторе --
-- --------
if current_high >= max_high then -- Пришел максимальный --
max_high = current_high
else
if index > kijun_period then -- начальный период завершен --
-- Максимум "ушел" из скользящего периода --
if QueueH_end >= max_high then -- поиск максимального в векторе
max_high = current_high
for j = 0, kijun_period - 1 do
current_high = QueueH[j]
if current_high > max_high then max_high = current_high end
end
end
end
end
---
if current_low <= min_low then -- Пришел минимальный
min_low = current_low
else
if index > kijun_period then
-- Ушел из скользящего периода минимальный --
if QueueL_end <= min_low then -- поиск минимального
min_low = current_low
for j = 0, kijun_period - 1 do
current_low = QueueL[j]
if current_low < min_low then min_low = current_low end
end
end
end
end
end
return (max_high + min_low) / 2
end
«При 300% прибыли нет такого преступления, на которое не рискнул бы капитал» К. Маркс.
И опять двойственность, или почему побитные операции 2^n предпочтительнее!
В Lua оператор % — это остаток от деления (modulus). Он возвращает остаток от деления одного числа на другое. И остаток от деления на степень двойки можно сделать с помощью побитовой маски. Если нам нужен остаток x % 2^n, то это равносильно тому, что мы обнуляем все биты выше n-го. Для этого используем побитовое И (bit.band): bit.band — чистая побитовая операция на целых, выполняется через С-библиотеку, работает на порядок быстрее, особенно если много итераций.
Этот метод работает только для делителей, являющихся степенью двойки 2^n (2, 4, 8, 16, …).
* Если делитель — степень двойки, используем bit.band(x, 2^n - 1). Это и быстрее, и чище в контексте масок. * Если делитель любой, лучше оставить обычный % или написать функцию на математику.
В своей практике, вспомнив высказывание К. Маркса, тоже не удержался, и перевел кольцевой буфер на bit.band. Кольцевой буфер как раз классический случай, где % заменяется на bit.band. И именно здесь ускорение становится реально заметным, потому что операция повторяется на каждом обращении к буферу. Простая замена оператора % (modulus) на bit.band приводит к приросту в скорости от 2 - 10 раз! Как здесь не вспомнить К. Маркса.
VPM написал: bit.band — чистая побитовая операция на целых, выполняется через С-библиотеку, работает на порядок быстрее, особенно если много итераций.
Вы это проверяли? bit.band это вызов функции, в теле которой выполняется битовая операция "И". Сам вызов до начала выполнения тела функции это довольно тяжелая операция, выполняющаяся дольше любой арифметической операции. В Lua 5.4 соотношение длительности выполнения приблизительно следующее: 1) % = 1 2) bit.band = 4,49 3) & (битовая операция "И" вместо bit.band , начиная c Lua 5.3) = 0,63
TGB, Да вы целиком правы, спасибо за уточнение! Производительность в Lua 5.4.1 реально зависит от использования встроенных операторов, и побитовая операция & становится явным лидером, когда дело операций с числами, являющимися степенями двойки.
--- Функциональный стиль: кольцевой буфер
-- @param size_power_of_two степень двойки (например, 3 → буфер на 8 элементов)
function CreateRingBuffer(size_power_of_two)
local size = 1 << size_power_of_two -- 2^n
local mask = size - 1
local data = {}
for i = 1, size do
data[i] = 0
end
local head = -1
local count = 0
local buffer = {}
--- Добавление элемента
function buffer:add(value)
head = (head + 1) & mask
data[head + 1] = value
count = math.min(count + 1, size)
end
--- Получение элемента (1 - последний)
function buffer:get(index)
if index < 1 or index > count then return nil end
local pos = (head - index + 1 + size) & mask
return data[pos + 1]
end
--- Последний элемент
function buffer:last()
if count == 0 then return nil end
return data[head + 1]
end
--- Первый элемент (самый старый)
function buffer:first()
if count == 0 then return nil end
local pos = (head - count + 1 + size) & mask
return data[pos + 1]
end
--- Количество элементов
function buffer:length()
return count
end
--- Очистка буфера
function buffer:clear()
head = -1
count = 0
end
--- Итератор для цикла for (новые → старые)
function buffer:iter()
local i = 0
return function()
i = i + 1
return buffer:get(i)
end
end
--- Преобразование в таблицу (новые сначала)
function buffer:toTable()
local t = {}
for i = 1, count do
t[i] = buffer:get(i)
end
return t
end
--- Проверка на заполненность
function buffer:isFull()
return count == size
end
return buffer
end