Блог им. amandra

Реализация медианного фильтра на QLUA

    • 05 апреля 2014, 00:34
    • |
    • amandra
  • Еще

Введение

Мне всегда не нравился QUIK тем, что в нем нельзя было писать собственные индикаторы, если быть точнее, можно, но результаты реализация индикатора вряд ли бы отвечала заявленным требованиям, так как скрипты на qpile запускаются по таймеру, а не по событию. Я не считаю язык qpile ущербным или не пригодным для реализации, по мнению некоторых робостроителей, сложных алгоритмов. Всякий алгоритм можно реализовать на любом языке программирования, вопрос только в уровне затрат на реализацию, наличие или отсутствие нужных функций и процедур.
С появлением в QUIK QLUA все заиграло для меня яркими красками.
В заметке хочу рассмотреть вопросы реализации медианного фильтра на QLUA.

Постановка задачи

Медианный фильтр используются для уменьшения уровня шума. Алгоритм рассчета фильтра достаточно прост и доступен широким массам. Одним из пунктов рассчета медианы — является сортировка подмассива. Вот на этом и остановимся подробнее.

 Сортировка массива

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

  Встроенный алгоритм сортировки

Рассмотрим первый вариант расчета медианного фильтра:
Settings=
{
  Name = "_Median",
  period = 12,
  line =
  { {
   Name = «Median»,
   Color = RGB(255, 0, 0),
   Type = TYPE_LINE, Width = 2 } } }
function Init()
 return 1
end
function OnCalculate(index)
  if index < Settings.period then
    return nil
  else
    local ind = 1
    local cash = {}
    for i = index-Settings.period+1, index do
         cash[ind] = C(i)
         ind = ind + 1
   end
   table.sort(cash)
   return cash[math.floor(Settings.period/2)]
 end
end

 
Здесь сортировка выполняется стандартной процедурой table.sort , один из её недостатков — не устойчивая сортировка (меняет местами элементы массива с одинаковыми значениями)


Сортировка «пузырьком»

Изменим предыдущий пример, заменив table.sort(cash) на следующий набор команд:
local tmp = 0
for i = 1, Settings.period-1 do
  for j = 1, Settings.period-1 do
    if cash[j] > cash[j+1] then
        tmp = cash[j]
        cash[j] = cash[j+1]
        cash[j+1] = tmp
     end
 end
end
 
Данный метод не особо «шустрый», но ничего страшного, нам он нужен для сравнения


Быстрая сортировка

Один из популярных алгоритмов сортировки массивов, приведем весь код индикатора:
Settings=
{
   Name = "_Median2",
   period = 12,
   line =
      { {
      Name = «Median2»,
      Color = RGB(255, 0, 0),
      Type = TYPE_LINE, Width = 2 } } }
function Init()
   return 1
end
function asort(ar1, low, high)
   local wsp = 0
   local i=low
   local j=high
   local m=ar1[math.floor((i+j) / 2)] — Взятие среднего опорного элемента
   repeat
      while ar1[i]<m do
         i = i + 1 — изменить порядок сортировки можно
      end
      while ar1[j]>m do
         j = j — 1 — поменяв >< в этих двух строках
      end
      if i<=j then
         wsp=ar1[i]
         ar1[i]=ar1[j]
         ar1[j]=wsp
         i = i + 1
         j = j — 1
      end;
   until i>j
   if low<j then
      ar1 = asort(ar1, low, j)
   end
   if i<high then
      ar1 = asort(ar1, i, high)
   end
   return ar1
end

function qSort(ar)
   return asort(ar, 1, Settings.period)
end
function OnCalculate(index)
   if index < Settings.period then
      return nil
   else
      local ind = 1
      local cash = {}
      for i = index-Settings.period+1, index do
      cash[ind] = C(i)
      ind = ind + 1
   end
   cash = qSort(cash)
  return cash[math.floor(Settings.period/2)]
end
end
 Здесь метод быстрой сортировки реализован на основе рекурсии

Авторский метод сортировки

Не скромно, но похожей реализации медианного фильтра не встречал. У всех рассмотренных методов сортировки один общий недостаток: при появлении нового значения цены все алгоритмы берут последние N отсчетов цены (N — параметр фильтрации) и сортируют их, то есть на каждом шаге выполняется сортировка массива данных, при этом на предыдущем отсчете N-1 значений уже были отсортированы.
Суть предлагаемого метода:
1) при появлении нового тика сразу сортируем массив и запоминаем его
2) при появлении следующего тика
  2.1) удалим N+1 отсчет (считая новый тик отсчетом номер один) в отсортированном массиве
  2.2) добавим к отсортированному массиву новый тик
  2.3) сортируем получившийся массив, для сортировки достаточно осуществить один проход, например, {1, 2, 5, 6, 4}, здесь {1, 2, 5, 6} часть массива, которая была отсортирована на предыдущем тике, добавили новый элемент {6} — нужен один проход, чтобы отсортировать исходный массив.
Код:

