SessionRiskCore.mqh The four calculation functions in SessionRiskCore.mqh provide shared price and predictor arithmetic for the exporter and verifier. ExportSessionRisk.mq5 handles window construction and historical queues; VerifySessionRisk.mq5 uses the supplied prices, scale and coefficients to reproduce SPSS probabilities. SessionRisk.sps fits the model. Window membership, bar closure and historical-queue construction are handled outside the include file, so a successful calculation is one part of the workflow. Agreement on a set of formulas doesn't ensure correctness or evaluate the model's forecast performance. The independent raw-bar audit examines window construction and historical queues using exported raw bars. ValidPriceBar ValidPriceBar checks the four OHLC prices of one bar. It returns true only when all prices are finite, the open and low are positive, and both the open and close lie within [low, high]. The exporter combines this price check with its timestamp, window-membership and closure checks when constructing a window. //+------------------------------------------------------------------+ //| ValidPriceBar - validate OHLC prices | //+------------------------------------------------------------------+ bool ValidPriceBar(double o,double h,double l,double c) { return MathIsValidNumber(o) && MathIsValidNumber(h) && MathIsValidNumber(l) && MathIsValidNumber(c) && o>0 && l>0 && h>=o && h>=c && l<=o && l<=c; } SessionFeatures SessionFeatures receives the morning OHLC prices and historical scale a and writes x1 and x2 through reference arguments. After ValidPriceBar accepts the OHLC values, it rejects nonfinite or nonpositive a and a morning range of zero or less. It then calculates x1 = ln(1 + morning range/a) and x2 = |morning close - morning open| / morning range. Use the outputs only when the function returns true, which also confirms that both calculated features are finite. //+------------------------------------------------------------------+ //| SessionFeatures - reconstruct the two morning predictors | //+------------------------------------------------------------------+ bool SessionFeatures(double o,double h,double l,double c,double a,double &x1,double &x2) { //--- Validate OHLC prices and the historical scale. if(!ValidPriceBar(o,h,l,c) || !MathIsValidNumber(a) || a<=0 || h<=l) return false; //--- Calculate the range and body predictors. x1=MathLog(1.0+(h-l)/a); x2=MathAbs(c-o)/(h-l); return MathIsValidNumber(x1) && MathIsValidNumber(x2); } ExceedsBudget ExceedsBudget receives target range r and historical scale a and returns 1 when r > 1.25 × a; otherwise it returns 0. Equality therefore produces 0. The caller must validate the inputs and confirm target-window closure before using the label. //+------------------------------------------------------------------+ //| ExceedsBudget - label a target range above its budget | //+------------------------------------------------------------------+ int ExceedsBudget(double r,double a) { return (r>1.25*a)?1:0; } RiskSigmoid RiskSigmoid converts a finite linear score z to a probability. For z >= 0, it uses 1/(1 + exp(-z)); for z < 0, it uses exp(z)/(1 + exp(z)). Both branches keep the exponential argument nonpositive to avoid overflow. The verifier checks that z is finite before calling the function. //+------------------------------------------------------------------+ //| RiskSigmoid - convert a finite score to a probability | //+------------------------------------------------------------------+ double RiskSigmoid(double z) { //--- Keep the exponential argument non-positive in both branches. if(z>=0) return 1.0/(1.0+MathExp(-z)); double e=MathExp(z); return e/(1.0+e); } ExportSessionRisk.mq5 The exporter uses both price values and an indicator specifying whether a window is available. The exporter contains a global array, bars, which contains a sequence of H1 bars. WindowPrices specifies the number of bars, an indicator of validity, and the aggregated price values. The function, ReadWindow, fills the WindowPrices structure and determines window validity. The function, OnStart, determines window closure. ReadWindow ReadWindow in ExportSessionRisk.mq5 receives the first opening time of a window, an array cursor and a WindowPrices structure. Since cursor and structure are passed by reference, the function updates them directly and doesn’t require a return value. Advancing the cursor avoids searching through previously scanned bars. The function assumes that the bars are ordered by timestamp, an eight-hour window is defined and partial windows are considered invalid. It also does not check window closure. ReadWindow begins by looking through the bars array for the expected 8 hourly opening bars, starting from the specified opening time. It saves the first opening price, the highest and lowest prices and the last closing price. To evaluate prices, it uses ValidPriceBar. If it does not find the expected 8 opening bars or finds invalid bars, it considers the window invalid. //+------------------------------------------------------------------+ //| ReadWindow - aggregate eight expected hourly bars | //+------------------------------------------------------------------+ void ReadWindow(datetime first,int &cursor,WindowPrices &w) { //--- Reset window prices and locate the first required opening. w.count=0; w.valid=true; w.o=0; w.h=0; w.l=0; w.c=0; while(cursor=ArraySize(bars) || bars[j].time!=expected) { w.valid=false; continue; } MqlRates b=bars[j]; if(w.count==0) { w.o=b.open; w.h=b.high; w.l=b.low; } w.h=MathMax(w.h,b.high); w.l=MathMin(w.l,b.low); w.c=b.close; w.count++; if(!ValidPriceBar(b.open,b.high,b.low,b.close)) w.valid=false; j++; } //--- Advance the cursor and reject an incomplete window. cursor=j; if(w.count!=8) w.valid=false; } Num ExportSessionRisk.mq5 uses the Num function to format a double value as a string with 16 digits after the decimal point. This function is used in the exporter to provide the desired format to prices and other numerical values in the exported files. It does not modify the passed numeric value or check if the value is valid. //+------------------------------------------------------------------+ //| Num - format a value with 16 decimal places | //+------------------------------------------------------------------+ string Num(double v) { return DoubleToString(v,16); } Stamp The function Stamp in ExportSessionRisk.mq5 receives a timestamp, and returns a string representation of the timestamp, in which the time is broken down to minutes. The function formats the timestamp as is, and therefore does not adjust for time zone offsets or align the timestamp with the broker's time. Stamp returns an empty string if the timestamp is less than or equal to 0. //+------------------------------------------------------------------+ //| Stamp - format a positive timestamp | //+------------------------------------------------------------------+ string Stamp(datetime t) { return t>0?TimeToString(t,TIME_DATE|TIME_MINUTES):""; } ValidStudyDates The function ValidStudyDates in ExportSessionRisk.mq5 gets the first and the exclusive end dates and returns a Boolean. This function validates the interval of dates, but does not verify if the broker’s time is in sync with the server time, or if the broker has the history asked for. ValidStudyDates checks the validity of an interval. The end and start dates must be set to midnight, and the start date must precede the end date. //+------------------------------------------------------------------+ //| ValidStudyDates - check ordered midnight boundaries | //+------------------------------------------------------------------+ bool ValidStudyDates(datetime first,datetime end) { return first='0' && StringGetCharacter(s,i)<='9') { i++; digits++; } //--- Accept an optional decimal part. if(i='0' && StringGetCharacter(s,i)<='9') { i++; digits++; } } if(digits==0) return false; //--- Accept an optional exponent only when it contains digits. if(i='0' && StringGetCharacter(s,i)<='9') { i++; exponentDigits++; } if(exponentDigits==0) return false; } if(i!=n) return false; //--- Convert only after the entire token has passed syntax checks. value=StringToDouble(s); return MathIsValidNumber(value); } UniqueHeader The function UniqueHeader in VerifySessionRisk.mq5 receives an array of strings and returns false if there are empty names or names that occur more than once. It returns true if names are distinct. This function is used to avoid looking up an ambiguous reference file. UniqueHeader performs case sensitive name comparisons. Therefore, it treats different cases of a name as different names. The function UniqueHeader does not validate that the header includes all required fields. The OnStart event of VerifySessionRisk.mq5 verifies the session header has 14 fields in a specific order. The SPSS reference file for the current model must contain the columns session_date, p_logit, x_range and x_body. The SPSS reference file may also contain other columns. The columns of the SPSS reference file can be in any order and all names must be unique and cannot be empty. //+------------------------------------------------------------------+ //| UniqueHeader - reject empty or duplicate header names | //+------------------------------------------------------------------+ bool UniqueHeader(string &header[]) { //--- Reject empty names and case-sensitive duplicates. for(int i=0;i