| //+------------------------------------------------------------------+
|
| //| Wavelet.mq5 |
|
| //| Copyright 2000-2025, MetaQuotes Ltd. |
|
| //| https://www.mql5.com |
|
| //+------------------------------------------------------------------+
|
| #property copyright "Copyright 2000-2025, MetaQuotes Ltd."
|
| #property link "https://www.mql5.com"
|
| #property version "1.00"
|
|
|
| #include <Math/Stat/Math.mqh>
|
| #include <Graphics/Graphic.mqh>
|
| #include <OpenCL/OpenCL.mqh>
|
|
|
| #define CPU_DATA 1
|
| #define GPU_DATA 2
|
|
|
| #define SIZE_X 600
|
| #define SIZE_Y 200
|
|
|
| #resource "Kernels/wavelet.cl" as string cl_program
|
| //+------------------------------------------------------------------+
|
| //| CWavelet |
|
| //+------------------------------------------------------------------+
|
| class CWavelet
|
| {
|
| protected:
|
| int m_xsize;
|
| int m_ysize;
|
| int m_maxcolor;
|
| string m_res_name;
|
| string m_label_name;
|
| uchar m_palette[3*256];
|
| //---
|
| double m_data[];
|
| double m_wavelet_data_CPU[];
|
| double m_wavelet_data_GPU[];
|
| uint m_bmp_buffer[];
|
|
|
| COpenCL m_OpenCL;
|
|
|
| double Morlet(const double t);
|
| void ShowWaveletData(const double &m_wavelet_data[]);
|
| int GetPalColor(const int index);
|
| void Blend(const uint c1,const uint c2,const uint r1,const uint g1,const uint b1,const uint r2,const uint g2,const uint b2);
|
| bool WaveletCPU(const double &data[],const int datacount,const int x_size,const int y_size,const int i,const int j,const bool norm,double &result[]);
|
| public:
|
| //---
|
| void Create(const string name,const int x0,const int y0,const int x_size,const int y_size);
|
| bool CalculateWavelet_CPU(const double &data[],uint &time);
|
| bool CalculateWavelet_GPU(double &data[],uint &time);
|
| void ShowWavelet(const int mode);
|
| };
|
| //+------------------------------------------------------------------+
|
| //| Morlet wavelet function |
|
| //+------------------------------------------------------------------+
|
| double CWavelet::Morlet(const double t)
|
| {
|
| double v=t;
|
| double res=MathExp(-v*v*0.5)*MathCos(M_2_PI*v);
|
| return ((double)res);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| GetPalColor |
|
| //+------------------------------------------------------------------+
|
| int CWavelet::GetPalColor(const int index)
|
| {
|
| int ind=index;
|
| if(ind<=0)
|
| ind=0;
|
| if(ind>255)
|
| ind=255;
|
| int idx=3*(ind);
|
| uchar r=m_palette[idx];
|
| uchar g=m_palette[idx+1];
|
| uchar b=m_palette[idx+2];
|
| //---
|
| return(b+256*g+65536*r);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Gradient palette |
|
| //+------------------------------------------------------------------+
|
| void CWavelet::Blend(const uint c1,const uint c2,const uint r1,const uint g1,const uint b1,const uint r2,const uint g2,const uint b2)
|
| {
|
| int n=int(c2-c1);
|
| for(int i=0; i<=n; i++)
|
| {
|
| if((c1+i+2)<m_palette.Size())
|
| {
|
| m_palette[3*(c1+i)]=uchar(MathRound(1*(r1*(n-i)+r2*i)*1.0/n));
|
| m_palette[3*(c1+i)+1]=uchar(MathRound(1*(g1*(n-i)+g2*i)*1.0/n));
|
| m_palette[3*(c1+i)+2]=uchar(MathRound(1*(b1*(n-i)+b2*i)*1.0/n));
|
| }
|
| }
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Create |
|
| //+------------------------------------------------------------------+
|
| void CWavelet::Create(const string name,const int x0,const int y0,const int x_size,const int y_size)
|
| {
|
| //---
|
| m_xsize=x_size;
|
| m_ysize=y_size;
|
| int size=m_xsize*m_ysize;
|
| ArrayResize(m_bmp_buffer,size);
|
| ArrayFill(m_bmp_buffer,0,size,0);
|
| ArrayResize(m_wavelet_data_CPU,size);
|
| ArrayResize(m_wavelet_data_GPU,size);
|
| ArrayFill(m_wavelet_data_CPU,0,size,0);
|
| ArrayFill(m_wavelet_data_GPU,0,size,0);
|
| m_res_name=name;
|
| m_label_name=m_res_name;
|
| StringToUpper(m_label_name);
|
| ResourceCreate(m_res_name,m_bmp_buffer,m_xsize,m_ysize,0,0,0,COLOR_FORMAT_XRGB_NOALPHA);
|
| ObjectCreate(0,m_label_name,OBJ_BITMAP_LABEL,0,0,0);
|
| ObjectSetInteger(0,m_label_name,OBJPROP_XDISTANCE,x0);
|
| ObjectSetInteger(0,m_label_name,OBJPROP_YDISTANCE,y0);
|
| ObjectSetString(0,m_label_name,OBJPROP_BMPFILE,NULL);
|
| ObjectSetString(0,m_label_name,OBJPROP_BMPFILE,"::"+m_label_name);
|
| //---
|
| m_maxcolor=100;
|
| Blend(0,20,0,0,95,0,0,246);
|
| Blend(21,40,0,0,246,0,236,226);
|
| Blend(41,60,0,236,226,226,246,0);
|
| Blend(61,80,226,246,0,226,0,0);
|
| Blend(81,100,226,0,0,123,0,0);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| WaveletCPU |
|
| //+------------------------------------------------------------------+
|
| bool CWavelet::WaveletCPU(const double &data[],const int datacount,const int x_size,const int y_size,const int i,const int j,const bool norm,double &result[])
|
| {
|
| double a1=(double)10e-10;
|
| double a2=(double)15.0;
|
| double da=(double)(a2-a1)/y_size;
|
| double db=(double)(datacount-0)/x_size;
|
| int pos=j*x_size+i;
|
| //---
|
| double a=a1+j*da;
|
| double b=i*db;
|
| double B=(double)1.0; //Morlet
|
| double B_inv=(double)1.0/B;
|
| double a_inv=(double)1/a;
|
| double dt=(double)1.0;
|
| double coef=(double)0.0;
|
| if(!norm)
|
| coef=(double)MathSqrt(a_inv);
|
| else
|
| {
|
| for(int k=0; k<datacount; k++)
|
| {
|
| double arg=(dt*k-b)*a_inv;
|
| arg=-B_inv*arg*arg;
|
| coef+=(double)MathExp(arg);
|
| }
|
| }
|
| double sum=0.0;
|
| for(int k=0; k<datacount; k++)
|
| {
|
| double arg=(dt*k-b)*a_inv;
|
| sum+=data[k]*Morlet(arg);
|
| }
|
| sum/=coef;
|
| result[pos]=sum;
|
| //---
|
| return(true);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| CalculateWavelet_CPU |
|
| //+------------------------------------------------------------------+
|
| bool CWavelet::CalculateWavelet_CPU(const double &data[],uint &time)
|
| {
|
| time=GetTickCount();
|
| int datacount=ArraySize(data);
|
| ArrayCopy(m_data,data,0,0,WHOLE_ARRAY);
|
| for(int i=0; i<m_xsize; i++)
|
| {
|
| for(int j=0; j<m_ysize; j++)
|
| {
|
| WaveletCPU(m_data,datacount,m_xsize,m_ysize,i,j,true,m_wavelet_data_CPU);
|
| }
|
| }
|
| time=GetTickCount()-time;
|
| //---
|
| return(true);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| CalculateWavelet_GPU |
|
| //+------------------------------------------------------------------+
|
| bool CWavelet::CalculateWavelet_GPU(double &data[],uint &time)
|
| {
|
| int datacount=ArraySize(data);
|
| if(!m_OpenCL.Initialize(cl_program,true))
|
| {
|
| PrintFormat("Error in OpenCL initialization. Error code=%d",GetLastError());
|
| return(false);
|
| }
|
| //--- check support working with double
|
| if(!m_OpenCL.SupportDouble())
|
| {
|
| PrintFormat("Working with double (cl_khr_fp64) is not supported on the device.");
|
| return(false);
|
| }
|
| //---
|
| m_OpenCL.SetKernelsCount(1);
|
| m_OpenCL.KernelCreate(0,"Wavelet_GPU");
|
| //---
|
| m_OpenCL.SetBuffersCount(2);
|
| if(!m_OpenCL.BufferFromArray(0,data,0,datacount,CL_MEM_READ_ONLY))
|
| {
|
| PrintFormat("Error in BufferFromArray for data array. Error code=%d",GetLastError());
|
| return(false);
|
| }
|
| if(!m_OpenCL.BufferCreate(1,m_xsize*m_ysize*sizeof(double),CL_MEM_READ_WRITE))
|
| {
|
| PrintFormat("Error in BufferCreate for data array. Error code=%d",GetLastError());
|
| return(false);
|
| }
|
| m_OpenCL.SetArgumentBuffer(0,0,0);
|
| m_OpenCL.SetArgumentBuffer(0,4,1);
|
| //---
|
| ArrayResize(m_wavelet_data_GPU,m_xsize*m_ysize);
|
| uint work[2];
|
| uint offset[2]={0,0};
|
| //--- set dimensions
|
| work[0]=m_xsize;
|
| work[1]=m_ysize;
|
| //--- set parameters and write data to buffer
|
| m_OpenCL.SetArgument(0,1,datacount);
|
| m_OpenCL.SetArgument(0,2,m_xsize);
|
| m_OpenCL.SetArgument(0,3,m_ysize);
|
| time=GetTickCount();
|
| //--- GPU calculation start
|
| if(!m_OpenCL.Execute(0,2,offset,work))
|
| {
|
| PrintFormat("Error in Execute. Error code=%d",GetLastError());
|
| return(false);
|
| }
|
| if(!m_OpenCL.BufferRead(1,m_wavelet_data_GPU,0,0,m_xsize*m_ysize))
|
| {
|
| PrintFormat("Error in BufferRead for m_wavelet_data_GPU array. Error code=%d",GetLastError());
|
| return(false);
|
| }
|
| //--- GPU calculation finish
|
| time=GetTickCount()-time;
|
| //---
|
| m_OpenCL.Shutdown();
|
| return(true);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| ShowWavelet |
|
| //+------------------------------------------------------------------+
|
| void CWavelet::ShowWavelet(const int mode)
|
| {
|
| if(mode==CPU_DATA)
|
| ShowWaveletData(m_wavelet_data_CPU);
|
| else
|
| if(mode==GPU_DATA)
|
| ShowWaveletData(m_wavelet_data_GPU);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| ShowWaveletData |
|
| //+------------------------------------------------------------------+
|
| void CWavelet::ShowWaveletData(const double &m_wavelet_data[])
|
| {
|
| //--- calculate min/max and range
|
| int count=ArraySize(m_wavelet_data);
|
| double min_value=m_wavelet_data[0];
|
| double max_value=m_wavelet_data[0];
|
| for(int i=1; i<count; i++)
|
| {
|
| min_value=MathMin(min_value,m_wavelet_data[i]);
|
| max_value=MathMax(max_value,m_wavelet_data[i]);
|
| }
|
| double range=max_value-min_value;
|
| if(range>0)
|
| {
|
| for(int j=0; j<m_ysize; j++)
|
| {
|
| for(int i=0; i<m_xsize; i++)
|
| {
|
| int pos=j*m_xsize+i;
|
| int colindex=int(m_maxcolor*(m_wavelet_data[pos]-min_value)/range);
|
| m_bmp_buffer[pos]=GetPalColor(colindex);
|
| }
|
| }
|
| //--- show image
|
| ResourceCreate(m_res_name,m_bmp_buffer,m_xsize,m_ysize,0,0,0,COLOR_FORMAT_XRGB_NOALPHA);
|
| ChartRedraw();
|
| }
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Weirstrass function |
|
| //+------------------------------------------------------------------+
|
| double Weirstrass(double x,double a,double b)
|
| {
|
| double sum=0.0;
|
| double b0=b;
|
| double a0=a;
|
| for(int n=0; n<35; n++)
|
| {
|
| double v=b0*(double)MathCos(a0*M_PI*x);
|
| sum=sum+v;
|
| a0=a0*a;
|
| b0=b0*b;
|
| }
|
| return(sum);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| PrepareModelData |
|
| //+------------------------------------------------------------------+
|
| void PrepareModelData(double &price_data[],const int datacount)
|
| {
|
| ArrayResize(price_data,datacount);
|
| //--- Weirstrass function
|
| double x1=0;
|
| double x2=2;
|
| double dx=(x2-x1)/datacount;
|
| for(int i=0; i<datacount; i++)
|
| {
|
| price_data[i]=Weirstrass(x1+dx*i,(double)3,(double)0.62);
|
| }
|
| }
|
| //+------------------------------------------------------------------+
|
| //| PreparePriceData |
|
| //+------------------------------------------------------------------+
|
| void PreparePriceData(const string symbol,ENUM_TIMEFRAMES timeframe,double &price_data[],const int datacount)
|
| {
|
| ArrayResize(price_data,datacount);
|
| CopyClose(symbol,timeframe,0,datacount,price_data);
|
| }
|
| //+------------------------------------------------------------------+
|
| //| PrepareMomentumData |
|
| //+------------------------------------------------------------------+
|
| void PrepareMomentumData(double &price_data[],double &momentum_data[],const int momentum_period)
|
| {
|
| int size=ArraySize(price_data);
|
| int datacount=size-momentum_period;
|
| //---
|
| ArrayResize(momentum_data,datacount);
|
| for(int i=0; i<datacount; i+=1)
|
| {
|
| momentum_data[i]=price_data[i+momentum_period]-price_data[i];
|
| }
|
| ArrayCopy(price_data,price_data,momentum_period,0,datacount);
|
| ArrayResize(price_data,datacount);
|
| //--- rescale momentum data
|
| double min_value=momentum_data[0];
|
| double max_value=momentum_data[0];
|
| for(int i=1; i<datacount; i++)
|
| {
|
| double value=momentum_data[i];
|
| if(momentum_data[i]>max_value)
|
| max_value=value;
|
|
|
| if(momentum_data[i]<min_value)
|
| min_value=value;
|
| }
|
| double range=max_value-min_value;
|
| for(int i=0; i<datacount; i+=1)
|
| momentum_data[i]=-1+2*(momentum_data[i]-min_value)/range;
|
| }
|
| //+------------------------------------------------------------------+
|
| //| Script program start function |
|
| //+------------------------------------------------------------------+
|
| void OnStart()
|
| {
|
| //---
|
| int momentum_period=8;
|
| double price_data[];
|
| double momentum_data[];
|
| PrepareModelData(price_data,SIZE_X+momentum_period);
|
| //PreparePriceData("EURUSD",PERIOD_M1,price_data,SIZE_X+momentum_period);
|
| PrepareMomentumData(price_data,momentum_data,momentum_period);
|
| //---
|
| CGraphic graph_price;
|
| CGraphic graph_momentum;
|
| graph_price.Create(0,"price",0,0,0,SIZE_X+130+6,SIZE_Y);
|
| graph_price.XAxis().MaxGrace(0);
|
| graph_price.HistorySymbolSize(10);
|
| graph_price.CurveAdd(price_data,ColorToARGB(clrRed,255),CURVE_LINES,"Price");
|
| graph_price.CurvePlotAll();
|
| graph_price.Redraw(true);
|
| graph_price.Update();
|
| //---
|
| graph_momentum.Create(0,"momentum",0,0,SIZE_Y,SIZE_X+130+6,SIZE_Y+SIZE_Y);
|
| graph_momentum.XAxis().MaxGrace(0);
|
| graph_momentum.HistorySymbolSize(10);
|
| graph_momentum.CurveAdd(momentum_data,ColorToARGB(clrBlue,255),CURVE_LINES,"Momentum");
|
| graph_momentum.CurvePlotAll();
|
| graph_momentum.Redraw(true);
|
| graph_momentum.Update();
|
| //---
|
| CWavelet wavelet;
|
| //---
|
| uint time_cpu=0;
|
| wavelet.Create("Wavelet",50,2*SIZE_Y,SIZE_X,SIZE_Y);
|
| if(!wavelet.CalculateWavelet_CPU(momentum_data,time_cpu))
|
| {
|
| PrintFormat("Error in calculation on CPU. Error code=%d",GetLastError());
|
| return;
|
| }
|
| //wavelet.ShowWavelet(CPU_DATA);
|
| uint time_gpu=0;
|
| if(!wavelet.CalculateWavelet_GPU(momentum_data,time_gpu))
|
| {
|
| PrintFormat("Error in calculation on GPU. Error code=%d",GetLastError());
|
| return;
|
| }
|
| wavelet.ShowWavelet(GPU_DATA);
|
| //---
|
| double CPU_GPU_ratio=0;
|
| if(time_gpu!=0)
|
| CPU_GPU_ratio=1.0*time_cpu/time_gpu;
|
| //---
|
| PrintFormat("time CPU=%d ms, time GPU=%d ms, CPU/GPU ratio: %f",time_cpu,time_gpu,CPU_GPU_ratio);
|
| //--- Sleep 10 seconds
|
| Sleep(10000);
|
| }
|
| //+------------------------------------------------------------------+
|