//+------------------------------------------------------------------+ //| NoncentralT.mqh | //| Copyright 2000-2025, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #include "Math.mqh" #include "T.mqh" #include "Normal.mqh" //+------------------------------------------------------------------+ //| Noncentral T probability density function (PDF) | //+------------------------------------------------------------------+ //| The function returns the probability density function | //| of the Noncentral T distribution with parameters nu and delta. | //| | //| Arguments: | //| x : Random variable | //| nu : Degrees of freedom | //| delta : Noncentrality parameter | //| log_mode : Logarithm mode, if true it calculates Log values | //| error_code : Variable for error code | //| | //| Return value: | //| The probability density evaluated at x. | //+------------------------------------------------------------------+ double MathProbabilityDensityNoncentralT(const double x,const double nu,const double delta,const bool log_mode,int &error_code) { //--- return T if(delta==0.0) return MathProbabilityDensityT(x,nu,error_code); //--- check NaN if(!MathIsValidNumber(x) || !MathIsValidNumber(nu) || !MathIsValidNumber(delta)) { error_code=ERR_ARGUMENTS_NAN; return QNaN; } //--- check nu if(nu!=MathRound(nu) || nu<=0.0) { error_code=ERR_ARGUMENTS_INVALID; return QNaN; } error_code=ERR_OK; //--- double nu_1=nu+1.0; double nu_1_half=nu_1*0.5; double log_nu=MathLog(nu); double factor1=MathExp(-0.5*(MathLog(M_PI)+log_nu)-(delta*delta)*0.5-MathGammaLog(nu*0.5)+nu_1_half*log_nu); double nu_xx=nu+x*x; double log_nu_xx=MathLog(nu_xx); double factor2=MathExp(-nu_1_half*log_nu_xx); //--- const int max_terms=500; double pwr=1.0; double pwr_factor=x*delta*M_SQRT2; double pwr_nuxx=1.0; double pwr_nuxx_factor=1.0/MathSqrt(nu_xx); double pwr_gamma=1.0; int j=0; double pdf=0.0; while(j0) { pwr_nuxx*=pwr_nuxx_factor; pwr_gamma/=j; pwr*=pwr_factor; } double t=pwr*pwr_gamma*pwr_nuxx*MathGamma((nu_1+j)*0.5); pdf+=t; //--- check precision if((t/(pdf+10E-10))<10E-20) break; j++; } //--- check convergence if(j0) { pwr_nuxx*=pwr_nuxx_factor; pwr_gamma/=j; pwr*=pwr_factor; } double t=pwr*pwr_gamma*pwr_nuxx*MathGamma((nu_1+j)*0.5); pdf+=t; //--- check precision if((t/(pdf+10E-10))<10E-20) break; j++; } //--- check convergence if(j=max_iterations) break; } else { if(error<=errtol || j>=max_iterations) break; } } //--- check convergence if(j=max_iterations) break; } else { if(error<=errtol || j>=max_iterations) break; } } //--- check convergence if(j1.0) { error_code=ERR_ARGUMENTS_INVALID; return QNaN; } if(prob==0.0 || prob==1.0) { error_code=ERR_RESULT_INFINITE; if(prob==0.0) return QNEGINF; else return QPOSINF; } //--- coefficients for pdf and cdf double sqr_delta_half=delta*delta*0.5; double nu_half=nu*0.5; double log_sqr_delta_half=MathLog(sqr_delta_half); double exp_sqr_delta_half=MathExp(-sqr_delta_half); double sqrt_sqr_delta_half=MathSqrt(sqr_delta_half); double nu_1=nu+1.0; double nu_1_half=nu_1*0.5; double log_nu=MathLog(nu); double factor1=MathExp(-0.5*(MathLog(M_PI)+log_nu)-sqr_delta_half-MathGammaLog(nu*0.5)+nu_1_half*log_nu); //--- probability for -delta int err_code=0; double p0=MathCumulativeDistributionNormal(-delta,0.0,1.0,err_code); //--- error_code=ERR_OK; double precision=10E-20; const int max_iterations=150; int iterations=0; double x=0.5; double h=1.0; double h_min=precision; const int max_terms=500; //--- Newton iterations while(iterationsh_min && MathAbs(h)>MathAbs(h_min*x))==false) break; //--- calculate pdf double x_arg_sqr=x*x; double nu_xx=nu+x_arg_sqr; double log_nu_xx=MathLog(nu_xx); double factor2=MathExp(-nu_1_half*log_nu_xx); double pwr=1.0; double pwr_factor=x*delta*M_SQRT2; double pwr_nuxx=1.0; double pwr_nuxx_factor=1.0/MathSqrt(nu_xx); double pwr_gamma=1.0; int j=0; double pdf=0.0; while(j0) { pwr_nuxx*=pwr_nuxx_factor; pwr_gamma/=j; pwr*=pwr_factor; } double t=pwr*pwr_gamma*pwr_nuxx*MathGamma((nu_1+j)*0.5); pdf+=t; //--- check precision if((t/(pdf+10E-10))<10E-20) break; j++; } //--- check convergence if(j>max_terms) return false; pdf=factor1*factor2*pdf; //--- calculate cdf double t=(x*x)/(nu+x*x); double sum1 = 0.0; double sum2 = 0.0; //--- double pwr_coef1=1.0; double pwr_coef2=1.0; double fact=1.0; j=0; if(x!=0) { while(j0) { pwr_coef1*=sqrt_sqr_delta_half; pwr_coef2*=sqr_delta_half; fact/=j; } double coef=1.0/MathGamma(j*0.5+1.0); //--- term1: t between 0 and x double t1=pwr_coef1*coef*MathBetaIncomplete(t,(j+1.0)*0.5,nu_half); sum1+=t1; //--- term2: t between x and -x double t2=pwr_coef2*fact*MathBetaIncomplete(t,(j+0.5),nu_half); sum2+=t2; //--- check precision if((MathAbs(t1/(sum1+10E-10))max_terms) return false; //--- compute probability for positive x double cdf=p0+exp_sqr_delta_half*sum1*0.5; //--- compute probability for negative x if(x<0) cdf=cdf-exp_sqr_delta_half*sum2; //--- take into account round-off errors for probability cdf=MathMin(cdf,1.0); //--- calculate ratio h=(cdf-prob)/pdf; double x_new=x-h; if(x_new<0.0) x_new=x*0.1; else if(x_new>1.0) x_new=1.0-(1.0-x)*0.1; if(MathAbs(x_new-x)<10E-15) break; x=x_new; iterations++; } //--- check convergence if(iterations1.0) return false; if(prob==0.0 || prob==1.0) { if(prob==0.0) result[i]=QNEGINF; else result[i]=QPOSINF; } else { double x=0.5; double h=1.0; int iterations=0; //--- Newton iterations while(iterationsh_min && MathAbs(h)>MathAbs(h_min*x))==false) break; //--- calculate pdf and cdf //double cdf=MathCumulativeDistributionNoncentralT(x,nu,delta,err_code); //double pdf=MathProbabilityDensityNoncentralT(x,nu,delta,err_code); //--- calculate pdf double x_arg_sqr=x*x; double nu_xx=nu+x_arg_sqr; double log_nu_xx=MathLog(nu_xx); double factor2=MathExp(-nu_1_half*log_nu_xx); double pwr=1.0; double pwr_factor=x*delta*M_SQRT2; double pwr_nuxx=1.0; double pwr_nuxx_factor=1.0/MathSqrt(nu_xx); double pwr_gamma=1.0; int j=0; double pdf=0.0; while(j0) { pwr_nuxx*=pwr_nuxx_factor; pwr_gamma/=j; pwr*=pwr_factor; } double t=pwr*pwr_gamma*pwr_nuxx*MathGamma((nu_1+j)*0.5); pdf+=t; //--- check precision if((t/(pdf+10E-10))<10E-20) break; j++; } //--- check convergence if(j>max_terms) return false; pdf=factor1*factor2*pdf; //--- calculate cdf double t=(x*x)/(nu+x*x); double sum1 = 0.0; double sum2 = 0.0; //--- double pwr_coef1=1.0; double pwr_coef2=1.0; double fact=1.0; j=0; if(x!=0) { while(j0) { pwr_coef1*=sqrt_sqr_delta_half; pwr_coef2*=sqr_delta_half; fact/=j; } double coef=1.0/MathGamma(j*0.5+1.0); //--- term1: t between 0 and x double t1=pwr_coef1*coef*MathBetaIncomplete(t,(j+1.0)*0.5,nu_half); sum1+=t1; //--- term2: t between x and -x double t2=pwr_coef2*fact*MathBetaIncomplete(t,(j+0.5),nu_half); sum2+=t2; //--- check precision if((MathAbs(t1/(sum1+10E-10))max_terms) return false; //--- compute probability for positive x double cdf=p0+exp_sqr_delta_half*sum1*0.5; //--- compute probability for negative x if(x<0) cdf=cdf-exp_sqr_delta_half*sum2; //--- take into account round-off errors for probability cdf=MathMin(cdf,1.0); //--- calculate ratio h=(cdf-prob)/pdf; double x_new=x-h; if(x_new<0.0) x_new=x*0.1; else if(x_new>1.0) x_new=1.0-(1.0-x)*0.1; if(MathAbs(x_new-x)<10E-15) break; x=x_new; iterations++; } //--- check convergence if(iterations1) mean=delta*MathSqrt(nu)*MathGamma((nu-1)*0.5)/(MathSqrt(2)*MathGamma(nu*0.5)); //--- delta^2 double delta_sqr=delta*delta; //--- 1/((nu-3)*(nu-2)) double nu32=1/((nu-3)*(nu-2)); if(nu>2) variance=((delta_sqr+1)*nu)/(nu-2)-MathPow(mean,2); //--- skewness if(nu>3) { skewness=-2*variance; skewness+= nu*(delta_sqr+2*nu-3)*nu32; skewness*= mean*MathPow(variance,-1.5); } //--- kurtosis if(nu>4) { kurtosis=-3*variance; kurtosis+= nu*(delta_sqr*(nu+1)+3*(3*nu-5))*nu32; kurtosis*= -MathPow(mean,2); kurtosis+= MathPow(nu,2)*(MathPow(delta,4)+6*delta_sqr+3)/((nu-4)*(nu-2)); kurtosis*= MathPow(variance,-2); kurtosis-=3; } //--- successful return true; } //+------------------------------------------------------------------+