190 lines
No EOL
7.1 KiB
MQL5
190 lines
No EOL
7.1 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| APARCH_Benchmark.mq5 |
|
|
//| Copyright 2025, MetaQuotes Ltd. |
|
|
//| https://www.mql5.com |
|
|
//+------------------------------------------------------------------+
|
|
#property copyright "Copyright 2025, MetaQuotes Ltd."
|
|
#property link "https://www.mql5.com"
|
|
#property version "1.00"
|
|
#property script_show_inputs
|
|
//---
|
|
#include"Arch\univariate\mean.mqh"
|
|
//--- Script Inputs
|
|
input int InpHoldoutSize = 100; // Forecast horizon
|
|
input int InpInSampleSize = 1000; // In-Sample Training Bar Count
|
|
input ENUM_TIMEFRAMES InpTimeframe = PERIOD_D1;// Execution Timeframe
|
|
input double InpScaleFactor = 100.0; // Scaling multiplier
|
|
input ENUM_FORECAST_METHOD InpForeCastMethod = FORECAST_ANALYTIC; // Forecasting method
|
|
|
|
// Struct to store final benchmarking metrics for each model
|
|
struct ModelPerformance
|
|
{
|
|
string model_name;
|
|
double aic;
|
|
double bic;
|
|
double oos_mse;
|
|
double oos_mae;
|
|
double log_like;
|
|
};
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Script program start function |
|
|
//+------------------------------------------------------------------+
|
|
void OnStart()
|
|
{
|
|
if(InpHoldoutSize<=0 || InpInSampleSize<50)
|
|
{
|
|
Alert("Invalid input parameters for InpHoldoutSize and/or InpInSampleSize\nBoth must be >0 and >=50 respectively.");
|
|
return;
|
|
}
|
|
Print("==========================================================");
|
|
Print(" Starting Volatility Model Evaluation: APARCH vs. Competitors");
|
|
Print("==========================================================");
|
|
|
|
// Prepare Returns Data
|
|
ulong total_needed = (ulong)(InpInSampleSize + InpHoldoutSize + 1);
|
|
vector close_prices;
|
|
|
|
if(!close_prices.CopyRates(_Symbol, InpTimeframe, COPY_RATES_CLOSE, 0, (uint)total_needed))
|
|
{
|
|
Print("Error: Failed to fetch price data.");
|
|
return;
|
|
}
|
|
|
|
// Compute Log Returns: r_t = ln(P_t / P_{t-1}) * scalefactor
|
|
ulong n_returns = close_prices.Size() - 1;
|
|
vector returns(n_returns);
|
|
for(ulong i = 0; i < n_returns; ++i)
|
|
{
|
|
returns[i] = MathLog(close_prices[i + 1] / close_prices[i]) * InpScaleFactor;
|
|
}
|
|
|
|
// Split into In-Sample and Out-of-Sample arrays
|
|
ulong in_sample_len = n_returns - (ulong)InpHoldoutSize;
|
|
vector in_sample_returns = np::sliceVector(returns, 0, (long)in_sample_len);
|
|
vector oos_returns = np::sliceVector(returns, (long)in_sample_len, (long)n_returns);
|
|
|
|
// Realized variance benchmark for OOS (using squared returns as proxy)
|
|
vector oos_realized_variance = MathPow(oos_returns, 2.0);
|
|
|
|
// Define Volatility Models to Compare
|
|
ENUM_VOLATILITY_MODEL models[] =
|
|
{
|
|
VOL_GARCH,
|
|
VOL_GJR_GARCH,
|
|
VOL_EGARCH,
|
|
VOL_TARCH,
|
|
VOL_APARCH
|
|
};
|
|
|
|
string model_names[] =
|
|
{
|
|
"GARCH(1,1)",
|
|
"GJR-GARCH(1,1,1)",
|
|
"EGARCH(1,1,1)",
|
|
"TARCH(1,1,1)",
|
|
"APARCH(1,1,1)"
|
|
};
|
|
|
|
uint num_models = ArraySize(models);
|
|
ModelPerformance results[];
|
|
ArrayResize(results, num_models);
|
|
|
|
// Loop Through and Fit Each Model
|
|
for(uint i = 0; i < num_models; ++i)
|
|
{
|
|
PrintFormat("\n--- Fitting Model [%d/%d]: %s ---", i + 1, num_models, model_names[i]);
|
|
|
|
// Populate base ARCH parameters structure
|
|
ArchParameters params;
|
|
params.observations = in_sample_returns;
|
|
params.mean_model_type = MEAN_CONSTANT;
|
|
params.vol_model_type = models[i];
|
|
params.dist_type = DIST_NORMAL;
|
|
params.garch_p = 1;
|
|
params.garch_o = 1; // Asymmetry term enabled where applicable
|
|
params.garch_q = 1;
|
|
|
|
// Special APARCH specific defaults if selected
|
|
if(models[i] == VOL_APARCH)
|
|
{
|
|
params.aparch_delta = EMPTY_VALUE; // Jointly estimate dynamic power parameter
|
|
params.aparch_common_asym = false;
|
|
}
|
|
|
|
// Construct model instance (using CConstantMean model wrapper)
|
|
ConstantMean model;
|
|
if(!model.initialize(params))
|
|
{
|
|
PrintFormat("Failed to initialize model: %s", model_names[i]);
|
|
continue;
|
|
}
|
|
|
|
// Optimize parameters over In-Sample dataset
|
|
ArchModelResult fit_result = model.fit();
|
|
|
|
if(fit_result.solver_return_code != 0)
|
|
{
|
|
PrintFormat("Warning: Solver ended with code %d for model %s", fit_result.solver_return_code, model_names[i]);
|
|
}
|
|
|
|
// Record In-Sample Information Criteria
|
|
results[i].model_name = model_names[i];
|
|
results[i].log_like = fit_result.loglikelihood;
|
|
results[i].aic = fit_result.aic();
|
|
results[i].bic = fit_result.bic();
|
|
|
|
// Evaluate Out-of-Sample (OOS) Volatility Forecasts
|
|
matrix dummy_x[];
|
|
vector fitted_params = model.get_params();
|
|
|
|
// Perform multi-step analytical forecast across the holdout period
|
|
ArchForecast fcast = model.forecast(fitted_params, dummy_x, (ulong)InpHoldoutSize, -1, InpForeCastMethod);
|
|
|
|
if(fcast.variance.Rows() > 0)
|
|
{
|
|
// Extract 1-step to H-step variance forecast vector
|
|
vector pred_variance = fcast.variance.Row(fcast.variance.Rows() - 1);
|
|
|
|
// Calculate Forecast Error Metrics (Predicted Variance vs Realized Proxy)
|
|
vector errors = oos_realized_variance - pred_variance;
|
|
|
|
results[i].oos_mse = MathPow(errors, 2.0).Mean();
|
|
results[i].oos_mae = MathAbs(errors).Mean();
|
|
}
|
|
else
|
|
{
|
|
results[i].oos_mse = DBL_MAX;
|
|
results[i].oos_mae = DBL_MAX;
|
|
}
|
|
}
|
|
|
|
// Print Comparison Summary Report Table
|
|
Print("\n=========================================================================");
|
|
Print(" MODEL COMPARISON RESULTS ");
|
|
Print("=========================================================================");
|
|
PrintFormat("%-15s | %-12s | %-12s | %-12s | %-12s", "Model", "AIC", "BIC", "OOS MSE", "OOS MAE");
|
|
Print("-------------------------------------------------------------------------");
|
|
|
|
int best_aic_idx = 0;
|
|
int best_mse_idx = 0;
|
|
|
|
for(uint i = 0; i < num_models; ++i)
|
|
{
|
|
PrintFormat("%-15s | %-12.4f | %-12.4f | %-12.6f | %-12.6f",
|
|
results[i].model_name,
|
|
results[i].aic,
|
|
results[i].bic,
|
|
results[i].oos_mse,
|
|
results[i].oos_mae);
|
|
|
|
if(results[i].aic < results[best_aic_idx].aic) best_aic_idx = (int)i;
|
|
if(results[i].oos_mse < results[best_mse_idx].oos_mse) best_mse_idx = (int)i;
|
|
}
|
|
|
|
Print("-------------------------------------------------------------------------");
|
|
PrintFormat("Optimal In-Sample Fit (Lowest AIC): %s", results[best_aic_idx].model_name);
|
|
PrintFormat("Optimal Out-of-Sample Robustness (Lowest MSE): %s", results[best_mse_idx].model_name);
|
|
Print("=========================================================================");
|
|
}
|
|
//+------------------------------------------------------------------+ |