Article-22015-Fixed-Width-F.../FFDValidation.mq5
PatrickNjoroge743 d8ccf65435 new files added
2026-06-19 05:17:42 +03:00

132 lines
4.7 KiB
MQL5

//+------------------------------------------------------------------+
//| FFDValidation.mq5 — Export FFD values for Python cross-check |
//| Copyright 2025, Patrick M. Njoroge |
//| |
//| Run as a script. Exports close prices and FFD values to CSV. |
//| Compare the CSV output against Python's frac_diff_ffd() to |
//| verify numerical equivalence (see ffd_cross_validate.py). |
//+------------------------------------------------------------------+
#property copyright "Patrick M. Njoroge"
#property link "https://www.mql5.com/en/users/patricknjoroge743"
#property version "1.11"
#property description "Exports FFD values for cross-validation with Python"
#property script_show_inputs
//--- Inputs
input double InpD = 0.4; // Differencing order d
input double InpThreshold = 1e-5; // Weight cutoff threshold
input bool InpUseLog = true; // Log-transform prices
input int InpBars = 5000; // Number of bars to export
//--- Include the computation engine from MQL5\Include\
#include "FFDEngine.mqh"
//+------------------------------------------------------------------+
//| Script program start function |
//+------------------------------------------------------------------+
void OnStart()
{
//--- Initialize engine
CFFDEngine engine;
if(!engine.Init(InpD, InpThreshold, InpUseLog))
{
Print("Validation: engine initialization failed");
return;
}
PrintFormat("Validation: requesting %d bars, engine width=%d",
InpBars, engine.GetWidth());
//--- Copy close prices
double close[];
int copied = CopyClose(_Symbol, _Period, 0, InpBars, close);
if(copied < engine.GetMinBars())
{
PrintFormat("Not enough bars: got %d, need at least %d",
copied, engine.GetMinBars());
return;
}
//--- Ensure chronological order (oldest first).
// CopyClose returns chronological by default, but if any other
// code in the terminal has set ArraySetAsSeries on this buffer
// globally, the order silently reverses and all FFD values are
// wrong. This defensive call costs nothing and prevents that.
ArraySetAsSeries(close, false);
PrintFormat("Copied %d bars", copied);
//--- Copy timestamps for reference
datetime time[];
CopyTime(_Symbol, _Period, 0, InpBars, time);
ArraySetAsSeries(time, false);
//--- Compute FFD for all bars
double ffd_buffer[];
ArrayResize(ffd_buffer, copied);
engine.ComputeBuffer(close, ffd_buffer, copied, 0);
//--- Write to CSV
string filename = StringFormat("ffd_validation_%s_%s_d%.2f.csv",
_Symbol,
EnumToString(_Period),
InpD);
int file = FileOpen(filename, FILE_WRITE | FILE_CSV, ",");
if(file == INVALID_HANDLE)
{
Print("Cannot open file: ", filename);
return;
}
//--- Header
FileWrite(file, "bar_index", "datetime", "close", "ffd");
//--- Data rows (skip EMPTY_VALUE bars)
int written = 0;
for(int i = 0; i < copied; i++)
{
if(ffd_buffer[i] != EMPTY_VALUE)
{
FileWrite(file,
IntegerToString(i),
TimeToString(time[i], TIME_DATE | TIME_SECONDS),
DoubleToString(close[i], 8),
DoubleToString(ffd_buffer[i], 12));
written++;
}
}
FileClose(file);
PrintFormat("Validation file written: %s (%d data rows out of %d bars)",
filename, written, copied);
PrintFormat("Expected %d empty bars (width) + %d data bars = %d total",
engine.GetWidth(), written, engine.GetWidth() + written);
//--- Self-check: verify the first data bar via Compute()
if(written > 0)
{
int first_bar = engine.GetWidth();
int window = engine.GetMinBars();
// Extract the first valid price window and compute independently
double check_prices[];
ArrayResize(check_prices, window);
for(int k = 0; k < window; k++)
check_prices[k] = close[k];
double check_val = engine.Compute(check_prices, window);
double buffer_val = ffd_buffer[first_bar];
double diff = MathAbs(check_val - buffer_val);
PrintFormat("Self-check (bar %d): Compute=%.12f, Buffer=%.12f, diff=%.2e",
first_bar, check_val, buffer_val, diff);
if(diff > 1e-12)
Print("WARNING: Compute() and ComputeBuffer() disagree.");
else
Print("Self-check PASSED: Compute() and ComputeBuffer() agree.");
}
}
//+------------------------------------------------------------------+