Article-23677-EGARCH-MQL5-V.../Arch/Univariate/ar.mqh

217 lines
8.9 KiB
MQL5
Raw Permalink Normal View History

2026-07-24 23:03:06 +02:00
//+------------------------------------------------------------------+
//| ar.mqh |
//| Copyright 2025, MetaQuotes Ltd. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link "https://www.mql5.com"
#include"..\..\np.mqh"
#include"..\..\Regression\OLS.mqh"
#include"mean.mqh"
//+------------------------------------------------------------------+
//| Information criteria enum for model selection optimization levels|
//+------------------------------------------------------------------+
enum ENUM_INFO_CRITERION
{
AIC=0, // Akaike Information Criterion: Penalizes parameters linearly (2*k)
BIC // Bayesian Information Criterion: Penalizes parameters logarithmically (k*ln(N)), favoring parsimony
};
//+------------------------------------------------------------------+
//| Bits view implementation: Deconstructs numeric vector values |
//| into custom byte-chunk integer representations for masking flags |
//+------------------------------------------------------------------+
matrix bitview(vector& data, int basis, int view)
{
if(!data.Size())
return matrix::Zeros(0,0);
// Allocate container workspace rows to match data size
matrix out = matrix::Zeros(data.Size(), basis / view);
int value = 0;
int rem = 0;
for(ulong i = 0; i < out.Rows(); ++i)
{
// Isolate the upper bit segment shifted by the designated view byte width block
value = int(fabs(data[i])) / int(pow(2, 8 * view));
// Calculate the modulus remainder mapping directly to the initial column index
rem = (int)MathMod(int(fabs(data[i])), int(pow(2, 8 * view)));
out[i, 0] = rem;
// Extract sub-byte chunks iteratively across columns until bit states reduce to 0
for(ulong j = 1; j < out.Cols(); ++j)
{
if(value)
{
out[i, j] = (value < int(pow(2, 8 * view))) ? value : MathMod(value, int(pow(2, 8 * view)));
}
else
break; // Abort early if the remaining bit value is fully drained
value /= (int)pow(2, 8 * view); // Right shift bits context down to processing space
}
}
return out;
}
//+------------------------------------------------------------------+
//| Unpacks the byte-chunks from bitview into an explicit binary matrix|
//+------------------------------------------------------------------+
matrix unpackbits(matrix& bview, ulong bits)
{
if(!bits || bits < bview.Cols() || !bview.Cols())
return matrix::Zeros(0,0);
matrix out = matrix::Zeros(bview.Rows(), bits);
ulong factor = bits / bview.Cols();
ulong k = 0;
for(ulong i = 0; i < out.Rows(); ++i)
{
for(ulong j = 0; j < bview.Cols(); ++j)
{
ulong value = ulong(bview[i, j]);
// Extract individual bit states from right to left using the standard base-2 modulus operation
for(long k = 7; k > -1; --k)
{
if(value)
out[i, j * 8 + k] = MathMod(value, 2); // Map remainder (0 or 1) onto active coordinate flags
else
break; // Optimization fallback if structural bit allocations reach 0
value /= 2;
}
}
}
return out;
}
//+------------------------------------------------------------------+
//| Autoregressive AR-X(p) optimal model lag order selection routine |
//+------------------------------------------------------------------+
vector ar_select_order(vector& endog, matrix& exog, ulong maxlag, bool use_constant=true, bool global_search = false, ENUM_INFO_CRITERION ic = BIC, ulong holdback=0, ulong period=0)
{
ulong nexog = exog.Cols();
// Configure data structural vectors into base optimization specs
ArchParameters modelparams;
modelparams.y = endog;
modelparams.x = exog;
modelparams.mean_constant = use_constant;
// Instantiate an AR-X linear structure to cleanly map out target regressors
ARX model;
if(!model.initialize(modelparams))
return vector::Zeros(0);
vector y = endog;
matrix x = model.get_regressors(); // Resolves internal matrix container layout containing all lags and raw variables
// Pin down the starting index offset for lag regressors inside the global variables space
ulong base_col = x.Cols() - nexog - maxlag;
vector sel = vector::Ones(x.Cols()); // Boolean selector mask tracking active features columns
vector lags[]; // Dynamic tracker mapping checked lag indexes variants
double ics[]; // Container recording evaluation metric scores output per loop step
int index[]; // Index tracker used for sorting sorting loops
matrix xx;
OLS ols;
// --- APPROACH A: Nested Sequential Search (Nested Lag Sets) ---
if(!global_search)
{
// Blank out candidate lag features space completely prior to sequential loop expansion entries
np::fillVector(sel, 0.0, long(base_col), long(base_col + maxlag));
ArrayResize(lags, int(maxlag + 1));
// Incrementally expand the lookback length from 0 to maxlag (testing nested subsets: e.g., [1], [1,2], [1,2,3])
for(ulong i = 0; i < maxlag + 1; ++i)
{
index.Push(int(i));
np::fillVector(sel, 1., long(base_col), long(base_col + i)); // Activate columns up to lag step i
if(!sel.Sum())
continue;
// Filter out deactivated components to form a clean regression payload matrix
xx = matrix::Zeros(y.Size(), ulong(sel.Sum()));
for(ulong k = 0, kk = 0; k < sel.Size(); ++k)
{
if(sel[k])
xx.Col(x.Col(k), kk++);
}
lags[i] = np::range(int(i + 1), 1); // Record sequentially assigned active lag steps arrays
// Fit the candidate linear model using standard Ordinary Least Squares
if(!ols.Fit(y, xx))
return vector::Zeros(0);
// Record chosen statistical criteria optimization score metric
ics.Push(ic == AIC ? ols.Aic() : ols.Bic());
}
}
// --- APPROACH B: Combinatorial Global Search (All Possible Subsets) ---
else
{
// Generate a sequence of integers up to 2^maxlag representing every possible lag combination
vector b = np::arange(ulong(pow(2, maxlag)));
matrix bview = bitview(b, 4, 1);
matrix bits = unpackbits(bview, 32); // Convert integers to a dense binary mask grid matrix
matrix bb;
// Reverse bit columns alignment chunks to correct for byte ordering anomalies
for(ulong i = 0; i < 4; ++i)
{
bb = np::sliceMatrixCols(bits, long(8 * i), long(8 * (i + 1)));
np::reverseMatrixCols(bb);
np::matrixCopyCols(bits, bb, long(8 * i), long(8 * (i + 1)));
}
// Extract the isolated lookback target validation search mask space matrix
matrix masks = np::sliceMatrixCols(bits, 0, long(maxlag));
ArrayResize(lags, int(masks.Rows()));
// Loop across each unique combinatorial combination pattern row
for(ulong i = 0; i < masks.Rows(); ++i)
{
index.Push(int(i));
// Apply the unique combinatorial configuration row mask to the feature selector vector
np::vectorCopy(sel, masks.Row(i), long(base_col), long(base_col + maxlag));
if(!sel.Sum())
continue; // Skip evaluation if no lag components are selected
xx = matrix::Zeros(y.Size(), ulong(sel.Sum()));
// Extract valid active columns to prepare the current iteration matrix setup
for(ulong k = 0, kk = 0; k < sel.Size(); ++k)
{
if(sel[k])
xx.Col(x.Col(k), kk++);
}
// Allocate a custom tracking vector mapping out exactly which lags are active in this trial
lags[i] = vector::Zeros(ulong(masks.Row(i).Sum()));
for(ulong j = 0, k = 0; j < lags[i].Size(); ++j)
if(masks[i, j])
lags[i][k++] = double(j + 1);
// Execute OLS regression step
if(!ols.Fit(y, xx))
return vector::Zeros(0);
ics.Push(ic == AIC ? ols.Aic() : ols.Bic());
}
}
// --- Step 3: Identify and Return the Globally Optimal Model Layout ---
// Sort evaluation log score outputs in ascending order (lower values signal optimal balance)
MathQuickSortAscending(ics, index, 0, int(ics.Size() - 1));
// Return the lag profile matching the lowest information criteria score value (index position [0])
return lags[index[0]];
}
//+------------------------------------------------------------------+