260 righe
Nessun fine linea
9,2 KiB
MQL5
260 righe
Nessun fine linea
9,2 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| Statistics.mqh |
|
|
//| Copyright 2026, MetaQuotes Ltd. |
|
|
//| www.mql5.com |
|
|
//+------------------------------------------------------------------+
|
|
#ifndef REQUEST_LATENCY_LAB_STATISTICS_MQH
|
|
#define REQUEST_LATENCY_LAB_STATISTICS_MQH
|
|
|
|
#include "..\..\Include\RequestLatencyLab\Models.mqh"
|
|
#include "..\..\Include\RequestLatencyLab\Legacy\BenchmarkStatistics.mqh"
|
|
//+------------------------------------------------------------------+
|
|
//| Классификация одной попытки для метрики (ТЗ §9): применимость |
|
|
//| сначала, затем пригодность. |
|
|
//+------------------------------------------------------------------+
|
|
class CQuality
|
|
{
|
|
public:
|
|
static ENUM_LAB_FITNESS FinalCategory(const bool conflict,
|
|
const bool interrupted,
|
|
const bool late,
|
|
const bool missing);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Строка summary (CSV schema summary.csv). |
|
|
//+------------------------------------------------------------------+
|
|
struct StatSummaryRow
|
|
{
|
|
string experiment_id;
|
|
string condition_id;
|
|
string series_id;
|
|
string metric_id;
|
|
string outcome_group;
|
|
string population; // PRIMARY / LATE_DIAGNOSTIC / CALLBACK_DIAGNOSTIC
|
|
string observation_unit; // REQUEST / CALLBACK
|
|
string segment_key;
|
|
string source_set_id;
|
|
string unit; // us
|
|
ulong n_attempted;
|
|
ulong n_applicable;
|
|
ulong n_not_applicable;
|
|
ulong n_applicability_unknown;
|
|
ulong n_valid;
|
|
ulong n_missing;
|
|
ulong n_late;
|
|
ulong n_conflict;
|
|
ulong n_interrupted;
|
|
ulong n_used;
|
|
double minimum;
|
|
double mean;
|
|
double median; // p50
|
|
double p90;
|
|
double p95;
|
|
double p99;
|
|
double p999; // p99.9
|
|
double maximum;
|
|
double stddev;
|
|
bool has_stats;
|
|
string percentile_method; // LINEAR_N_MINUS_ONE
|
|
double p95_tail_expected_n;
|
|
double p99_tail_expected_n;
|
|
double p999_tail_expected_n;
|
|
string tail_warning_codes;
|
|
ulong n_deadline_exceeded;
|
|
ulong deadline_denominator;
|
|
double deadline_rate;
|
|
ulong n_timestamp_missing_at_deadline;
|
|
double missing_timestamp_rate;
|
|
bool conditional_distribution;
|
|
|
|
void Zero(void);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| CStatistics — расчёт summary из значений пригодной выборки. |
|
|
//+------------------------------------------------------------------+
|
|
class CStatistics
|
|
{
|
|
public:
|
|
//--- расчёт статистик по непустой выборке; p99.9 отдельно
|
|
static bool Compute(double &row_values[], const int count, StatSummaryRow &row);
|
|
//--- балансы счётчиков (ТЗ §9)
|
|
static ulong SumApplicable(const StatSummaryRow &r)
|
|
{
|
|
return(r.n_conflict + r.n_interrupted + r.n_late + r.n_missing + r.n_valid);
|
|
}
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Внешние определения методов. |
|
|
//+------------------------------------------------------------------+
|
|
ENUM_LAB_FITNESS CQuality::FinalCategory(const bool conflict,
|
|
const bool interrupted, const bool late, const bool missing)
|
|
{
|
|
if(conflict)
|
|
return(LAB_FIT_CONFLICT);
|
|
if(interrupted)
|
|
return(LAB_FIT_INTERRUPTED);
|
|
if(late)
|
|
return(LAB_FIT_LATE);
|
|
if(missing)
|
|
return(LAB_FIT_MISSING);
|
|
return(LAB_FIT_VALID);
|
|
}
|
|
void StatSummaryRow::Zero(void)
|
|
{
|
|
experiment_id = "";
|
|
condition_id = "";
|
|
series_id = "";
|
|
metric_id = "";
|
|
outcome_group = "";
|
|
population = "PRIMARY";
|
|
observation_unit = "REQUEST";
|
|
segment_key = "{}";
|
|
source_set_id = "";
|
|
unit = "us";
|
|
n_attempted = 0;
|
|
n_applicable = 0;
|
|
n_not_applicable = 0;
|
|
n_applicability_unknown = 0;
|
|
n_valid = 0;
|
|
n_missing = 0;
|
|
n_late = 0;
|
|
n_conflict = 0;
|
|
n_interrupted = 0;
|
|
n_used = 0;
|
|
minimum = 0.0;
|
|
mean = 0.0;
|
|
median = 0.0;
|
|
p90 = 0.0;
|
|
p95 = 0.0;
|
|
p99 = 0.0;
|
|
p999 = 0.0;
|
|
maximum = 0.0;
|
|
stddev = 0.0;
|
|
has_stats = false;
|
|
percentile_method = "LINEAR_N_MINUS_ONE";
|
|
p95_tail_expected_n = 0.0;
|
|
p99_tail_expected_n = 0.0;
|
|
p999_tail_expected_n = 0.0;
|
|
tail_warning_codes = "[]";
|
|
n_deadline_exceeded = 0;
|
|
deadline_denominator = 0;
|
|
deadline_rate = 0.0;
|
|
n_timestamp_missing_at_deadline = 0;
|
|
missing_timestamp_rate = 0.0;
|
|
conditional_distribution = false;
|
|
}
|
|
bool CStatistics::Compute(double &row_values[], const int count, StatSummaryRow &row)
|
|
{
|
|
if(count <= 0)
|
|
return(false);
|
|
double tmp[];
|
|
if(ArrayResize(tmp, count) != count)
|
|
return(false);
|
|
for(int i = 0; i < count; i++)
|
|
tmp[i] = row_values[i];
|
|
row.n_used = (ulong)count;
|
|
row.has_stats = true;
|
|
if(!LabMinValue(tmp, count, row.minimum) ||
|
|
!LabMaxValue(tmp, count, row.maximum) ||
|
|
!LabAvgValue(tmp, count, row.mean))
|
|
return(false);
|
|
if(!LabPercentile(tmp, count, 50.0, row.median) ||
|
|
!LabPercentile(tmp, count, 90.0, row.p90) ||
|
|
!LabPercentile(tmp, count, 95.0, row.p95) ||
|
|
!LabPercentile(tmp, count, 99.0, row.p99) ||
|
|
!LabPercentile(tmp, count, 99.9, row.p999))
|
|
return(false);
|
|
LabStdDev(tmp, count, row.stddev);
|
|
row.p95_tail_expected_n = count * 0.05;
|
|
row.p99_tail_expected_n = count * 0.01;
|
|
row.p999_tail_expected_n = count * 0.001;
|
|
//--- предупреждение о редком хвосте: tail_expected_n < 10 (ТЗ §7)
|
|
string warns = "[";
|
|
bool first = true;
|
|
if(row.p95_tail_expected_n < 10.0)
|
|
{
|
|
warns += (first ? "" : ",") + "\"P95_TAIL_LT10\"";
|
|
first = false;
|
|
}
|
|
if(row.p99_tail_expected_n < 10.0)
|
|
{
|
|
warns += (first ? "" : ",") + "\"P99_TAIL_LT10\"";
|
|
first = false;
|
|
}
|
|
if(row.p999_tail_expected_n < 10.0)
|
|
{
|
|
warns += (first ? "" : ",") + "\"P999_TAIL_LT10\"";
|
|
first = false;
|
|
}
|
|
warns += "]";
|
|
row.tail_warning_codes = warns;
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Гистограмма неотрицательных интервалов (мс), ТЗ §7. |
|
|
//+------------------------------------------------------------------+
|
|
#define LAB_HISTOGRAM_BINS 10
|
|
#define LAB_HIST_BIN_MS 256000ul
|
|
//+------------------------------------------------------------------+
|
|
//| Подсчёт значений по бинам гистограммы |
|
|
//+------------------------------------------------------------------+
|
|
bool HistogramCount(const long &values[], const int count, ulong &bins[])
|
|
{
|
|
ArrayInitialize(bins, 0);
|
|
if(count <= 0)
|
|
return(false);
|
|
const ulong bounds[LAB_HISTOGRAM_BINS] =
|
|
{1000, 2000, 4000, 8000, 16000, 32000, 64000, 128000, 256000, 0};
|
|
for(int i = 0; i < count; i++)
|
|
{
|
|
const long v = values[i];
|
|
if(v < 0)
|
|
continue; // только неотрицательные интервалы
|
|
int bin = -1;
|
|
for(int b = 0; b < LAB_HISTOGRAM_BINS - 1; b++)
|
|
{
|
|
if((ulong)v < bounds[b])
|
|
{
|
|
bin = b;
|
|
break;
|
|
}
|
|
}
|
|
if(bin == -1)
|
|
bin = LAB_HISTOGRAM_BINS - 1;
|
|
bins[bin]++;
|
|
}
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Знаки знаковой разности (T2-T4): NEGATIVE/ZERO/POSITIVE. |
|
|
//+------------------------------------------------------------------+
|
|
void OffsetSignCounts(const long &values[], const int count,
|
|
ulong &n_neg, ulong &n_zero, ulong &n_pos)
|
|
{
|
|
n_neg = 0;
|
|
n_zero = 0;
|
|
n_pos = 0;
|
|
for(int i = 0; i < count; i++)
|
|
{
|
|
if(values[i] < 0)
|
|
n_neg++;
|
|
else
|
|
if(values[i] == 0)
|
|
n_zero++;
|
|
else
|
|
n_pos++;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Вычисление знаковой разности без беззнакового вычитания с cast |
|
|
//| (ТЗ §9): signed = сравнение + модуль разности. |
|
|
//+------------------------------------------------------------------+
|
|
long SignedDelta(const ulong a, const ulong b)
|
|
{
|
|
if(a >= b)
|
|
return(long)(a - b);
|
|
return(-(long)(b - a));
|
|
}
|
|
|
|
#endif // REQUEST_LATENCY_LAB_STATISTICS_MQH
|
|
//+------------------------------------------------------------------+ |