//+------------------------------------------------------------------+ //| NoncentralChiSquare.mqh | //| Copyright 2000-2025, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #include "Math.mqh" #include "Normal.mqh" #include "Poisson.mqh" #include "ChiSquare.mqh" //+------------------------------------------------------------------+ //| Noncentral Chi-Square distribution density function (PDF) | //+------------------------------------------------------------------+ //| The function returns the probability density function of the | //| Noncentral Chi-Square distribution with parameters nu and sigma. | //| | //| Arguments: | //| x : Random variable | //| nu : Degrees of freedom | //| sigma : Noncentrality parameter | //| log_mode : Logarithm mode flag, if true it returns Log values | //| error_code : Variable for error code | //| | //| Return value: | //| The probability density evaluated at x. | //+------------------------------------------------------------------+ double MathProbabilityDensityNoncentralChiSquare(const double x,const double nu,const double sigma,const bool log_mode,int &error_code) { //--- check NaN if(!MathIsValidNumber(x) || !MathIsValidNumber(nu) || !MathIsValidNumber(sigma)) { error_code=ERR_ARGUMENTS_NAN; return QNaN; } //--- check arguments if(nu!=MathRound(nu) || nu<=0.0) { error_code=ERR_ARGUMENTS_INVALID; return QNaN; } error_code=ERR_OK; if(x<=0.0) return TailLog0(true,log_mode); //--- prepare parameters int err_code=0; int max_terms=1000; double lambda=sigma*0.5; double half_nu=nu*0.5; double pwr_lambda=1.0; double pwr_two=MathExp(-half_nu*MathLog(2)); double pwr_x=MathExp((half_nu-1.0)*MathLog(x)); double fact_mult=1.0; double coef_lambda_x=MathExp(-lambda-x*0.5); double coef_gamma=1.0/MathGamma(half_nu); double inv_factor=1.0; //--- calculate density using direct summation int j=0; double pdf=0; while(j0) { pwr_lambda*=lambda; pwr_x*=x; pwr_two*=0.5; fact_mult*=1.0/j; inv_factor*=1.0/(j+half_nu-1); } double dp=coef_gamma*inv_factor*pwr_lambda*pwr_two*pwr_x*fact_mult*coef_lambda_x; pdf=pdf+dp; //--- check stop if(dp/(pdf+10E-10)<10E-16) break; j++; } //--- check convergence if(j0) { pwr_lambda*=lambda; pwr_x*=x_arg; pwr_two*=0.5; fact_mult*=1.0/j; inv_factor*=1.0/(j+half_nu-1); } double dp=coef_gamma*inv_factor*pwr_lambda*pwr_two*pwr_x*fact_mult*coef_lambda_x; pdf=pdf+dp; //--- check stop if(dp/(pdf+10E-10)<10E-16) break; j++; } //--- check convergence if(j0) { pwr_lambda*=lambda; fact_mult/=j; } double coef1=coef_lambda*pwr_lambda*fact_mult; double coef2=MathMin(MathGammaIncomplete(half_x,half_nu+j),1.0); double dp=coef1*coef2; cdf=cdf+dp; if((dp/(cdf+10E-10))<10E-16) break; j++; } //--- if(j0) { pwr_lambda*=lambda; fact_mult/=j; } double coef1=coef_lambda*pwr_lambda*fact_mult; double coef2=MathMin(MathGammaIncomplete(half_x,half_nu+j),1.0); double dp=coef1*coef2; cdf=cdf+dp; if((dp/(cdf+10E-10))<10E-16) break; j++; } //--- if(j1.0) { error_code=ERR_ARGUMENTS_INVALID; return QNaN; } error_code=ERR_OK; if(prob==0.0) return 0.0; if(prob==1.0) return QPOSINF; error_code=ERR_OK; //--- common factors for pdf and cdf calculation const int max_terms=1000; double lambda=sigma*0.5; double half_nu=nu*0.5; double coef_lambda=MathExp(-lambda); double half_nu_m1=half_nu-1.0; double coef_gamma=1.0/MathGamma(half_nu); double pwr_two2=MathExp(-half_nu*MathLog(2)); double pwr_half_num1=(half_nu-1.0); //--- prepare values for initial x estimation double x=0.5; double h=1.0; double h_min=10E-10; //--- Newton iterations const int max_iterations=50; int iterations=0; // int err_code=0; while(iterationsh_min && MathAbs(h)>MathAbs(h_min*x))==false) break; //double pdf=MathProbabilityDensityNoncentralChiSquare(x,nu,sigma,false,err_code); double half_x=x*0.5; double pwr_lambda=1.0; double pwr_two=pwr_two2; double pwr_x=MathPow(x,pwr_half_num1); double fact_mult=1.0; double coef_lambda_x=coef_lambda*MathExp(-half_x); double inv_factor=1.0; //--- calculate density using direct summation int j=0; double pdf=0; while(j0) { pwr_lambda*=lambda; pwr_x*=x; pwr_two*=0.5; fact_mult*=1.0/j; inv_factor*=1.0/(j+half_nu-1); } double dp=coef_gamma*inv_factor*pwr_lambda*pwr_two*pwr_x*fact_mult*coef_lambda_x; pdf=pdf+dp; //--- check stop if(dp/(pdf+10E-10)<10E-16) break; j++; } //--- check convergence if(j>max_terms) { error_code=ERR_NON_CONVERGENCE; return QNaN; } //--- calculate cdf pwr_lambda=1.0; fact_mult=1.0; double cdf=0.0; j=0; //--- direct summation while(j0) { pwr_lambda*=lambda; fact_mult/=j; } double coef1=coef_lambda*pwr_lambda*fact_mult; double coef2=MathMin(MathGammaIncomplete(half_x,half_nu+j),1.0); double dp=coef1*coef2; cdf=cdf+dp; if((dp/(cdf+10E-10))<10E-16) break; j++; } //--- if(j>max_terms) { error_code=ERR_NON_CONVERGENCE; return QNaN; } //--- 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-x)*0.1; x=x_new; iterations++; } //--- check convergence if(iterations1.0) return false; //--- prepare values for initial x estimation int err_code=0; double x=0.5; double h=1.0; //--- Newton iterations int iterations=0; while(iterationsh_min && MathAbs(h)>MathAbs(h_min*x))==false) break; //double pdf=MathProbabilityDensityNoncentralChiSquare(x,nu,sigma,false,err_code); double half_x=x*0.5; double pwr_lambda=1.0; double pwr_two=pwr_two0; double pwr_x=MathPow(x,half_nu_m1); double fact_mult=1.0; double coef_lambda_x=coef_lambda*MathExp(-half_x); double inv_factor=1.0; //--- calculate density using direct summation int j=0; double pdf=0; while(j0) { pwr_lambda*=lambda; pwr_x*=x; pwr_two*=0.5; fact_mult*=1.0/j; inv_factor*=1.0/(j+half_nu-1); } double dp=pwr_gamma0*inv_factor*pwr_lambda*pwr_two*pwr_x*fact_mult*coef_lambda_x; pdf=pdf+dp; //--- check stop if(dp/(pdf+10E-10)<10E-16) break; j++; } //--- check convergence if(j>max_terms) return false; //--- calculate cdf pwr_lambda=1.0; fact_mult=1.0; pwr_lambda=1.0; fact_mult=1.0; double cdf=0.0; j=0; //--- direct summation while(j0) { pwr_lambda*=lambda; fact_mult/=j; } double coef1=coef_lambda*pwr_lambda*fact_mult; double coef2=MathMin(MathGammaIncomplete(half_x,half_nu+j),1.0); double dp=coef1*coef2; cdf=cdf+dp; if((dp/(cdf+10E-10))<10E-16) break; j++; } //--- if(j>max_terms) return false; //--- 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-x)*0.1; x=x_new; iterations++; } //--- check convergence if(iterations1.0) { double rnd_chisquare=MathRandomGamma((nu-1)*0.5,2.0,err_code); double rnd_normal=MathSqrt(sigma)+MathRandomNormal(0,1,err_code); return rnd_chisquare+rnd_normal*rnd_normal; } else { int rnd_poisson=(int)MathRandomPoisson(sigma*0.5); return MathRandomChiSquare(nu+2*rnd_poisson,err_code); } } //+------------------------------------------------------------------+ //| Random variate from the Noncentral Chi-Square distribution | //+------------------------------------------------------------------+ //| Generates random variables from the Noncentral Chi-Square | //| distribution with parameters nu and sigma. | //| | //| Arguments: | //| nu : Degrees of freedom | //| sigma : Noncentrality parameter | //| data_count : Number of values needed | //| result : Output array with random values | //| | //| Return value: | //| true if successful, otherwise false. | //+------------------------------------------------------------------+ //| Author: Robert Kern | //+------------------------------------------------------------------+ bool MathRandomNoncentralChiSquare(const double nu,const double sigma,const int data_count,double &result[]) { //--- return ChiSquare if sigma==0 if(sigma==0.0) return MathRandomChiSquare(nu,data_count,result); //--- check NaN if(!MathIsValidNumber(nu) || !MathIsValidNumber(sigma)) return false; //--- check nu if(nu!=MathRound(nu) || nu<=0) return false; //--- check sigma if(sigma<0.0) return false; int err_code=0; //--- prepare output array and calculate random values ArrayResize(result,data_count); for(int i=0; i1.0) { double rnd_chisquare=MathRandomGamma((nu-1)*0.5,2.0,err_code); double rnd_normal=MathSqrt(sigma)+MathRandomNormal(0,1,err_code); result[i]=rnd_chisquare+rnd_normal*rnd_normal; } else { int rnd_poisson=(int)MathRandomPoisson(sigma*0.5); result[i]=MathRandomChiSquare(nu+2*rnd_poisson,err_code); } } return true; } //+------------------------------------------------------------------+ //| Noncentral Chi-Square distribution moments | //+------------------------------------------------------------------+ //| The function calculates 4 first moments of Noncental Chi-Square | //| distribution with parameters nu and sigma. | //| | //| Arguments: | //| nu : Degrees of freedom | //| sigma : Noncentrality parameter | //| mean : Variable for mean value (1st moment) | //| variance : Variable for variance value (2nd moment) | //| skewness : Variable for skewness value (3rd moment) | //| kurtosis : Variable for kurtosis value (4th moment) | //| error_code : Variable for error code | //| | //| Return value: | //| true if moments calculated successfully, otherwise false. | //+------------------------------------------------------------------+ bool MathMomentsNoncentralChiSquare(const double nu,const double sigma,double &mean,double &variance,double &skewness,double &kurtosis,int &error_code) { //--- default values mean =QNaN; variance=QNaN; skewness=QNaN; kurtosis=QNaN; //--- check NaN if(!MathIsValidNumber(nu) || !MathIsValidNumber(sigma)) { error_code=ERR_ARGUMENTS_NAN; return false; } //--- check nu if(nu!=MathRound(nu) || nu<=0.0) { error_code=ERR_ARGUMENTS_INVALID; return false; } error_code=ERR_OK; //--- calculate moments mean =nu+sigma; variance=2*nu+4*sigma; skewness=2*M_SQRT2*(nu+3*sigma)*MathPow(nu+2*sigma,-1.5); kurtosis=12*(nu+4*sigma)*MathPow(nu+2*sigma,-2); //--- successful return true; } //+------------------------------------------------------------------+