Сделал я как-то такую штуку ...

 

Однажды я вдруг осознал простую вещь: аппроксимация по методу наименьших квадратов по сути сводится к минимизации линейной комбинации векторов. То есть можно изготовить некую универсальную функцию-аппроксиматор. Сказано - сделано, вот заголовок функции:

//+------------------------------------------------------------------+
//  Аппроксимация методом наименьших квадратов                       |
//+------------------------------------------------------------------+
bool LSA(double& V[], int M, int N, double& A[], double& C[]) {
// Имеется N векторов размером M
// и вектор их линейной комбинации Y размером естестственно тоже M.
// На вход функции они подаются в виде матрицы V[0..M-1][0..N],
// где Y размещён в столбце N 
// На выходе мы должны получить вектор коэффициентов C размером M.
// Нам нужна также матрица A[N][N+1] для размещения коэффициентов системы уравнений

Важная деталь, все массивы V и A реально одномерные, массив А впрочем чисто рабочий, а вот массив V должен быть правильно заполнен.

Для работы ей нужна также функция для решения системы линейных уравнений. Когда я это сочинял мне была известна только одна реализация на MQL, использованный ANG3110 для полиномиалной регрессии метод Гаусса. Естественно, я пошёл по пути наименьшего сопротивления и использовал для функции именно этот алгоритм. Другими словами существуют более эффективные алгоритмы, тем более что матрица получается симметричной, но я к ним не обращался.

Как ей пользоваться:

Сначала решим, какой функцией мы будем аппроксимировать. Пусть, для примера, это будет линейная регрессия. То есть у нас будет линейная комбинация A*1 + B*X, всего два вектора, единичный и собственно аргумент.

Заведём рабочие массивы и где-нибудь в init() отведём для них память

double V[];
double A[],С[];

...

  ArrayResize(A,6);  // размер N*(N+1)
  ArrayResize(C,2);  // размер N
  ArrayResize(V,M*3);  // M*(N+1), M - не что иное как размер выборки

Осталось правильно заполнить массив V и рассчитать коэффициенты. Это можно сделать так:

    ind = 0;
    Stop = Start + M;
// Заполняем векторы
    for(pos = Start; i < Stop; i++) {
     V[ind] = 1.0;
     ind++;
     V[ind] = pos;
     ind++;
     V[ind] = Close[pos];   
     ind++;
    }
// Считаем коэффициенты
   LSA(V, M, N, A, C);

Дело сделано, линейная регрессия C[0] + C[1]*pos готова.

Первым делом нам нужно проверить алгоритм. Для этого на базе индикатора ang_PR (Din)-v1.mq4 ( ANG3110 ) с использованием LSA был написан индикатор полиномиальной регрессии и произведено сравнение результатов. Результаты визуально совпали, на этом проверка была закончена :). Индикатор LSA_PR.mq4 прилагается.

Файлы:
pr_lsa.mq4  7 kb
 

Это всё было довольно давно, а недавно я вспомнил про это и решил опять пустить изготовленный инструмент в дело.

Первая возникшая мысль была поискать в графике котировок периодичность. Может возникнуть вопрос зачем, ведь для поиска гармоник есть дискретное преобразование Фурье (ДПФ)? Но ДПФ даст только гармоники с периодом меньшим длины выборки. Мы же теперь можем попробовать примерить к графику цены гармоники с периодом большим длины выборки. Разумеется успешная примерка не будет "железным" доводом в пользу её реального существования, вопрос о степени доверия к конкретной аппроксимации нужно решать отдельно.

В прилагаемом индикаторе LSA_SinLRR.mq4 gрежде чем примерять гармонику вычитается линейный тренд. Рассчитывается он по старшему горизонту. Перебираются все возможные длины выборок в определённом диапазоне и выбирается та из них, для которой будет минимальна среднеквадратичная ошибка на out of Sample выборке (которая взята размером в 1/4 базовой выборки).

Период гармоники привязывается к длине выборки, посредством умножения её на заданный коэффициент. Если он будет равен, например, 2 - в выборке поместится полпериода гармоники, если же, например, 0.5 то два периода. Сама длина выборки определяется так же, как и для линейной регрессии, только перебор идёт внутри младшего горизонта.

Для сокращения объёма вычислений для каждого горизонта берётся свой шаг дискретизации.

Матрица векторов заполняется так

  for(i = IntShift; i < Frame; i++) {
    pos = HShift+i*Step;
    VT[ind] = MathSin(AFreq*pos);
    ind ++;
    VT[ind] = MathCos(AFreq*pos);
    ind ++;
    VT[ind] = Resid[i];   
    ind ++;
  }  //  for(i = IntShift; i < Frame; i++)

Resid - это разница между ценой и старшим трендом.

Параметры индикатора:

extern double kPer = 4.0;   // Коэффициент для определения периода
extern int LRRank = 1;      // Номер старшего горизонта, больше 3-х не ставить
extern int FShift = 120;    // Расстояние в барах, на которое производится экстраполяция в будущее
extern int HShift = 1;      // Сдвиг текущего времени индикатора в прошлое в барах, бары правее него тоже будут проэктраполированы
extern double PointFactor = 0.1;  // Масштабирование гистограммы ошибок аппроксимации, 0.1 подойдёт для минуток на пятизнаке 
extern bool PriceClose = true; // Если false, то будет считаться по HL/2


