I made one of these things once ...

 

One day I suddenly realised a simple thing: least-squares approximation is essentially about minimising a linear combination of vectors. That is, some kind of universal approximator function can be manufactured. It's a done deal, so here is the title of the function:

//+------------------------------------------------------------------+
//  Аппроксимация методом наименьших квадратов                       |
//+------------------------------------------------------------------+
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] для размещения коэффициентов системы уравнений

Important detail, all arrays V and A are really one-dimensional, array A is purely working though, but array V needs to be filled correctly.

It also needs a function to solve a system of linear equations. When I was making this up, I only knew one implementation in MQL, the Gaussian method used by ANG3110 for polynomial regression. Naturally, I took the path of least resistance and used this particular algorithm for the function. In other words, there are more efficient algorithms, especially since the matrix is symmetric, but I didn't resort to them.

How to use it:

First we decide which function we are going to approximate. Let it be a linear regression, for example. That is, we will have a linear combination of A*1 + B*X, just two vectors, a unit vector and the argument itself.

Let's create work arrays and allocate memory for them somewhere in init()

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

...

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

All that remains is to correctly fill in the V array and calculate the coefficients. This can be done as follows:

    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);

It is done, the linear regression C[0] + C[1]*pos is ready.

The first thing we need to do is to check the algorithm. For this purpose, based on the ang_PR (Din)-v1.mq4 indicator ( ANG3110 ) we wrote a polynomial regression indicator using LSA and compared the results. The results coincided visually, that was the end of the test :). The LSA_PR.mq4 indicator is attached.

Files:
pr_lsa.mq4  7 kb
 

This was all quite a long time ago, and recently I remembered it and decided to put the tool I made into action again.

My first thought was to look for periodicity in the quotation graph. The question may arise why, because there is a discrete Fourier transform (DFT) for searching harmonics. But FFT will only give harmonics with a period shorter than the sample length. We can now try to fit harmonics with the period longer than the sample length to the price chart. Of course, the successful fitting will not be "ironclad" argument in favor of its real existence, the question of the degree of confidence in a particular approximation should be decided separately.

In the attached indicator LSA_SinLRR.mq4 the linear trend is calculated before trying on the harmonic. It is calculated using the upper horizon. All possible sample lengths in a certain range are tried out and the one that has the minimum RMS error on the out of sample (which is taken as 1/4 of the base sample size) is selected.

The harmonic period is related to the length of the sample, by multiplying it by a given factor. If it equals 2, for example, then the sample will contain half a period of the harmonic, and if it equals 0.5, then two periods. The sample length itself is determined in the same way as for linear regression, but the search is performed within the low-order horizon.

To reduce the amount of calculations, a different sampling step is taken for each horizon.

The vector matrix is filled in as follows

  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 is the difference between the price and the older trend.

Indicator parameters:

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


Ugh, somehow I got tired to write :)

Briefly, the gist is as follows: This function doesn't pretend to be optimal, it simply allows you to make approximations as pancakes and immediately try them, right out of the pan :) . Accordingly, the indicators are made in such a way, that is, they make no pretensions to anything, in terms of style and efficiency.

This case works not very quickly, so the history is not calculated. It means that it's better to study in visualizer. But it gets boring quickly :). But it cannot be ruled out that someone patient can find a way to benefit from it :).


In general, I am aware that I could not make a coherent description, if someone is interested, he can probably count on explanations.


Basically, we are supposed to talk about another indicator, it approximates and extrapolates more beautifully than the presented ones. That is, I am so impressed by it, that I have apparently finally become in the position of piecewise price determinism. But that doesn't make it any easier, because I haven't yet managed to determine the length of those pieces :) .

But I don't have energy to write anymore, I can only give a couple of pictures :)

Example of approximation with successful extrapolation

Example of approximation with extrapolation "thwarted" by an impulse

Files:
lsa_sinlr.mq4  14 kb
 
Candid:

Could you add a piece of code, that HShift is not set, but it is determined by the actual position of the first line? more precisely if it is set <0 - then both mechanisms will work and it will be possible to pull it on the chart deep into the history and analyse how the forecast at that point coincided with what happened after it. it will be interesting ;)

 
ForexTools:

Could you add a little piece of code, that HShift is not set, but is determined by the actual position of the first line? More precisely, if it's set <0 - then both mechanisms will work and you can drag it on a chart deep into the history and analyze how the forecast at that point coincided with what happened after it. it will be interesting ;)

Yes, it's pretty handy, I'll give you a version. I should point out that I'm not particularly keen on graphical control, so there's no guarantee that everything will be smooth.


By the way, I don't think I explicitly said anywhere that the histograms in the bottom right corner show the approximation error on the base sample and the extrapolation error on the out of sample. It is reasonable to assume that this information is relevant to assess the situation.


P.S. Yeah, forgot one line to add. Indicator replaced at 11:50

Files:
 

A few more words. Why exactly a linear trend is taken? Anyway, real trends look linear. The assumption is that such detrending can be done unconsciously, then there is hope that there can be reality behind the harmonics as well.

In principle, raising the degree of a polynomial is not a problem, in fact I started with the parabola option. This is done elementary, in a few added and corrected lines, anyone can try it themselves, as an exercise.

 

Good afternoon.

Please explain the drawings. I am interested in the vertical lines. did i understand correctly that it is in the interval between the blue lines the data for the forecast ? what does the red one mean ? - the moment of divergence with the forecast ? why the red (blue) forecast lines have gaps ?

i haven't looked at the code because your level of MQL programming is too low for me, it's like a dream to get closer to that level ?

 

Prival:

please explain the drawings. I am interested in the vertical lines. Did I understand correctly that it is in the interval between the blue lines the data for the forecast ? what does the red one mean ? - why the red (blue) forecast lines have gaps ?


Yes, actually the approximation is done between the blue lines. Between the blue and the red one the RMS of the extrapolation is calculated. It's just in our power to shift it with HShift (and now just dragging the line along the chart) and see what it doesn't see.

The gaps are "waste", the working window of the indicator shifts over time, tails are left behind. This would be easy to fix with a permanent window, but as it adapts, a cheap way to clean it up hasn't occurred to me yet.

I give you the version with the lines repainted.

Files:
 

your new code didn't work as I wanted it to :(

I took the liberty of tearing down my edits. works great especially when paired with ft.AutoRefresh

Files:
lsa_sinlr_1.mq4  16 kb
 

ForexTools:

Here I allowed myself to tear down my edits. works great especially when paired with ft.AutoRefresh

Well, I think your version is more interference-proof, but you probably need to add HShift-- there too, so that when there is no user action, the window will move over time. Although, hmm, maybe that's exactly what you wanted to avoid?
 
Of course! When analysing history, the graph should not go anywhere but "stand" in the place where I put the line
 
ForexTools:
Of course! When analysing history, the graph should not go anywhere but "stand" in the place where I put the line

Well, I first made a stationary variant and then replaced it :). I was interested, first of all, in the dynamics of redrawing. Actually it is easy to add a parameter like

if (ModeMoving) HShift--;
But too good is not good either, let the variant lie idle.