259 linhas
Sem EOL
10 KiB
MQL5
259 linhas
Sem EOL
10 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| Configuration.mqh |
|
|
//| Copyright 2026, MetaQuotes Ltd. |
|
|
//| www.mql5.com |
|
|
//+------------------------------------------------------------------+
|
|
#ifndef REQUEST_LATENCY_LAB_CONFIGURATION_MQH
|
|
#define REQUEST_LATENCY_LAB_CONFIGURATION_MQH
|
|
|
|
#include "..\..\Include\RequestLatencyLab\Models.mqh"
|
|
//+------------------------------------------------------------------+
|
|
//| Режимы работы советника (ТЗ §1). |
|
|
//+------------------------------------------------------------------+
|
|
enum ENUM_LAB_RUN_MODE
|
|
{
|
|
LAB_RUN_LOCAL_ONLY = 0, // по умолчанию; без торговли
|
|
LAB_RUN_PENDING_CREATE_DELETE,
|
|
LAB_RUN_MARKET_ORDER
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Настройки лаборатории (заполняются из input советника). |
|
|
//+------------------------------------------------------------------+
|
|
struct LabSettings
|
|
{
|
|
string campaign_id;
|
|
string session_prefix;
|
|
ENUM_LAB_EXPERIMENT experiment_id;
|
|
int series_id; // 1..5
|
|
string symbol;
|
|
ulong magic;
|
|
int deviation_points;
|
|
int pending_distance_ticks; // D
|
|
int count_per_condition; // 100
|
|
int warmup_per_condition; // 10
|
|
int outcome_timeout_ms; // 5000
|
|
int late_grace_ms; // 1000
|
|
int pause_ms; // 1000
|
|
int timer_requested_ms; // 20
|
|
int feature_window_sec; // 60
|
|
int feature_max_age_ms; // 1000
|
|
int quote_max_age_ms;
|
|
string calibration_id;
|
|
ulong seed;
|
|
int cleanup_max_attempts; // 1
|
|
int sample_capacity; // 2048
|
|
int event_capacity; // 65536
|
|
int error_reserve_capacity;
|
|
double volume_units_tolerance; // 1e-7
|
|
int address_read_budget;
|
|
int history_scan_budget;
|
|
int e3_campaign_limit_min; // 0=без лимита; иначе минуты
|
|
ENUM_LAB_RUN_MODE run_mode;
|
|
string data_origin; // DEMO/SYNTHETIC
|
|
string configuration_id;
|
|
|
|
void Zero(void);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| CConfiguration — проверка и фиксация конфигурации. |
|
|
//+------------------------------------------------------------------+
|
|
class CConfiguration
|
|
{
|
|
public:
|
|
//--- базовая проверка входных параметров
|
|
static bool Validate(const LabSettings &s, LabError &error);
|
|
//--- правила символа из терминала
|
|
static bool BuildSymbolRules(const string symbol, SymbolRules &rules, LabError &error);
|
|
//--- описание символа для манифеста
|
|
static string ManifestSymbolInfo(const SymbolRules &r)
|
|
{
|
|
return(StringFormat("%s|digits=%d|point=%.10g|tick_size=%.10g|vol_min=%.4g|vol_max=%.4g|vol_step=%.4g",
|
|
r.symbol, r.digits, r.point, r.tick_size,
|
|
r.volume_min, r.volume_max, r.volume_step));
|
|
}
|
|
//--- минимальный допустимый объём (ТЗ §2.4). Фиксируется до серии.
|
|
static double MinVolume(const SymbolRules &r) { return(r.volume_min); }
|
|
//--- детерминированный отпечаток конфигурации (без случайности и дат)
|
|
static string ConfigurationId(const LabSettings &s, const SymbolRules &rules);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Внешние определения методов. |
|
|
//+------------------------------------------------------------------+
|
|
void LabSettings::Zero(void)
|
|
{
|
|
campaign_id = "";
|
|
session_prefix = "";
|
|
experiment_id = LAB_EXP_E1;
|
|
series_id = 1;
|
|
symbol = "";
|
|
magic = 0;
|
|
deviation_points = 0;
|
|
pending_distance_ticks = 0;
|
|
count_per_condition = 100;
|
|
warmup_per_condition = 10;
|
|
outcome_timeout_ms = 5000;
|
|
late_grace_ms = 1000;
|
|
pause_ms = 1000;
|
|
timer_requested_ms = 20;
|
|
feature_window_sec = 60;
|
|
feature_max_age_ms = 1000;
|
|
quote_max_age_ms = 500;
|
|
calibration_id = "";
|
|
seed = 1;
|
|
cleanup_max_attempts = 1;
|
|
sample_capacity = 2048;
|
|
event_capacity = 65536;
|
|
error_reserve_capacity = 4096;
|
|
volume_units_tolerance = 1e-7;
|
|
address_read_budget = 20000;
|
|
history_scan_budget = 100000;
|
|
e3_campaign_limit_min = 30;
|
|
run_mode = LAB_RUN_LOCAL_ONLY;
|
|
data_origin = "SYNTHETIC";
|
|
configuration_id = "";
|
|
}
|
|
bool CConfiguration::Validate(const LabSettings &s, LabError &error)
|
|
{
|
|
error.Reset();
|
|
error.component = LAB_COMP_CONFIG;
|
|
if(s.count_per_condition <= 0 || s.warmup_per_condition < 0)
|
|
{
|
|
error.code = 1;
|
|
error.severity = LAB_SEV_BLOCKER;
|
|
error.message = "count_per_condition must be >0";
|
|
return(false);
|
|
}
|
|
if(s.warmup_per_condition > 0 && s.warmup_per_condition >= s.count_per_condition)
|
|
{
|
|
error.code = 2;
|
|
error.severity = LAB_SEV_BLOCKER;
|
|
error.message = "warmup must be smaller than count_per_condition";
|
|
return(false);
|
|
}
|
|
if(s.series_id < 1 || s.series_id > 5)
|
|
{
|
|
error.code = 3;
|
|
error.severity = LAB_SEV_BLOCKER;
|
|
error.message = "series_id must be 1..5";
|
|
return(false);
|
|
}
|
|
if(s.outcome_timeout_ms <= 0 || s.late_grace_ms < 0)
|
|
{
|
|
error.code = 4;
|
|
error.severity = LAB_SEV_BLOCKER;
|
|
error.message = "timeouts must be positive";
|
|
return(false);
|
|
}
|
|
if(s.pause_ms < 0 || s.timer_requested_ms <= 0)
|
|
{
|
|
error.code = 5;
|
|
error.severity = LAB_SEV_BLOCKER;
|
|
error.message = "pause/timer must be valid";
|
|
return(false);
|
|
}
|
|
if(s.feature_window_sec <= 0 || s.feature_max_age_ms <= 0)
|
|
{
|
|
error.code = 6;
|
|
error.severity = LAB_SEV_BLOCKER;
|
|
error.message = "feature window must be positive";
|
|
return(false);
|
|
}
|
|
if(s.seed == 0 || s.seed >= 2147483646ul)
|
|
{
|
|
error.code = 7;
|
|
error.severity = LAB_SEV_BLOCKER;
|
|
error.message = "seed must be in [1,2147483646]";
|
|
return(false);
|
|
}
|
|
if(s.sample_capacity <= 0 || s.event_capacity <= 0)
|
|
{
|
|
error.code = 8;
|
|
error.severity = LAB_SEV_BLOCKER;
|
|
error.message = "capacities must be positive";
|
|
return(false);
|
|
}
|
|
if(s.volume_units_tolerance <= 0.0 || s.volume_units_tolerance >= 1e-3)
|
|
{
|
|
error.code = 9;
|
|
error.severity = LAB_SEV_BLOCKER;
|
|
error.message = "volume tolerance must be in (0,1e-3)";
|
|
return(false);
|
|
}
|
|
if(s.cleanup_max_attempts < 1 || s.cleanup_max_attempts > 10)
|
|
{
|
|
error.code = 10;
|
|
error.severity = LAB_SEV_BLOCKER;
|
|
error.message = "cleanup_max_attempts must be 1..10";
|
|
return(false);
|
|
}
|
|
if(s.e3_campaign_limit_min < 0 || s.e3_campaign_limit_min > 1440)
|
|
{
|
|
error.code = 11;
|
|
error.severity = LAB_SEV_BLOCKER;
|
|
error.message = "e3_campaign_limit_min must be 0..1440";
|
|
return(false);
|
|
}
|
|
//--- data_origin задаётся оператором в файле запуска
|
|
//--- data_origin устанавливается в FillSettings (не здесь)
|
|
//--- торговый режим требует data_origin оператора (проверка выше)
|
|
//--- data_origin=DEMO задаётся в настроечном файле запуска
|
|
return(true);
|
|
}
|
|
bool CConfiguration::BuildSymbolRules(const string symbol, SymbolRules &rules, LabError &error)
|
|
{
|
|
error.Reset();
|
|
error.component = LAB_COMP_CONFIG;
|
|
if(StringLen(symbol) == 0)
|
|
{
|
|
error.code = 20;
|
|
error.severity = LAB_SEV_BLOCKER;
|
|
error.message = "symbol not set";
|
|
return(false);
|
|
}
|
|
if(!SymbolSelect(symbol, true))
|
|
{
|
|
error.code = 21;
|
|
error.severity = LAB_SEV_BLOCKER;
|
|
error.message = "symbol unavailable in Market Watch";
|
|
return(false);
|
|
}
|
|
ZeroMemory(rules);
|
|
rules.symbol = symbol;
|
|
rules.digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
|
|
rules.point = SymbolInfoDouble(symbol, SYMBOL_POINT);
|
|
rules.tick_size = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE);
|
|
rules.volume_min = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
|
|
rules.volume_max = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
|
|
rules.volume_step = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
|
|
rules.filling_mode = SymbolInfoInteger(symbol, SYMBOL_FILLING_MODE);
|
|
rules.trade_mode = SymbolInfoInteger(symbol, SYMBOL_TRADE_MODE);
|
|
rules.bid = SymbolInfoDouble(symbol, SYMBOL_BID);
|
|
rules.ask = SymbolInfoDouble(symbol, SYMBOL_ASK);
|
|
if(!rules.IsValid())
|
|
{
|
|
error.code = 22;
|
|
error.severity = LAB_SEV_BLOCKER;
|
|
error.message = "symbol rules invalid (tick size or volume step missing)";
|
|
return(false);
|
|
}
|
|
return(true);
|
|
}
|
|
string CConfiguration::ConfigurationId(const LabSettings &s, const SymbolRules &rules)
|
|
{
|
|
string id = StringFormat("%s|exp=%s|ser=%d|sym=%s|mag=%I64u|dev=%d|D=%d|n=%d|w=%d|t=%d|g=%d|pause=%d|timer=%d|win=%d|age=%d|quote=%d|cal=%s|seed=%I64u|cln=%d|e3=%d|cap=%d/%d|tol=%.10g|mode=%d",
|
|
s.campaign_id, LabExperimentName(s.experiment_id), s.series_id,
|
|
s.symbol, s.magic, s.deviation_points, s.pending_distance_ticks,
|
|
s.count_per_condition, s.warmup_per_condition,
|
|
s.outcome_timeout_ms, s.late_grace_ms, s.pause_ms, s.timer_requested_ms,
|
|
s.feature_window_sec, s.feature_max_age_ms, s.quote_max_age_ms,
|
|
s.calibration_id, s.seed, s.cleanup_max_attempts,
|
|
s.e3_campaign_limit_min,
|
|
s.sample_capacity, s.event_capacity, s.volume_units_tolerance,
|
|
(int)s.run_mode);
|
|
StringReplace(id, "|", "_");
|
|
StringReplace(id, "/", "_");
|
|
return(id);
|
|
}
|
|
|
|
#endif // REQUEST_LATENCY_LAB_CONFIGURATION_MQH
|
|
//+------------------------------------------------------------------+ |