Уф, как-то я устал писать :)

Кратко суть в следующем: Эта функция не претендует на оптимальность, она просто позволяет штамповать аппроксимации как блины и тут же пробовать, прямо со сковородки :) . Соответственно индикаторы именно так и слеплены, то есть ни на что не претендуют, в смысле стиля и эффективности.

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


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


В принципе, предполагается поговорить ещё об одном индикаторе, он аппроксимирует и экстраполирует покрасивее представленных. То есть меня это настолько впечатлило, что я видимо окончательно стал на позиции кусочной детерминированности цены. Что правда облегчения не приносит, поскольку длину этих кусков определить пока не удаётся :) .

Но сил писать больше нет, могу только пару картинок дать :)

Пример аппроксимации с удачной экстраполяцией

Пример аппроксимации с экстраполяцией, "сорванной" импульсом

Файлы:
lsa_sinlr.mq4  14 kb
 
Candid:

а можете добавить кусочек кода чтобы HShift не задавался, а определялся по фактическому месту первой линии? точнее если он задан <0 - тогда будут работать оба механизма и можно будет тягать ее по графику вглубь истории и анализировать как прогноз в той точке совпал с тем что случилось после нее. интересно будет ;)

 
ForexTools:

а можете добавить кусочек кода чтобы HShift не задавался, а определялся по фактическому месту первой линии? точнее если он задан <0 - тогда будут работать оба механизма и можно будет тягать ее по графику вглубь истории и анализировать как прогноз в той точке совпал с тем что случилось после нее. интересно будет ;)

Да, это довольно удобно, даю версию. Оговорюсь, что графическим управлением я особо не увлекался, гарантии что всё будет гладко не даю.


Кстати, вроде я нигде явно не сказал, что гистограммы в правом нижнем углу показывают ошибку аппроксимации на базовой выборке и ошибку экстраполяции на out of sample. Разумно предположить что эта информация имеет значение для оценки ситуации.


P.S. Ага, забыл одну строку добавить. Индикатор заменён в 11:50

Файлы:
 

Ещё пару слов. Почему старший тренд взят именно линейный? Так или иначе реальные тренды выглядят линейными. Предположение состоит в том, что такое детрендирование человек может производить бессознательно, тогда есть надежда что и за гармоникой может стоять реальность.

В принципе поднять степень полинома не проблема, на самом деле я начинал вариантом с параболой. Делается это элементарно, в несколько добавленных и исправленных строчек, любой может попробовать это сделать сам, в качестве упражнения.

 

Добрый день. 

поясни пожалуйста рисунки. Интерисуют вертикальные линии. Правильно ли я понял,  что именно в интервале между синими линиями данные для прогноза ? что означает красная ? - момент расхождения с прогнозом ? почему красные (синии) линии прогноза имеют разрывы ?

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

 

Prival:

поясни пожалуйста рисунки. Интерисуют вертикальные линии. Правильно ли я понял, что именно в интервале между синими линиями данные для прогноза ? что означает красная ? - момент расхождения с прогнозом ? почему красные (синии) линии прогноза имеют разрывы ?


Да, собственно аппроксимация ведётся между синими линиями. Между синей и красной рассчитывается СКО экстраполяции. правее красной - просто будущее, во всяком случае для индикатора. Просто в наших силах его сместить с помощью HShift (а теперь и просто таская линию по графику) и увидеть то, чего он не видит.

Разрывы - это "отходы", рабочее окно индикатора смещается со временем, сзади остаются хвосты. Это было бы легко исправить при постоянном окне, но поскольку оно адаптируется, дешёвый способ очистки мне пока в голову не пришёл.

Даю версию с перекрашенными линиями.

Файлы:
 

чтото ваш новый код не заработал как хотелось :(

вот я позволил себе снести свои правочки. замечательно работает особенно в паре с ft.AutoRefresh

Файлы:
lsa_sinlr_1.mq4  16 kb
 

ForexTools:

вот я позволил себе снести свои правочки. замечательно работает особенно в паре с ft.AutoRefresh

Ну, я думаю, ваш вариант более помехозащищённый, но там наверное тоже надо добавить HShift--, чтобы в отсутствие действий пользователя окно двигалось со временем. Хотя, хм, может этого вы как раз и хотели избежать?
 
ну конечно! при анализе истории график никуда не должен ездить а "стоять" на том месте куда я линию поставил
 
ForexTools:
ну конечно! при анализе истории график никуда не должен ездить а "стоять" на том месте куда я линию поставил

Ну вот, а я сначала и сделал неподвижный вариант, а потом его заменил :). Меня-то в первую очередь интересовала как раз динамика перерисовки. Собственно легко добавить параметр типа

if (ModeMoving) HShift--;
Но слишком хорошо тоже нехорошо, пусть вылежится вариант.