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[cursor].time<first)
      cursor++;
   int j=cursor;
//--- Collect eight exact hourly openings and validate their prices.
   for(int k=0;k<8;k++)
     {
      datetime expected=first+k*3600;
      while(j<ArraySize(bars) && bars[j].time<expected)
         j++;
      if(j>=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<end && (long)first%86400==0 && (long)end%86400==0;
  }

OnStart: historical export

OnStart is the main function of the ExportSessionRisk.mq5 script. It has no function arguments and returns nothing. There are script inputs named StudySymbol, FirstDate and EndExclusive with default values EURUSD, January 1st 2019 inclusive and January 1st 2025 exclusive.

OnStart writes and checks a fresh INCOMPLETE entry in export_status.txt, and then validates the specified ordered midnight boundaries, selects the study symbol and requests H1 data via CopyRates, allowing up to 40 attempts and pauses of 500 milliseconds between each.

The loop stops when the data contains a first bar opening before FirstDate plus seven days and a last bar opening at or after EndExclusive minus seven days. OnStart rejects any returned history that is empty, interrupted, duplicated or unordered. It also rejects history that does not satisfy the endpoint checks or is outside the requested time boundaries. The endpoint checks require data near the ends of the time interval. They do not ensure that data exists throughout the time interval.

Following acquisition, the exporter rejects empty result sets and duplicate or unordered timestamps. The closure cutoff is the earlier of either TimeLocal or the last copied bar’s opening time. A bar observed to occur later indicates that earlier hourly bars have closed. Offline TimeCurrent may contain an old, unrelated server quote. The exporter opens its output files, writes their headers and saves the copied bars to raw_bars.csv for independent reconstruction. This historical rule does not define when a live broker would make the data available.

The exporter’s OnStart function maintains two circular buffers, where the invalid or incomplete ranges or events are discarded. The buffers retain 20 complete, valid target ranges or 60 eligible event labels, respectively. The buffers maintain a count (rc for target ranges, ec for event labels) and a position variable, where the next entry will be written. If a circular buffer is full, the oldest entry will be removed.

The buffers maintain the latest and oldest historical closing times for audit, and the script stops if the latest retained closing time does not precede the current decision time.

During historical replay, OnStart in ExportSessionRisk.mq5 invokes ReadWindow to reconstruct the morning window for 00:00 to 08:00 and the target window for 08:00 to 16:00 on weekdays. Forecast eligibility requires the morning window to contain all eight closed and valid H1 bars with the range being positive. The positive mean of the ranges of the 20 previous complete, valid target windows must also be available. SessionFeatures must accept the input values.

Estimation eligibility hinges on forecast conditions and a closed, valid current target window. Comparison eligibility additionally requires 60 earlier resolved, estimation-eligible outcomes.

The exporter writes all candidate weekdays to session_audit.csv. It includes the reason, if the weekday is excluded. OnStart also writes the historical endpoints. The exporter writes estimation-eligible rows, beginning 2020, to sessions.csv. The exporter does not limit the file to comparison eligible rows.

The exporter does not compute smoothed rolling probabilities. Current session inputs are computed and written prior to updating the historical queues. If a case becomes estimation eligible, it is added to the event history. A valid and closed current target window results in its range being added to the range history, regardless of the session’s forecast eligibility.

Broker settings and the first and last copied bar times are saved along with the closure cutoff and counts in native_manifest.txt. The exporter verifies the success of each FileWrite function, flushes the output buffers and checks for errors. An export that results in no study rows, an interrupted export or an export that results in write errors leaves export_status.txt as INCOMPLETE. The exporter reports a successful process, PASS, in a temporary file and checks the file move that replaces the INCOMPLETE receipt.

A fresh export PASS confirms that the export procedure was completed and that at least one study row was retained. Write checks also were successful. A PASS does not confirm complete broker history, input readiness, model convergence or forecast quality. If the initial receipt cannot be written, the export procedure stops, and a previous PASS must not be considered evidence of the current export.

The historical scale is defined by the 20 most recent complete and valid target ranges. The rolling baseline, which represents the frequency of events, is defined by the 60 most recent eligible outcomes. A target range is considered large if it is greater than 1.25 times the mean of the 20 most recent target ranges. Equality is not considered an event.

The 25% increase and the lookbacks for the event-frequency baseline and historical scale were determined in advance and have not been optimized based on the outcomes for the year 2024. Whether the parameters are robust is a different research question.

Supporting checks

The supporting checks follow the data through the workflow. audit_export.py independently rebuilds windows and historical queues from the exported raw bars, then validate_inputs.py checks whether sessions.csv is ready for SPSS analysis. After SessionRisk.sps fits the model, audit_results.py reconciles the saved predictions and summaries. Both audit_results.py and extract_coefficients.py use spss_model.py for coefficient extraction. Each check addresses a different stage, so read its receipt alongside the exporter and verifier receipts.

audit_export.py

Within audit_export.py, rows(name) reads CSV records from the data directory into dictionaries, while stamp(t) gives the audit a consistent timestamp format for comparing session endpoints and decision times. stamp(t) returns a string with minute precision for a datetime value and an empty string for None; seconds are ignored and no time-zone conversion is performed. Loading leaves schema and value checks to the main audit, and a missing or unreadable file stops the run.

The window function in audit_export.py takes the opening time of a window and looks up the eight expected hourly timestamps from the global raw-bar dictionary. It returns a tuple containing price information as well as a Boolean that indicates whether all eight bars are present and their finite, positive prices satisfy the OHLC conditions. The function returns individual bars as opposed to combining them into a session. It also does not verify if the window is closed. The audit combines prices and determines closure against the recorded closed_through cutoff in native_manifest.txt.

The main operation of audit_export.py reads raw_bars.csv, session_audit.csv and sessions.csv to independently rebuild the morning and target windows. It uses separate 20-range and 60-event histories to evaluate exported session prices, scales, eligibility flags and exclusion reasons. It assesses historical endpoints, weekday coverage, exported row count, and the separation of training labels from holdout decision times.

It begins with export_audit.json as INCOMPLETE and on successful verification changes it to PASS. It writes the number of checks performed and hash values for the three CSV inputs and native_manifest.txt. The calendar and anticipated historical years of the export audit are aligned to the study’s 2019-2024 export period. This audit does not fit a model or validate the availability of live data, and changes to the period covered require changes to this audit.

validate_inputs.py

The validate_inputs.py script defines no functions. Its core functionality reads sessions.csv to check the following.

1. It checks the expected schema.

2. It checks if the dates, in order, correspond to the weekdays.

3. It checks if the study years are correct.

4. It checks if sessions are held in appropriate time intervals.

5. It checks if the numerical values correspond to the expected price relationships.

6. It checks that historical event totals and counts are integers within the permitted bounds, and that the comparison flag agrees with a full history count.

The script requires both outcome classes in the training sample, a nonempty holdout, and full rank for the three training-model columns. It performs matrix elimination to check rank. The script sets analysis_readiness.json to INCOMPLETE and validates input to set it to PASS along with the count and hash. The input is validated for the expected 2020-2023 training and 2024 validation. SPSS convergence and the case counts remain to be verified.

Run the Python validators with assertions enabled: -O or an effective PYTHONOPTIMIZE setting is rejected. Assertion failures and file I/O errors stop a run, so inspect its current receipts and input hashes before continuing. Existing files or a PASS from an earlier run do not establish that the current checks succeeded.

audit_results.py: read

The read(p) function in audit_results.py reads a CSV file and returns a list of dictionaries. It expects the CSV to have a present, nonempty header list with no duplicate names, and rejects rows containing additional, unnamed fields. It expects all fields in a row to be present.

The read(p) function in audit_results.py does not check data types and allows empty entries in fields. The main part of the script checks the values needed for the analysis in this report. The program stops if an empty file is read, if there are duplicate header names, or if the number of data fields in a row does not conform to the expected case. Other errors related to reading a file also stop the program.

audit_results.py: main operation

audit_results.py first compares input hash values with those in a successful export audit, then compares the saved SPSS rows with the session dataset. It performs a series of checks to ensure features, labels and coefficients are as expected, and that the frequency baselines, bins, losses and holdout coverage are as expected. The reliability groups and metrics are also verified.

It writes a descriptive breakdown by quarter, prepares forecast_only.csv with morning inputs for forecasts without valid outcomes, and publishes the extracted coefficients. After successful checks, it saves PASS, observed differences and input hash values to result_audit.json.

The results checker takes current files and infers row counts. Training for 2020-2023 and the 2024 holdout remain fixed. Rolling probabilities and losses are unavailable until 60 earlier eligible outcomes are available. Empty calibration groups have zero counts and missing means. Score difference is not defined for quarters without cases. This audit in Python complements the native MQL5 replay.

Check how probabilities match outcomes

Training session probabilities in SessionRisk.sps are ordered in increasing order, breaking ties by session date. Positions rounded up from one third and two thirds of the total training cases were used to derive cutoffs. In the original sample of 1,036 cases, the 346th and 691st cases gave boundaries of 0.183927548531895 and 0.234717083535336, respectively.

The order is deterministic due to the date rule. Boundaries from this training sample remain fixed for the 2024 data. Probabilities that equal a boundary become members of the lower group. Probabilities beyond the training range are placed in the lowest or highest group.

Coefficient handoffspss_model.py: extract

extract(path) in spss_model.py reads the SPSS model XML and returns Constant, x_range and x_body in a coefficient dictionary. It checks PMML 4.3, a single supported GeneralRegressionModel, the event category, predictor-to-parameter mapping and finite coefficients before returning. Failed model checks and file or XML errors stop extraction. These checks cover the supported structure, not the full PMML specification.

coefficient_csv and publish_coefficients

coefficient_csv(coeff) serializes Constant, x_range and x_body into a CSV string with a header and up to 17 significant digits per value. It assumes a valid coefficient dictionary and leaves file creation to publish_coefficients(coeff, target). That function writes the text to a temporary file, replaces the target and returns None; its output is the file, and the target path must be writable. Keeping serialization separate from publication lets the extractor and results audit use the same coefficient format.

extract_coefficients.py

The extract_coefficients.py script does not define any functions. It writes INCOMPLETE to results/coefficient_extraction.json and calls the extraction function on results/session_model.xml. It then writes results/model_coefficients.csv. If the script completes successfully, it records PASS and the hashes of results/session_model.xml and results/model_coefficients.csv in the receipt. The script saves coefficients in a format with up to 17 significant digits. For verification, use these values rather than the displayed values in Figure 2.

VerifySessionRisk.mq5

ReadRow

ReadRow in VerifySessionRisk.mq5 accepts an open file handle and a string array passed by reference. The array is cleared, and the method reads fields until the current line or the file ends. The method returns the number of fields read. A result of zero means no fields were read.

The file is opened in CSV mode with the expected separator by the method's caller. The method reads fields and does not check the expected number of fields in the record. The method also does not interpret values and does not validate the record.

//+------------------------------------------------------------------+
//| ReadRow - collect fields from one CSV record                     |
//+------------------------------------------------------------------+
int ReadRow(int file,string &values[])
  {
//--- Collect the fields of the current CSV record.
   ArrayResize(values,0);
   while(!FileIsEnding(file))
     {
      int n=ArraySize(values);
      ArrayResize(values,n+1);
      values[n]=FileReadString(file);
      if(FileIsLineEnding(file))
         break;
     }
   return ArraySize(values);
  }

Column

Column in VerifySessionRisk.mq5 is handed a header array and the name of the column of interest. It returns the zero-based index of the first matching column, or -1 when the column of interest is not found. It does not check for duplicate column names. OnStart uses this function to locate the column of interest, as well as other header checks, prior to using the returned column index. Column is case sensitive and returns a matching column index only if the header name has the same case as the requested name.

//+------------------------------------------------------------------+
//| Column - find a case-sensitive header name                       |
//+------------------------------------------------------------------+
int Column(string &header[],string name)
  {
   for(int j=0;j<ArraySize(header);j++)
      if(header[j]==name)
         return j;
   return -1;
  }

StrictNumber

The function StrictNumber in VerifySessionRisk.mq5 gets a text field and a double referenced variable and returns true if the field contains a number in a permitted format and false otherwise. After syntactic check, it writes the interpreted number to the reference variable and returns true if the number is finite. StrictNumber allows an optional sign, a decimal point and a scientific-notation exponent. It requires digits in the number and in any exponent. It allows no leading, trailing or internal field whitespace.

The caller should use that output only after a true result. The function validates the text field for a number in a required format and checks if the number is finite. It does not check if the number is appropriate and relevant for the field or if the number represents a price. These validations are done elsewhere.

//+------------------------------------------------------------------+
//| StrictNumber - validate and convert a complete numeric token     |
//+------------------------------------------------------------------+
bool StrictNumber(string s,double &value)
  {
//--- Validate the complete numeric token before conversion.
   int n=StringLen(s),i=0,digits=0;
   if(n==0)
      return false;
   if(StringGetCharacter(s,i)=='+' || StringGetCharacter(s,i)=='-')
      i++;
   while(i<n && StringGetCharacter(s,i)>='0' && StringGetCharacter(s,i)<='9')
     {
      i++;
      digits++;
     }
//--- Accept an optional decimal part.
   if(i<n && StringGetCharacter(s,i)=='.')
     {
      i++;
      while(i<n && StringGetCharacter(s,i)>='0' && StringGetCharacter(s,i)<='9')
        {
         i++;
         digits++;
        }
     }
   if(digits==0)
      return false;
//--- Accept an optional exponent only when it contains digits.
   if(i<n && (StringGetCharacter(s,i)=='e' || StringGetCharacter(s,i)=='E'))
     {
      i++;
      int exponentDigits=0;
      if(i<n && (StringGetCharacter(s,i)=='+' || StringGetCharacter(s,i)=='-'))
         i++;
      while(i<n && StringGetCharacter(s,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<ArraySize(header);i++)
     {
      if(header[i]=="")
         return false;
      for(int j=0;j<i;j++)
         if(header[j]==header[i])
            return false;
     }
   return true;
  }
//+------------------------------------------------------------------+

OnStart: reproduce and compare the SPSS model

OnStart in VerifySessionRisk.mq5 reproduces saved SPSS calculations without re-fitting or adjusting the model. ExpectedRows defaults to 1295 and needs to be set to the total number of session data rows, excluding the header, for each new export. During verification, the verifier checks that this positive count matches estimation_or_holdout_rows in native_manifest.txt and the final number of paired session/SPSS rows.

Before opening the comparison inputs, OnStart writes fresh INCOMPLETE receipts for the main and optional checks and removes any old forecast_only_predictions.csv. It then reads sessions.csv and native_manifest.txt from MQL5/Files/SessionRisk/data, plus model_coefficients.csv and spss_predictions.csv from MQL5/Files/SessionRisk/results. Receipt preparation, cleanup or input-opening failures stop the run.

OnStart requires exactly one finite coefficient for Constant, x_range and x_body. It checks the session file against the prescribed 14-column header, uses UniqueHeader to reject empty or duplicate reference names, and uses Column to locate session_date, p_logit, x_range and x_body. Each paired row must have the expected field count and the same identifier, with identifiers strictly increasing. These checks prevent mismatched or reordered records; ordering identifiers as strings does not validate their calendar dates.

For each accepted row, the verifier applies StrictNumber to the four morning prices, historical scale, reference probability and the two reference predictors. SessionFeatures reconstructs the predictors. The verifier calculates the linear score using three coefficients, rejects any non-finite score, and provides the score to RiskSigmoid. The verifier compares the probability and the two predictors with the SPSS references, and accepts absolute differences of at most 1e-10.

Compared rows are written to native_predictions.csv with the reconstructed features, both probabilities and their absolute difference. Before publishing native_parity.txt, the verifier checks row writes and flush errors, rejects extra SPSS rows and reconciles the final count with ExpectedRows. It writes the summary through a temporary receipt and checks the file replacement. A completed main comparison reports PASS or FAIL; an early input or receipt error may leave INCOMPLETE and report FAIL in the terminal log. A fresh main PASS confirms schemas, identities, counts, arithmetic agreement within tolerance and output-write checks. Raw H1 reconstruction, complete target/time validation, statistical validity, feature usefulness and trading performance remain outside its scope.

Optional forecast-only calculation

The optional forecast-only calculation runs after the main comparison passes. It reads forecast_only.csv from MQL5/Files/SessionRisk/results. This file must contain the six columns session_date, pre_open, pre_high, pre_low, pre_close and prior20_mean in this order. Dates must be valid, use YYYY.MM.DD format and be strictly increasing. StrictNumber parses the five numeric fields and SessionFeatures verifies the prices and scale. The verifier rejects rows if the model score is not finite or the probability is invalid.

Any rows accepted by the verification are written to forecast_only_predictions.csv with an empty outcome field and the evaluated field set to zero. The branch checks its writes and records its status in forecast_only_status.txt. PASS indicates the calculation completed successfully, while FAIL indicates a failed optional check.

If no input file is supplied, the script reports SKIP. A header-only input file can pass with zero scored rows. If the calculation or publication of its receipt is not completed, the status remains INCOMPLETE. The main parity outcome is independent. These receipts do not verify H1 bar closure, live data availability or forecast performance without target outcomes.

Files

File | Location | Purpose

ExportSessionRisk.mq5 | MQL5/Scripts/SessionRisk | Exports historical H1 bars, session data, and a completion receipt.

VerifySessionRisk.mq5 | MQL5/Scripts/SessionRisk | Recalculates the predictors and probabilities and compares them with the SPSS results.

SessionRiskCore.mqh | MQL5/Include/SessionRisk | Includes the price checks and shared calculation functions used by the scripts.

SessionRisk.sps | MQL5/Files/SessionRisk | Builds variables, performs logistic regression, and stores SPSS predictions and summaries.

audit_export.py | MQL5/Files/SessionRisk/tools | Verifies the exported data and independently recalculates historical windows and queues.

validate_inputs.py | MQL5/Files/SessionRisk/tools | Checks the session dataset for suitability for SPSS analysis.

audit_results.py | MQL5/Files/SessionRisk/tools | Checks SPSS predictions, labels, baselines and summaries.

spss_model.py | MQL5/Files/SessionRisk/tools | Reads and validates a supported SPSS model XML and returns its three coefficients.

extract_coefficients.py | MQL5/Files/SessionRisk/tools | Extracts the three coefficients from an SPSS model XML and saves them to a CSV file.

MQL5.zip | MQL5 | Contains the project files in their terminal folder structure. Extract the files to the data folder of MetaTrader 5 to load the scripts, include files and support files. Compile the scripts locally.
