//+------------------------------------------------------------------+ //| RBase.mqh | //| Copyright 2026, Leo. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2026, Leo." #property link "https://www.mql5.com/en/users/nique_372" #property strict #ifndef SCALERBYLEO_SRC_SCALER_RSCALER_RBASE_MQH #define SCALERBYLEO_SRC_SCALER_RSCALER_RBASE_MQH //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ #include "..\\ScalerBase.mqh" //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ namespace TSN { //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ #define ROBUSTSCALER_PERCENTILE(vec, l, h, w) ((l == h) ? (vec[l]) : ((vec[l] * (1.0 - w)) + (vec[h] * w))) //+------------------------------------------------------------------+ //| Robust Scaler Base | //+------------------------------------------------------------------+ template class RobustScalerBase : public ScalerBase { protected: vector m_medians, m_iqrs, m_iqrs_inv; virtual bool OnLoad() { return true; } //--- static __forceinline void MakeValues(const double rank, ulong &l, ulong &h, double &w); public: RobustScalerBase() : ScalerBase() { m_prefix_file = "_median_iqr.csv"; } //--- bool Save(string prefix_name, int extra_file_flags = FILE_COMMON); bool LoadByFile(string prefix_name, int extra_file_flags = FILE_COMMON); bool LoadByArr(const string &arr[]) override; //--- bool Fit(const matrix &X); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ template static __forceinline void RobustScalerBase::MakeValues(const double rank, ulong &l, ulong &h, double &w) { l = (ulong)MathFloor(rank); h = (ulong)MathCeil(rank); w = rank - l; } //+------------------------------------------------------------------+ //| Save / Load | //+------------------------------------------------------------------+ template bool RobustScalerBase::Save(string prefix_name, int extra_file_flags = FILE_COMMON) { //--- m_file_name_out = prefix_name + m_prefix_file; FileDelete(m_file_name_out); ResetLastError(); //--- const int handle = FileOpen(m_file_name_out, FILE_WRITE | FILE_CSV | extra_file_flags); if(handle == INVALID_HANDLE) { m_last_err = SCALERBYLEO_ERR_FILE_OPEN; return false; } //--- FileWrite(handle, vector_to_string(m_iqrs)); FileWrite(handle, vector_to_string(m_medians)); FileWrite(handle, m_i_start); FileWrite(handle, m_i_count); //--- FileClose(handle); return true; } //+------------------------------------------------------------------+ template bool RobustScalerBase::LoadByFile(string prefix_name, int extra_file_flags = FILE_COMMON) { //--- m_file_name_out = prefix_name + m_prefix_file; //--- ResetLastError(); const int handle = FileOpen(m_file_name_out, FILE_READ | FILE_CSV | extra_file_flags, "\n"); if(handle == INVALID_HANDLE) { m_last_err = SCALERBYLEO_ERR_FILE_OPEN; return false; } //--- CStringFunc::ToVector(FileReadString(handle), m_iqrs, ','); CStringFunc::ToVector(FileReadString(handle), m_medians, ','); m_i_start = ulong(FileReadString(handle)); m_i_count = long(FileReadString(handle)); //--- m_iqrs_inv = 1.0 / m_iqrs; //--- FileClose(handle); return (m_loaded_scaler = OnLoad()); } //+------------------------------------------------------------------+ template bool RobustScalerBase::LoadByArr(const string &arr[]) { if(ArraySize(arr) < 4) { m_last_err = SCALERBYLEO_ERR_INVALID_DATA; return false; } //--- int p = 0; CStringFunc::ToVector(arr[p++], m_iqrs, ','); CStringFunc::ToVector(arr[p++], m_medians, ','); m_i_start = ulong(arr[p++]); m_i_count = long(arr[p++]); //--- m_iqrs_inv = 1.0 / m_iqrs; //--- return (m_loaded_scaler = OnLoad()); } //+------------------------------------------------------------------+ //| Fit | //+------------------------------------------------------------------+ template bool RobustScalerBase::Fit(const matrix &X) { //--- const ulong total_cols = X.Cols(); const ulong rows = X.Rows(); if(total_cols == 0 || rows == 0) { m_last_err = SCALERBYLEO_ERR_EMPTY_INPUT; return false; } if(m_i_start >= total_cols) { m_last_err = SCALERBYLEO_ERR_INVALID_RANGE; return false; } //--- const ulong fs = SCALERBYLEO_SIZE(total_cols); //--- m_medians.Resize(fs); m_iqrs.Resize(fs); m_iqrs_inv.Resize(fs); //--- const double rs_1 = double(rows - 1); //--- make values ulong l50, h50; double w50; MakeValues(0.50 * rs_1, l50, h50, w50); ulong l25, h25; double w25; MakeValues(0.25 * rs_1, l25, h25, w25); ulong l75, h75; double w75; MakeValues(0.75 * rs_1, l75, h75, w75); //--- iter for(ulong c = 0; c < fs; c++) { //--- vector col = X.Col(m_i_start + c); col.Sort(SORT_ASCENDING); //--- const double median_v = ROBUSTSCALER_PERCENTILE(col, l50, h50, w50); const double iqr_v = ROBUSTSCALER_PERCENTILE(col, l75, h75, w75) - ROBUSTSCALER_PERCENTILE(col, l25, h25, w25); //--- if(fabs(iqr_v) < SCALER_BASE_MIN_VALUE) { m_last_err = SCALERBYLEO_ERR_DEGENERATE_SCALE; return false; } //--- m_medians[c] = median_v; m_iqrs[c] = iqr_v; m_iqrs_inv[c] = 1.0 / iqr_v; } //--- return (m_loaded_scaler = OnLoad()); } } #endif // SCALERBYLEO_SRC_SCALER_RSCALER_RBASE_MQH //+------------------------------------------------------------------+