Settings=
{
    Name = "_Median3",
    period = 12,
    line =
        { {
        Name = «Median3»,
        Color = RGB(255, 0, 0),
        Type = TYPE_LINE, Width = 2 } } }
function Init()
    m_cach = {}
    m_index = {}
    last_ind = 0
    return 1
end
function qSort1(arr,arr_ind)
    if #arr > 1 then
        local ind = #arr
        local tmp = 0
        local tmp_i = 0
        local cycle = false
        repeat
        if (ind > 1) then
            if (arr[ind] < arr[ind-1]) then
                tmp = arr[ind-1]
                tmp_i = arr_ind[ind-1]
                arr[ind-1] = arr[ind]
                arr_ind[ind-1] = arr_ind[ind]
                arr[ind] = tmp
                arr_ind[ind] = tmp_i
                ind = ind — 1
            else
                cycle = true
            end
        else
            cycle = true
        end
    until cycle
    end
    return arr, arr_ind
end
function min_arr (_arr)
    local min_index = _arr[1]
    local min_i = 1
    for i = 2,#_arr do
        if min_index > _arr[i] then
            min_index = _arr[i]
            min_i = i
        end
    end
    return min_i
end
function find_index (_arr,index1)
    local ind = 1
    while _arr[ind]~=index1 do
        ind = ind +1
    end
    return ind
end
function OnCalculate(index)
    if index <= Settings.period then
        if index == 1 then
            m_cach = {}
            m_index = {}
            last_ind = 0
        end
        table.insert(m_cach,C(index))
        table.insert(m_index,index)
        m_cach, m_index = qSort1(m_cach,m_index)
        last_ind = index
        return nil
    else
        if (last_ind == index) then
            local ind = find_index(m_index,index)
            table.remove(m_cach,ind)
            table.remove(m_index,ind)
        else
            local min_index = min_arr(m_index)
            table.remove(m_cach,min_index)
            table.remove(m_index,min_index)
        end
        table.insert(m_cach,C(index))
        table.insert(m_index,index)
        m_cach,m_index = qSort1(m_cach,m_index)
        last_ind = index
        return m_cach[math.floor(Settings.period/2)]
    end
end


Результаты

 Для сравнения медианных фильтров, использующих разные алгоритмы сортировки используем тиковый график цен на акции Сбербанка на 04.03.2014  с окном фильтрации в 200 тиков:
  • встроенный алгоритм сортировки — 35 сек
  • сортировка пузырьком — 485 сек
  • быстрая сортировка — 39 сек
  • авторская сортировка — 7 сек
 
Медиана с сортировкой пузырьком показала худшее время — все очевидно, время сортировки пропорционально квадрату n (n — размерность массива), быстрая сортировка — хорошо, но реализован на рекурсии, что кушает ресурсы + многократное копирование массива в памяти… Преимущество авторской сортировки очевидны — сортировка заточена под предметную область.

Буду рад, если кому-то мои результаты будут полезны.

Медианный фильтр с окном 12
 
 
 
 
 
 
Данная публикация является личным мнением автора. Мнение владельца сайта может не совпадать с мнением автора.
  • Ключевые слова:
  • qlua
454 | ★2
6 комментариев
реализую еще какой-нибудь индикатор на qlua и выложу результаты
avatar
amandra, а как насчет перевести индикатор с С# на qlua?
У меня что-то никак с индикаторами не ладится.
avatar
Vkt, Нужно помочь разобраться или перевести? пиши на почту amandra@mail.ru
avatar
amandra, письмо отправил, спасибо!
avatar
«2.2) добавим к отсортированному массиву новый тик
2.3) сортируем получившийся массив»
Вы этот тик в конец добавляете, а можно просто вставить новый тик в нужное место в массив. Учитывая, что массив сортирован, нет необходимости его весь перебирать — место для вставки можно найти методом бисекции.
avatar
Юрий Ч., так он и не сортируется в привычном понимании, за один не полный проход цикла while определяется место, куда нужно вставить последний тик
avatar

Читайте на SMART-LAB:
Фото
GBP/USD: Покупатели фунта занимают круговую оборону
Британская валюта вплотную приблизилась к мощному форпосту — области поддержки в диапазоне 1.3140–1.3160. Судя по всему, покупатели окопались...
Фото
Курс закончился. Что дальше❓
На курсе всё обычно выглядит проще. Есть преподаватель, разборы, примеры сделок. Вам всё разжёвывают :) Потом курс заканчивается, и трейдер...
Фото
Денис Баранов о вызовах и возможностях индустрии кибербеза
Во время главной пленарной сессии форума «Цифровые решения» представители ведущих ИТ-компаний страны вместе с Михаилом Мишустиным и...
Фото
Хэдхантер. Рынок труда в сентябре: hh.индекс снизился до 8,5 - становится лучше?
Вышла статистика рынка труда за сентябрь 2026 года (за август можно почитать здесь ), которую Хедхантер публикует ежемесячно, что же там...

теги блога amandra

....все тэги



UPDONW
Новый дизайн