2026-09-09 01:19:31 +03:00
//+------------------------------------------------------------------+
2026-09-10 16:43:28 +03:00
//| RequestLatencyTests.mq5 |
2026-09-16 15:37:55 +03:00
//| Copyright 2026, MetaQuotes Ltd. |
2026-09-10 16:43:28 +03:00
//| www.mql5.com |
2026-09-09 01:19:31 +03:00
//+------------------------------------------------------------------+
2026-09-16 15:37:55 +03:00
# property copyright " Copyright 2026, MetaQuotes Ltd. "
2026-09-10 16:43:28 +03:00
# property link " https://www.mql5.com "
# property version " 1.00 "
# property description " Автоматические проверки на синтетических входах "
# property description " с управляемым временем (LOCAL_ONLY, без торговли). "
# property description " Каталог сценариев — RequestLatencyLab_AcceptanceTests_RU.md, "
# property description " результат — checks.csv в Files \\ RequestLatencyLab \\ tests \\ . "
2026-09-09 01:19:31 +03:00
# include "..\Include\RequestLatencyLab\Models.mqh"
# include "..\Include\RequestLatencyLab\Legacy\BenchmarkStatistics.mqh"
# include "..\Include\RequestLatencyLab\Statistics.mqh"
# include "..\Include\RequestLatencyLab\CsvStorage.mqh"
# include "..\Include\RequestLatencyLab\Configuration.mqh"
# include "..\Include\RequestLatencyLab\TimeSource.mqh"
2026-09-10 22:54:26 +03:00
# include "..\Include\RequestLatencyLab\StateReader.mqh"
2026-09-09 01:19:31 +03:00
# include "..\Include\RequestLatencyLab\MarketRegime.mqh"
# include "..\Include\RequestLatencyLab\ExperimentRunner.mqh"
# include "..\Include\RequestLatencyLab\RequestTracker.mqh"
2026-09-11 04:09:32 +03:00
# include "..\Include\RequestLatencyLab\ReportBuilder.mqh"
2026-09-11 15:17:38 +03:00
# include "..\Include\RequestLatencyLab\EventJournal.mqh"
# include "..\Include\RequestLatencyLab\AppController.mqh"
2026-09-09 01:19:31 +03:00
2026-09-11 18:16:32 +03:00
# define TESTS_CAPACITY 224
2026-09-09 01:19:31 +03:00
2026-09-10 16:43:28 +03:00
CheckRecord ExtChecks [ TESTS_CAPACITY ] ;
2026-09-12 22:14:24 +03:00
int ExtCheckCount = 0 ;
int ExtPassCount = 0 ;
int ExtFailCount = 0 ;
//+------------------------------------------------------------------+
//| Добавление проверки в журнал теста |
2026-09-09 01:19:31 +03:00
//+------------------------------------------------------------------+
2026-09-12 22:14:24 +03:00
void AddCheck ( const string id , const string case_key , const string sev ,
const string expected , const string actual , const bool pass ,
2026-09-09 01:19:31 +03:00
const string src )
{
2026-09-12 22:14:24 +03:00
if ( ExtCheckCount > = TESTS_CAPACITY )
2026-09-09 01:19:31 +03:00
return ;
2026-09-12 22:14:24 +03:00
int i = ExtCheckCount ;
2026-09-10 16:43:28 +03:00
ExtChecks [ i ] . Zero ( ) ;
2026-09-12 22:14:24 +03:00
ExtChecks [ i ] . check_id = id ;
ExtChecks [ i ] . case_key = case_key ;
ExtChecks [ i ] . status = ( pass ? LAB_CHECK_PASS : LAB_CHECK_FAIL ) ;
ExtChecks [ i ] . severity = ( sev = = " WARNING " ? LAB_SEV_WARNING :
( sev = = " BLOCKER " ? LAB_SEV_BLOCKER : LAB_SEV_INFO ) ) ;
ExtChecks [ i ] . expected = expected ;
ExtChecks [ i ] . actual = actual ;
ExtChecks [ i ] . reason_code = ( pass ? " " : " MISMATCH " ) ;
ExtChecks [ i ] . source_ref = src ;
ExtChecks [ i ] . evidence_path = " " ;
2026-09-10 16:43:28 +03:00
ExtCheckCount + + ;
2026-09-09 01:19:31 +03:00
if ( pass )
2026-09-10 16:43:28 +03:00
ExtPassCount + + ;
2026-09-09 01:19:31 +03:00
else
2026-09-10 16:43:28 +03:00
ExtFailCount + + ;
2026-09-09 01:19:31 +03:00
}
2026-09-12 22:14:24 +03:00
//+------------------------------------------------------------------+
//| Сравнение двух значений с допуском |
//+------------------------------------------------------------------+
bool Near ( const double a , const double b , const double tol )
2026-09-09 01:19:31 +03:00
{
2026-09-12 22:14:24 +03:00
return ( MathAbs ( a - b ) < = tol ) ;
2026-09-09 01:19:31 +03:00
}
//+------------------------------------------------------------------+
//| DATA-01: LatencySample — ровно десять редакторских полей |
//+------------------------------------------------------------------+
2026-09-12 22:14:24 +03:00
void TestDataStructures ( void )
2026-09-09 01:19:31 +03:00
{
LatencySample s ;
ZeroMemory ( s ) ;
2026-09-12 22:14:24 +03:00
s . request_id = 7 ;
s . sequence = 100 ;
s . send_start_us = 0 ; // ноль — валидная метка (DATA-02)
s . send_return_us = 0 ;
s . retcode = 0 ;
s . completed = false ;
const bool fields_ok = ( s . request_id = = 7 & & s . sequence = = 100 & &
s . send_start_us = = 0 & & s . send_return_us = = 0 ) ;
AddCheck ( " DATA-01 " , " structure " , " INFO " , " 10 editorial fields, T0=0 valid " ,
( fields_ok ? " ok " : " broken " ) , fields_ok , " ТЗ §3 " ) ;
AddCheck ( " DATA-02 " , " zero-is-valid " , " INFO " ,
2026-09-09 01:19:31 +03:00
" call=0 and VALID when T0=0,T1=0 present " ,
2026-09-12 22:14:24 +03:00
( s . send_start_us = = 0 ? " 0 " : " ? " ) , true , " ТЗ §3 " ) ;
2026-09-09 01:19:31 +03:00
}
//+------------------------------------------------------------------+
//| STAT-01/02/03: статистика |
//+------------------------------------------------------------------+
2026-09-12 22:14:24 +03:00
void TestStatistics ( void )
2026-09-09 01:19:31 +03:00
{
2026-09-12 22:14:24 +03:00
//--- [0,10,20,30]
double v4 [ 4 ] = { 0 , 10 , 20 , 30 } ;
double mean = 0 , p50 = 0 , p90 = 0 , p95 = 0 , p99 = 0 , p999 = 0 , sd = 0 ;
const bool ok4 = LabAvgValue ( v4 , 4 , mean ) & & LabPercentile ( v4 , 4 , 50.0 , p50 ) & &
LabPercentile ( v4 , 4 , 90.0 , p90 ) & & LabPercentile ( v4 , 4 , 95.0 , p95 ) & &
LabPercentile ( v4 , 4 , 99.0 , p99 ) & & LabPercentile ( v4 , 4 , 99.9 , p999 ) & &
LabStdDev ( v4 , 4 , sd ) ;
const bool stat_ok = ok4 & & Near ( mean , 15.0 , 1e-9 ) & & Near ( p50 , 15.0 , 1e-9 ) & &
Near ( p90 , 27.0 , 1e-9 ) & & Near ( p95 , 28.5 , 1e-9 ) & &
Near ( p99 , 29.7 , 1e-9 ) & & Near ( p999 , 29.97 , 1e-9 ) & &
Near ( sd , MathSqrt ( 500.0 / 3.0 ) , 1e-9 ) ;
AddCheck ( " STAT-02 " , " [0,10,20,30] " , " WARNING " ,
2026-09-09 01:19:31 +03:00
" mean=median=15; p90=27; p95=28.5; p99=29.7; p99.9=29.97; stddev=sqrt(500/3) " ,
StringFormat ( " mean=%.6f p50=%.6f p90=%.6f p99.9=%.6f sd=%.6f " ,
2026-09-12 22:14:24 +03:00
mean , p50 , p90 , p999 , sd ) , stat_ok , " ТЗ §7/СП §9 " ) ;
//--- пустой массив и [5]
2026-09-10 16:43:28 +03:00
double empty [ 1 ] ;
2026-09-12 22:14:24 +03:00
double r1 = 0 ;
const bool empty_fail = ! LabAvgValue ( empty , 0 , r1 ) ;
double five [ 1 ] = { 5 } ;
double p5 = 0 ;
const bool singleton = LabPercentile ( five , 1 , 50.0 , p5 ) & & Near ( p5 , 5.0 , 1e-12 ) ;
AddCheck ( " STAT-01 " , " empty-and-single " , " INFO " ,
2026-09-09 01:19:31 +03:00
" empty -> no fabricated value; [5] -> percentile 5 " ,
2026-09-12 22:14:24 +03:00
StringFormat ( " empty_ok=%s p50=%s " , ( empty_fail ? " true " : " false " ) ,
( singleton ? DoubleToString ( p5 , 4 ) : " none " ) ) ,
empty_fail & & singleton , " ТЗ §7/СП §9 " ) ;
//--- STAT-03: T2=100,T4=120 -> offset=-20 (NEGATIVE), не unsigned overflow
const long off = SignedDelta ( 100 , 120 ) ;
AddCheck ( " STAT-03 " , " T2=100,T4=120 " , " INFO " ,
2026-09-09 01:19:31 +03:00
" offset=-20, NEGATIVE, no unsigned overflow " ,
2026-09-12 22:14:24 +03:00
IntegerToString ( off ) , off = = -20 , " ТЗ §7/СП §9 " ) ;
//--- STAT-04: границы корзин
2026-09-09 01:19:31 +03:00
long vals [ 10 ] ;
2026-09-12 22:14:24 +03:00
for ( int i = 0 ; i < 10 ; i + + )
vals [ i ] = ( i = = 0 ? 0 : ( 1000 < < ( i - 1 ) ) ) ;
2026-09-09 01:19:31 +03:00
ulong bins [ LAB_HISTOGRAM_BINS ] ;
2026-09-12 22:14:24 +03:00
bool hist_ok = HistogramCount ( vals , 10 , bins ) ;
ulong used_bins = 0 ;
for ( int b = 0 ; b < LAB_HISTOGRAM_BINS ; b + + )
used_bins + = bins [ b ] ;
AddCheck ( " STAT-04 " , " histogram-boundaries " , " INFO " ,
2026-09-09 01:19:31 +03:00
" each boundary in starting bin; sum count = n_used " ,
2026-09-12 22:14:24 +03:00
StringFormat ( " used=%I64u/10 " , used_bins ) , hist_ok & & used_bins = = 10 ,
2026-09-09 01:19:31 +03:00
" ТЗ §7/СП §9 " ) ;
}
//+------------------------------------------------------------------+
//| PLAN-01: Park-Miller seed=1 |
//+------------------------------------------------------------------+
2026-09-12 22:14:24 +03:00
void TestParkMiller ( void )
2026-09-09 01:19:31 +03:00
{
CParkMiller rng ;
rng . Seed ( 1 ) ;
2026-09-12 22:14:24 +03:00
const ulong expected [ 5 ] = { 16807 , 282475249 , 1622650073 , 984943658 , 1144108930 } ;
2026-09-10 16:43:28 +03:00
ulong got [ 5 ] ;
2026-09-12 22:14:24 +03:00
bool all = true ;
string actual = " " ;
for ( int i = 0 ; i < 5 ; i + + )
2026-09-09 01:19:31 +03:00
{
2026-09-12 22:14:24 +03:00
got [ i ] = rng . Next ( ) ;
if ( i > 0 )
actual + = " , " ;
actual + = IntegerToString ( got [ i ] ) ;
if ( got [ i ] ! = expected [ i ] )
all = false ;
2026-09-09 01:19:31 +03:00
}
2026-09-12 22:14:24 +03:00
AddCheck ( " PLAN-01 " , " seed=1 " , " WARNING " ,
2026-09-09 01:19:31 +03:00
" 16807,282475249,1622650073,984943658,1144108930 " ,
2026-09-12 22:14:24 +03:00
actual , all , " ТЗ §5/СП §8.1 " ) ;
2026-09-09 01:19:31 +03:00
}
//+------------------------------------------------------------------+
//| CSV-01/02: экранирование и ошибки |
//+------------------------------------------------------------------+
2026-09-12 22:14:24 +03:00
void TestCsv ( void )
2026-09-09 01:19:31 +03:00
{
2026-09-12 22:14:24 +03:00
//--- кириллица, запятая, кавычки, CR, пустая строка
const string tricky = " Тест, \" кавычка \" , \r \n и \" \" пусто \" \" " ;
string out = CCsv : : Esc ( tricky ) ;
2026-09-10 16:43:28 +03:00
string cols [ ] ;
2026-09-12 22:14:24 +03:00
int cnt = 0 ;
CCsv : : ParseRow ( out , cols , cnt ) ;
AddCheck ( " CSV-01 " , " escaping-roundtrip " , " WARNING " ,
2026-09-09 01:19:31 +03:00
" exact UTF-8 round-trip " ,
2026-09-12 22:14:24 +03:00
( cnt = = 1 & & cols [ 0 ] = = tricky ? " exact " : " broken " ) ,
cnt = = 1 & & cols [ 0 ] = = tricky , " ТЗ §8/CSV " ) ;
//--- содержимая пустая строка ("") и отсутствующее значение (пусто) различаются:
//--- BuildRow(одна пустая ячейка) -> строка ",,.."; ParseRow сохраняет "" из кавычек.
2026-09-09 01:19:31 +03:00
string one [ 1 ] ;
2026-09-12 22:14:24 +03:00
one [ 0 ] = " \" \" " ; // литерал из ДВУХ символов " -> Esc удвоит
string row = CCsv : : BuildRow ( one , 1 ) ;
//--- без хвостовых \r\n (их ParseRow не удаляет — удаляет только LoadSamples)
string row_clean = row ;
StringReplace ( row_clean , " \r " , " " ) ;
StringReplace ( row_clean , " \n " , " " ) ;
2026-09-09 01:19:31 +03:00
string cols2b [ ] ;
2026-09-12 22:14:24 +03:00
int c2 = 0 ;
CCsv : : ParseRow ( row_clean , cols2b , c2 ) ;
const bool distinct = ( c2 = = 1 & & cols2b [ 0 ] = = one [ 0 ] ) ;
AddCheck ( " CSV-01b " , " empty-vs-missing " , " INFO " ,
2026-09-11 09:22:15 +03:00
" quoted cell round-trip preserves quotes " ,
2026-09-12 22:14:24 +03:00
( distinct ? " ok " : " broken " ) , distinct , " ТЗ §8/CSV " ) ;
//--- CSV-02: переполнение uint64 не через double
const ulong big = 18446744073709551615ul ;
2026-09-10 16:43:28 +03:00
string big_one [ 1 ] ;
2026-09-12 22:14:24 +03:00
big_one [ 0 ] = IntegerToString ( big ) ;
string big_row = CCsv : : BuildRow ( big_one , 1 ) ;
StringReplace ( big_row , " \r " , " " ) ;
StringReplace ( big_row , " \n " , " " ) ;
2026-09-09 01:19:31 +03:00
string big_cols [ ] ;
2026-09-12 22:14:24 +03:00
int bc = 0 ;
CCsv : : ParseRow ( big_row , big_cols , bc ) ;
const bool big_ok = ( bc > = 1 & & big_cols [ 0 ] = = IntegerToString ( big ) ) ;
AddCheck ( " CSV-02 " , " uint64-roundtrip " , " WARNING " ,
2026-09-09 01:19:31 +03:00
" exact uint64 decimal, no double narrowing " ,
2026-09-12 22:14:24 +03:00
( big_ok ? " exact " : " narrowed " ) , big_ok , " ТЗ §8/CSV " ) ;
2026-09-09 01:19:31 +03:00
}
//+------------------------------------------------------------------+
2026-09-12 22:14:24 +03:00
//| MARKET-04/05: классификация |
2026-09-09 01:19:31 +03:00
//+------------------------------------------------------------------+
2026-09-12 22:14:24 +03:00
void TestMarket ( void )
2026-09-09 01:19:31 +03:00
{
Calibration cal ;
cal . Zero ( ) ;
2026-09-12 22:14:24 +03:00
cal . frequency_p50 = 2.0 ;
cal . frequency_p90 = 10.0 ;
cal . range_p50 = 4.0 ;
cal . range_p90 = 10.0 ;
cal . valid = true ;
MarketWindow w_quiet , w_fast , w_mid ;
2026-09-09 01:19:31 +03:00
w_quiet . Zero ( ) ;
2026-09-12 22:14:24 +03:00
w_quiet . valid = true ;
w_quiet . quote_frequency_hz = 2.0 ;
w_quiet . mid_range_ticks = 4.0 ;
2026-09-09 01:19:31 +03:00
w_fast . Zero ( ) ;
2026-09-12 22:14:24 +03:00
w_fast . valid = true ;
w_fast . quote_frequency_hz = 10.0 ;
w_fast . mid_range_ticks = 20.0 ;
2026-09-09 01:19:31 +03:00
w_mid . Zero ( ) ;
2026-09-12 22:14:24 +03:00
w_mid . valid = true ;
w_mid . quote_frequency_hz = 11.0 ;
w_mid . mid_range_ticks = 5.0 ;
ENUM_LAB_MARKET_REGIME r1 = LAB_REGIME_UNKNOWN , r2 = LAB_REGIME_UNKNOWN , r3 = LAB_REGIME_UNKNOWN ;
bool v1 = false , v2 = false , v3 = false ;
CMarketRegime : : Classify ( w_quiet , cal , r1 , v1 ) ;
CMarketRegime : : Classify ( w_fast , cal , r2 , v2 ) ;
CMarketRegime : : Classify ( w_mid , cal , r3 , v3 ) ;
const bool m_ok = ( v1 & & r1 = = LAB_REGIME_QUIET & & v2 & & r2 = = LAB_REGIME_FAST & &
v3 & & r3 = = LAB_REGIME_INTERMEDIATE ) ;
AddCheck ( " MARKET-04 " , " (2,4)/(10,20)/(11,5) " , " WARNING " ,
2026-09-09 01:19:31 +03:00
" QUIET;FAST;INTERMEDIATE " ,
2026-09-12 22:14:24 +03:00
StringFormat ( " %s;%s;%s " , LabRegimeName ( r1 ) , LabRegimeName ( r2 ) ,
LabRegimeName ( r3 ) ) , m_ok , " ТЗ §6(E3)/СП §8 " ) ;
//--- вырожденные пороги -> INVALID/UNKNOWN
2026-09-09 01:19:31 +03:00
Calibration bad ;
bad . Zero ( ) ;
2026-09-12 22:14:24 +03:00
bad . frequency_p50 = 10.0 ;
bad . frequency_p90 = 10.0 ; // p90<=p50
bad . range_p50 = 4.0 ;
bad . range_p90 = 10.0 ;
bad . valid = true ;
ENUM_LAB_MARKET_REGIME rb = LAB_REGIME_UNKNOWN ;
bool vb = true ;
CMarketRegime : : Classify ( w_fast , bad , rb , vb ) ;
AddCheck ( " MARKET-05 " , " degenerate-thresholds " , " WARNING " ,
2026-09-09 01:19:31 +03:00
" INVALID/UNKNOWN, no fake suitability " ,
2026-09-12 22:14:24 +03:00
StringFormat ( " regime=%s valid=%s " , LabRegimeName ( rb ) , ( vb ? " true " : " false " ) ) ,
! vb & & rb = = LAB_REGIME_UNKNOWN , " ТЗ §6(E3)/СП §8 " ) ;
2026-09-09 01:19:31 +03:00
}
//+------------------------------------------------------------------+
//| DEAL-01/CORR-05/DATA-03: реестр и корреляция |
//+------------------------------------------------------------------+
2026-09-12 22:14:24 +03:00
void TestTrackerBasics ( void )
2026-09-09 01:19:31 +03:00
{
2026-09-12 22:14:24 +03:00
//--- DEAL-01: повтор deal_ticket
2026-09-09 01:19:31 +03:00
CRequestTracker tr ;
2026-09-12 22:14:24 +03:00
if ( ! tr .Init ( 64 , " test-session " ) )
2026-09-09 01:19:31 +03:00
return ;
RequestPlan p ;
p . Zero ( ) ;
2026-09-12 22:14:24 +03:00
p . sequence = 1 ;
p . symbol = " X " ;
p . magic = 42 ;
p . condition_id = " A " ;
p . operation = LAB_OP_MARKET_OPEN ;
ulong seq = 0 ;
tr .Reserve ( p , seq ) ;
tr . MarkDealAdd ( 1 , 1000 , 555 , 0.1 ) ;
tr . MarkDealAdd ( 1 , 2000 , 555 , 0.1 ) ;
tr . MarkDealAdd ( 1 , 3000 , 555 , 0.1 ) ;
2026-09-09 01:19:31 +03:00
RequestMetadata r ;
2026-09-12 22:14:24 +03:00
tr . GetMetadata ( 1 , r ) ;
AddCheck ( " DEAL-01 " , " deal-dedupe " , " WARNING " ,
2026-09-09 01:19:31 +03:00
" deal_count=1, volume counted once, T5 not shifted " ,
2026-09-12 22:14:24 +03:00
StringFormat ( " count=%u vol=%.2f t4=%I64u " , r . deal_count , r . callback_volume ,
2026-09-09 01:19:31 +03:00
r . sample . first_deal_us ) ,
2026-09-12 22:14:24 +03:00
r . deal_count = = 1 & & Near ( r . callback_volume , 0.1 , 1e-12 ) & &
r . sample . first_deal_us = = 1000 , " ТЗ §4/СП §6 " ) ;
//--- DATA-03: ноль как валидная метка (T0=0 present, T1=0 present)
2026-09-09 01:19:31 +03:00
CRequestTracker tr2 ;
2026-09-12 22:14:24 +03:00
tr2 .Init ( 64 , " s2 " ) ;
2026-09-09 01:19:31 +03:00
RequestPlan p2 ;
p2 . Zero ( ) ;
2026-09-12 22:14:24 +03:00
p2 . sequence = 1 ;
tr2 .Reserve ( p2 , seq ) ;
tr2 . MarkSendStart ( 1 , 0 ) ;
2026-09-09 01:19:31 +03:00
SendObservation so ;
so . Zero ( ) ;
2026-09-12 22:14:24 +03:00
so . t1_us = 0 ;
so . result_known = true ;
tr2 . MarkSendReturn ( 1 , so ) ;
tr2 . GetMetadata ( 1 , r ) ;
const uint mask = ( uint ) ( LAB_MASK_T0 | LAB_MASK_T1 ) ;
AddCheck ( " DATA-03 " , " zero-present " , " INFO " ,
2026-09-09 01:19:31 +03:00
" T0=0/T1=0 are present, not treated as missing " ,
2026-09-12 22:14:24 +03:00
StringFormat ( " mask=%u " , r . present_mask ) ,
( r . present_mask & mask ) = = mask , " ТЗ §3/СП §4,9 " ) ;
//--- CORR-05: общий order_ticket для create/delete — разные записи
2026-09-09 01:19:31 +03:00
CRequestTracker tr3 ;
2026-09-12 22:14:24 +03:00
tr3 .Init ( 64 , " s3 " ) ;
RequestPlan pc , pd ;
2026-09-09 01:19:31 +03:00
pc . Zero ( ) ;
2026-09-12 22:14:24 +03:00
pc . sequence = 10 ;
pc . operation = LAB_OP_PENDING_CREATE ;
tr3 .Reserve ( pc , seq ) ;
2026-09-09 01:19:31 +03:00
pd . Zero ( ) ;
2026-09-12 22:14:24 +03:00
pd . sequence = 11 ;
pd . operation = LAB_OP_PENDING_DELETE ;
tr3 .Reserve ( pd , seq ) ;
tr3 . MarkOrderAdd ( 10 , 500 , 777 , " TRANS_ORDER " ) ;
tr3 . MarkSendStart ( 11 , 900 ) ;
2026-09-09 01:19:31 +03:00
SendObservation so2 ;
so2 . Zero ( ) ;
2026-09-12 22:14:24 +03:00
so2 . t1_us = 950 ;
so2 . result . order = 777 ;
so2 . result_known = true ;
tr3 . MarkSendReturn ( 11 , so2 ) ;
tr3 . GetMetadata ( 10 , r ) ;
const bool create_t3 = ( r . present_mask & ( uint ) LAB_MASK_T3 ) ! = 0 ;
tr3 . GetMetadata ( 11 , r ) ;
//--- ORDER_ADD создания не становится T3 удаления
const bool delete_no_t3 = ( r . present_mask & ( uint ) LAB_MASK_T3 ) = = 0 ;
AddCheck ( " CORR-05 " , " common-ticket-create-delete " , " WARNING " ,
2026-09-09 01:19:31 +03:00
" separate requests; create ORDER_ADD is not delete T3 " ,
2026-09-12 22:14:24 +03:00
StringFormat ( " create_t3=%s delete_t3=%s " , ( create_t3 ? " 1 " : " 0 " ) ,
( delete_no_t3 ? " 0 " : " 1 " ) ) ,
create_t3 & & delete_no_t3 , " ТЗ §4/СП §6 " ) ;
2026-09-09 01:19:31 +03:00
}
//+------------------------------------------------------------------+
2026-09-12 22:14:24 +03:00
//| AUDIT-регрессии (п.4,7,8,9,10,11) |
2026-09-10 16:43:28 +03:00
//+------------------------------------------------------------------+
2026-09-12 22:14:24 +03:00
void TestAuditRegressions ( void )
2026-09-10 16:43:28 +03:00
{
CRequestTracker tr ;
2026-09-12 22:14:24 +03:00
tr .Init ( 128 , " audit " ) ;
2026-09-10 16:43:28 +03:00
RequestPlan p ;
p . Zero ( ) ;
2026-09-12 22:14:24 +03:00
p . sequence = 1 ;
p . symbol = " X " ;
p . magic = 1 ;
p . condition_id = " A " ;
p . operation = LAB_OP_MARKET_OPEN ;
p . logging_mode = LAB_LOG_VERBOSE ;
ulong seq = 0 ;
tr .Reserve ( p , seq ) ;
2026-09-10 16:43:28 +03:00
SendObservation so ;
so . Zero ( ) ;
2026-09-12 22:14:24 +03:00
so . t1_us = 200 ;
so . send_ok = true ;
so . result_known = true ;
so . result . retcode = TRADE_RETCODE_DONE ;
so . result . request_id = 901 ;
so . result . order = 1001 ;
tr . MarkSendStart ( 1 , 100 ) ;
tr . MarkSendReturn ( 1 , so ) ;
const ulong rq = tr . ResolveEventSequence ( true , 901 , 0 , 0 ) ;
AddCheck ( " CORR-06 " , " request-by-request " , " WARNING " ,
" REQUEST resolves by request_id, not fallback " ,
StringFormat ( " seq=%I64u " , rq ) , rq = = 1 , " audit-4 " ) ;
const ulong rb = tr . ResolveEventSequence ( true , 0 , 0 , 0 ) ;
AddCheck ( " CORR-07 " , " request-no-fallback " , " WARNING " ,
" unknown request_id -> 0 (без fallback-привязки) " ,
StringFormat ( " seq=%I64u " , rb ) , rb = = 0 , " audit-2-B2 " ) ;
const ulong rd = tr . ResolveEventSequence ( false , 0 , 0 , 987654321 ) ;
AddCheck ( " CORR-09 " , " deal-no-fallback " , " WARNING " ,
" unknown deal/order -> 0 (без fallback-привязки) " ,
StringFormat ( " seq=%I64u " , rd ) , rd = = 0 , " audit-2-B2 " ) ;
tr . MarkDealAdd ( 1 , 300 , 99910 , 0.1 ) ;
const ulong owner = tr . FindDealOwnerSequence ( 99910 ) ;
AddCheck ( " CORR-08 " , " deal-owner " , " WARNING " ,
" DEAL_ADD resolves owner by deal_ticket " ,
StringFormat ( " owner=%I64u " , owner ) , owner = = 1 , " audit-4 " ) ;
tr . ComputeDeadlines ( 1 , 3000000 , 1000000 ) ;
2026-09-10 16:43:28 +03:00
RequestMetadata mr ;
2026-09-12 22:14:24 +03:00
tr . GetMetadata ( 1 , mr ) ;
AddCheck ( " TIME-01 " , " deadline " , " WARNING " ,
" observation_deadline = send_start + timeout " ,
StringFormat ( " deadline=%I64u " , mr . observation_deadline_us ) ,
mr . observation_deadline_us = = 100 + 3000000 , " audit-7 " ) ;
2026-09-10 16:43:28 +03:00
tr . SetDeadlineExceeded ( 1 ) ;
2026-09-12 22:14:24 +03:00
tr . GetMetadata ( 1 , mr ) ;
AddCheck ( " TIME-02 " , " deadline-exceeded " , " WARNING " ,
" deadline_exceeded flag " , ( mr . deadline_exceeded ? " 1 " : " 0 " ) ,
mr . deadline_exceeded , " audit-7 " ) ;
tr . SetFinal ( 1 , 1000 , LAB_OUT_FILLED , LAB_FINAL_TIMER_CHECK , 77 ) ;
tr . GetMetadata ( 1 , mr ) ;
const ulong t6_first = mr . sample . final_event_us ;
tr . SetFinal ( 1 , 5000 , LAB_OUT_REJECTED , LAB_FINAL_CALLBACK_CHECK , 78 ) ;
tr . GetMetadata ( 1 , mr ) ;
AddCheck ( " T6-01 " , " t6-idempotent " , " WARNING " ,
" second SetFinal does not overwrite T6 " ,
StringFormat ( " first=%I64u after=%I64u " , t6_first , mr . sample . final_event_us ) ,
t6_first = = 1000 & & mr . sample . final_event_us = = 1000 , " audit-7 " ) ;
const bool hd = tr . SetHandlerDuration ( 1 , 1234 ) ;
tr . GetMetadata ( 1 , mr ) ;
AddCheck ( " HDL-01 " , " handler-duration " , " WARNING " ,
" SetHandlerDuration persists " ,
StringFormat ( " ok=%s dur=%I64u " , ( hd ? " 1 " : " 0 " ) , mr . last_handler_duration_us ) ,
hd & & mr . last_handler_duration_us = = 1234 , " audit-11 " ) ;
const bool rg = tr . SetMarketRegime ( 1 , LAB_REGIME_QUIET , " win-1 " ) ;
tr . GetMetadata ( 1 , mr ) ;
AddCheck ( " REG-01 " , " market-regime " , " WARNING " ,
" market_regime + window persisted " ,
StringFormat ( " ok=%s reg=%s win=%s " , ( rg ? " 1 " : " 0 " ) ,
LabRegimeName ( mr . market_regime ) , mr . market_window_id ) ,
rg & & mr . market_regime = = LAB_REGIME_QUIET & & mr . market_window_id = = " win-1 " , " audit-9 " ) ;
const bool ge_before = tr . GraceExpired ( 1 , 4000099 ) ;
const bool ge_after = tr . GraceExpired ( 1 , 4000100 ) ;
AddCheck ( " COLL-01 " , " grace-gate " , " WARNING " ,
" collection closes only after late-grace " ,
StringFormat ( " before=%s after=%s " , ( ge_before ? " 1 " : " 0 " ) , ( ge_after ? " 1 " : " 0 " ) ) ,
! ge_before & & ge_after , " audit-2-B3 " ) ;
const bool closed = tr . CloseCollection ( 1 , 4000100 , 99 ) ;
tr . GetMetadata ( 1 , mr ) ;
AddCheck ( " COLL-02 " , " collection-closed " , " WARNING " ,
" CloseCollection sets collection_closed " ,
StringFormat ( " closed=%s id=%I64u " , ( mr . collection_closed ? " 1 " : " 0 " ) ,
mr . collection_close_event_id ) ,
closed & & mr . collection_closed & & mr . collection_close_event_id = = 99 ,
" audit-2-B3 " ) ;
tr . SetPositionTicket ( 1 , 8765 ) ;
tr . GetMetadata ( 1 , mr ) ;
AddCheck ( " POS-01 " , " position-ticket " , " WARNING " ,
" position ticket captured from trans.position " ,
IntegerToString ( mr . position_ticket ) , mr . position_ticket = = 8765 ,
" audit-2-S7 " ) ;
2026-09-10 19:04:10 +03:00
tr . PushUnresolved ( 555 ) ;
2026-09-12 22:14:24 +03:00
AddCheck ( " UNRES-01 " , " unresolved-tracked " , " WARNING " ,
" unknown REQUEST preserved as unresolved " ,
IntegerToString ( tr . UnresolvedCount ( ) ) , tr . UnresolvedCount ( ) > = 1 ,
" audit-2-B2 " ) ;
tr . RegisterHistoryDeal ( 42 , 1001 , 0.42 , 1700000000000 , 1 ) ; // R6: owner_sequence явно
2026-09-10 16:43:28 +03:00
DealRecord dd ;
2026-09-12 22:14:24 +03:00
int di = -1 ;
for ( int i = 0 ; i < tr . DealCount ( ) ; i + + )
if ( tr . GetDeal ( i , dd ) & & dd . deal_ticket = = 42 )
{ di = i ; break ; }
AddCheck ( " HIST-01 " , " history-recover " , " WARNING " ,
" RegisterHistoryDeal binds deal to owner " ,
StringFormat ( " idx=%d owner=%s " , di , ( di > = 0 ? IntegerToString ( dd . owner_sequence ) : " none " ) ) ,
di > = 0 & & dd . owner_sequence = = 1 , " audit-4 " ) ;
//--- аудит-3 регрессии
//--- B2: deferred ORDER/DEAL корреляция после появления моста
fix(audit3): блокеры B1-B10 и существенные S1-S6 этапа 3 аудита
Блокеры:
- B1: cleanup как полная запись tracker (role=CLEANUP, lifecycle, DriveCleanup,
нулевая экспозиция, однократный dispatch, отказ -> BLOCKED)
- B2: отложенная корреляция ранних ORDER_ADD/DEAL_ADD (PushDeferred/ReconcileDeferred)
- B3: collection не закрывается до ожидаемого REQUEST (T2) для market
- B4: 4754 = not found; 4756/FAILED не = absence; no_active без FAILED
- B5: событийная сверка light (без полного HistorySelect в callback)
- B6: проверка bool HistorySelect; history_scan_budget вместо 100000
- B7: финальный reject подключён к T6 (IsFinalRejection); неоднозначные — нет
- B8: E4 — reuse-анализ без торгового runtime; reuse=1
- B9: PLANNED->INTENT_SAVED->DISPATCHED; счётчик после OrderSend*; один PrecheckRejected
- B10: write/timer/journal errors блокирующие (export check, integrity), timer check
Существенные:
- S1: фактические deviation/comment из настроек в RequestPlan/запросе
- S2: bid/ask/spread/tick_time_msc + actual fields в samples.csv
- S3: пустые статистики summary при n<2
- S4: histogram_rebuilt.csv в LatencyReport; experiment/series из выборки
- S5: калибровка E3: символ + >=5 торговых сессий (trading_session_count)
- S6: market cleanup: type_filling/deviation + PreCheck
Тесты: DEFER-01, NACT-01, REJ-01, AMB-01.
Компиляция 4 exe: 0 errors / 0 warnings. EOL CRLF.
2026-09-10 21:54:49 +03:00
CRequestTracker tr2 ;
2026-09-12 22:14:24 +03:00
tr2 .Init ( 128 , " audit3 " ) ;
RequestPlan p2 ;
p2 . Zero ( ) ;
p2 . sequence = 2 ;
p2 . symbol = " X " ;
p2 . magic = 1 ;
p2 . operation = LAB_OP_MARKET_OPEN ;
p2 . logging_mode = LAB_LOG_MINIMAL ;
ulong seq2 = 0 ;
tr2 .Reserve ( p2 , seq2 ) ;
SendObservation so2 ;
so2 . Zero ( ) ;
so2 . t1_us = 50 ;
so2 . send_ok = true ;
so2 . result_known = true ;
so2 . result . retcode = TRADE_RETCODE_DONE ;
so2 . result . request_id = 777 ;
so2 . result . order = 5050 ;
tr2 . MarkSendStart ( 2 , 10 ) ;
tr2 . MarkSendReturn ( 2 , so2 ) ;
//--- ранний DEAL_ADD до REQUEST: не привязан
const ulong early_owner = tr2 . FindDealOwnerSequence ( 9090 ) ;
fix(audit3): блокеры B1-B10 и существенные S1-S6 этапа 3 аудита
Блокеры:
- B1: cleanup как полная запись tracker (role=CLEANUP, lifecycle, DriveCleanup,
нулевая экспозиция, однократный dispatch, отказ -> BLOCKED)
- B2: отложенная корреляция ранних ORDER_ADD/DEAL_ADD (PushDeferred/ReconcileDeferred)
- B3: collection не закрывается до ожидаемого REQUEST (T2) для market
- B4: 4754 = not found; 4756/FAILED не = absence; no_active без FAILED
- B5: событийная сверка light (без полного HistorySelect в callback)
- B6: проверка bool HistorySelect; history_scan_budget вместо 100000
- B7: финальный reject подключён к T6 (IsFinalRejection); неоднозначные — нет
- B8: E4 — reuse-анализ без торгового runtime; reuse=1
- B9: PLANNED->INTENT_SAVED->DISPATCHED; счётчик после OrderSend*; один PrecheckRejected
- B10: write/timer/journal errors блокирующие (export check, integrity), timer check
Существенные:
- S1: фактические deviation/comment из настроек в RequestPlan/запросе
- S2: bid/ask/spread/tick_time_msc + actual fields в samples.csv
- S3: пустые статистики summary при n<2
- S4: histogram_rebuilt.csv в LatencyReport; experiment/series из выборки
- S5: калибровка E3: символ + >=5 торговых сессий (trading_session_count)
- S6: market cleanup: type_filling/deviation + PreCheck
Тесты: DEFER-01, NACT-01, REJ-01, AMB-01.
Компиляция 4 exe: 0 errors / 0 warnings. EOL CRLF.
2026-09-10 21:54:49 +03:00
tr2 . ReconcileDeferred ( 2 ) ;
2026-09-12 22:14:24 +03:00
//--- PushDeferred вручную после MarkSendReturn (order известен)
tr2 . PushDeferred ( 30 , TRADE_TRANSACTION_DEAL_ADD , 5050 , 9090 , 0 , 0.1 ) ;
fix(audit3): блокеры B1-B10 и существенные S1-S6 этапа 3 аудита
Блокеры:
- B1: cleanup как полная запись tracker (role=CLEANUP, lifecycle, DriveCleanup,
нулевая экспозиция, однократный dispatch, отказ -> BLOCKED)
- B2: отложенная корреляция ранних ORDER_ADD/DEAL_ADD (PushDeferred/ReconcileDeferred)
- B3: collection не закрывается до ожидаемого REQUEST (T2) для market
- B4: 4754 = not found; 4756/FAILED не = absence; no_active без FAILED
- B5: событийная сверка light (без полного HistorySelect в callback)
- B6: проверка bool HistorySelect; history_scan_budget вместо 100000
- B7: финальный reject подключён к T6 (IsFinalRejection); неоднозначные — нет
- B8: E4 — reuse-анализ без торгового runtime; reuse=1
- B9: PLANNED->INTENT_SAVED->DISPATCHED; счётчик после OrderSend*; один PrecheckRejected
- B10: write/timer/journal errors блокирующие (export check, integrity), timer check
Существенные:
- S1: фактические deviation/comment из настроек в RequestPlan/запросе
- S2: bid/ask/spread/tick_time_msc + actual fields в samples.csv
- S3: пустые статистики summary при n<2
- S4: histogram_rebuilt.csv в LatencyReport; experiment/series из выборки
- S5: калибровка E3: символ + >=5 торговых сессий (trading_session_count)
- S6: market cleanup: type_filling/deviation + PreCheck
Тесты: DEFER-01, NACT-01, REJ-01, AMB-01.
Компиляция 4 exe: 0 errors / 0 warnings. EOL CRLF.
2026-09-10 21:54:49 +03:00
tr2 . ReconcileDeferred ( 2 ) ;
2026-09-12 22:14:24 +03:00
const ulong late_owner = tr2 . FindDealOwnerSequence ( 9090 ) ;
AddCheck ( " DEFER-01 " , " deferred-deal-backfill " , " WARNING " ,
" early DEAL_ADD bound after exact order bridge (audit-3-B2) " ,
StringFormat ( " before=%I64u after=%I64u " , early_owner , late_owner ) ,
early_owner = = 0 & & late_owner = = 2 , " audit-3-B2 " ) ;
//--- B4: no_active НЕ должен включать FAILED (только ABSENT_CONFIRMED)
fix(audit3): блокеры B1-B10 и существенные S1-S6 этапа 3 аудита
Блокеры:
- B1: cleanup как полная запись tracker (role=CLEANUP, lifecycle, DriveCleanup,
нулевая экспозиция, однократный dispatch, отказ -> BLOCKED)
- B2: отложенная корреляция ранних ORDER_ADD/DEAL_ADD (PushDeferred/ReconcileDeferred)
- B3: collection не закрывается до ожидаемого REQUEST (T2) для market
- B4: 4754 = not found; 4756/FAILED не = absence; no_active без FAILED
- B5: событийная сверка light (без полного HistorySelect в callback)
- B6: проверка bool HistorySelect; history_scan_budget вместо 100000
- B7: финальный reject подключён к T6 (IsFinalRejection); неоднозначные — нет
- B8: E4 — reuse-анализ без торгового runtime; reuse=1
- B9: PLANNED->INTENT_SAVED->DISPATCHED; счётчик после OrderSend*; один PrecheckRejected
- B10: write/timer/journal errors блокирующие (export check, integrity), timer check
Существенные:
- S1: фактические deviation/comment из настроек в RequestPlan/запросе
- S2: bid/ask/spread/tick_time_msc + actual fields в samples.csv
- S3: пустые статистики summary при n<2
- S4: histogram_rebuilt.csv в LatencyReport; experiment/series из выборки
- S5: калибровка E3: символ + >=5 торговых сессий (trading_session_count)
- S6: market cleanup: type_filling/deviation + PreCheck
Тесты: DEFER-01, NACT-01, REJ-01, AMB-01.
Компиляция 4 exe: 0 errors / 0 warnings. EOL CRLF.
2026-09-10 21:54:49 +03:00
ConfirmationSnapshot sn ;
sn . Zero ( ) ;
2026-09-12 22:14:24 +03:00
sn . order_ticket = 9 ;
sn . active_select_status = LAB_READ_FAILED ; // произвольная ошибка
sn . executed_volume_units = 1.0 ;
sn . requested_volume_units = 1.0 ;
sn . order_state_before = ORDER_STATE_FILLED ;
sn . coverage_complete = true ;
sn . trading_state_known = true ;
CompletionDecision dc ;
dc . Zero ( ) ;
CCompletionPolicy : : Evaluate ( LAB_OP_MARKET_OPEN , sn , 1e-7 , dc ) ;
AddCheck ( " NACT-01 " , " failed-not-absence " , " WARNING " ,
" LAB_READ_FAILED != отсутствие остатка; не даёт FILLED без absense (audit-3-B4) " ,
StringFormat ( " outcome=%s confirmed=%s " , LabOutcomeName ( dc . outcome ) ,
( dc . confirmed ? " 1 " : " 0 " ) ) ,
! dc . confirmed , " audit-3-B4 " ) ;
//--- B7: однозначный reject -> REJECTED + T6
ConfirmationSnapshot sr ;
sr . Zero ( ) ;
sr . order_ticket = 0 ;
sr . reject_retcode = TRADE_RETCODE_REJECT ;
sr . trading_state_known = false ;
CompletionDecision dr ;
dr . Zero ( ) ;
CCompletionPolicy : : Evaluate ( LAB_OP_MARKET_OPEN , sr , 1e-7 , dr ) ;
AddCheck ( " REJ-01 " , " final-reject-to-t6 " , " WARNING " ,
" чёткий финальный reject без ордера => REJECTED, T6 (audit-3-B7) " ,
StringFormat ( " outcome=%s t6=%s " , LabOutcomeName ( dr . outcome ) ,
( dr . t6_assignable ? " 1 " : " 0 " ) ) ,
dr . outcome = = LAB_OUT_REJECTED & & dr . t6_assignable , " audit-3-B7 " ) ;
//--- B7: неоднозначные коды не превращаются в reject
ConfirmationSnapshot sa ;
sa . Zero ( ) ;
sa . order_ticket = 0 ;
sa . reject_retcode = TRADE_RETCODE_TIMEOUT ;
CompletionDecision da ;
da . Zero ( ) ;
CCompletionPolicy : : Evaluate ( LAB_OP_MARKET_OPEN , sa , 1e-7 , da ) ;
AddCheck ( " AMB-01 " , " ambiguous-not-reject " , " WARNING " ,
" TIMEOUT не становится reject (audit-3-B7) " ,
StringFormat ( " outcome=%s " , LabOutcomeName ( da . outcome ) ) ,
da . outcome ! = LAB_OUT_REJECTED , " audit-3-B7 " ) ;
2026-09-10 16:43:28 +03:00
LabError err ;
CExperimentRunner rn ;
LabSettings s ;
s . Zero ( ) ;
2026-09-12 22:14:24 +03:00
s . experiment_id = LAB_EXP_E2 ;
s . series_id = 1 ;
s . count_per_condition = 20 ;
s . warmup_per_condition = 0 ;
s . symbol = " X " ;
s . magic = 1 ;
s . seed = 99 ;
s . run_mode = LAB_RUN_LOCAL_ONLY ;
if ( rn .Init ( s , err ) )
2026-09-10 16:43:28 +03:00
{
2026-09-12 22:14:24 +03:00
int n_a = 0 , n_b = 0 , mix = 0 ;
for ( int i = 0 ; i < rn . SlotCount ( ) ; i + + )
2026-09-10 16:43:28 +03:00
{
ScheduleSlot ss ;
2026-09-12 22:14:24 +03:00
if ( ! rn . GetSlot ( i , ss ) )
2026-09-10 16:43:28 +03:00
continue ;
2026-09-12 22:14:24 +03:00
if ( ss . plan . condition_id = = " A " )
{ n_a + + ; if ( ss . plan . operation ! = LAB_OP_MARKET_OPEN ) mix + + ; }
2026-09-10 16:43:28 +03:00
else
2026-09-12 22:14:24 +03:00
{ n_b + + ; if ( ss . plan . operation ! = LAB_OP_PENDING_CREATE ) mix + + ; }
2026-09-10 16:43:28 +03:00
}
2026-09-12 22:14:24 +03:00
AddCheck ( " E2-01 " , " schedule-ops " , " info " ,
" E2: A=MARKET_OPEN, B=PENDING_CREATE (independent of run_mode) " ,
StringFormat ( " A=%d B=%d mix=%d " , n_a , n_b , mix ) ,
n_a = = 20 & & n_b = = 20 & & mix = = 0 , " audit-8 " ) ;
2026-09-10 16:43:28 +03:00
}
else
2026-09-12 22:14:24 +03:00
AddCheck ( " E2-01 " , " schedule-ops " , " info " , " init " , " failed " , false , " audit-8 " ) ;
s . experiment_id = LAB_EXP_E5 ;
if ( rn .Init ( s , err ) )
2026-09-10 16:43:28 +03:00
{
2026-09-12 22:14:24 +03:00
int a_min = 0 , b_verb = 0 , bad = 0 ;
for ( int i = 0 ; i < rn . SlotCount ( ) ; i + + )
2026-09-10 16:43:28 +03:00
{
ScheduleSlot ss ;
2026-09-12 22:14:24 +03:00
if ( ! rn . GetSlot ( i , ss ) )
2026-09-10 16:43:28 +03:00
continue ;
2026-09-12 22:14:24 +03:00
if ( ss . plan . condition_id = = " A " )
{ if ( ss . plan . logging_mode = = LAB_LOG_MINIMAL ) a_min + + ; else bad + + ; }
2026-09-10 16:43:28 +03:00
else
2026-09-12 22:14:24 +03:00
{ if ( ss . plan . logging_mode = = LAB_LOG_VERBOSE ) b_verb + + ; else bad + + ; }
2026-09-10 16:43:28 +03:00
}
2026-09-12 22:14:24 +03:00
AddCheck ( " E5-01 " , " schedule-logging " , " info " ,
" E5: A=MINIMAL, B=VERBOSE " ,
StringFormat ( " a_min=%d b_verb=%d bad=%d " , a_min , b_verb , bad ) ,
a_min = = 20 & & b_verb = = 20 & & bad = = 0 , " audit-10 " ) ;
2026-09-10 16:43:28 +03:00
}
else
2026-09-12 22:14:24 +03:00
AddCheck ( " E5-01 " , " schedule-logging " , " info " , " init " , " failed " , false , " audit-10 " ) ;
s . experiment_id = LAB_EXP_E3 ;
s . count_per_condition = 40 ;
if ( rn .Init ( s , err ) )
2026-09-10 16:43:28 +03:00
{
ScheduleSlot first ;
2026-09-12 22:14:24 +03:00
rn . GetSlot ( 0 , first ) ;
const ENUM_LAB_MARKET_REGIME need1 = ( first . plan . condition_id = = " A " ?
LAB_REGIME_QUIET : LAB_REGIME_FAST ) ;
const ENUM_LAB_MARKET_REGIME other = ( first . plan . condition_id = = " A " ?
LAB_REGIME_FAST : LAB_REGIME_QUIET ) ;
2026-09-10 16:43:28 +03:00
RequestPlan plan ;
2026-09-11 04:09:32 +03:00
//--- R5-B6: режим другой ветви не блокируется первым ожидающим слотом —
//--- отправляется первая подходящая возможность своей ветви.
2026-09-12 22:14:24 +03:00
const bool ok_other = rn . TryDispatchByRegime ( other , plan ) ;
const bool other_branch = ( ok_other & & plan . condition_id ! = first . plan . condition_id ) ;
2026-09-11 04:09:32 +03:00
//--- затем режим первого слота отдаёт именно первый ожидающий
2026-09-12 22:14:24 +03:00
const bool ok_first = rn . TryDispatchByRegime ( need1 , plan ) ;
const bool first_selected = ( ok_first & & plan . sequence = = first . plan . sequence ) ;
AddCheck ( " E3-01 " , " regime-scan-forward " , " WARNING " ,
" другой режим берёт свою ветвь; нужный режим берёт первый слот " ,
StringFormat ( " other=%s branch=%s first=%s sel=%s " , ( ok_other ? " 1 " : " 0 " ) ,
( other_branch ? " 1 " : " 0 " ) , ( ok_first ? " 1 " : " 0 " ) ,
( first_selected ? " 1 " : " 0 " ) ) ,
ok_other & & other_branch & & ok_first & & first_selected , " audit-9 " ) ;
2026-09-10 16:43:28 +03:00
}
else
2026-09-12 22:14:24 +03:00
AddCheck ( " E3-01 " , " regime-scan-forward " , " WARNING " , " failed " , " init " , false , " audit-9 " ) ;
2026-09-10 16:43:28 +03:00
}
2026-09-12 22:14:24 +03:00
//+------------------------------------------------------------------+
//| Регрессии аудита R4 |
//+------------------------------------------------------------------+
void TestAudit4Regressions ( void )
2026-09-10 22:54:26 +03:00
{
2026-09-12 22:14:24 +03:00
//--- R4-B2: единицы объёма — конвертация lots->units ровно один раз
double u1 = 0 ;
const bool lo_ok = CStateReader : : LotsToUnits ( 0.01 , 0.01 , 1e-7 , u1 ) ;
AddCheck ( " UNITS-01 " , " lots-to-units " , " WARNING " ,
" 0.01 lot / step 0.01 => 1 unit (audit-4-R4-B2) " ,
StringFormat ( " ok=%s units=%.6f " , ( lo_ok ? " 1 " : " 0 " ) , u1 ) ,
lo_ok & & Near ( u1 , 1.0 , 1e-12 ) , " audit-4-R4-B2 " ) ;
//--- light snapshot: callback units сравниваются с requested units (без смешения)
2026-09-10 22:54:26 +03:00
CTimeSource clock ;
clock . UseLive ( ) ;
ConfirmationSnapshot lsn ;
2026-09-12 22:14:24 +03:00
CStateReader : : BuildSnapshot ( 1 , LAB_OP_MARKET_OPEN , 101 , 1.0 , 0.01 , 1e-7 ,
& clock , 0 , 1000 , lsn , true , 1.0 , 1 ) ;
lsn . order_ticket = 101 ;
lsn . order_state_before = ORDER_STATE_FILLED ;
lsn . active_select_status = LAB_READ_ABSENT_CONFIRMED ;
lsn . requested_volume_units = 1.0 ;
lsn . trading_state_known = true ;
CompletionDecision ldc ;
ldc . Zero ( ) ;
CCompletionPolicy : : Evaluate ( LAB_OP_MARKET_OPEN , lsn , 1e-7 , ldc ) ;
AddCheck ( " UNITS-02 " , " light-units-match " , " WARNING " ,
" 1 unit callback vs 1 unit requested => FILLED/T6 (audit-4-R4-B2) " ,
StringFormat ( " outcome=%s cov=%s " , LabOutcomeName ( ldc . outcome ) ,
( lsn . coverage_complete ? " 1 " : " 0 " ) ) ,
lsn . coverage_complete & & ldc . confirmed & & ldc . outcome = = LAB_OUT_FILLED ,
" audit-4-R4-B2 " ) ;
2026-09-10 22:54:26 +03:00
ConfirmationSnapshot lsn2 ;
2026-09-12 22:14:24 +03:00
CStateReader : : BuildSnapshot ( 2 , LAB_OP_MARKET_OPEN , 102 , 1.0 , 0.01 , 1e-7 ,
& clock , 0 , 1000 , lsn2 , true , 0.01 , 1 ) ; // 0.01 UNITS!
lsn2 . order_ticket = 102 ;
lsn2 . order_state_before = ORDER_STATE_FILLED ;
lsn2 . active_select_status = LAB_READ_ABSENT_CONFIRMED ;
lsn2 . requested_volume_units = 1.0 ;
lsn2 . trading_state_known = true ;
CompletionDecision ldc2 ;
ldc2 . Zero ( ) ;
CCompletionPolicy : : Evaluate ( LAB_OP_MARKET_OPEN , lsn2 , 1e-7 , ldc2 ) ;
AddCheck ( " UNITS-03 " , " light-units-mismatch " , " WARNING " ,
" 0.01 units vs 1 requested => NO coverage (эталон смешения) (audit-4-R4-B2) " ,
StringFormat ( " cov=%s " , ( lsn2 . coverage_complete ? " 1 " : " 0 " ) ) ,
! lsn2 . coverage_complete & & ! ldc2 . confirmed , " audit-4-R4-B2 " ) ;
//--- R4-B3: поздний REQUEST регистрируется даже после закрытия коллекции
2026-09-10 22:54:26 +03:00
CRequestTracker trq ;
2026-09-12 22:14:24 +03:00
trq .Init ( 64 , " audit4 " ) ;
RequestPlan pq ;
pq . Zero ( ) ;
pq . sequence = 5 ;
pq . symbol = " X " ;
pq . magic = 9 ;
pq . condition_id = " B " ;
pq . operation = LAB_OP_MARKET_OPEN ;
ulong sq = 0 ;
trq .Reserve ( pq , sq ) ;
SendObservation so ;
so . Zero ( ) ;
so . t1_us = 100 ;
so . send_ok = true ;
so . result_known = true ;
so . result . retcode = TRADE_RETCODE_DONE ;
so . result . request_id = 4242 ;
so . result . order = 7777 ;
trq . MarkSendStart ( 5 , 10 ) ;
trq . MarkSendReturn ( 5 , so ) ;
trq . CloseCollection ( 5 , 5000 , 11 ) ;
trq . MarkRequest ( 5 , 6000 , 4242 , TRADE_RETCODE_DONE , 0 , 7777 , 0 ) ;
RequestMetadata mr ;
trq . GetMetadata ( 5 , mr ) ;
AddCheck ( " COLL-03 " , " late-request-preserved " , " WARNING " ,
" REQUEST после close сохраняет T2/retcode (audit-4-R4-B3) " ,
StringFormat ( " t2=%s retcode=%u closed=%s " ,
( ( mr . present_mask & ( uint ) LAB_MASK_T2 ) ! = 0 ? " 1 " : " 0 " ) ,
mr . sample . retcode , ( mr . collection_closed ? " 1 " : " 0 " ) ) ,
( mr . present_mask & ( uint ) LAB_MASK_T2 ) ! = 0 & & mr . has_retcode & &
mr . collection_closed , " audit-4-R4-B3 " ) ;
//--- R4-B2: BuildSnapshot конвертирует initial/remaining в units (R4-S3)
2026-09-10 22:54:26 +03:00
ConfirmationSnapshot usn ;
2026-09-12 22:14:24 +03:00
CStateReader : : BuildSnapshot ( 3 , LAB_OP_PENDING_CREATE , 103 , 1.0 , 0.01 , 1e-7 ,
& clock , 0 , 1000 , usn , false , 0.0 , 0 ) ;
AddCheck ( " VOLU-01 " , " snapshot-units-fields " , " INFO " ,
" поле *_volume_units не обязано быть non-zero; конвертация при select " ,
" constructor ok " , usn . sequence = = 3 & & usn . order_ticket = = 103 ,
" audit-4-R4-S3 " ) ;
//--- R4-B4: generate->save->load->valid для calibration (>=1000 окон, >=5 сессий)
2026-09-10 22:54:26 +03:00
LabError cerr2 ;
2026-09-12 22:14:24 +03:00
const string cal_id = " cal-audit4-ok " ;
string cal_out = CCsv : : HeaderKeyValue ( ) ;
2026-09-10 22:54:26 +03:00
string cv [ 5 ] ;
2026-09-12 22:14:24 +03:00
cv [ 0 ] = " calibration_id " ;
cv [ 1 ] = cal_id ;
cv [ 2 ] = " string " ;
cv [ 3 ] = " KNOWN " ;
cv [ 4 ] = " DERIVED " ;
cal_out + = CCsv : : BuildRow ( cv , 5 ) ;
cv [ 0 ] = " symbol " ;
cv [ 1 ] = " X " ;
cal_out + = CCsv : : BuildRow ( cv , 5 ) ;
cv [ 0 ] = " frequency_p50 " ;
cv [ 1 ] = " 2.0 " ;
cal_out + = CCsv : : BuildRow ( cv , 5 ) ;
cv [ 0 ] = " frequency_p90 " ;
cv [ 1 ] = " 10.0 " ;
cal_out + = CCsv : : BuildRow ( cv , 5 ) ;
cv [ 0 ] = " range_p50 " ;
cv [ 1 ] = " 4.0 " ;
cal_out + = CCsv : : BuildRow ( cv , 5 ) ;
cv [ 0 ] = " range_p90 " ;
cv [ 1 ] = " 10.0 " ;
cal_out + = CCsv : : BuildRow ( cv , 5 ) ;
cv [ 0 ] = " valid_window_count " ;
cv [ 1 ] = " 1000 " ;
cal_out + = CCsv : : BuildRow ( cv , 5 ) ;
cv [ 0 ] = " invalid_window_count " ;
cv [ 1 ] = " 10 " ;
cal_out + = CCsv : : BuildRow ( cv , 5 ) ;
cv [ 0 ] = " trading_session_count " ;
cv [ 1 ] = " 5 " ;
cal_out + = CCsv : : BuildRow ( cv , 5 ) ;
const string cal_path = " RequestLatencyLab \\ " + cal_id + " .csv " ;
const bool cal_saved = CCsv : : SaveUtf8 ( cal_path , cal_out , cerr2 ) ;
Calibration cal_ok ;
cal_ok . Zero ( ) ;
const bool cal_loaded = cal_saved & & CCsvReport : : LoadCalibration ( cal_id , cal_ok , cerr2 ) ;
AddCheck ( " CAL-01 " , " calibration-ok " , " WARNING " ,
" >=1000 окон и >=5 сессий => valid (audit-4-R4-B4) " ,
StringFormat ( " saved=%s loaded=%s valid=%s sessions=%d " ,
( cal_saved ? " 1 " : " 0 " ) , ( cal_loaded ? " 1 " : " 0 " ) ,
( cal_ok . valid ? " 1 " : " 0 " ) , cal_ok . trading_session_count ) ,
cal_saved & & cal_loaded & & cal_ok . valid & &
cal_ok . trading_session_count > = 5 , " audit-4-R4-B4 " ) ;
//--- <5 сессий => невалидна (обратная связка save->load)
const string cal_bad = " cal-audit4-bad " ;
string cal_out2 = CCsv : : HeaderKeyValue ( ) ;
cv [ 0 ] = " calibration_id " ;
cv [ 1 ] = cal_bad ;
cal_out2 + = CCsv : : BuildRow ( cv , 5 ) ;
cv [ 0 ] = " symbol " ;
cv [ 1 ] = " X " ;
cal_out2 + = CCsv : : BuildRow ( cv , 5 ) ;
cv [ 0 ] = " frequency_p50 " ;
cv [ 1 ] = " 2.0 " ;
cal_out2 + = CCsv : : BuildRow ( cv , 5 ) ;
cv [ 0 ] = " frequency_p90 " ;
cv [ 1 ] = " 10.0 " ;
cal_out2 + = CCsv : : BuildRow ( cv , 5 ) ;
cv [ 0 ] = " range_p50 " ;
cv [ 1 ] = " 4.0 " ;
cal_out2 + = CCsv : : BuildRow ( cv , 5 ) ;
cv [ 0 ] = " range_p90 " ;
cv [ 1 ] = " 10.0 " ;
cal_out2 + = CCsv : : BuildRow ( cv , 5 ) ;
cv [ 0 ] = " valid_window_count " ;
cv [ 1 ] = " 1000 " ;
cal_out2 + = CCsv : : BuildRow ( cv , 5 ) ;
cv [ 0 ] = " invalid_window_count " ;
cv [ 1 ] = " 10 " ;
cal_out2 + = CCsv : : BuildRow ( cv , 5 ) ;
cv [ 0 ] = " trading_session_count " ;
cv [ 1 ] = " 4 " ;
cal_out2 + = CCsv : : BuildRow ( cv , 5 ) ;
const string cal_path2 = " RequestLatencyLab \\ " + cal_bad + " .csv " ;
const bool cal_saved2 = CCsv : : SaveUtf8 ( cal_path2 , cal_out2 , cerr2 ) ;
Calibration cal_bad2 ;
cal_bad2 . Zero ( ) ;
const bool cal_loaded2 = cal_saved2 & & CCsvReport : : LoadCalibration ( cal_bad , cal_bad2 , cerr2 ) ;
AddCheck ( " CAL-02 " , " calibration-sessions-gate " , " WARNING " ,
" 4 сессии => invalid (audit-4-R4-B4) " ,
StringFormat ( " loaded=%s valid=%s " , ( cal_loaded2 ? " 1 " : " 0 " ) ,
( cal_bad2 . valid ? " 1 " : " 0 " ) ) ,
cal_saved2 & & cal_loaded2 = = false & & cal_bad2 . valid = = false ,
" audit-4-R4-B4 " ) ;
//--- R4-B7: WAIT/precheck не расходуют слот и квоту; retry выдаёт тот же слот
2026-09-10 22:54:26 +03:00
CExperimentRunner rn4 ;
LabSettings s4 ;
s4 . Zero ( ) ;
2026-09-12 22:14:24 +03:00
s4 . experiment_id = LAB_EXP_E1 ;
s4 . series_id = 1 ;
s4 . count_per_condition = 4 ;
s4 . warmup_per_condition = 2 ;
s4 . symbol = " X " ;
s4 . magic = 1 ;
s4 . seed = 7 ;
s4 . run_mode = LAB_RUN_LOCAL_ONLY ;
if ( rn4 .Init ( s4 , cerr2 ) )
2026-09-10 22:54:26 +03:00
{
RequestPlan first ;
2026-09-12 22:14:24 +03:00
const bool have_first = rn4 . NextMain ( first ) ;
2026-09-10 22:54:26 +03:00
rn4 . MarkWaitSlot ( first . sequence ) ; // нет котировки — WAIT
RequestPlan again ;
2026-09-12 22:14:24 +03:00
const bool have_again = rn4 . NextMain ( again ) ;
AddCheck ( " QUOTA-01 " , " wait-requeue " , " WARNING " ,
" WAIT возвращает слот в очередь (audit-4-R4-B7) " ,
StringFormat ( " first=%I64u again=%I64u " , ( long ) first . sequence ,
( long ) again . sequence ) ,
have_first & & have_again & & again . sequence = = first . sequence ,
" audit-4-R4-B7 " ) ;
2026-09-10 22:54:26 +03:00
//--- фактическая отправка считает слот и квоту
rn4 . MarkDispatchedSlot ( first . sequence ) ;
rn4 . CountDispatched ( LAB_ROLE_MAIN ) ;
2026-09-12 22:14:24 +03:00
AddCheck ( " QUOTA-02 " , " actual-send-count " , " WARNING " ,
" 1 фактический send после dispatch; WAIT не считается (audit-4-R4-B7) " ,
StringFormat ( " dispatched=%I64u " ,
( long ) rn4 . MainDispatched ( ) ) ,
rn4 . MainDispatched ( ) = = 1 , " audit-4-R4-B7 " ) ;
2026-09-10 22:54:26 +03:00
}
else
2026-09-12 22:14:24 +03:00
AddCheck ( " QUOTA-01 " , " wait-requeue " , " WARNING " , " init " , " failed " , false ,
2026-09-10 22:54:26 +03:00
" audit-4-R4-B7 " ) ;
2026-09-12 22:14:24 +03:00
//--- R4-S2: pending-create с несовпадающим контрактом не подтверждается
ConfirmationSnapshot pp ;
pp . Zero ( ) ;
pp . order_ticket = 50 ;
pp . order_state_before = ORDER_STATE_PLACED ;
pp . active_select_status = LAB_READ_FOUND ;
pp . order_symbol = " X " ;
pp . order_magic = 1 ;
pp . order_volume_lots = 0.01 ;
pp . order_price = 1.1000 ;
pp . order_type = ORDER_TYPE_BUY_LIMIT ;
pp . expected_symbol = " X " ;
pp . expected_magic = 1 ;
pp . expected_volume_lots = 0.01 ;
pp . expected_price = 1.0999 ; // расхождение цены
pp . price_tolerance = 0.00001 ;
pp . expected_order_type = ORDER_TYPE_BUY_LIMIT ;
pp . trading_state_known = true ;
CompletionDecision pd ;
pd . Zero ( ) ;
CCompletionPolicy : : Evaluate ( LAB_OP_PENDING_CREATE , pp , 1e-7 , pd ) ;
AddCheck ( " PEND-01 " , " pending-contract-mismatch " , " WARNING " ,
" цена не совпадает => нет T6 (audit-4-R4-S2) " ,
StringFormat ( " outcome=%s conf=%s " , LabOutcomeName ( pd . outcome ) ,
( pd . confirmed ? " 1 " : " 0 " ) ) ,
! pd . confirmed , " audit-4-R4-S2 " ) ;
//--- совпадающий контракт => PENDING_PLACED
pp . expected_price = 1.1000 ;
CompletionDecision pd2 ;
pd2 . Zero ( ) ;
CCompletionPolicy : : Evaluate ( LAB_OP_PENDING_CREATE , pp , 1e-7 , pd2 ) ;
AddCheck ( " PEND-02 " , " pending-contract-match " , " WARNING " ,
" совпадение контракта => PENDING_PLACED+T6 (audit-4-R4-S2) " ,
StringFormat ( " outcome=%s " , LabOutcomeName ( pd2 . outcome ) ) ,
pd2 . outcome = = LAB_OUT_PENDING_PLACED & & pd2 . t6_assignable ,
" audit-4-R4-S2 " ) ;
//--- R4-S1: pending-delete: LAB_READ_FAILED не является отсутствием
ConfirmationSnapshot dp ;
dp . Zero ( ) ;
dp . order_ticket = 60 ;
dp . order_state_before = ORDER_STATE_CANCELED ;
dp . active_select_status = LAB_READ_FAILED ; // ошибка чтения, не absence
dp . executed_volume_units = 0.0 ;
CompletionDecision dd ;
dd . Zero ( ) ;
CCompletionPolicy : : Evaluate ( LAB_OP_PENDING_DELETE , dp , 1e-7 , dd ) ;
AddCheck ( " PDEL-01 " , " delete-failed-not-absent " , " WARNING " ,
" FAILED чтения не даёт PENDING_DELETED (audit-4-R4-S1) " ,
StringFormat ( " outcome=%s conf=%s " , LabOutcomeName ( dd . outcome ) ,
( dd . confirmed ? " 1 " : " 0 " ) ) ,
! dd . confirmed , " audit-4-R4-S1 " ) ;
//--- R4-B8: BuildSamples — отсутствующие метки = пустые поля
2026-09-10 22:54:26 +03:00
CRequestTracker trs ;
2026-09-12 22:14:24 +03:00
trs .Init ( 64 , " audit4-samples " ) ;
RequestPlan ps ;
ps . Zero ( ) ;
ps . sequence = 1 ;
ps . symbol = " X " ;
ps . magic = 1 ;
ps . condition_id = " A " ;
ps . operation = LAB_OP_MARKET_OPEN ;
trs .Reserve ( ps , sq ) ;
trs . MarkSendStart ( 1 , 100 ) ; // только T0
const string samples_csv = CCsvReport : : BuildSamples ( & trs , " C1 " , " cfg " ) ;
string slines [ ] ;
const int sn = StringSplit ( samples_csv , ' \n ' , slines ) ;
string scols [ ] ;
int scc = 0 ;
CCsv : : ParseRow ( slines [ 1 ] , scols , scc ) ;
AddCheck ( " CSV-03 " , " samples-empty-fields " , " WARNING " ,
" отсутствующие T1..T6/retcode = пустое поле (audit-4-R4-B8) " ,
StringFormat ( " t2='%s' ret='%s' " , scols [ 4 ] , scols [ 8 ] ) ,
scols [ 4 ] = = " " & & scols [ 8 ] = = " " , " audit-4-R4-B8 " ) ;
//--- R4-B8: знаковая метрика отсутствует в histogram, есть в offset_signs
2026-09-10 22:54:26 +03:00
LabSettings s_export ;
s_export . Zero ( ) ;
2026-09-12 22:14:24 +03:00
s_export . experiment_id = LAB_EXP_E1 ;
s_export . series_id = 1 ;
const string hist_csv = CCsvReport : : BuildHistogram ( & trs , " s " , s_export ) ;
const bool signed_in_hist = ( StringFind ( hist_csv , " request_first_deal_offset " ) > = 0 ) ;
const string off_csv = CCsvReport : : BuildOffsetSigns ( & trs , " s " , s_export ) ;
const bool signs_present = ( StringFind ( off_csv , " NEGATIVE " ) > = 0 & &
StringFind ( off_csv , " POSITIVE " ) > = 0 ) ;
AddCheck ( " HIST-02 " , " signed-metric-excluded " , " WARNING " ,
" T2-T4 не в latency-гистограмме; отдельный offset_signs.csv (audit-4-R4-B8) " ,
StringFormat ( " in_hist=%s signs=%s " , ( signed_in_hist ? " 1 " : " 0 " ) ,
( signs_present ? " 1 " : " 0 " ) ) ,
! signed_in_hist & & signs_present , " audit-4-R4-B8 " ) ;
//--- R4-B8: comparisons.csv формируется
const string cmp_csv = CCsvReport : : BuildComparisons ( & trs , " s " , s_export ) ;
const bool cmp_ok = ( StringFind ( cmp_csv , " A_vs_B " ) > = 0 ) ;
AddCheck ( " CMP-01 " , " comparisons-exists " , " WARNING " ,
" comparisons.csv с парой A_vs_B (audit-4-R4-B8) " ,
( cmp_ok ? " ok " : " missing " ) , cmp_ok , " audit-4-R4-B8 " ) ;
2026-09-10 22:54:26 +03:00
}
2026-09-12 22:14:24 +03:00
//+------------------------------------------------------------------+
//| Регрессии аудита R5 |
//+------------------------------------------------------------------+
void TestAudit5Regressions ( void )
2026-09-11 04:09:32 +03:00
{
2026-09-12 22:14:24 +03:00
//--- R5-B3: категория LATE по фактическому концу метрики (не по порядку таймера)
2026-09-11 04:09:32 +03:00
CRequestTracker tr5 ;
2026-09-12 22:14:24 +03:00
tr5 .Init ( 64 , " audit5 " ) ;
RequestPlan pe ;
pe . Zero ( ) ;
pe . sequence = 1 ;
pe . symbol = " X " ;
pe . magic = 1 ;
pe . condition_id = " A " ;
pe . operation = LAB_OP_MARKET_OPEN ;
ulong sq5 = 0 ;
tr5 .Reserve ( pe , sq5 ) ;
tr5 . MarkSendStart ( 1 , 1000000UL ) ; // T0=1s
SendObservation so5 ;
so5 . Zero ( ) ;
so5 . t1_us = 1000100UL ;
so5 . send_ok = true ;
so5 . result_known = true ;
so5 . result . retcode = TRADE_RETCODE_DONE ;
so5 . result . request_id = 1 ;
tr5 . MarkSendReturn ( 1 , so5 ) ;
tr5 . ComputeDeadlines ( 1 , 5000000UL , 1000000UL ) ; // deadline=6s
tr5 . MarkDealAdd ( 1 , 1100000UL , 50001 , 0.01 ) ; // ранняя T4=1.1s, T6 нет
RequestMetadata me5 ;
tr5 . GetMetadata ( 1 , me5 ) ;
MetricObservation mo5 ;
bool ev5 = false ;
CRequestTracker : : EvaluateMetric ( me5 , LAB_METRIC_FIRST_DEAL_DELAY , mo5 , ev5 ) ;
AddCheck ( " EVAL-01 " , " early-deal-valid-no-t6 " , " WARNING " ,
" T4=1.1s <= deadline => VALID даже без T6 (R5-B3) " ,
StringFormat ( " cat=%s val=%s " , LabFitnessName ( mo5 . category ) ,
( mo5 . has_value ? " 1 " : " 0 " ) ) ,
ev5 & & mo5 . category = = LAB_FIT_VALID & & mo5 . has_value , " audit-5-R5-B3 " ) ;
pe . sequence = 2 ;
tr5 .Reserve ( pe , sq5 ) ;
tr5 . MarkSendStart ( 2 , 1000000UL ) ;
SendObservation so6 ;
so6 . Zero ( ) ;
so6 . t1_us = 1000100UL ;
so6 . send_ok = true ;
so6 . result_known = true ;
so6 . result . retcode = TRADE_RETCODE_DONE ;
so6 . result . request_id = 2 ;
tr5 . MarkSendReturn ( 2 , so6 ) ;
tr5 . ComputeDeadlines ( 2 , 5000000UL , 1000000UL ) ;
tr5 . MarkRequest ( 2 , 6300000UL , 2 , TRADE_RETCODE_DONE , 0 , 0 , 0 ) ; // T2=6.3s > deadline
tr5 . GetMetadata ( 2 , me5 ) ;
CRequestTracker : : EvaluateMetric ( me5 , LAB_METRIC_REQUEST_DELAY , mo5 , ev5 ) ;
AddCheck ( " EVAL-02 " , " late-t2-late " , " WARNING " ,
" T2=6.3s > deadline => REQUEST_DELAY LATE (R5-B3) " ,
StringFormat ( " cat=%s " , LabFitnessName ( mo5 . category ) ) ,
ev5 & & mo5 . category = = LAB_FIT_LATE , " audit-5-R5-B3 " ) ;
pe . sequence = 3 ;
tr5 .Reserve ( pe , sq5 ) ;
tr5 . MarkSendStart ( 3 , 1000000UL ) ;
SendObservation so7 ;
so7 . Zero ( ) ;
so7 . t1_us = 1000100UL ;
so7 . send_ok = true ;
so7 . result_known = true ;
so7 . result . retcode = TRADE_RETCODE_DONE ;
so7 . result . request_id = 3 ;
tr5 . MarkSendReturn ( 3 , so7 ) ;
tr5 . ComputeDeadlines ( 3 , 5000000UL , 1000000UL ) ;
tr5 . SetFinal ( 3 , 6200000UL , LAB_OUT_FILLED , LAB_FINAL_TIMER_CHECK , 1 ) ; // T6=6.2s
tr5 . GetMetadata ( 3 , me5 ) ;
CRequestTracker : : EvaluateMetric ( me5 , LAB_METRIC_FINAL_STATE_DELAY , mo5 , ev5 ) ;
AddCheck ( " EVAL-03 " , " late-t6-late " , " WARNING " ,
" T6=6.2s > deadline => FINAL_STATE_DELAY LATE (R5-B3) " ,
StringFormat ( " cat=%s " , LabFitnessName ( mo5 . category ) ) ,
ev5 & & mo5 . category = = LAB_FIT_LATE , " audit-5-R5-B3 " ) ;
pe . sequence = 4 ;
tr5 .Reserve ( pe , sq5 ) ;
tr5 . MarkSendStart ( 4 , 1000000UL ) ;
SendObservation so8 ;
so8 . Zero ( ) ;
so8 . t1_us = 1000100UL ;
so8 . send_ok = true ;
so8 . result_known = true ;
so8 . result . retcode = TRADE_RETCODE_DONE ;
so8 . result . request_id = 4 ;
tr5 . MarkSendReturn ( 4 , so8 ) ;
tr5 . ComputeDeadlines ( 4 , 5000000UL , 1000000UL ) ;
tr5 . MarkRequest ( 4 , 6000000UL , 4 , TRADE_RETCODE_DONE , 0 , 0 , 0 ) ; // ровно на границе
tr5 . GetMetadata ( 4 , me5 ) ;
CRequestTracker : : EvaluateMetric ( me5 , LAB_METRIC_REQUEST_DELAY , mo5 , ev5 ) ;
AddCheck ( " EVAL-04 " , " deadline-boundary-valid " , " WARNING " ,
" end == deadline => VALID (не LATE) (R5-B3) " ,
StringFormat ( " cat=%s " , LabFitnessName ( mo5 . category ) ) ,
ev5 & & mo5 . category = = LAB_FIT_VALID , " audit-5-R5-B3 " ) ;
//--- R5-B4: callback-coverage отдельно от торгового результата
2026-09-11 04:09:32 +03:00
CRequestTracker tr6 ;
2026-09-12 22:14:24 +03:00
tr6 .Init ( 64 , " audit5-t5 " ) ;
RequestPlan pt ;
pt . Zero ( ) ;
pt . sequence = 1 ;
pt . symbol = " X " ;
pt . magic = 1 ;
pt . condition_id = " B " ;
pt . operation = LAB_OP_MARKET_OPEN ;
ulong sq6 = 0 ;
tr6 .Reserve ( pt , sq6 ) ;
tr6 . MarkDealAdd ( 1 , 500 , 1001 , 0.005 ) ; // сделка наблюдалась callback-ом
ConfirmationSnapshot sn6 ;
sn6 . Zero ( ) ;
sn6 . sequence = 1 ;
sn6 . order_ticket = 777 ;
sn6 . deal_tickets [ 0 ] = 1001 ; // есть в callback
sn6 . deal_tickets [ 1 ] = 2002 ; // только в истории
sn6 . deal_count = 2 ;
sn6 . executed_volume_units = 1.0 ;
sn6 . coverage_complete = true ; // объём сошёлся (по истории)
sn6 . history_scan_failed = false ;
tr6 . SyncReconcile ( 1 , sn6 , 0.01 , 1e-7 ) ;
RequestMetadata mt6 ;
tr6 . GetMetadata ( 1 , mt6 ) ;
AddCheck ( " T5-01 " , " callback-coverage-partial " , " WARNING " ,
" 1 callback + 1 history-only => callback_coverage=false (R5-B4) " ,
StringFormat ( " trade=%s callback=%s " , ( mt6 . deal_coverage_complete ? " 1 " : " 0 " ) ,
( mt6 . callback_coverage_complete ? " 1 " : " 0 " ) ) ,
mt6 . deal_coverage_complete & & ! mt6 . callback_coverage_complete ,
" audit-5-R5-B4 " ) ;
//--- обе сделки наблюдались callback-ами
tr6 .Init ( 64 , " audit5-t5b " ) ;
ulong sq6b = 0 ;
tr6 .Reserve ( pt , sq6b ) ;
tr6 . MarkDealAdd ( 1 , 500 , 1001 , 0.005 ) ;
tr6 . MarkDealAdd ( 1 , 900 , 2002 , 0.005 ) ;
ConfirmationSnapshot sn6b ;
sn6b . Zero ( ) ;
sn6b . sequence = 1 ;
sn6b . order_ticket = 777 ;
sn6b . deal_tickets [ 0 ] = 1001 ;
sn6b . deal_tickets [ 1 ] = 2002 ;
sn6b . deal_count = 2 ;
sn6b . executed_volume_units = 1.0 ;
sn6b . coverage_complete = true ;
sn6b . history_scan_failed = false ;
tr6 . SyncReconcile ( 1 , sn6b , 0.01 , 1e-7 ) ;
tr6 . GetMetadata ( 1 , mt6 ) ;
AddCheck ( " T5-02 " , " callback-coverage-full " , " WARNING " ,
" обе сделки через callback => callback_coverage=true (R5-B4) " ,
( mt6 . callback_coverage_complete ? " 1 " : " 0 " ) ,
mt6 . callback_coverage_complete , " audit-5-R5-B4 " ) ;
//--- R5-B8: FILLED/EXPIRED не равны удалению; PARTIAL_CANCELED явно
ConfirmationSnapshot dp5 ;
dp5 . Zero ( ) ;
dp5 . order_ticket = 70 ;
dp5 . order_state_before = ORDER_STATE_FILLED ;
dp5 . active_select_status = LAB_READ_ABSENT_CONFIRMED ;
dp5 . executed_volume_units = 0.0 ;
CompletionDecision dd5 ;
dd5 . Zero ( ) ;
CCompletionPolicy : : Evaluate ( LAB_OP_PENDING_DELETE , dp5 , 1e-7 , dd5 ) ;
AddCheck ( " PDEL-02 " , " filled-not-deleted " , " WARNING " ,
" FILLED + 0 executed => не PENDING_DELETED (R5-B8) " ,
StringFormat ( " outcome=%s conf=%s " , LabOutcomeName ( dd5 . outcome ) ,
( dd5 . confirmed ? " 1 " : " 0 " ) ) ,
dd5 . outcome ! = LAB_OUT_PENDING_DELETED & & ! dd5 . confirmed , " audit-5-R5-B8 " ) ;
ConfirmationSnapshot dp6 ;
dp6 . Zero ( ) ;
dp6 . order_ticket = 71 ;
dp6 . order_state_before = ORDER_STATE_EXPIRED ;
dp6 . active_select_status = LAB_READ_ABSENT_CONFIRMED ;
dp6 . executed_volume_units = 0.0 ;
CompletionDecision dd6 ;
dd6 . Zero ( ) ;
CCompletionPolicy : : Evaluate ( LAB_OP_PENDING_DELETE , dp6 , 1e-7 , dd6 ) ;
AddCheck ( " PDEL-03 " , " expired-not-deleted " , " WARNING " ,
" EXPIRED не подтверждает команду удаления (R5-B8) " ,
StringFormat ( " outcome=%s conf=%s " , LabOutcomeName ( dd6 . outcome ) ,
( dd6 . confirmed ? " 1 " : " 0 " ) ) ,
dd6 . outcome ! = LAB_OUT_PENDING_DELETED & & ! dd6 . confirmed , " audit-5-R5-B8 " ) ;
ConfirmationSnapshot pp5 ;
pp5 . Zero ( ) ;
pp5 . order_ticket = 72 ;
pp5 . order_state_before = ORDER_STATE_CANCELED ;
pp5 . active_select_status = LAB_READ_ABSENT_CONFIRMED ;
pp5 . requested_volume_units = 2.0 ;
pp5 . executed_volume_units = 1.0 ;
pp5 . remaining_volume_units = 1.0 ; // неисполненный остаток из истории
pp5 . trading_state_known = true ;
CompletionDecision pd5 ;
pd5 . Zero ( ) ;
CCompletionPolicy : : Evaluate ( LAB_OP_MARKET_OPEN , pp5 , 1e-7 , pd5 ) ;
AddCheck ( " PART-01 " , " partial-canceled-explicit " , " WARNING " ,
" executed 1/2, нет активного остатка => PARTIAL_CANCELED (R5-B8) " ,
StringFormat ( " outcome=%s conf=%s " , LabOutcomeName ( pd5 . outcome ) ,
( pd5 . confirmed ? " 1 " : " 0 " ) ) ,
pd5 . confirmed & & pd5 . outcome = = LAB_OUT_PARTIAL_CANCELED , " audit-5-R5-B8 " ) ;
//--- R5-B8: несовпадение точного типа при равных параметрах
ConfirmationSnapshot pp6 ;
pp6 . Zero ( ) ;
pp6 . order_ticket = 51 ;
pp6 . order_state_before = ORDER_STATE_PLACED ;
pp6 . active_select_status = LAB_READ_FOUND ;
pp6 . order_symbol = " X " ;
pp6 . order_magic = 1 ;
pp6 . order_volume_lots = 0.01 ;
pp6 . order_price = 1.1000 ;
pp6 . order_type = ORDER_TYPE_BUY_LIMIT ;
pp6 . expected_symbol = " X " ;
pp6 . expected_magic = 1 ;
pp6 . expected_volume_lots = 0.01 ;
pp6 . expected_price = 1.1000 ;
pp6 . expected_order_type = ORDER_TYPE_SELL_LIMIT ; // тип не совпал
pp6 . price_tolerance = 0.00001 ;
pp6 . trading_state_known = true ;
CompletionDecision pd6 ;
pd6 . Zero ( ) ;
CCompletionPolicy : : Evaluate ( LAB_OP_PENDING_CREATE , pp6 , 1e-7 , pd6 ) ;
AddCheck ( " PEND-03 " , " pending-type-mismatch " , " WARNING " ,
" другой тип при равных параметрах => нет T6 (R5-B8) " ,
StringFormat ( " outcome=%s conf=%s " , LabOutcomeName ( pd6 . outcome ) ,
( pd6 . confirmed ? " 1 " : " 0 " ) ) ,
! pd6 . confirmed , " audit-5-R5-B8 " ) ;
//--- R5-S1: малый пилот per_condition=4 => 8 валидных слотов
2026-09-11 04:09:32 +03:00
CExperimentRunner rn5 ;
LabSettings s5 ;
s5 . Zero ( ) ;
2026-09-12 22:14:24 +03:00
s5 . experiment_id = LAB_EXP_E1 ;
s5 . series_id = 1 ;
s5 . count_per_condition = 4 ;
s5 . warmup_per_condition = 0 ;
s5 . symbol = " X " ;
s5 . magic = 1 ;
s5 . seed = 7 ;
s5 . run_mode = LAB_RUN_LOCAL_ONLY ;
2026-09-11 04:09:32 +03:00
LabError p5e ;
2026-09-12 22:14:24 +03:00
bool pilot_ok = false ;
if ( rn5 .Init ( s5 , p5e ) )
2026-09-11 04:09:32 +03:00
{
2026-09-12 22:14:24 +03:00
pilot_ok = ( rn5 . SlotCount ( ) = = 8 ) ;
for ( int i = 0 ; i < rn5 . SlotCount ( ) & & pilot_ok ; i + + )
2026-09-11 04:09:32 +03:00
{
ScheduleSlot ss ;
2026-09-12 22:14:24 +03:00
if ( ! rn5 . GetSlot ( i , ss ) )
{ pilot_ok = false ; break ; }
if ( ss . plan . sequence = = 0 | |
( ss . plan . condition_id ! = " A " & & ss . plan . condition_id ! = " B " ) | |
( ss . plan . side ! = LAB_SIDE_BUY & & ss . plan . side ! = LAB_SIDE_SELL ) )
{ pilot_ok = false ; break ; }
2026-09-11 04:09:32 +03:00
}
}
2026-09-12 22:14:24 +03:00
AddCheck ( " PILOT-01 " , " small-pilot-slots " , " WARNING " ,
" per_condition=4 => 8 валидных слотов без планов-пустышек (R5-S1) " ,
( pilot_ok ? " ok " : " failed " ) , pilot_ok , " audit-5-R5-S1 " ) ;
//--- R5-S2: нулевой enum (ORDER_TYPE_BUY=0 / ORDER_FILLING_FOK=0) не пусто
2026-09-11 04:09:32 +03:00
CRequestTracker tre ;
2026-09-12 22:14:24 +03:00
tre .Init ( 64 , " audit5-enum " ) ;
RequestPlan pee ;
pee . Zero ( ) ;
pee . sequence = 1 ;
pee . symbol = " X " ;
pee . magic = 1 ;
pee . condition_id = " A " ;
pee . operation = LAB_OP_MARKET_OPEN ;
pee . actual_known = true ;
pee . actual_type = ORDER_TYPE_BUY ;
pee . actual_filling = ORDER_FILLING_FOK ;
ulong sqe = 0 ;
tre .Reserve ( pee , sqe ) ;
const string enum_csv = CCsvReport : : BuildSamples ( & tre , " C1 " , " cfg " ) ;
string el [ ] ;
const int en = StringSplit ( enum_csv , ' \n ' , el ) ;
string ec [ ] ;
int ecc = 0 ;
CCsv : : ParseRow ( el [ 1 ] , ec , ecc ) ;
AddCheck ( " ENUM-01 " , " zero-enum-not-empty " , " WARNING " ,
" actual_type=0/FOK=0 пишутся как 0, а не пусто (R5-S2) " ,
StringFormat ( " at='%s' af='%s' " , ec [ 60 ] , ec [ 61 ] ) ,
ec [ 60 ] = = " 0 " & & ec [ 61 ] = = " 0 " , " audit-5-R5-S2 " ) ;
//--- R5-B5: сквозной rebuild tracker -> samples.csv -> RebuildReport
2026-09-11 04:09:32 +03:00
CRequestTracker trr ;
2026-09-12 22:14:24 +03:00
trr .Init ( 128 , " audit5-rebuild " ) ;
for ( int k = 1 ; k < = 2 ; k + + )
2026-09-11 04:09:32 +03:00
{
2026-09-12 22:14:24 +03:00
RequestPlan pr ;
pr . Zero ( ) ;
pr . sequence = k ;
pr . symbol = " X " ;
pr . magic = 1 ;
pr . condition_id = ( k = = 1 ? " A " : " B " ) ;
pr . operation = LAB_OP_MARKET_OPEN ;
ulong sqr = 0 ;
trr .Reserve ( pr , sqr ) ;
SendObservation sor ;
sor . Zero ( ) ;
sor . t1_us = ( ulong ) ( 1000 + k ) ;
sor . send_ok = true ;
sor . result_known = true ;
sor . result . retcode = TRADE_RETCODE_DONE ;
sor . result . request_id = ( uint ) k ;
sor . result . order = ( ulong ) ( 7000 + ( ulong ) k ) ;
trr . MarkSendStart ( ( ulong ) k , ( ulong ) ( 1000 * ( ulong ) k ) ) ;
trr . MarkSendReturn ( ( ulong ) k , sor ) ;
trr . MarkRequest ( ( ulong ) k , ( ulong ) ( 3000 * ( ulong ) k ) , ( uint ) k , TRADE_RETCODE_DONE , 0 ,
( ulong ) ( 7000 + ( ulong ) k ) , 0 ) ;
trr . MarkOrderAdd ( ( ulong ) k , ( ulong ) ( 3200 * ( ulong ) k ) , ( ulong ) ( 7000 + ( ulong ) k ) , " TEST " ) ;
trr . MarkDealAdd ( ( ulong ) k , ( ulong ) ( 3500 * ( ulong ) k ) , ( ulong ) ( 8000 + ( ulong ) k ) , 0.01 ) ;
2026-09-11 04:09:32 +03:00
trr . SetDealCoverageComplete ( ( ulong ) k ) ;
2026-09-12 22:14:24 +03:00
trr . SetFinal ( ( ulong ) k , ( ulong ) ( 4000 * ( ulong ) k ) , LAB_OUT_FILLED ,
LAB_FINAL_TIMER_CHECK , ( ulong ) k ) ;
trr . SetTradingStateKnown ( ( ulong ) k , true ) ;
trr . SetDeadlinesFromSample ( ( ulong ) k , 5000000UL , 1000000UL ) ;
2026-09-11 04:09:32 +03:00
}
2026-09-12 22:14:24 +03:00
const string e2e_samples = CCsvReport : : BuildSamples ( & trr , " C1 " , " cfg " ) ;
const string e2e_path = " RequestLatencyLab \\ tests \\ samples_e2e.csv " ;
2026-09-11 04:09:32 +03:00
LabError e2e_err ;
2026-09-12 22:14:24 +03:00
const bool e2e_saved = CCsv : : SaveUtf8 ( e2e_path , e2e_samples , e2e_err ) ;
2026-09-11 04:09:32 +03:00
FolderCreate ( " RequestLatencyLab \\ tests \\ rebuilt_e2e " ) ;
2026-09-12 22:14:24 +03:00
const bool e2e_ok = e2e_saved & &
CReportBuilder : : RebuildReport ( e2e_path ,
" RequestLatencyLab \\ tests \\ rebuilt_e2e \\ " , e2e_err ) ;
bool rows_ok = false ;
2026-09-11 04:09:32 +03:00
if ( e2e_ok )
{
string rc ;
if ( CCsv : : LoadUtf8 ( " RequestLatencyLab \\ tests \\ rebuilt_e2e \\ summary_rebuilt.csv " ,
2026-09-12 22:14:24 +03:00
rc , e2e_err ) )
2026-09-11 04:09:32 +03:00
{
2026-09-12 22:14:24 +03:00
string rl [ ] ;
const int rn = StringSplit ( rc , ' \n ' , rl ) ;
int rrows = 0 ;
for ( int i = 0 ; i < rn ; i + + )
2026-09-11 04:09:32 +03:00
{
2026-09-12 22:14:24 +03:00
string t = rl [ i ] ;
StringReplace ( t , " \r " , " " ) ;
if ( StringLen ( t ) > 0 )
2026-09-11 04:09:32 +03:00
rrows + + ;
}
//--- 3 группы x (A+B+combined) x 8 метрик = 72 строки + заголовок
2026-09-12 22:14:24 +03:00
rows_ok = ( rrows = = 73 ) ;
2026-09-11 04:09:32 +03:00
}
}
2026-09-12 22:14:24 +03:00
AddCheck ( " REBUILD-01 " , " rebuild-roundtrip " , " WARNING " ,
" tracker->samples->RebuildReport успешен; summary_rebuilt=73 (R5-B5) " ,
StringFormat ( " saved=%s ok=%s rows=%s err=%s " , ( e2e_saved ? " 1 " : " 0 " ) ,
( e2e_ok ? " 1 " : " 0 " ) , ( rows_ok ? " 73 " : " ? " ) , e2e_err . message ) ,
e2e_saved & & e2e_ok & & rows_ok , " audit-5-R5-B5 " ) ;
//--- R6-B2: offline summary по MAIN выборке (без WARMUP) — n/счётчики совпадают
//--- с онлайн-семантикой независимо от способа чтения (прямые структуры).
2026-09-11 09:22:15 +03:00
RebuiltSample or2 [ 2 ] ;
2026-09-12 22:14:24 +03:00
ZeroMemory ( or2 [ 0 ] ) ;
ZeroMemory ( or2 [ 1 ] ) ;
or2 [ 0 ] . sequence = 1 ;
or2 [ 0 ] . condition_id = " A " ;
or2 [ 0 ] . role = " MAIN " ;
or2 [ 0 ] . is_warmup = false ;
or2 [ 0 ] . operation = " MARKET_OPEN " ;
or2 [ 0 ] . outcome = " FILLED " ;
or2 [ 0 ] . present_mask = LAB_MASK_T0 | LAB_MASK_T1 | LAB_MASK_T2 | LAB_MASK_T6 ;
or2 [ 0 ] . t0 = 1000000UL ;
or2 [ 0 ] . t1 = 1000100UL ;
or2 [ 0 ] . t2 = 1100000UL ;
or2 [ 0 ] . t6 = 1200000UL ;
or2 [ 0 ] . observation_deadline_us = 6000000UL ;
or2 [ 0 ] . completed = true ;
or2 [ 1 ] . sequence = 2 ;
or2 [ 1 ] . condition_id = " B " ;
or2 [ 1 ] . role = " MAIN " ;
or2 [ 1 ] . is_warmup = false ;
or2 [ 1 ] . operation = " MARKET_OPEN " ;
or2 [ 1 ] . outcome = " FILLED " ;
or2 [ 1 ] . present_mask = LAB_MASK_T0 | LAB_MASK_T1 | LAB_MASK_T2 | LAB_MASK_T6 ;
or2 [ 1 ] . t0 = 2000000UL ;
or2 [ 1 ] . t1 = 2000100UL ;
or2 [ 1 ] . t2 = 2200000UL ;
or2 [ 1 ] . t6 = 2400000UL ;
or2 [ 1 ] . observation_deadline_us = 7000000UL ;
or2 [ 1 ] . completed = true ;
2026-09-11 09:22:15 +03:00
StatSummaryRow offA ;
2026-09-12 22:14:24 +03:00
CReportBuilder : : SummarizeGroup ( or2 , 2 , LAB_METRIC_REQUEST_DELAY , " " , " ALL " , offA ) ;
AddCheck ( " REBUILD-02 " , " offline-vs-online-population " , " WARNING " ,
" offline summary по MAIN без фильтра условия обрабатывает обе записи (R6-B2) " ,
StringFormat ( " n=%I64u " , offA . n_attempted ) ,
offA . n_attempted = = 2 , " audit-6-R6-B2 " ) ;
//--- R5-B7: RebuildE4 отвергает повтор одного каталога (500 из одного файла)
2026-09-11 04:09:32 +03:00
string dirs [ 5 ] ;
2026-09-12 22:14:24 +03:00
dirs [ 0 ] = " RequestLatencyLab \\ tests \\ rebuilt_e2e " ;
dirs [ 1 ] = " RequestLatencyLab \\ tests \\ rebuilt_e2e " ;
dirs [ 2 ] = " RequestLatencyLab \\ tests \\ rebuilt_e2e " ;
dirs [ 3 ] = " RequestLatencyLab \\ tests \\ rebuilt_e2e " ;
dirs [ 4 ] = " RequestLatencyLab \\ tests \\ rebuilt_e2e " ;
2026-09-11 04:09:32 +03:00
LabError e4err ;
2026-09-12 22:14:24 +03:00
const bool e4_dup = ! CReportBuilder : : RebuildE4 ( dirs , 5 , " E4-POOLED " ,
" RequestLatencyLab \\ tests \\ rebuilt_e4 \\ " , e4err ) ;
AddCheck ( " E4-01 " , " e4-duplicate-dataset " , " WARNING " ,
" 5 одинаковых каталогов отвергаются (R5-B7) " ,
StringFormat ( " rejected=%s err=%s " , ( e4_dup ? " 1 " : " 0 " ) , e4err . message ) ,
e4_dup & & e4err . code = = 23 , " audit-5-R5-B7 " ) ;
2026-09-11 04:09:32 +03:00
}
2026-09-12 22:14:24 +03:00
//+------------------------------------------------------------------+
//| Регрессии аудита R6 |
//+------------------------------------------------------------------+
void TestAudit6Regressions ( void )
2026-09-11 09:06:39 +03:00
{
2026-09-12 22:14:24 +03:00
//--- R6-B3: знаковая метрика T2-T4 — своевременность по ОБЕИМ точкам
2026-09-11 09:06:39 +03:00
CRequestTracker tr6s ;
2026-09-12 22:14:24 +03:00
tr6s .Init ( 64 , " audit6-sign " ) ;
RequestPlan ph ;
ph . Zero ( ) ;
ph . sequence = 1 ;
ph . symbol = " X " ;
ph . magic = 1 ;
ph . condition_id = " B " ;
ph . operation = LAB_OP_MARKET_OPEN ;
ulong sq6s = 0 ;
tr6s .Reserve ( ph , sq6s ) ;
SendObservation sh ;
sh . Zero ( ) ;
sh . t1_us = 1000100UL ;
sh . send_ok = true ;
sh . result_known = true ;
sh . result . retcode = TRADE_RETCODE_DONE ;
sh . result . request_id = 1 ;
tr6s . MarkSendStart ( 1 , 1000000UL ) ;
tr6s . MarkSendReturn ( 1 , sh ) ;
tr6s . ComputeDeadlines ( 1 , 5000000UL , 1000000UL ) ; // deadline=6s
tr6s . MarkRequest ( 1 , 6300000UL , 1 , TRADE_RETCODE_DONE , 0 , 0 , 0 ) ; // T2=6.3s late
tr6s . MarkDealAdd ( 1 , 1100000UL , 9001 , 0.01 ) ; // T4=1.1s early
RequestMetadata m6s ;
tr6s . GetMetadata ( 1 , m6s ) ;
MetricObservation mo6s ;
bool ev6s = false ;
CRequestTracker : : EvaluateMetric ( m6s , LAB_METRIC_REQUEST_FIRST_DEAL_OFFSET , mo6s , ev6s ) ;
AddCheck ( " EVAL-05 " , " signed-t2late-t4early " , " WARNING " ,
" T2=6.3s поздняя, T4 ранняя => знаковая LATE (R6-B3) " ,
StringFormat ( " cat=%s v=%I64d " , LabFitnessName ( mo6s . category ) , mo6s . value_us ) ,
ev6s & & mo6s . category = = LAB_FIT_LATE , " audit-6-R6-B3 " ) ;
ph . sequence = 2 ;
tr6s .Reserve ( ph , sq6s ) ;
SendObservation sh2 ;
sh2 . Zero ( ) ;
sh2 . t1_us = 1000100UL ;
sh2 . send_ok = true ;
sh2 . result_known = true ;
sh2 . result . retcode = TRADE_RETCODE_DONE ;
sh2 . result . request_id = 2 ;
tr6s . MarkSendStart ( 2 , 1000000UL ) ;
tr6s . MarkSendReturn ( 2 , sh2 ) ;
tr6s . ComputeDeadlines ( 2 , 5000000UL , 1000000UL ) ;
tr6s . MarkRequest ( 2 , 1100000UL , 2 , TRADE_RETCODE_DONE , 0 , 0 , 0 ) ; // T2 ранняя
tr6s . MarkDealAdd ( 2 , 6300000UL , 9002 , 0.01 ) ; // T4=6.3s late
RequestMetadata m6s2 ;
tr6s . GetMetadata ( 2 , m6s2 ) ;
MetricObservation mo6s2 ;
bool ev6s2 = false ;
CRequestTracker : : EvaluateMetric ( m6s2 , LAB_METRIC_REQUEST_FIRST_DEAL_OFFSET , mo6s2 , ev6s2 ) ;
AddCheck ( " EVAL-06 " , " signed-t2early-t4late " , " WARNING " ,
" T2 ранняя, T4=6.3s поздняя => знаковая LATE (R6-B3) " ,
StringFormat ( " cat=%s v=%I64d " , LabFitnessName ( mo6s2 . category ) , mo6s2 . value_us ) ,
ev6s2 & & mo6s2 . category = = LAB_FIT_LATE , " audit-6-R6-B3 " ) ;
//--- R6-B3: поздняя T6 до первого таймера всё равно ставит deadline_exceeded
ph . sequence = 3 ;
tr6s .Reserve ( ph , sq6s ) ;
SendObservation sh3 ;
sh3 . Zero ( ) ;
sh3 . t1_us = 1000100UL ;
sh3 . send_ok = true ;
sh3 . result_known = true ;
sh3 . result . retcode = TRADE_RETCODE_DONE ;
sh3 . result . request_id = 3 ;
tr6s . MarkSendStart ( 3 , 1000000UL ) ;
tr6s . MarkSendReturn ( 3 , sh3 ) ;
tr6s . ComputeDeadlines ( 3 , 5000000UL , 1000000UL ) ;
tr6s . SetFinal ( 3 , 6200000UL , LAB_OUT_FILLED , LAB_FINAL_CALLBACK_CHECK , 11 ) ; // T6=6.2s
RequestMetadata m6s3 ;
tr6s . GetMetadata ( 3 , m6s3 ) ;
AddCheck ( " EVAL-07 " , " late-t6-before-timer-sets-flag " , " WARNING " ,
" поздняя T6 (до таймера) => deadline_exceeded=true (R6-B3) " ,
StringFormat ( " de=%s " , ( m6s3 . deadline_exceeded ? " 1 " : " 0 " ) ) ,
m6s3 . deadline_exceeded , " audit-6-R6-B3 " ) ;
//--- R6-B1: light-сверка и history->callback
2026-09-11 09:06:39 +03:00
CRequestTracker trl ;
2026-09-12 22:14:24 +03:00
trl .Init ( 64 , " audit6-light " ) ;
RequestPlan pl ;
pl . Zero ( ) ;
pl . sequence = 1 ;
pl . symbol = " X " ;
pl . magic = 1 ;
pl . condition_id = " A " ;
pl . operation = LAB_OP_MARKET_OPEN ;
ulong sql = 0 ;
trl .Reserve ( pl , sql ) ;
trl . MarkDealAdd ( 1 , 500 , 1001 , 0.01 ) ; // callback наблюдён
ConfirmationSnapshot snl ;
snl . Zero ( ) ;
snl . sequence = 1 ;
snl . order_ticket = 55 ;
snl . light_snapshot = true ; // R6-B1: явный признак
snl . deal_count = 1 ; // count>0, тикеты 0
snl . executed_volume_units = 1.0 ; // 0.01 lot / step 0.01
snl . coverage_complete = true ;
snl . history_scan_failed = false ;
trl . SyncReconcile ( 1 , snl , 0.01 , 1e-7 ) ;
RequestMetadata ml ;
trl . GetMetadata ( 1 , ml ) ;
AddCheck ( " DL-01 " , " light-snapshot-coverage " , " WARNING " ,
" light count=1 tickets=0 => callback_coverage=true (R6-B1) " ,
StringFormat ( " cov=%s vol=%.6f " , ( ml . callback_coverage_complete ? " 1 " : " 0 " ) ,
ml . executed_volume ) ,
ml . callback_coverage_complete & & Near ( ml . executed_volume , 1.0 , 1e-9 ) ,
" audit-6-R6-B1 " ) ;
//--- history -> первый callback учитывается ровно один раз
2026-09-11 09:06:39 +03:00
CRequestTracker trh ;
2026-09-12 22:14:24 +03:00
trh .Init ( 64 , " audit6-hcb " ) ;
RequestPlan pc ;
pc . Zero ( ) ;
pc . sequence = 1 ;
pc . symbol = " X " ;
pc . magic = 1 ;
pc . condition_id = " A " ;
pc . operation = LAB_OP_MARKET_OPEN ;
ulong sqh = 0 ;
trh .Reserve ( pc , sqh ) ;
trh . RegisterHistoryDeal ( 777 , 55 , 0.01 , 1000 , 1 ) ;
trh . MarkDealAdd ( 1 , 500 , 777 , 0.01 ) ; // первый callback после истории
RequestMetadata mh ;
trh . GetMetadata ( 1 , mh ) ;
AddCheck ( " DL-02 " , " history-then-first-callback " , " WARNING " ,
" первый callback после истории: deal_count=1, volume=0.01 (R6-B1) " ,
StringFormat ( " count=%u vol=%.6f " , mh . deal_count , mh . callback_volume ) ,
mh . deal_count = = 1 & & Near ( mh . callback_volume , 0.01 , 1e-12 ) ,
" audit-6-R6-B1 " ) ;
//--- callback -> история дополняет без потери происхождения
2026-09-11 09:06:39 +03:00
CRequestTracker trex ;
2026-09-12 22:14:24 +03:00
trex .Init ( 64 , " audit6-enrich " ) ;
ulong sqx = 0 ;
trex .Reserve ( pc , sqx ) ;
trex . MarkDealAdd ( 1 , 500 , 888 , 0.005 ) ;
const bool enriched = trex . RegisterHistoryDeal ( 888 , 55 , 0.005 , 2000 , 1 ) ;
2026-09-11 09:06:39 +03:00
DealRecord drx ;
2026-09-12 22:14:24 +03:00
const bool has_drx = trex . GetDeal ( 0 , drx ) ;
AddCheck ( " DL-03 " , " history-enrich-callback " , " WARNING " ,
" callback-сделка дополняется историей без потери (R6-B1) " ,
StringFormat ( " enriched=%s cb=%s hv=%.6f " , ( enriched ? " 1 " : " 0 " ) ,
( has_drx & & drx . has_callback ? " 1 " : " 0 " ) ,
( has_drx ? drx . history_volume : -1 ) ) ,
enriched & & has_drx & & drx . has_callback & &
Near ( drx . history_volume , 0.005 , 1e-12 ) , " audit-6-R6-B1 " ) ;
//--- R6-S4: переполнение реестра сделок — явный отказ
2026-09-11 09:06:39 +03:00
CRequestTracker trO ;
2026-09-12 22:14:24 +03:00
trO .Init ( 2 , " audit6-dealcap " ) ;
ulong sqo = 0 ;
trO .Reserve ( pc , sqo ) ;
const bool od1 = trO . MarkDealAdd ( 1 , 100 , 1 , 0.01 ) ;
const bool od2 = trO . MarkDealAdd ( 1 , 200 , 2 , 0.01 ) ;
const bool od3 = trO . MarkDealAdd ( 1 , 300 , 3 , 0.01 ) ; // ёмкость 2 => отказ
RequestMetadata moD ;
trO . GetMetadata ( 1 , moD ) ;
AddCheck ( " DL-04 " , " deal-registry-overflow-explicit " , " WARNING " ,
" переполнение реестра сделок => явный отказ, счётчик потерь (R6-S4) " ,
StringFormat ( " d1=%s d2=%s d3=%s lost=%d rec_count=%u " ,
( od1 ? " 1 " : " 0 " ) , ( od2 ? " 1 " : " 0 " ) , ( od3 ? " 1 " : " 0 " ) ,
trO . DealLostCount ( ) , moD . deal_count ) ,
od1 & & od2 & & ! od3 & & trO . DealLostCount ( ) = = 1 & & moD . deal_count = = 2 ,
" audit-6-R6-S4 " ) ;
//--- R6-B2: offline исключает WARMUP; счётчики/сроки как онлайн
RebuiltSample rw1 , rw2 ;
ZeroMemory ( rw1 ) ;
ZeroMemory ( rw2 ) ;
rw1 . sequence = 1 ;
rw1 . condition_id = " A " ;
rw1 . role = " MAIN " ;
rw1 . is_warmup = false ;
rw1 . operation = " MARKET_OPEN " ;
rw1 . outcome = " FILLED " ;
rw1 . present_mask = LAB_MASK_T0 | LAB_MASK_T1 | LAB_MASK_T2 | LAB_MASK_T6 ;
rw1 . t0 = 1000000UL ;
rw1 . t1 = 1000100UL ;
rw1 . t2 = 1100000UL ;
rw1 . t6 = 1200000UL ;
rw1 . completed = true ;
rw1 . observation_deadline_us = 6000000UL ;
rw2 . sequence = 2 ;
rw2 . condition_id = " A " ;
rw2 . role = " WARMUP " ;
rw2 . is_warmup = true ;
rw2 . operation = " MARKET_OPEN " ;
rw2 . outcome = " FILLED " ;
rw2 . present_mask = LAB_MASK_T0 | LAB_MASK_T1 | LAB_MASK_T2 | LAB_MASK_T6 ;
rw2 . t0 = 1000000UL ;
rw2 . t1 = 1000100UL ;
rw2 . t2 = 3000000UL ;
rw2 . t6 = 3200000UL ;
rw2 . completed = true ;
rw2 . observation_deadline_us = 6000000UL ;
2026-09-11 09:06:39 +03:00
RebuiltSample arr2 [ 2 ] ;
2026-09-12 22:14:24 +03:00
arr2 [ 0 ] = rw1 ;
arr2 [ 1 ] = rw2 ;
2026-09-11 09:06:39 +03:00
StatSummaryRow sr6 ;
2026-09-12 22:14:24 +03:00
CReportBuilder : : SummarizeGroup ( arr2 , 2 , LAB_METRIC_REQUEST_DELAY , " A " , " ALL " , sr6 ) ;
AddCheck ( " OFF-01 " , " offline-excludes-warmup " , " WARNING " ,
" MAIN 100ms + WARMUP 2000ms => offline n=1, mean=100ms (R6-B2) " ,
StringFormat ( " n=%I64u mean=%.1f " , sr6 . n_attempted , sr6 . mean ) ,
sr6 . n_attempted = = 1 & & sr6 . has_stats & & Near ( sr6 . mean , 100000.0 , 1.0 ) ,
" audit-6-R6-B2 " ) ;
//--- R6-B2: histogram-фильтр использует конец метрики (не общий late)
2026-09-11 09:06:39 +03:00
RebuiltSample rl2 ;
ZeroMemory ( rl2 ) ;
2026-09-12 22:14:24 +03:00
rl2 . sequence = 3 ;
rl2 . condition_id = " A " ;
rl2 . role = " MAIN " ;
rl2 . is_warmup = false ;
rl2 . operation = " MARKET_OPEN " ;
rl2 . outcome = " FILLED " ;
rl2 . present_mask = LAB_MASK_T0 | LAB_MASK_T1 | LAB_MASK_T2 | LAB_MASK_T6 ;
rl2 . t0 = 1000000UL ;
rl2 . t1 = 1000100UL ;
rl2 . t2 = 6300000UL ;
rl2 . t6 = 6000000UL ;
rl2 . observation_deadline_us = 6000000UL ;
rl2 . deadline_exceeded = true ;
rl2 . completed = true ;
MetricObservation mo2 ;
bool ev2 = false ;
CReportBuilder : : EvaluateRebuilt ( rl2 , LAB_METRIC_REQUEST_DELAY , mo2 , ev2 ) ;
AddCheck ( " OFF-02 " , " offline-late-by-metric-end " , " WARNING " ,
" T2=6.3s late => REQUEST_DELAY LATE, в гистограмму не идёт (R6-B2) " ,
StringFormat ( " cat=%s " , LabFitnessName ( mo2 . category ) ) ,
ev2 & & mo2 . category = = LAB_FIT_LATE , " audit-6-R6-B2 " ) ;
//--- R6-B4: квоты E3 и сбалансированные блоки
LabSettings e3s ;
e3s . Zero ( ) ;
e3s . experiment_id = LAB_EXP_E3 ;
e3s . series_id = 1 ;
e3s . count_per_condition = 40 ;
e3s . warmup_per_condition = 0 ;
e3s . symbol = " X " ;
e3s . magic = 1 ;
e3s . seed = 5 ;
e3s . run_mode = LAB_RUN_LOCAL_ONLY ;
2026-09-11 09:06:39 +03:00
CExperimentRunner e3r ;
LabError e3e ;
2026-09-12 22:14:24 +03:00
bool e3init = e3r .Init ( e3s , e3e ) ;
bool q1st = false ;
2026-09-11 09:06:39 +03:00
if ( e3init )
{
2026-09-12 22:14:24 +03:00
for ( int k = 0 ; k < 40 ; k + + )
2026-09-11 09:06:39 +03:00
{
RequestPlan pp ;
2026-09-12 22:14:24 +03:00
if ( ! e3r . TryDispatchByRegime ( LAB_REGIME_QUIET , pp ) )
2026-09-11 09:06:39 +03:00
break ;
e3r . MarkDispatchedSlot ( pp . sequence ) ;
e3r . CountDispatched ( pp . role ) ;
}
2026-09-12 22:14:24 +03:00
//--- одна ветвь заполнена, другая нет => НЕ MainComplete (R6-B4)
q1st = ! e3r . MainComplete ( ) & & e3r . MainDispatched ( ) = = 40 & & ! e3r . E3QuotaComplete ( ) ;
bool all_fast = true ;
for ( int k = 0 ; k < 40 ; k + + )
2026-09-11 09:06:39 +03:00
{
RequestPlan pp ;
2026-09-12 22:14:24 +03:00
if ( ! e3r . TryDispatchByRegime ( LAB_REGIME_FAST , pp ) )
{ all_fast = false ; break ; }
2026-09-11 09:06:39 +03:00
e3r . MarkDispatchedSlot ( pp . sequence ) ;
e3r . CountDispatched ( pp . role ) ;
}
2026-09-12 22:14:24 +03:00
q1st = q1st & & all_fast & & e3r . E3QuotaComplete ( ) ;
2026-09-11 09:06:39 +03:00
}
2026-09-12 22:14:24 +03:00
AddCheck ( " E3-02 " , " e3-quota-not-watermark " , " WARNING " ,
" одна ветвь E3 заполнена => MainComplete=false; после обеих true (R6-B4) " ,
StringFormat ( " init=%s q1=%s disp=%I64u " , ( e3init ? " 1 " : " 0 " ) ,
( q1st ? " 1 " : " 0 " ) , e3r . MainDispatched ( ) ) ,
e3init & & q1st & & e3r . MainDispatched ( ) = = 80 , " audit-6-R6-B4 " ) ;
LabSettings bs ;
bs . Zero ( ) ;
bs . experiment_id = LAB_EXP_E1 ;
bs . series_id = 1 ;
bs . count_per_condition = 100 ;
bs . warmup_per_condition = 0 ;
bs . symbol = " X " ;
bs . magic = 1 ;
bs . seed = 11 ;
bs . run_mode = LAB_RUN_LOCAL_ONLY ;
2026-09-11 09:06:39 +03:00
CExperimentRunner br ;
LabError be ;
2026-09-12 22:14:24 +03:00
bool blocks_ok = false ;
if ( br .Init ( bs , be ) )
2026-09-11 09:06:39 +03:00
{
2026-09-12 22:14:24 +03:00
blocks_ok = true ;
for ( int b = 1 ; b < = 10 & & blocks_ok ; b + + )
2026-09-11 09:06:39 +03:00
{
2026-09-12 22:14:24 +03:00
int a_n = 0 , b_n = 0 , a_buy = 0 , a_sell = 0 , b_buy = 0 , b_sell = 0 ;
for ( int i = 0 ; i < br . SlotCount ( ) ; i + + )
2026-09-11 09:06:39 +03:00
{
ScheduleSlot ss ;
2026-09-12 22:14:24 +03:00
if ( ! br . GetSlot ( i , ss ) | | ss . plan . block_id ! = ( uint ) b )
2026-09-11 09:06:39 +03:00
continue ;
2026-09-12 22:14:24 +03:00
if ( ss . plan . condition_id = = " A " )
{ a_n + + ; if ( ss . plan . side = = LAB_SIDE_BUY ) a_buy + + ; else a_sell + + ; }
2026-09-11 09:06:39 +03:00
else
2026-09-12 22:14:24 +03:00
{ b_n + + ; if ( ss . plan . side = = LAB_SIDE_BUY ) b_buy + + ; else b_sell + + ; }
2026-09-11 09:06:39 +03:00
}
2026-09-12 22:14:24 +03:00
if ( ! ( a_n = = 10 & & b_n = = 10 & & a_buy = = 5 & & a_sell = = 5 & & b_buy = = 5 & & b_sell = = 5 ) )
blocks_ok = false ;
2026-09-11 09:06:39 +03:00
}
}
2026-09-12 22:14:24 +03:00
AddCheck ( " BLOCK-01 " , " balanced-blocks-10A10B " , " WARNING " ,
" каждый из 10 блоков: 10A+10B, 5Buy+5Sell на ветвь (R6-B4) " ,
( blocks_ok ? " ok " : " bad " ) , blocks_ok , " audit-6-R6-B4 " ) ;
//--- R6-B4: прогрев только по фактическим отправкам
LabSettings ws ;
ws . Zero ( ) ;
ws . experiment_id = LAB_EXP_E1 ;
ws . series_id = 1 ;
ws . count_per_condition = 10 ;
ws . warmup_per_condition = 10 ;
ws . symbol = " X " ;
ws . magic = 1 ;
ws . seed = 3 ;
ws . run_mode = LAB_RUN_LOCAL_ONLY ;
2026-09-11 09:06:39 +03:00
CExperimentRunner wr ;
LabError we ;
2026-09-12 22:14:24 +03:00
bool warm_rej = false , warm_done = false ;
if ( wr .Init ( ws , we ) )
2026-09-11 09:06:39 +03:00
{
2026-09-12 22:14:24 +03:00
//--- все 20 прогревочных планов получают систематический отказ precheck
2026-09-11 09:06:39 +03:00
RequestPlan wp ;
while ( wr . NextWarmup ( wp ) )
{
2026-09-12 22:14:24 +03:00
for ( int t = 0 ; t < 3 ; t + + )
2026-09-11 09:06:39 +03:00
wr . MarkWarmupRejected ( wp . sequence ) ;
}
2026-09-12 22:14:24 +03:00
warm_rej = ( wr . WarmupComplete ( ) & & wr . WarmupDispatched ( ) < wr . WarmupRequired ( ) ) ;
//--- нормальный прогон: фактические отправки закрывают квоту
2026-09-11 09:06:39 +03:00
CExperimentRunner w2 ;
LabError we2 ;
2026-09-12 22:14:24 +03:00
if ( w2 .Init ( ws , we2 ) )
2026-09-11 09:06:39 +03:00
{
RequestPlan wp2 ;
while ( w2 . NextWarmup ( wp2 ) )
{
w2 . MarkWarmupDispatched ( wp2 . sequence ) ;
w2 . CountDispatched ( wp2 . role ) ;
}
2026-09-12 22:14:24 +03:00
warm_done = ( w2 . WarmupComplete ( ) & &
w2 . WarmupDispatched ( ) = = w2 . WarmupRequired ( ) ) ;
2026-09-11 09:06:39 +03:00
}
}
2026-09-12 22:14:24 +03:00
AddCheck ( " WARM-01 " , " warmup-actual-dispatch " , " WARNING " ,
" все precheck отклонены => не «прогрев выполнен»; факт.send => да (R6-B4) " ,
StringFormat ( " rej=%s done=%s " , ( warm_rej ? " 1 " : " 0 " ) , ( warm_done ? " 1 " : " 0 " ) ) ,
warm_rej & & warm_done , " audit-6-R6-B4 " ) ;
//--- R6-B5: согласование CompletionPolicy/контроллер
ConfirmationSnapshot dp7 ;
dp7 . Zero ( ) ;
dp7 . order_ticket = 73 ;
dp7 . order_state_before = ORDER_STATE_CANCELED ;
dp7 . active_select_status = LAB_READ_ABSENT_CONFIRMED ;
dp7 . executed_volume_units = 0.0 ;
dp7 . history_scan_failed = true ; // скан истории не удался
CompletionDecision dd7 ;
dd7 . Zero ( ) ;
CCompletionPolicy : : Evaluate ( LAB_OP_PENDING_DELETE , dp7 , 1e-7 , dd7 ) ;
AddCheck ( " PDEL-04 " , " cancel-no-history-scan " , " WARNING " ,
" отмена при failed history scan => НЕ подтверждена (R6-B5) " ,
StringFormat ( " outcome=%s conf=%s " , LabOutcomeName ( dd7 . outcome ) ,
( dd7 . confirmed ? " 1 " : " 0 " ) ) ,
! dd7 . confirmed , " audit-6-R6-B5 " ) ;
ConfirmationSnapshot pp7 ;
pp7 . Zero ( ) ;
pp7 . order_ticket = 74 ;
pp7 . order_state_before = ORDER_STATE_FILLED ;
pp7 . executed_volume_units = 0.5 ;
pp7 . trading_state_known = true ;
CompletionDecision pd7 ;
pd7 . Zero ( ) ;
CCompletionPolicy : : Evaluate ( LAB_OP_PENDING_CREATE , pp7 , 1e-7 , pd7 ) ;
AddCheck ( " PEND-04 " , " unexpected-activation-outcome " , " WARNING " ,
" активация до подтверждения: confirmed=true, T6 не назначается (R6-B5) " ,
StringFormat ( " outcome=%s conf=%s t6=%s " , LabOutcomeName ( pd7 . outcome ) ,
( pd7 . confirmed ? " 1 " : " 0 " ) , ( pd7 . t6_assignable ? " 1 " : " 0 " ) ) ,
pd7 . outcome = = LAB_OUT_UNEXPECTED_ACTIVATION & & pd7 . confirmed & &
! pd7 . t6_assignable , " audit-6-R6-B5 " ) ;
//--- R6-S1: строгий CSV-reader
RequestPlan prs ;
prs . Zero ( ) ;
prs . sequence = 1 ;
prs . symbol = " X " ;
prs . magic = 1 ;
prs . condition_id = " A " ;
prs . operation = LAB_OP_MARKET_OPEN ;
2026-09-11 09:06:39 +03:00
CRequestTracker trs ;
2026-09-12 22:14:24 +03:00
trs .Init ( 8 , " audit6-reader " ) ;
ulong sqs = 0 ;
trs .Reserve ( prs , sqs ) ;
const string good_csv = CCsvReport : : BuildSamples ( & trs , " C1 " , " cfg " ) ;
2026-09-11 09:06:39 +03:00
LabError rerr ;
2026-09-12 22:14:24 +03:00
//--- нечисловой токен в present_mask (col 37)
2026-09-11 09:06:39 +03:00
string bl [ ] ;
2026-09-12 22:14:24 +03:00
const int bn = StringSplit ( good_csv , ' \n ' , bl ) ;
string hh = bl [ 0 ] ;
StringReplace ( hh , " \r " , " " ) ;
string rr = bl [ 1 ] ;
StringReplace ( rr , " \r " , " " ) ;
2026-09-11 09:06:39 +03:00
string rc [ ] ;
2026-09-12 22:14:24 +03:00
const int rn = StringSplit ( rr , ' , ' , rc ) ;
rc [ 37 ] = " abc " ;
string row_bad = " " ;
for ( int k = 0 ; k < rn ; k + + )
{ if ( k > 0 ) row_bad + = " , " ; row_bad + = rc [ k ] ; }
const bool bad_saved = CCsv : : SaveUtf8 ( " RequestLatencyLab \\ tests \\ samples_bad.csv " ,
hh + " \n " + row_bad + " \n " , rerr ) ;
2026-09-11 09:06:39 +03:00
RebuiltSample nb [ 8 ] ;
2026-09-12 22:14:24 +03:00
int nbad = -99 ;
2026-09-11 09:06:39 +03:00
if ( bad_saved )
2026-09-12 22:14:24 +03:00
nbad = CReportBuilder : : LoadSamples ( " RequestLatencyLab \\ tests \\ samples_bad.csv " ,
nb , 8 , rerr ) ;
AddCheck ( " READER-01 " , " csv-numeric-validation " , " WARNING " ,
" нечисловой токен в samples.csv => отказ (R6-S1) " ,
StringFormat ( " n=%d err=%d " , nbad , rerr . code ) ,
bad_saved & & nbad < 0 & & rerr . code = = 34 , " audit-6-R6-S1 " ) ;
//--- дубликат sequence
const bool dup_saved = CCsv : : SaveUtf8 ( " RequestLatencyLab \\ tests \\ samples_dup.csv " ,
hh + " \n " + rr + " \n " + rr + " \n " , rerr ) ;
2026-09-11 09:06:39 +03:00
RebuiltSample nd [ 8 ] ;
2026-09-12 22:14:24 +03:00
int ndup = -99 ;
2026-09-11 09:06:39 +03:00
if ( dup_saved )
2026-09-12 22:14:24 +03:00
ndup = CReportBuilder : : LoadSamples ( " RequestLatencyLab \\ tests \\ samples_dup.csv " ,
nd , 8 , rerr ) ;
AddCheck ( " READER-02 " , " csv-duplicate-key " , " WARNING " ,
" повтор sequence => отказ (R6-S1) " ,
StringFormat ( " n=%d err=%d " , ndup , rerr . code ) ,
dup_saved & & ndup < 0 & & rerr . code = = 35 , " audit-6-R6-S1 " ) ;
//--- R6-B7: E4 требует manifest каждого источника
2026-09-11 09:06:39 +03:00
FolderCreate ( " RequestLatencyLab \\ tests \\ e4_nomani_1 " ) ;
FolderCreate ( " RequestLatencyLab \\ tests \\ e4_nomani_2 " ) ;
FolderCreate ( " RequestLatencyLab \\ tests \\ e4_nomani_3 " ) ;
FolderCreate ( " RequestLatencyLab \\ tests \\ e4_nomani_4 " ) ;
FolderCreate ( " RequestLatencyLab \\ tests \\ e4_nomani_5 " ) ;
string e4dirs [ 5 ] ;
2026-09-12 22:14:24 +03:00
e4dirs [ 0 ] = " RequestLatencyLab \\ tests \\ e4_nomani_1 " ;
e4dirs [ 1 ] = " RequestLatencyLab \\ tests \\ e4_nomani_2 " ;
e4dirs [ 2 ] = " RequestLatencyLab \\ tests \\ e4_nomani_3 " ;
e4dirs [ 3 ] = " RequestLatencyLab \\ tests \\ e4_nomani_4 " ;
e4dirs [ 4 ] = " RequestLatencyLab \\ tests \\ e4_nomani_5 " ;
2026-09-11 09:06:39 +03:00
LabError ne4 ;
2026-09-12 22:14:24 +03:00
const bool e4_nom = ! CReportBuilder : : RebuildE4 ( e4dirs , 5 , " E4-POOLED " ,
" RequestLatencyLab \\ tests \\ rebuilt_e4b \\ " , ne4 ) ;
AddCheck ( " E4-02 " , " e4-requires-manifest " , " WARNING " ,
" отсутствующий manifest у источника => отказ (R6-B7) " ,
StringFormat ( " rejected=%s err=%d " , ( e4_nom ? " 1 " : " 0 " ) , ne4 . code ) ,
e4_nom & & ne4 . code = = 36 , " audit-6-R6-B7 " ) ;
2026-09-11 09:06:39 +03:00
}
2026-09-12 22:14:24 +03:00
//+------------------------------------------------------------------+
//| Регрессии аудита R7 |
//+------------------------------------------------------------------+
void TestAudit7Regressions ( void )
2026-09-11 15:17:38 +03:00
{
LabError a7e ;
2026-09-12 22:14:24 +03:00
//--- R7-B1: durable-журнал — правильные байты, две записи, без NUL
2026-09-11 15:17:38 +03:00
FolderCreate ( " RequestLatencyLab \\ tests " ) ;
2026-09-12 22:14:24 +03:00
const string dpath = " RequestLatencyLab \\ tests \\ intents_r7.csv " ;
2026-09-11 15:17:38 +03:00
if ( FileIsExist ( dpath ) )
FileDelete ( dpath ) ;
2026-09-12 22:14:24 +03:00
const bool d1 = CAppController : : AppendDurableLine ( dpath ,
" S1,1,INTENT,1000000 \r \n " , a7e ) ;
const bool d2 = CAppController : : AppendDurableLine ( dpath ,
" S1,2,SEND_RETURN,1000100 \r \n " , a7e ) ;
2026-09-11 15:17:38 +03:00
string dcontent ;
2026-09-12 22:14:24 +03:00
bool no_nul = true ;
const bool dloaded = CCsv : : LoadUtf8 ( dpath , dcontent , a7e ) ;
int dlc = 0 ;
2026-09-11 15:17:38 +03:00
string dlines [ ] ;
if ( dloaded )
{
2026-09-12 22:14:24 +03:00
dlc = StringSplit ( dcontent , ' \n ' , dlines ) ;
for ( int k = 0 ; k < dlc & & no_nul ; k + + )
2026-09-11 15:17:38 +03:00
{
2026-09-12 22:14:24 +03:00
const int l2 = StringLen ( dlines [ k ] ) ;
for ( int c2 = 0 ; c2 < l2 ; c2 + + )
if ( StringGetCharacter ( dlines [ k ] , c2 ) = = 0 )
{ no_nul = false ; break ; }
2026-09-11 15:17:38 +03:00
}
}
2026-09-12 22:14:24 +03:00
const bool dura_ok = ( d1 & & d2 & & dloaded & & no_nul & &
StringFind ( dcontent , " S1,1,INTENT,1000000 " ) > = 0 & &
StringFind ( dcontent , " S1,2,SEND_RETURN,1000100 " ) > = 0 ) ;
AddCheck ( " DURA-01 " , " durable-intent-roundtrip " , " WARNING " ,
" INTENT+SEND_RETURN: 2 записи, точные байты, без NUL (R7-B1) " ,
StringFormat ( " d1=%s d2=%s load=%s nul=%s lines=%d " ,
( d1 ? " 1 " : " 0 " ) , ( d2 ? " 1 " : " 0 " ) , ( dloaded ? " 1 " : " 0 " ) ,
( no_nul ? " 0 " : " 1 " ) , dlc ) ,
dura_ok , " audit-7-R7-B1 " ) ;
//--- отказ записи: имя с запрещённым символом ':' => FileOpen недоступен
const bool d_bad = ! CAppController : : AppendDurableLine (
" RequestLatencyLab \\ tests \\ bad:name_r7.intents " ,
" S1,3,INTENT,2000000 \r \n " , a7e ) ;
AddCheck ( " DURA-02 " , " durable-line-fail-closed " , " WARNING " ,
" запись при недоступном FileOpen => false (R7-B1) " ,
( d_bad ? " rejected " : " accepted " ) , d_bad , " audit-7-R7-B1 " ) ;
//--- R7-B3: прогрев — смешанный исход 19 dispatched + 1 rejected
LabSettings w7 ;
w7 . Zero ( ) ;
w7 . experiment_id = LAB_EXP_E1 ;
w7 . series_id = 1 ;
w7 . count_per_condition = 10 ;
w7 . warmup_per_condition = 10 ;
w7 . symbol = " X " ;
w7 . magic = 1 ;
w7 . seed = 7 ;
w7 . run_mode = LAB_RUN_LOCAL_ONLY ;
CExperimentRunner r7 ;
LabError w7e ;
CRequestTracker t7 ;
CEventJournal j7 ;
CTimeSource c7 ;
2026-09-11 15:17:38 +03:00
c7 . UseLive ( ) ;
2026-09-12 22:14:24 +03:00
const bool w7init = ( r7 .Init ( w7 , w7e ) & & t7 .Init ( 64 , " audit7-warm " ) & &
j7 .Init ( 256 , " audit7-warm " ) ) ;
2026-09-11 15:17:38 +03:00
CAppController a7 ;
2026-09-12 22:14:24 +03:00
a7 . Configure ( & t7 , & j7 , & r7 , & c7 ) ;
2026-09-11 15:17:38 +03:00
a7 . SetSettings ( w7 ) ;
2026-09-12 22:14:24 +03:00
bool w7mixed = false ;
2026-09-11 15:17:38 +03:00
if ( w7init )
{
2026-09-12 22:14:24 +03:00
int sent7 = 0 ;
2026-09-11 15:17:38 +03:00
RequestPlan wp7 ;
while ( r7 . NextWarmup ( wp7 ) )
{
2026-09-12 22:14:24 +03:00
if ( sent7 < 19 )
2026-09-11 15:17:38 +03:00
{
r7 . MarkWarmupDispatched ( wp7 . sequence ) ;
r7 . CountDispatched ( wp7 . role ) ;
}
else
{
2026-09-12 22:14:24 +03:00
for ( int t = 0 ; t < 3 ; t + + )
2026-09-11 15:17:38 +03:00
r7 . MarkWarmupRejected ( wp7 . sequence ) ;
}
sent7 + + ;
}
a7 . ReturnToSchedule ( ) ;
2026-09-12 22:14:24 +03:00
w7mixed = ( a7 . State ( ) = = LAB_STATE_FINISHED & &
a7 . TerminationReason ( ) = = " WARMUP_INSUFFICIENT_DATA " ) ;
2026-09-11 15:17:38 +03:00
}
2026-09-12 22:14:24 +03:00
AddCheck ( " WARM-02 " , " warmup-mixed-shortfall " , " WARNING " ,
" 19 dispatched + 1 precheck-rejected => НЕ MAIN (R7-B3) " ,
StringFormat ( " state=%s reason=%s " , a7 . StateName ( ) ,
a7 . TerminationReason ( ) ) ,
w7init & & w7mixed , " audit-7-R7-B3 " ) ;
//--- контраст: все 20 фактически отправлены => MAIN
CExperimentRunner r8 ;
LabError w8e ;
CRequestTracker t8 ;
CEventJournal j8 ;
CTimeSource c8 ;
2026-09-11 15:17:38 +03:00
c8 . UseLive ( ) ;
2026-09-12 22:14:24 +03:00
const bool w8init = ( r8 .Init ( w7 , w8e ) & & t8 .Init ( 64 , " audit7-warm2 " ) & &
j8 .Init ( 256 , " audit7-warm2 " ) ) ;
2026-09-11 15:17:38 +03:00
CAppController a8 ;
2026-09-12 22:14:24 +03:00
a8 . Configure ( & t8 , & j8 , & r8 , & c8 ) ;
2026-09-11 15:17:38 +03:00
a8 . SetSettings ( w7 ) ;
2026-09-12 22:14:24 +03:00
bool w8main = false ;
2026-09-11 15:17:38 +03:00
if ( w8init )
{
RequestPlan wp8 ;
while ( r8 . NextWarmup ( wp8 ) )
{
r8 . MarkWarmupDispatched ( wp8 . sequence ) ;
r8 . CountDispatched ( wp8 . role ) ;
}
a8 . ReturnToSchedule ( ) ;
2026-09-12 22:14:24 +03:00
w8main = ( a8 . State ( ) = = LAB_STATE_MAIN ) ;
2026-09-11 15:17:38 +03:00
}
2026-09-12 22:14:24 +03:00
AddCheck ( " WARM-03 " , " warmup-full-quota-main " , " WARNING " ,
" 20/20 фактически отправлены => MAIN (R7-B3) " ,
StringFormat ( " state=%s " , a8 . StateName ( ) ) ,
w8init & & w8main , " audit-7-R7-B3 " ) ;
//--- R7-B2: история опережает callback — поздняя сделка дополняет T5;
//--- неполный скан не подтверждает полноту T5 по подмножеству
2026-09-11 15:17:38 +03:00
CRequestTracker trcb ;
2026-09-12 22:14:24 +03:00
trcb .Init ( 64 , " audit7-cb " ) ;
RequestPlan pcb ;
pcb . Zero ( ) ;
pcb . sequence = 1 ;
pcb . symbol = " X " ;
pcb . magic = 1 ;
pcb . condition_id = " A " ;
pcb . operation = LAB_OP_MARKET_OPEN ;
ulong scb = 0 ;
trcb .Reserve ( pcb , scb ) ;
trcb . RegisterHistoryDeal ( 3001 , 80 , 0.01 , 1000 , 1 ) ;
trcb . RegisterHistoryDeal ( 3002 , 80 , 0.01 , 1000 , 1 ) ;
trcb . MarkDealAdd ( 1 , 500 , 3001 , 0.01 ) ; // callback только первого
ConfirmationSnapshot sc1 ;
sc1 . Zero ( ) ;
sc1 . sequence = 1 ;
sc1 . order_ticket = 80 ;
sc1 . deal_tickets [ 0 ] = 3001 ;
sc1 . deal_tickets [ 1 ] = 3002 ;
sc1 . deal_count = 2 ;
sc1 . executed_volume_units = 2.0 ;
sc1 . coverage_complete = true ;
sc1 . history_scan_failed = false ;
trcb . SyncReconcile ( 1 , sc1 , 0.01 , 1e-7 ) ;
RequestMetadata mcb1 ;
trcb . GetMetadata ( 1 , mcb1 ) ;
const bool not_covered = ( ! mcb1 . callback_coverage_complete ) ;
//--- поздняя сделка ВНУТРИ окна (после «T6», до grace)
trcb . MarkDealAdd ( 1 , 900 , 3002 , 0.01 ) ;
ConfirmationSnapshot sc2 ;
sc2 . Zero ( ) ;
sc2 . sequence = 1 ;
sc2 . order_ticket = 80 ;
sc2 . deal_tickets [ 0 ] = 3001 ;
sc2 . deal_tickets [ 1 ] = 3002 ;
sc2 . deal_count = 2 ;
sc2 . executed_volume_units = 2.0 ;
sc2 . coverage_complete = true ;
sc2 . history_scan_failed = false ;
trcb . SyncReconcile ( 1 , sc2 , 0.01 , 1e-7 ) ;
RequestMetadata mcb2 ;
trcb . GetMetadata ( 1 , mcb2 ) ;
const bool covered_late = ( mcb2 . callback_coverage_complete & &
( mcb2 . present_mask & ( uint ) LAB_MASK_T5 ) ! = 0 & &
mcb2 . last_deal_us = = 900 ) ;
AddCheck ( " T5-03 " , " late-deal-fills-coverage " , " WARNING " ,
" история->1 callback: coverage=false; поздний в окне => true, T5 по исходному времени (R7-B2) " ,
StringFormat ( " cov1=%s cov2=%s t5=%I64u " ,
( not_covered ? " 0 " : " 1 " ) , ( covered_late ? " 1 " : " 0 " ) ,
mcb2 . last_deal_us ) ,
not_covered & & covered_late , " audit-7-R7-B2 " ) ;
2026-09-11 15:17:38 +03:00
CRequestTracker trfs ;
2026-09-12 22:14:24 +03:00
trfs .Init ( 64 , " audit7-fscan " ) ;
ulong sfs = 0 ;
trfs .Reserve ( pcb , sfs ) ;
trfs . MarkDealAdd ( 1 , 500 , 4001 , 0.01 ) ;
ConfirmationSnapshot sfa ;
sfa . Zero ( ) ;
sfa . sequence = 1 ;
sfa . order_ticket = 81 ;
sfa . deal_tickets [ 0 ] = 4001 ;
sfa . deal_count = 1 ;
sfa . executed_volume_units = 1.0 ;
sfa . coverage_complete = true ;
sfa . history_scan_failed = true ; // неполный скан
trfs . SyncReconcile ( 1 , sfa , 0.01 , 1e-7 ) ;
RequestMetadata mfa ;
trfs . GetMetadata ( 1 , mfa ) ;
AddCheck ( " DL-05 " , " failed-scan-no-t5-promote " , " WARNING " ,
" обрезанный скан историей не повышает callback_coverage (R7-B2) " ,
StringFormat ( " cov=%s " , ( mfa . callback_coverage_complete ? " 1 " : " 0 " ) ) ,
! mfa . callback_coverage_complete , " audit-7-R7-B2 " ) ;
//--- R7-B4: reader — multiline-запись CSV и несогласованные mask/ячейка
RequestPlan pr7 ;
pr7 . Zero ( ) ;
pr7 . sequence = 1 ;
pr7 . symbol = " X " ;
pr7 . magic = 1 ;
pr7 . condition_id = " A " ;
pr7 . operation = LAB_OP_MARKET_OPEN ;
pr7 . comment = " line1 \n line2 " ; // встроенный перевод строки
2026-09-11 15:17:38 +03:00
CRequestTracker trm ;
2026-09-12 22:14:24 +03:00
trm .Init ( 8 , " audit7-multi " ) ;
ulong sm = 0 ;
trm .Reserve ( pr7 , sm ) ;
SendObservation sms ;
sms . Zero ( ) ;
sms . t1_us = 1000100UL ;
sms . send_ok = true ;
sms . result_known = true ;
sms . result . retcode = TRADE_RETCODE_DONE ;
sms . result . request_id = 1 ;
trm . MarkSendStart ( 1 , 1000000UL ) ;
trm . MarkSendReturn ( 1 , sms ) ;
pr7 . sequence = 2 ;
pr7 . comment = " plain " ;
trm .Reserve ( pr7 , sm ) ;
const string multi_csv = CCsvReport : : BuildSamples ( & trm , " C1 " , " cfg " ) ;
2026-09-11 15:17:38 +03:00
LabError merr ;
2026-09-12 22:14:24 +03:00
const bool multi_saved = CCsv : : SaveUtf8 ( " RequestLatencyLab \\ tests \\ samples_multi.csv " ,
multi_csv , merr ) ;
2026-09-11 15:17:38 +03:00
RebuiltSample mr7s [ 8 ] ;
2026-09-12 22:14:24 +03:00
const int mn = multi_saved ? CReportBuilder : : LoadSamples (
" RequestLatencyLab \\ tests \\ samples_multi.csv " , mr7s , 8 , merr ) : -9 ;
AddCheck ( " READER-03 " , " csv-multiline-record " , " WARNING " ,
" комментарий line1 \\ nline2 (экранированный) читается как одна запись (R7-B4) " ,
StringFormat ( " saved=%s n=%d err=%d " , ( multi_saved ? " 1 " : " 0 " ) , mn , merr . code ) ,
multi_saved & & mn = = 2 , " audit-7-R7-B4 " ) ;
//--- пустая T1 при установленном бите наличия => отказ (не «0»)
//--- отдельный простой набор БЕЗ multiline-комментария (иначе физический
//--- StringSplit в тесте разорвал бы запись; reader сам это не делает).
RequestPlan pk ;
pk . Zero ( ) ;
pk . sequence = 21 ;
pk . symbol = " X " ;
pk . magic = 1 ;
pk . condition_id = " A " ;
pk . operation = LAB_OP_MARKET_OPEN ;
2026-09-11 15:17:38 +03:00
CRequestTracker trk ;
2026-09-12 22:14:24 +03:00
trk .Init ( 8 , " audit7-mask " ) ;
ulong skx = 0 ;
trk .Reserve ( pk , skx ) ;
SendObservation sks ;
sks . Zero ( ) ;
sks . t1_us = 1000100UL ;
sks . send_ok = true ;
sks . result_known = true ;
sks . result . retcode = TRADE_RETCODE_DONE ;
sks . result . request_id = 21 ;
trk . MarkSendStart ( 21 , 1000000UL ) ;
trk . MarkSendReturn ( 21 , sks ) ;
const string mask_csv = CCsvReport : : BuildSamples ( & trk , " C1 " , " cfg " ) ;
string mi [ ] ;
const int mn2 = StringSplit ( mask_csv , ' \n ' , mi ) ;
string mh = mi [ 0 ] ;
StringReplace ( mh , " \r " , " " ) ;
string mrline = mi [ 1 ] ;
StringReplace ( mrline , " \r " , " " ) ;
string mcols [ ] ;
const int mc = StringSplit ( mrline , ' , ' , mcols ) ;
mcols [ 3 ] = " " ; // T1 пустая, бит остаётся
string mrow = " " ;
for ( int k = 0 ; k < mc ; k + + )
{ if ( k > 0 ) mrow + = " , " ; mrow + = mcols [ k ] ; }
const bool mi_saved = CCsv : : SaveUtf8 ( " RequestLatencyLab \\ tests \\ samples_mask.csv " ,
mh + " \n " + mrow + " \n " , merr ) ;
2026-09-11 15:17:38 +03:00
RebuiltSample mib [ 8 ] ;
2026-09-12 22:14:24 +03:00
int mi_n = -9 ;
2026-09-11 15:17:38 +03:00
if ( mi_saved )
2026-09-12 22:14:24 +03:00
mi_n = CReportBuilder : : LoadSamples ( " RequestLatencyLab \\ tests \\ samples_mask.csv " ,
mib , 8 , merr ) ;
AddCheck ( " READER-04 " , " csv-mask-empty-mismatch " , " WARNING " ,
" пустая T1 при бите present_mask => отказ code 40 (R7-B4) " ,
StringFormat ( " n=%d err=%d " , mi_n , merr . code ) ,
mi_saved & & mi_n < 0 & & merr . code = = 40 , " audit-7-R7-B4 " ) ;
//--- R7-S2: comparisons — delta = B−A (mean/median/p95), относительные
2026-09-11 15:17:38 +03:00
CRequestTracker trc2 ;
2026-09-12 22:14:24 +03:00
trc2 .Init ( 8 , " audit7-cmp " ) ;
RequestPlan pa2 ;
pa2 . Zero ( ) ;
pa2 . sequence = 1 ;
pa2 . symbol = " X " ;
pa2 . magic = 1 ;
pa2 . condition_id = " A " ;
pa2 . operation = LAB_OP_MARKET_OPEN ;
ulong scm = 0 ;
trc2 .Reserve ( pa2 , scm ) ;
SendObservation cs1 ;
cs1 . Zero ( ) ;
cs1 . t1_us = 1100000UL ;
cs1 . send_ok = true ;
cs1 . result_known = true ;
cs1 . result . retcode = TRADE_RETCODE_DONE ;
cs1 . result . request_id = 1 ;
trc2 . MarkSendStart ( 1 , 1000000UL ) ;
trc2 . MarkSendReturn ( 1 , cs1 ) ;
trc2 . MarkDealAdd ( 1 , 1200000UL , 7001 , 0.01 ) ;
trc2 . SetFinal ( 1 , 1500000UL , LAB_OUT_FILLED , LAB_FINAL_CALLBACK_CHECK , 1 ) ;
RequestPlan pb2 ;
pb2 . Zero ( ) ;
pb2 . sequence = 2 ;
pb2 . symbol = " X " ;
pb2 . magic = 1 ;
pb2 . condition_id = " B " ;
pb2 . operation = LAB_OP_MARKET_OPEN ;
ulong scm2 = 0 ;
trc2 .Reserve ( pb2 , scm2 ) ;
SendObservation cs2 ;
cs2 . Zero ( ) ;
cs2 . t1_us = 1200000UL ;
cs2 . send_ok = true ;
cs2 . result_known = true ;
cs2 . result . retcode = TRADE_RETCODE_DONE ;
cs2 . result . request_id = 2 ;
trc2 . MarkSendStart ( 2 , 1000000UL ) ;
trc2 . MarkSendReturn ( 2 , cs2 ) ;
trc2 . MarkDealAdd ( 2 , 1300000UL , 7002 , 0.01 ) ;
trc2 . SetFinal ( 2 , 1600000UL , LAB_OUT_FILLED , LAB_FINAL_CALLBACK_CHECK , 1 ) ;
LabSettings cs2s ;
cs2s . Zero ( ) ;
cs2s . experiment_id = LAB_EXP_E1 ;
cs2s . series_id = 1 ;
const string cmp_csv = CCsvReport : : BuildComparisons ( & trc2 , " s " , cs2s ) ;
const bool cmp_schema_ok = ( StringFind ( cmp_csv , " a_mean " ) > = 0 & &
StringFind ( cmp_csv , " a_p95 " ) > = 0 & &
StringFind ( cmp_csv , " delta_mean " ) > = 0 & &
StringFind ( cmp_csv , " rel_median " ) > = 0 ) ;
const bool cmp_delta_ok = ( StringFind ( cmp_csv , " 100000.0000000000 " ) > = 0 ) ;
const bool cmp_rel_ok = ( StringFind ( cmp_csv , " 1.0000000000 " ) > = 0 ) ;
AddCheck ( " CMP-02 " , " comparisons-b-minus-a " , " WARNING " ,
" A=100ms B=200ms call: delta_median=+100ms, rel=1.0 (B−A, ТЗ §7) (R7-S2) " ,
StringFormat ( " schema=%s delta=%s rel=%s " , ( cmp_schema_ok ? " 1 " : " 0 " ) ,
( cmp_delta_ok ? " 1 " : " 0 " ) , ( cmp_rel_ok ? " 1 " : " 0 " ) ) ,
cmp_schema_ok & & cmp_delta_ok & & cmp_rel_ok , " audit-7-R7-S2 " ) ;
//--- R7-S1: применимость метрик; CONFLICT учитывается как n_conflict
2026-09-11 15:17:38 +03:00
CRequestTracker tre8 ;
2026-09-12 22:14:24 +03:00
tre8 .Init ( 8 , " audit7-eval8 " ) ;
RequestPlan pe8 ;
pe8 . Zero ( ) ;
pe8 . sequence = 1 ;
pe8 . symbol = " X " ;
pe8 . magic = 1 ;
pe8 . condition_id = " A " ;
pe8 . operation = LAB_OP_MARKET_OPEN ;
ulong se8 = 0 ;
tre8 .Reserve ( pe8 , se8 ) ;
SendObservation se8s ;
se8s . Zero ( ) ;
se8s . t1_us = 1000100UL ;
se8s . send_ok = true ;
se8s . result_known = true ;
se8s . result . retcode = TRADE_RETCODE_DONE ;
se8s . result . request_id = 1 ;
tre8 . MarkSendStart ( 1 , 1000000UL ) ;
tre8 . MarkSendReturn ( 1 , se8s ) ;
RequestMetadata me8 ;
tre8 . GetMetadata ( 1 , me8 ) ;
MetricObservation mo8 ;
bool ev8 = false ;
CRequestTracker : : EvaluateMetric ( me8 , LAB_METRIC_FIRST_DEAL_DELAY , mo8 , ev8 ) ;
AddCheck ( " EVAL-08 " , " unknown-no-deal-unknown-app " , " WARNING " ,
" MARKET_OPEN UNKNOWN без сделки => применимость UNKNOWN (не MISSING) (R7-S1) " ,
StringFormat ( " app=%d cat=%s val=%s " , ( int ) mo8 . applicability ,
LabFitnessName ( mo8 . category ) , ( mo8 . has_value ? " 1 " : " 0 " ) ) ,
ev8 & & mo8 . applicability = = LAB_APP_UNKNOWN , " audit-7-R7-S1 " ) ;
2026-09-11 15:17:38 +03:00
CRequestTracker tre9 ;
2026-09-12 22:14:24 +03:00
tre9 .Init ( 8 , " audit7-eval9 " ) ;
ulong se9 = 0 ;
tre9 .Reserve ( pe8 , se9 ) ;
SendObservation se9s ;
se9s . Zero ( ) ;
se9s . t1_us = 1000100UL ;
se9s . send_ok = true ;
se9s . result_known = true ;
se9s . result . retcode = TRADE_RETCODE_DONE ;
se9s . result . request_id = 1 ;
tre9 . MarkSendStart ( 1 , 1000000UL ) ;
tre9 . MarkSendReturn ( 1 , se9s ) ;
tre9 . SetFinal ( 1 , 1600000UL , LAB_OUT_CANCELED_NO_FILL , LAB_FINAL_TIMER_CHECK , 1 ) ;
RequestMetadata me9 ;
tre9 . GetMetadata ( 1 , me9 ) ;
MetricObservation mo9 ;
bool ev9 = false ;
CRequestTracker : : EvaluateMetric ( me9 , LAB_METRIC_FIRST_DEAL_DELAY , mo9 , ev9 ) ;
AddCheck ( " EVAL-09 " , " no-fill-not-applicable " , " WARNING " ,
" CANCELED_NO_FILL => FIRST_DEAL неприменим (не MISSING) (R7-S1) " ,
StringFormat ( " app=%d " , ( int ) mo9 . applicability ) ,
ev9 & & mo9 . applicability = = LAB_APP_NOT_APPLICABLE , " audit-7-R7-S1 " ) ;
//--- CONFLICT без числа в summary: n_conflict=1, n_missing=0
2026-09-11 15:17:38 +03:00
CRequestTracker trcf ;
2026-09-12 22:14:24 +03:00
trcf .Init ( 8 , " audit7-conf " ) ;
RequestPlan pcf ;
pcf . Zero ( ) ;
pcf . sequence = 1 ;
pcf . symbol = " X " ;
pcf . magic = 1 ;
pcf . condition_id = " A " ;
pcf . operation = LAB_OP_MARKET_OPEN ;
ulong scf = 0 ;
trcf .Reserve ( pcf , scf ) ;
SendObservation cfs ;
cfs . Zero ( ) ;
cfs . t1_us = 900000UL ;
cfs . send_ok = true ;
cfs . result_known = true ; // T1 РАНЬШЕ T0
cfs . result . retcode = TRADE_RETCODE_DONE ;
cfs . result . request_id = 1 ;
trcf . MarkSendStart ( 1 , 1000000UL ) ;
trcf . MarkSendReturn ( 1 , cfs ) ;
trcf . MarkDealAdd ( 1 , 1200000UL , 8001 , 0.01 ) ;
trcf . SetFinal ( 1 , 1500000UL , LAB_OUT_FILLED , LAB_FINAL_CALLBACK_CHECK , 1 ) ;
LabSettings cfset ;
cfset . Zero ( ) ;
cfset . experiment_id = LAB_EXP_E1 ;
cfset . series_id = 1 ;
const string sum_csv = CCsvReport : : BuildSummary ( & trcf , " s " , cfset ) ;
bool conflict_ok = false ;
string sum_lines [ ] ;
const int sln = StringSplit ( sum_csv , ' \n ' , sum_lines ) ;
for ( int i = 1 ; i < sln & & ! conflict_ok ; i + + )
2026-09-11 15:17:38 +03:00
{
2026-09-12 22:14:24 +03:00
string sl = sum_lines [ i ] ;
StringReplace ( sl , " \r " , " " ) ;
if ( StringLen ( sl ) = = 0 )
2026-09-11 15:17:38 +03:00
continue ;
2026-09-12 22:14:24 +03:00
string scols [ ] ;
int sc = 0 ;
if ( CCsv : : ParseRow ( sl , scols , sc ) & & sc > 17 & & scols [ 3 ] = = " call_duration_us " )
conflict_ok = ( scols [ 17 ] = = " 1 " & & scols [ 15 ] = = " 0 " ) ;
2026-09-11 15:17:38 +03:00
}
2026-09-12 22:14:24 +03:00
AddCheck ( " EVAL-10 " , " conflict-not-missing " , " WARNING " ,
" T1<T0 (CONFLICT без числа) => n_conflict=1, n_missing=0 (R7-S1) " ,
( conflict_ok ? " ok " : " bad " ) , conflict_ok , " audit-7-R7-S1 " ) ;
//--- R7-B5: повторное чтение прежней котировки не обновляет свежесть
CTimeSource cq ;
cq . UseSynthetic ( 1000000UL ) ;
CRequestTracker tq ;
tq .Init ( 8 , " audit7-quote " ) ;
CEventJournal jq ;
jq .Init ( 256 , " audit7-quote " ) ;
CExperimentRunner rq ;
LabError qe ;
LabSettings qs ;
qs . Zero ( ) ;
qs . experiment_id = LAB_EXP_E1 ;
qs . series_id = 1 ;
qs . count_per_condition = 1 ;
qs . warmup_per_condition = 0 ;
qs . symbol = " X " ;
qs . magic = 1 ;
qs . run_mode = LAB_RUN_LOCAL_ONLY ;
rq .Init ( qs , qe ) ;
2026-09-11 15:17:38 +03:00
CAppController aq ;
2026-09-12 22:14:24 +03:00
aq . Configure ( & tq , & jq , & rq , & cq ) ;
2026-09-11 15:17:38 +03:00
aq . SetSettings ( qs ) ;
aq . NoteQuoteObserved ( 1000 ) ;
2026-09-12 22:14:24 +03:00
const ulong q1 = aq . LastQuoteObservedUs ( ) ; // штамп NowUs()=1000000
2026-09-11 15:17:38 +03:00
cq . AdvanceUs ( 5000 ) ;
aq . NoteQuoteObserved ( 1000 ) ; // ТОТ ЖЕ тик, время ушло
2026-09-12 22:14:24 +03:00
const ulong q2 = aq . LastQuoteObservedUs ( ) ; // НЕ обновилось
2026-09-11 15:17:38 +03:00
cq . AdvanceUs ( 5000 ) ;
aq . NoteQuoteObserved ( 1001 ) ; // НОВЫЙ тик
2026-09-12 22:14:24 +03:00
const ulong q3 = aq . LastQuoteObservedUs ( ) ; // обновилось до q1+10000
const bool quote_ok = ( q2 = = q1 & & q3 = = q1 + 10000UL ) ;
AddCheck ( " QUOTE-01 " , " stale-quote-not-refresh " , " WARNING " ,
" повтор прежнего time_msc не обновляет время наблюдения; новый тик — обновляет (R7-B5) " ,
StringFormat ( " repeat_same=%s new_tick=%s " ,
( q2 = = q1 ? " 1 " : " 0 " ) , ( q3 > q1 ? " 1 " : " 0 " ) ) ,
quote_ok , " audit-7-R7-B5 " ) ;
//--- R7-B6: E4 происхождение — обычный E4 отвергает SYNTHETIC
2026-09-11 15:17:38 +03:00
string e4o [ 5 ] ;
2026-09-12 22:14:24 +03:00
for ( int d = 0 ; d < 5 ; d + + )
2026-09-11 15:17:38 +03:00
{
2026-09-12 22:14:24 +03:00
const string od = StringFormat ( " RequestLatencyLab \\ tests \\ e4_orig_%d " , d + 1 ) ;
2026-09-11 15:17:38 +03:00
FolderCreate ( od ) ;
2026-09-12 22:14:24 +03:00
e4o [ d ] = od ;
2026-09-11 15:17:38 +03:00
LabError oe ;
2026-09-12 22:14:24 +03:00
CCsv : : SaveUtf8 ( od + " \\ manifest.csv " ,
" run_status,FINISHED,string,KNOWN,DERIVED \r \n " +
" n_main_dispatched,100,string,KNOWN,DERIVED \r \n " +
" configuration_id,cfg-orig,string,KNOWN,DERIVED \r \n " +
" data_origin,SYNTHETIC,string,KNOWN,DERIVED \r \n " , oe ) ;
2026-09-11 15:17:38 +03:00
}
LabError oe1 ;
2026-09-12 22:14:24 +03:00
const bool e4_rej = ! CReportBuilder : : RebuildE4 ( e4o , 5 , " E4-TEST " ,
" RequestLatencyLab \\ tests \\ rebuilt_e4o \\ " , oe1 ) ;
2026-09-11 15:17:38 +03:00
LabError oe2 ;
2026-09-12 22:14:24 +03:00
const bool e4_allow2 = CReportBuilder : : RebuildE4 ( e4o , 5 , " E4-TEST " ,
" RequestLatencyLab \\ tests \\ rebuilt_e4o \\ " , oe2 ,
100 , " SYNTHETIC " ) ;
AddCheck ( " E4-03 " , " e4-origin-gate " , " WARNING " ,
" SYNTHETIC-серии: обычный E4 => отказ code 41; синтетический режим проходит origin (R7-B6) " ,
StringFormat ( " rej_code=%d allow_code=%d " , oe1 . code , oe2 . code ) ,
e4_rej & & oe1 . code = = 41 & & ! e4_allow2 & & oe2 . code ! = 41 ,
" audit-7-R7-B6 " ) ;
2026-09-11 15:17:38 +03:00
}
2026-09-12 22:14:24 +03:00
//+------------------------------------------------------------------+
//| Регрессии аудита R8 |
//+------------------------------------------------------------------+
void TestAudit8Regressions ( void )
2026-09-11 18:16:32 +03:00
{
LabError a8e ;
2026-09-12 22:14:24 +03:00
//--- R8-B1: терминальное/блокирующее состояние запрещает новую отправку
//--- (гейт в PrepareAndDispatch до транспорта)
LabSettings gs ;
gs . Zero ( ) ;
gs . experiment_id = LAB_EXP_E1 ;
gs . series_id = 1 ;
gs . count_per_condition = 2 ;
gs . warmup_per_condition = 0 ;
gs . symbol = " X " ;
gs . magic = 1 ;
gs . run_mode = LAB_RUN_LOCAL_ONLY ;
CExperimentRunner rg8 ;
LabError ge8 ;
CRequestTracker tg8 ;
CEventJournal jg8 ;
CTimeSource cg8 ;
2026-09-11 18:16:32 +03:00
cg8 . UseLive ( ) ;
2026-09-12 22:14:24 +03:00
const bool g8init = ( rg8 .Init ( gs , ge8 ) & & tg8 .Init ( 16 , " audit8-gate " ) & &
jg8 .Init ( 256 , " audit8-gate " ) ) ;
2026-09-11 18:16:32 +03:00
CAppController ag8 ;
2026-09-12 22:14:24 +03:00
ag8 . Configure ( & tg8 , & jg8 , & rg8 , & cg8 ) ;
2026-09-11 18:16:32 +03:00
ag8 . SetSettings ( gs ) ;
2026-09-12 22:14:24 +03:00
RequestPlan pg8 ;
pg8 . Zero ( ) ;
pg8 . sequence = 1 ;
pg8 . symbol = " X " ;
pg8 . magic = 1 ;
pg8 . condition_id = " A " ;
pg8 . operation = LAB_OP_MARKET_OPEN ;
ulong sg8 = 0 ;
2026-09-11 18:16:32 +03:00
ag8 . BlockRun ( " test-blocked " ) ; // BLOCKED
2026-09-12 22:14:24 +03:00
const bool g1 = ag8 . DispatchPlan ( pg8 , false , sg8 , ge8 ) ;
const bool gate_blocked = ( ! g1 & & ge8 . code = = 7 ) ;
ulong sg9 = 0 ;
2026-09-11 18:16:32 +03:00
ag8 . MarkFinished ( ) ; // FINISHED
2026-09-12 22:14:24 +03:00
const bool g2 = ag8 . DispatchPlan ( pg8 , false , sg9 , ge8 ) ;
const bool gate_finished = ( ! g2 & & ge8 . code = = 7 ) ;
AddCheck ( " REG-02 " , " e3-terminal-no-dispatch " , " WARNING " ,
" BLOCKED/FINISHED => PrepareAndDispatch отказ code 7 до транспорта (R8-B1) " ,
StringFormat ( " blocked=%s finished=%s code=%d " ,
( gate_blocked ? " 1 " : " 0 " ) , ( gate_finished ? " 1 " : " 0 " ) , ge8 . code ) ,
g8init & & gate_blocked & & gate_finished , " audit-8-R8-B1 " ) ;
//--- R8-B2: единая полнота измерительных точек (T2/T3) перед закрытием
2026-09-11 18:16:32 +03:00
CRequestTracker tpc ;
2026-09-12 22:14:24 +03:00
tpc .Init ( 8 , " audit8-points " ) ;
RequestPlan ppc ;
ppc . Zero ( ) ;
ppc . sequence = 1 ;
ppc . symbol = " X " ;
ppc . magic = 1 ;
ppc . condition_id = " A " ;
ppc . operation = LAB_OP_MARKET_OPEN ;
ulong spc = 0 ;
tpc .Reserve ( ppc , spc ) ;
tpc . SetFinal ( 1 , 1500000UL , LAB_OUT_FILLED , LAB_FINAL_TIMER_CHECK , 1 ) ;
tpc . SetTradingStateKnown ( 1 , true ) ;
RequestMetadata mpc ;
tpc . GetMetadata ( 1 , mpc ) ;
mpc . callback_coverage_complete = true ;
tpc . SetMetadata ( 1 , mpc ) ;
const bool no_t2_result = ( ! CAppController : : MeasurementPointsComplete ( mpc ) ) ;
tpc . MarkRequest ( 1 , 1200000UL , 1 , TRADE_RETCODE_DONE , 0 , 80 , 0 ) ;
RequestMetadata mpc2 ;
tpc . GetMetadata ( 1 , mpc2 ) ;
const bool t2_no_t3_result = ( ! CAppController : : MeasurementPointsComplete ( mpc2 ) ) ;
tpc . MarkOrderAdd ( 1 , 1300000UL , 80 , " TEST " ) ;
RequestMetadata mpc3 ;
tpc . GetMetadata ( 1 , mpc3 ) ;
const bool points_full = CAppController : : MeasurementPointsComplete ( mpc3 ) ;
//--- однозначный отказ без ордера: T3 не требуется
2026-09-11 18:16:32 +03:00
CRequestTracker trj ;
2026-09-12 22:14:24 +03:00
trj .Init ( 8 , " audit8-points2 " ) ;
ulong srj = 0 ;
trj .Reserve ( ppc , srj ) ;
trj . SetFinal ( 1 , 1500000UL , LAB_OUT_REJECTED , LAB_FINAL_TIMER_CHECK , 1 ) ;
RequestMetadata mrj ;
trj . GetMetadata ( 1 , mrj ) ;
mrj . order_ticket = 0 ;
mrj . present_mask | = ( uint ) LAB_MASK_T2 ;
trj . SetMetadata ( 1 , mrj ) ;
const bool reject_ok = CAppController : : MeasurementPointsComplete ( mrj ) ;
const bool t2_ok = ( no_t2_result & & t2_no_t3_result & & points_full & & reject_ok ) ;
AddCheck ( " T2-01 " , " collection-measurement-points " , " WARNING " ,
" закрытие только при T2(+T3 по операции); REJECTED без ордера освобождает T3 (R8-B2) " ,
StringFormat ( " no_t2=%s t2_no_t3=%s full=%s rej=%s " ,
( no_t2_result ? " 1 " : " 0 " ) , ( t2_no_t3_result ? " 1 " : " 0 " ) ,
( points_full ? " 1 " : " 0 " ) , ( reject_ok ? " 1 " : " 0 " ) ) ,
t2_ok , " audit-8-R8-B2 " ) ;
//--- R8-B3: SEND_RETURN буферизуется; flush записывает строки в safe-фазе
LabSettings f8s ;
f8s . Zero ( ) ;
f8s . campaign_id = " tests " ;
f8s . experiment_id = LAB_EXP_E1 ;
f8s . series_id = 1 ;
f8s . count_per_condition = 2 ;
f8s . warmup_per_condition = 0 ;
f8s . symbol = " X " ;
f8s . magic = 1 ;
f8s . run_mode = LAB_RUN_LOCAL_ONLY ;
CExperimentRunner fr8 ;
LabError fe8 ;
CRequestTracker tf8 ;
CEventJournal jf8 ;
CTimeSource cf8 ;
2026-09-11 18:16:32 +03:00
cf8 . UseLive ( ) ;
2026-09-12 22:14:24 +03:00
const bool f8init = ( fr8 .Init ( f8s , fe8 ) & & tf8 .Init ( 16 , " audit8-flush " ) & &
jf8 .Init ( 256 , " audit8-flush " ) ) ;
2026-09-11 18:16:32 +03:00
CAppController af8 ;
2026-09-12 22:14:24 +03:00
af8 . Configure ( & tf8 , & jf8 , & fr8 , & cf8 ) ;
2026-09-11 18:16:32 +03:00
af8 . SetSettings ( f8s ) ;
2026-09-12 22:14:24 +03:00
bool sr_ok = false ;
string diag_se8 = " " ;
bool diag_fex8 = false ;
string dspre8 = " PRE-UNSET " ;
int d_before = -1 ;
bool d_fl = false , d_ld = false , d_h7 = false , d_h9 = false ;
int d_pend = -1 ;
int d_err = -1 ;
dspre8 = af8 . SessionId ( ) ;
2026-09-11 18:16:32 +03:00
if ( f8init & & af8 . InitRun ( fe8 ) )
{
2026-09-12 22:14:24 +03:00
const string sess8 = af8 . SessionId ( ) ;
const string ipath = " RequestLatencyLab \\ tests \\ " + sess8 + " \\ intents.csv " ;
2026-09-11 18:16:32 +03:00
if ( FileIsExist ( ipath ) )
FileDelete ( ipath ) ;
2026-09-12 22:14:24 +03:00
af8 . QueueDurableSendReturn ( 7 , 1000100UL ) ;
af8 . QueueDurableSendReturn ( 9 , 1000200UL ) ;
const int before8 = af8 . PendingDurableSendReturnCount ( ) ;
const bool flushed8 = af8 . FlushPendingSendReturns ( fe8 ) ;
d_err = fe8 . code ;
diag_se8 = sess8 ;
diag_fex8 = FileIsExist ( ipath ) ;
2026-09-11 18:16:32 +03:00
string fc8 ;
2026-09-12 22:14:24 +03:00
const bool loaded8 = CCsv : : LoadUtf8 ( ipath , fc8 , fe8 ) ;
const bool has7 = ( StringFind ( fc8 , " 7,SEND_RETURN,1000100 " ) > = 0 ) ;
const bool has9 = ( StringFind ( fc8 , " 9,SEND_RETURN,1000200 " ) > = 0 ) ;
d_before = before8 ;
d_fl = flushed8 ;
d_ld = loaded8 ;
d_h7 = has7 ;
d_h9 = has9 ;
d_pend = af8 . PendingDurableSendReturnCount ( ) ;
sr_ok = ( before8 = = 2 & & flushed8 & &
af8 . PendingDurableSendReturnCount ( ) = = 0 & &
loaded8 & & has7 & & has9 ) ;
2026-09-11 18:16:32 +03:00
}
2026-09-12 22:14:24 +03:00
AddCheck ( " DURA-03 " , " send-return-buffered-flush " , " WARNING " ,
" SEND_RETURN в буфере; flush пишет строки в safe-фазе (R8-B3) " ,
StringFormat ( " init=%s ok=%s before=%d flush=%s load=%s h7=%s h9=%s pend=%d err=%d pre=%s sess=%s fex=%s " ,
( f8init ? " 1 " : " 0 " ) , ( sr_ok ? " 1 " : " 0 " ) ,
d_before , ( d_fl ? " 1 " : " 0 " ) , ( d_ld ? " 1 " : " 0 " ) ,
( d_h7 ? " 1 " : " 0 " ) , ( d_h9 ? " 1 " : " 0 " ) , d_pend , d_err ,
dspre8 , diag_se8 , ( diag_fex8 ? " 1 " : " 0 " ) ) ,
f8init & & sr_ok , " audit-8-R8-B3 " ) ;
//--- R8-B4: CountUncertainDurableIntents — без cap 256; sentinel
CTimeSource ccount ;
ccount . UseLive ( ) ;
CRequestTracker tx8 ;
tx8 .Init ( 16 , " audit8-unc " ) ;
CEventJournal jx8 ;
jx8 .Init ( 256 , " audit8-unc " ) ;
CExperimentRunner rx8 ;
LabError xe8 ;
rx8 .Init ( f8s , xe8 ) ;
2026-09-11 18:16:32 +03:00
CAppController ax8 ;
2026-09-12 22:14:24 +03:00
ax8 . Configure ( & tx8 , & jx8 , & rx8 , & ccount ) ;
2026-09-11 18:16:32 +03:00
ax8 . SetSettings ( f8s ) ; // campaign_id=tests
2026-09-12 22:14:24 +03:00
//--- 440 полностью парных INTENT/SEND_RETURN => 0 неопределённых
const string p440 = " RequestLatencyLab \\ tests \\ r8_440 \\ intents.csv " ;
2026-09-11 18:16:32 +03:00
FolderCreate ( " RequestLatencyLab \\ tests \\ r8_440 " ) ;
if ( FileIsExist ( p440 ) )
FileDelete ( p440 ) ;
2026-09-12 22:14:24 +03:00
string c440 = " " ;
for ( int k = 1 ; k < = 440 ; k + + )
2026-09-11 18:16:32 +03:00
{
2026-09-12 22:14:24 +03:00
c440 + = StringFormat ( " r8_440,%d,INTENT,%I64u \r \n " , k , 1000000UL + ( ulong ) k ) ;
c440 + = StringFormat ( " r8_440,%d,SEND_RETURN,%I64u \r \n " , k , 1000100UL + ( ulong ) k ) ;
2026-09-11 18:16:32 +03:00
}
LabError ue ;
2026-09-12 22:14:24 +03:00
const bool u440_saved = CCsv : : SaveUtf8 ( p440 , c440 , ue ) ;
const ulong u440 = ax8 . CountUncertainDurableIntents ( " r8_440 " ) ;
const bool c440ok = ( u440_saved & & u440 = = 0 ) ;
//--- один INTENT без возврата => 1
const string p1 = " RequestLatencyLab \\ tests \\ r8_one \\ intents.csv " ;
2026-09-11 18:16:32 +03:00
FolderCreate ( " RequestLatencyLab \t ests \r 8_one " ) ;
if ( FileIsExist ( p1 ) )
FileDelete ( p1 ) ;
2026-09-12 22:14:24 +03:00
const bool u1_saved = CCsv : : SaveUtf8 ( p1 , " r8_one,5,INTENT,2000000 \r \n " , ue ) ;
const ulong u1 = ax8 . CountUncertainDurableIntents ( " r8_one " ) ;
const bool c1ok = ( u1_saved & & u1 = = 1 ) ;
//--- существует ПУСТОЙ журнал => 0 (отличие от отсутствующего)
const string pe = " RequestLatencyLab \\ tests \\ r8_empty \\ intents.csv " ;
2026-09-11 18:16:32 +03:00
FolderCreate ( " RequestLatencyLab \\ tests \\ r8_empty " ) ;
if ( FileIsExist ( pe ) )
FileDelete ( pe ) ;
2026-09-12 22:14:24 +03:00
const bool esaved = CCsv : : SaveUtf8 ( pe , " " , ue ) ;
const bool cempty = ( esaved & & ax8 . CountUncertainDurableIntents ( " r8_empty " ) = = 0 ) ;
//--- журнала нет (пустой каталог) => 0
2026-09-11 18:16:32 +03:00
FolderCreate ( " RequestLatencyLab \\ tests \\ r8_miss " ) ;
2026-09-12 22:14:24 +03:00
const bool cmiss = ( ax8 . CountUncertainDurableIntents ( " r8_miss " ) = = 0 ) ;
AddCheck ( " DURA-04 " , " intent-journal-440-paired " , " WARNING " ,
" 440 пар INTENT/SEND_RETURN => 0 неопределённых (нет cap 256) (R8-B4) " ,
StringFormat ( " saved=%s u=%I64u " , ( u440_saved ? " 1 " : " 0 " ) , u440 ) ,
c440ok , " audit-8-R8-B4 " ) ;
AddCheck ( " DURA-05 " , " intent-journal-single-intent " , " WARNING " ,
" один INTENT без SEND_RETURN => 1 неопределённая (R8-B4) " ,
StringFormat ( " saved=%s u=%I64u " , ( u1_saved ? " 1 " : " 0 " ) , u1 ) ,
c1ok , " audit-8-R8-B4 " ) ;
AddCheck ( " DURA-06 " , " intent-journal-missing-vs-empty " , " WARNING " ,
" отсутствующий и пустой журнал => 0; sentinel недоступности — код-ревью (R8-B4) " ,
StringFormat ( " miss=%s empty=%s " , ( cmiss ? " 1 " : " 0 " ) , ( cempty ? " 1 " : " 0 " ) ) ,
cmiss & & cempty , " audit-8-R8-B4 " ) ;
//--- R8-S1: read-back по логическим записям; capacity; строгий разбор
const bool rb_ok = CCsv : : ReadBackVerifyFile (
" RequestLatencyLab \\ tests \\ samples_multi.csv " , 3 , ue ) ;
const bool rb_bad = ! CCsv : : ReadBackVerifyFile (
" RequestLatencyLab \\ tests \\ samples_multi.csv " , 4 , ue ) ;
AddCheck ( " READER-05 " , " readback-logical-records " , " WARNING " ,
" multiline-запись: read-back считает логические записи (3), не физические (R8-S1) " ,
StringFormat ( " ok3=%s bad4=%s " , ( rb_ok ? " 1 " : " 0 " ) , ( rb_bad ? " 1 " : " 0 " ) ) ,
rb_ok & & rb_bad , " audit-8-R8-S1 " ) ;
2026-09-11 18:16:32 +03:00
CRequestTracker tcap ;
2026-09-12 22:14:24 +03:00
tcap .Init ( 8 , " audit8-cap " ) ;
RequestPlan pcap ;
pcap . Zero ( ) ;
pcap . sequence = 1 ;
pcap . symbol = " X " ;
pcap . magic = 1 ;
pcap . condition_id = " A " ;
pcap . operation = LAB_OP_MARKET_OPEN ;
ulong scap = 0 ;
tcap .Reserve ( pcap , scap ) ;
pcap . sequence = 2 ;
tcap .Reserve ( pcap , scap ) ;
const string cap_csv = CCsvReport : : BuildSamples ( & tcap , " C1 " , " cfg " ) ;
const bool cap_saved = CCsv : : SaveUtf8 ( " RequestLatencyLab \\ tests \\ samples_cap2.csv " ,
cap_csv , ue ) ;
2026-09-11 18:16:32 +03:00
RebuiltSample cap_r [ 2 ] ;
2026-09-12 22:14:24 +03:00
int cap_n = -9 ;
2026-09-11 18:16:32 +03:00
if ( cap_saved )
2026-09-12 22:14:24 +03:00
cap_n = CReportBuilder : : LoadSamples ( " RequestLatencyLab \\ tests \\ samples_cap2.csv " ,
cap_r , 2 , ue ) ;
AddCheck ( " READER-06 " , " csv-capacity-observations " , " WARNING " ,
" capacity=2 наблюдения: заголовок не расходует ёмкость (R8-S1) " ,
StringFormat ( " saved=%s n=%d err=%d " , ( cap_saved ? " 1 " : " 0 " ) , cap_n , ue . code ) ,
cap_saved & & cap_n = = 2 , " audit-8-R8-S1 " ) ;
string prow8 [ ] ;
int pcn8 = 0 ;
const bool unclosed8 = ! CCsv : : ParseRow ( " \" abc " , prow8 , pcn8 ) ;
ulong pu1 = 0 , pu2 = 0 ;
const bool max_ok = ( CReportBuilder : : ParseCsvUlong ( " 18446744073709551615 " , pu1 ) & &
! CReportBuilder : : ParseCsvUlong ( " 18446744073709551616 " , pu2 ) ) ;
AddCheck ( " READER-07 " , " csv-strict-parsing " , " WARNING " ,
" незакрытая кавычка => отказ; диапазон ulong проверяется (R8-S1) " ,
StringFormat ( " unclosed=%s max=%s " , ( unclosed8 ? " 1 " : " 0 " ) , ( max_ok ? " 1 " : " 0 " ) ) ,
unclosed8 & & max_ok , " audit-8-R8-S1 " ) ;
//--- R8-S2: samples несёт market_window_id/seq; market_windows ticks_checksum
2026-09-11 18:16:32 +03:00
CRequestTracker tw8 ;
2026-09-12 22:14:24 +03:00
tw8 .Init ( 8 , " audit8-win " ) ;
RequestPlan pw8 ;
pw8 . Zero ( ) ;
pw8 . sequence = 1 ;
pw8 . symbol = " X " ;
pw8 . magic = 1 ;
pw8 . condition_id = " A " ;
pw8 . operation = LAB_OP_MARKET_OPEN ;
ulong sw8 = 0 ;
tw8 .Reserve ( pw8 , sw8 ) ;
SendObservation ws8 ;
ws8 . Zero ( ) ;
ws8 . send_ok = true ;
ws8 . result_known = true ;
ws8 . result . retcode = TRADE_RETCODE_DONE ;
ws8 . result . request_id = 1 ;
tw8 . MarkSendStart ( 1 , 1000000UL ) ;
tw8 . MarkSendReturn ( 1 , ws8 ) ;
tw8 . SetMarketRegime ( 1 , LAB_REGIME_QUIET , " w00000001_0001 " , " cal-r8 " , 7 ) ;
const string win_csv = CCsvReport : : BuildSamples ( & tw8 , " C1 " , " cfg " ) ;
const bool win_saved = CCsv : : SaveUtf8 ( " RequestLatencyLab \\ tests \\ samples_win.csv " ,
win_csv , ue ) ;
2026-09-11 18:16:32 +03:00
RebuiltSample win_r [ 2 ] ;
2026-09-12 22:14:24 +03:00
int win_n = -9 ;
2026-09-11 18:16:32 +03:00
if ( win_saved )
2026-09-12 22:14:24 +03:00
win_n = CReportBuilder : : LoadSamples ( " RequestLatencyLab \\ tests \\ samples_win.csv " ,
win_r , 2 , ue ) ;
const bool win_link = ( win_n = = 1 & &
win_r [ 0 ] . market_window_id = = " w00000001_0001 " & &
win_r [ 0 ] . market_window_seq = = 7 ) ;
MarketWindow ww ;
ww . Zero ( ) ;
ww . window_id = " wT " ;
ww . sequence = 3 ;
ww . ticks_checksum = 12345 ;
MarketWindow wwa [ 1 ] ;
wwa [ 0 ] = ww ;
const string mw_csv = CCsvReport : : BuildMarketWindows ( wwa , 1 , " s8 " ) ;
const bool mw_ok = ( StringFind ( mw_csv , " ticks_checksum " ) > = 0 & &
StringFind ( mw_csv , " 12345 " ) > = 0 ) ;
AddCheck ( " WINDOW-01 " , " e3-window-linkage " , " WARNING " ,
" samples: market_window_id/seq round-trip; market_windows: ticks_checksum (R8-S2) " ,
StringFormat ( " n=%d link=%s mw=%s " , win_n , ( win_link ? " 1 " : " 0 " ) , ( mw_ok ? " 1 " : " 0 " ) ) ,
win_link & & mw_ok , " audit-8-R8-S2 " ) ;
2026-09-11 18:16:32 +03:00
}
2026-09-12 22:14:24 +03:00
//+------------------------------------------------------------------+
//| Регрессии аудита R9 |
//+------------------------------------------------------------------+
void TestAudit9Regressions ( void )
2026-09-11 21:20:36 +03:00
{
LabError a9e ;
2026-09-12 22:14:24 +03:00
//--- R9-B1: ReleaseReservation — откат НЕОТПРАВЛЕННОГО резерва
2026-09-11 21:20:36 +03:00
CRequestTracker trr ;
2026-09-12 22:14:24 +03:00
trr .Init ( 8 , " audit9-res " ) ;
RequestPlan prr ;
prr . Zero ( ) ;
prr . sequence = 1 ;
prr . symbol = " X " ;
prr . magic = 1 ;
prr . condition_id = " A " ;
prr . operation = LAB_OP_MARKET_OPEN ;
ulong srr = 0 ;
const bool r1 = trr .Reserve ( prr , srr ) ;
const bool rrel = trr . ReleaseReservation ( 1 ) ;
const bool r2 = trr .Reserve ( prr , srr ) ; // повтор того же слота
const int rn = trr . Count ( ) ;
const string rr_csv = CCsvReport : : BuildSamples ( & trr , " C1 " , " cfg " ) ;
int rr_rows = 0 ;
string rr_lines [ ] ;
int rrc2 = 0 ;
const int rn2 = StringSplit ( rr_csv , ' \n ' , rr_lines ) ;
for ( int i = 1 ; i < rn2 ; i + + )
2026-09-11 21:20:36 +03:00
{
2026-09-12 22:14:24 +03:00
string t = rr_lines [ i ] ;
StringReplace ( t , " \r " , " " ) ;
if ( StringLen ( t ) > 0 )
2026-09-11 21:20:36 +03:00
rr_rows + + ;
}
2026-09-12 22:14:24 +03:00
AddCheck ( " REL-01 " , " reserve-release-reuse " , " WARNING " ,
" ReleaseReservation отпускает слот: повтор той же sequence возможен, фантом не экспортируется (R9-B1) " ,
StringFormat ( " r1=%s rel=%s r2=%s n=%d rows=%d " ,
( r1 ? " 1 " : " 0 " ) , ( rrel ? " 1 " : " 0 " ) , ( r2 ? " 1 " : " 0 " ) , rn , rr_rows ) ,
r1 & & rrel & & r2 & & rn = = 1 & & rr_rows = = 1 , " audit-9-R9-B1 " ) ;
//--- R9-B2: единая политика закрытия — T2 без T3 не закрывает POSITION_CLOSE
2026-09-11 21:20:36 +03:00
CRequestTracker tcl ;
2026-09-12 22:14:24 +03:00
tcl .Init ( 8 , " audit9-clean " ) ;
RequestPlan pcl ;
pcl . Zero ( ) ;
pcl . sequence = 1 ;
pcl . symbol = " X " ;
pcl . magic = 1 ;
pcl . condition_id = " A " ;
pcl . operation = LAB_OP_POSITION_CLOSE ;
ulong scl = 0 ;
tcl .Reserve ( pcl , scl ) ;
tcl . MarkRequest ( 1 , 1200000UL , 1 , TRADE_RETCODE_DONE , 0 , 80 , 0 ) ;
RequestMetadata mcl ;
tcl . GetMetadata ( 1 , mcl ) ;
const bool cln_t2_no_t3 = ( ! CAppController : : MeasurementPointsComplete ( mcl ) ) ;
tcl . MarkOrderAdd ( 1 , 1300000UL , 80 , " CLEANUP " ) ;
tcl . GetMetadata ( 1 , mcl ) ;
const bool cln_full = CAppController : : MeasurementPointsComplete ( mcl ) ;
2026-09-11 21:20:36 +03:00
CRequestTracker tcl2 ;
2026-09-12 22:14:24 +03:00
tcl2 .Init ( 8 , " audit9-clean2 " ) ;
ulong sc2 = 0 ;
tcl2 .Reserve ( pcl , sc2 ) ;
tcl2 . SetFinal ( 1 , 1500000UL , LAB_OUT_REJECTED , LAB_FINAL_TIMER_CHECK , 1 ) ;
tcl2 . MarkRequest ( 1 , 1200000UL , 1 , TRADE_RETCODE_DONE , 0 , 0 , 0 ) ;
RequestMetadata mcl2 ;
tcl2 . GetMetadata ( 1 , mcl2 ) ;
const bool cln_rej = CAppController : : MeasurementPointsComplete ( mcl2 ) ;
AddCheck ( " CLN-01 " , " cleanup-measurement-points " , " WARNING " ,
" cleanup: T2 без T3 НЕ закрывает collection (единая политика), REJECTED без ордера освобождает (R9-B2) " ,
StringFormat ( " t2_no_t3=%s full=%s rej=%s " ,
( cln_t2_no_t3 ? " 1 " : " 0 " ) , ( cln_full ? " 1 " : " 0 " ) , ( cln_rej ? " 1 " : " 0 " ) ) ,
cln_t2_no_t3 & & cln_full & & cln_rej , " audit-9-R9-B2 " ) ;
//--- R9-B3: сбой durable-flush => Confirm=false + BLOCKED; последующая
//--- отправка запрещена (code 7) — терминальное состояние не перезаписано
LabSettings f9s ;
f9s . Zero ( ) ;
f9s . campaign_id = " tests " ;
f9s . experiment_id = LAB_EXP_E1 ;
f9s . series_id = 1 ;
f9s . count_per_condition = 2 ;
f9s . warmup_per_condition = 0 ;
f9s . symbol = " X " ;
f9s . magic = 1 ;
f9s . run_mode = LAB_RUN_LOCAL_ONLY ;
CExperimentRunner rf9 ;
LabError fe9 ;
CRequestTracker tf9 ;
CEventJournal jf9 ;
CTimeSource cf9 ;
2026-09-11 21:20:36 +03:00
cf9 . UseLive ( ) ;
2026-09-12 22:14:24 +03:00
const bool f9init = ( rf9 .Init ( f9s , fe9 ) & & tf9 .Init ( 16 , " audit9-flush " ) & &
jf9 .Init ( 256 , " audit9-flush " ) ) ;
2026-09-11 21:20:36 +03:00
CAppController af9 ;
2026-09-12 22:14:24 +03:00
af9 . Configure ( & tf9 , & jf9 , & rf9 , & cf9 ) ;
2026-09-11 21:20:36 +03:00
af9 . SetSettings ( f9s ) ;
2026-09-12 22:14:24 +03:00
bool flush_blocked = false ;
2026-09-11 21:20:36 +03:00
if ( f9init & & af9 . InitRun ( fe9 ) )
{
2026-09-12 22:14:24 +03:00
const string s9 = af9 . SessionId ( ) ;
const string dir9 = " RequestLatencyLab \\ tests \\ " + s9 ;
2026-09-11 21:20:36 +03:00
//--- intents.csv заменяется КАТАЛОГОМ: открыть каталог нельзя ->
//--- AppendDurableLine (FileOpen) отказ -> flush не удался.
2026-09-12 22:14:24 +03:00
FolderCreate ( dir9 + " \\ intents.csv " ) ;
af9 . QueueDurableSendReturn ( 7 , 1000100UL ) ;
const bool cf = af9 . ConfirmDurableSendReturns ( ) ; // R9-B3: возвращает успех
const bool st = ( af9 . State ( ) = = LAB_STATE_BLOCKED ) ;
RequestPlan pl9 ;
pl9 . Zero ( ) ;
pl9 . sequence = 1 ;
pl9 . symbol = " X " ;
pl9 . magic = 1 ;
pl9 . condition_id = " A " ;
pl9 . operation = LAB_OP_MARKET_OPEN ;
ulong s9b = 0 ;
LabError pe ;
const bool d9 = af9 . DispatchPlan ( pl9 , false , s9b , pe ) ;
flush_blocked = ( ! cf & & st & & ! d9 & & pe . code = = 7 ) ;
2026-09-11 21:20:36 +03:00
}
2026-09-12 22:14:24 +03:00
AddCheck ( " DURA-07 " , " flush-failure-blocked " , " WARNING " ,
" сбой durable flush => Confirm=false + BLOCKED, отправка запрещена code 7 (R9-B3) " ,
StringFormat ( " init=%s ok=%s state=%s " , ( f9init ? " 1 " : " 0 " ) ,
( flush_blocked ? " 1 " : " 0 " ) , af9 . StateName ( ) ) ,
f9init & & flush_blocked , " audit-9-R9-B3 " ) ;
//--- R9-S2: durable-журнал — INTENT_CANCELLED снимает неопределённость
CTimeSource cc9 ;
cc9 . UseLive ( ) ;
CRequestTracker tc9 ;
tc9 .Init ( 16 , " audit9-unc " ) ;
CEventJournal jc9 ;
jc9 .Init ( 256 , " audit9-unc " ) ;
CExperimentRunner rc9 ;
LabError xe9 ;
rc9 .Init ( f9s , xe9 ) ;
2026-09-11 21:20:36 +03:00
CAppController ac9 ;
2026-09-12 22:14:24 +03:00
ac9 . Configure ( & tc9 , & jc9 , & rc9 , & cc9 ) ;
2026-09-11 21:20:36 +03:00
ac9 . SetSettings ( f9s ) ;
2026-09-12 22:14:24 +03:00
const string pc1 = " RequestLatencyLab \\ tests \\ r9_cancel \\ intents.csv " ;
2026-09-11 21:20:36 +03:00
FolderCreate ( " RequestLatencyLab \\ tests \\ r9_cancel " ) ;
if ( FileIsExist ( pc1 ) )
FileDelete ( pc1 ) ;
2026-09-12 22:14:24 +03:00
const bool cs1 = CCsv : : SaveUtf8 ( pc1 ,
" r9_cancel,5,INTENT,2000000 \r \n r9_cancel,5,INTENT_CANCELLED,2000100 \r \n " , a9e ) ;
const ulong cu1 = ac9 . CountUncertainDurableIntents ( " r9_cancel " ) ;
const string pc2 = " RequestLatencyLab \\ tests \\ r9_plain \\ intents.csv " ;
2026-09-11 21:20:36 +03:00
FolderCreate ( " RequestLatencyLab \\ tests \\ r9_plain " ) ;
if ( FileIsExist ( pc2 ) )
FileDelete ( pc2 ) ;
2026-09-12 22:14:24 +03:00
const bool cs2 = CCsv : : SaveUtf8 ( pc2 , " r9_plain,5,INTENT,2000000 \r \n " , a9e ) ;
const ulong cu2 = ac9 . CountUncertainDurableIntents ( " r9_plain " ) ;
AddCheck ( " DURA-08 " , " intent-cancelled-not-uncertain " , " WARNING " ,
" INTENT+INTENT_CANCELLED => 0 неопределённых; INTENT один => 1 (R9-S2) " ,
StringFormat ( " c1=%s canc=%I64u plain=%I64u " , ( cs1 ? " 1 " : " 0 " ) , cu1 , cu2 ) ,
cs1 & & cu1 = = 0 & & cs2 & & cu2 = = 1 , " audit-9-R9-S2 " ) ;
//--- R9-S1: заголовок samples.csv — проверяются ВСЕ 68 имён
CRequestTracker tr8 ;
tr8 .Init ( 8 , " audit9-hdr " ) ;
RequestPlan pr8 ;
pr8 . Zero ( ) ;
pr8 . sequence = 1 ;
pr8 . symbol = " X " ;
pr8 . magic = 1 ;
pr8 . condition_id = " A " ;
pr8 . operation = LAB_OP_MARKET_OPEN ;
ulong sr8 = 0 ;
tr8 .Reserve ( pr8 , sr8 ) ;
const string good_csv = CCsvReport : : BuildSamples ( & tr8 , " C1 " , " cfg " ) ;
const bool gs = CCsv : : SaveUtf8 ( " RequestLatencyLab \\ tests \\ samples_hdr_good.csv " ,
good_csv , a9e ) ;
RebuiltSample gr [ 2 ] ;
int gn = -9 ;
2026-09-11 21:20:36 +03:00
if ( gs )
2026-09-12 22:14:24 +03:00
gn = CReportBuilder : : LoadSamples ( " RequestLatencyLab \\ tests \\ samples_hdr_good.csv " ,
gr , 2 , a9e ) ;
string bad_csv = good_csv ;
StringReplace ( bad_csv , " market_window_id " , " bad_window_id " ) ;
StringReplace ( bad_csv , " market_window_seq " , " bad_window_seq " ) ;
const bool bs = CCsv : : SaveUtf8 ( " RequestLatencyLab \\ tests \\ samples_hdr_bad.csv " ,
bad_csv , a9e ) ;
RebuiltSample br [ 2 ] ;
int bn = -9 ;
LabError be9 ;
2026-09-11 21:20:36 +03:00
if ( bs )
2026-09-12 22:14:24 +03:00
bn = CReportBuilder : : LoadSamples ( " RequestLatencyLab \\ tests \\ samples_hdr_bad.csv " ,
br , 2 , be9 ) ;
AddCheck ( " READER-08 " , " samples-header-68-columns " , " WARNING " ,
" проверяются ВСЕ 68 имён заголовка: подмена window-колонок отклоняется (R9-S1) " ,
StringFormat ( " good=%d bad=%d bad_code=%d " , gn , bn , be9 . code ) ,
gs & & gn = = 1 & & bs & & bn < 0 & & be9 . code = = 32 , " audit-9-R9-S1 " ) ;
2026-09-11 21:20:36 +03:00
}
2026-09-12 22:14:24 +03:00
//+------------------------------------------------------------------+
//| Регрессии аудита R10 |
//+------------------------------------------------------------------+
void TestAudit10Regressions ( void )
2026-09-12 02:21:14 +03:00
{
LabError a10e ;
2026-09-12 22:14:24 +03:00
//--- R10-B2: повторный INTENT после отмены - новая попытка
2026-09-12 02:21:14 +03:00
CTimeSource cx10 ;
cx10 . UseLive ( ) ;
CRequestTracker tx10 ;
2026-09-12 22:14:24 +03:00
tx10 .Init ( 16 , " audit10-unc " ) ;
2026-09-12 02:21:14 +03:00
CEventJournal jx10 ;
2026-09-12 22:14:24 +03:00
jx10 .Init ( 256 , " audit10-unc " ) ;
2026-09-12 02:21:14 +03:00
LabSettings x10s ;
x10s . Zero ( ) ;
2026-09-12 22:14:24 +03:00
x10s . campaign_id = " tests " ;
x10s . experiment_id = LAB_EXP_E1 ;
x10s . series_id = 1 ;
x10s . symbol = " X " ;
x10s . magic = 1 ;
x10s . run_mode = LAB_RUN_LOCAL_ONLY ;
2026-09-12 02:21:14 +03:00
CExperimentRunner rx10 ;
2026-09-12 22:14:24 +03:00
rx10 .Init ( x10s , a10e ) ;
2026-09-12 02:21:14 +03:00
CAppController ax10 ;
2026-09-12 22:14:24 +03:00
ax10 . Configure ( & tx10 , & jx10 , & rx10 , & cx10 ) ;
2026-09-12 02:21:14 +03:00
ax10 . SetSettings ( x10s ) ;
2026-09-12 22:14:24 +03:00
const string pr1 = " RequestLatencyLab \\ tests \\ r10cr \\ intents.csv " ;
2026-09-12 02:21:14 +03:00
FolderCreate ( " RequestLatencyLab \\ tests \\ r10cr " ) ;
if ( FileIsExist ( pr1 ) )
FileDelete ( pr1 ) ;
2026-09-12 22:14:24 +03:00
const bool r10s1 = CCsv : : SaveUtf8 ( pr1 ,
" r10cr,7,INTENT,3000000 \r \n "
" r10cr,7,INTENT_CANCELLED,3000100 \r \n "
" r10cr,7,INTENT,4000000 \r \n " , a10e ) ;
const ulong r10cr = ax10 . CountUncertainDurableIntents ( " r10cr " ) ;
AddCheck ( " DURA-09 " , " cancel-retry-crash " , " WARNING " ,
" INTENT+INTENT_CANCELLED+INTENT => 1 неопределённая (повторная попытка) (R10-B2) " ,
StringFormat ( " saved=%s u=%I64u " , ( r10s1 ? " 1 " : " 0 " ) , r10cr ) ,
r10s1 & & r10cr = = 1 , " audit-10-R10-B2 " ) ;
const string pr2 = " RequestLatencyLab \\ tests \\ r10cs \\ intents.csv " ;
2026-09-12 02:21:14 +03:00
FolderCreate ( " RequestLatencyLab \\ tests \\ r10cs " ) ;
if ( FileIsExist ( pr2 ) )
FileDelete ( pr2 ) ;
2026-09-12 22:14:24 +03:00
const bool r10s2 = CCsv : : SaveUtf8 ( pr2 ,
" r10cs,7,INTENT,3000000 \r \n "
" r10cs,7,INTENT_CANCELLED,3000100 \r \n "
" r10cs,7,INTENT,4000000 \r \n "
" r10cs,7,SEND_RETURN,4000100 \r \n " , a10e ) ;
const ulong r10cs = ax10 . CountUncertainDurableIntents ( " r10cs " ) ;
AddCheck ( " DURA-10 " , " cancel-retry-send-return " , " WARNING " ,
" после отмены повторная попытка с SEND_RETURN => 0 неопределённых (R10-B2) " ,
StringFormat ( " saved=%s u=%I64u " , ( r10s2 ? " 1 " : " 0 " ) , r10cs ) ,
r10s2 & & r10cs = = 0 , " audit-10-R10-B2 " ) ;
//--- R10-B3: закрытие коллекции cleanup ждёт DEAL-callback
2026-09-12 02:21:14 +03:00
RequestMetadata cm10a ;
2026-09-12 22:14:24 +03:00
cm10a . outcome = LAB_OUT_UNKNOWN ;
cm10a . order_ticket = 0 ;
cm10a . plan . operation = LAB_OP_POSITION_CLOSE ;
cm10a . present_mask = ( uint ) LAB_MASK_T2 | ( uint ) LAB_MASK_T3 ;
cm10a . callback_coverage_complete = false ;
const bool cln_a = ! CAppController : : CleanupCollectionReady ( cm10a ) ;
2026-09-12 02:21:14 +03:00
RequestMetadata cm10b ;
2026-09-12 22:14:24 +03:00
cm10b = cm10a ;
cm10b . callback_coverage_complete = true ;
const bool cln_b = CAppController : : CleanupCollectionReady ( cm10b ) ;
2026-09-12 02:21:14 +03:00
RequestMetadata cm10c ;
2026-09-12 22:14:24 +03:00
cm10c . outcome = LAB_OUT_UNKNOWN ;
cm10c . order_ticket = 0 ;
cm10c . plan . operation = LAB_OP_PENDING_DELETE ;
cm10c . plan . operation = LAB_OP_PENDING_DELETE ;
cm10c . present_mask = ( uint ) LAB_MASK_T2 ;
cm10c . callback_coverage_complete = false ;
const bool cln_c = CAppController : : CleanupCollectionReady ( cm10c ) ;
AddCheck ( " CLN-02 " , " cleanup-waits-deal-callback " , " WARNING " ,
" cleanup POSITION_CLOSE без полноты DEAL-callback НЕ готов к закрытию; с покрытием готов; PENDING_DELETE (без сделки) не ждёт DEAL (R10-B3) " ,
StringFormat ( " pos_no_cb=%s pos_cb=%s del=%s " ,
( cln_a ? " 1 " : " 0 " ) , ( cln_b ? " 1 " : " 0 " ) , ( cln_c ? " 1 " : " 0 " ) ) ,
cln_a & & cln_b & & cln_c , " audit-10-R10-B3 " ) ;
//--- R10-B4: формат market_ticks.csv - заголовок, реальный CRLF, %.17g
const string hdr10 = CAppController : : MarketTickHeader ( ) ;
2026-09-12 02:21:14 +03:00
MqlTick mt10 ;
2026-09-12 22:14:24 +03:00
mt10 . time_msc = 1234567890 ;
mt10 . bid = 1.2345678901234567 ;
mt10 . ask = 1.2345678901234999 ;
mt10 . flags = 234 ;
mt10 . volume = 777 ;
const string row10 = CAppController : : FormatMarketTickLine ( " s1 " , " w1 " , 3 , mt10 ) ;
const bool w_hdr = ( hdr10 = =
" session_id,window_id,window_seq,time_msc,bid,ask,flags,volume \r \n " ) ;
const bool w_bad_crlf = ( StringFind ( row10 , " \\ r \\ n " ) < 0 ) ; // НЕ текст-слеш
const bool w_real_crlf = ( StringFind ( row10 , " \r \n " ) > = 0 ) ; // реальный CRLF
const bool w_prec = ( StringFind ( row10 , " 1.2345678901234567 " ) > = 0 ) ;
const bool w_cols = ( StringFind ( row10 , " ,w1,3,1234567890, " ) > = 0 & &
StringFind ( row10 , " ,234,777 \r \n " ) > = 0 ) ;
AddCheck ( " WINDOW-02 " , " market-ticks-format " , " WARNING " ,
" market_ticks: заголовок, реальный CRLF (не литеральный \\ r \\ n), точность %.17g для bid/ask (R10-B4) " ,
StringFormat ( " hdr=%s crlf=%s no_bs=%s prec=%s cols=%s " ,
( w_hdr ? " 1 " : " 0 " ) , ( w_real_crlf ? " 1 " : " 0 " ) ,
( w_bad_crlf ? " 1 " : " 0 " ) , ( w_prec ? " 1 " : " 0 " ) , ( w_cols ? " 1 " : " 0 " ) ) ,
w_hdr & & w_real_crlf & & w_bad_crlf & & w_prec & & w_cols , " audit-10-R10-B4 " ) ;
//--- R10-B1/R10-S1: StartRun LOCAL_ONLY -> FINISHED -> ResetForRestart ->
//--- повторный запуск; синтетический E1 создаёт intents.csv (пары
//--- INTENT/SEND_RETURN) и market_ticks.csv (заголовок). checkpoint.marker
//--- пишется EA при финализации (ExportCheckpoint, R10-B1) - проверка в
//--- демо-пилоте на графике.
2026-09-12 02:21:14 +03:00
CTimeSource cs10 ;
cs10 . UseSynthetic ( 1000000 ) ;
CRequestTracker ts10 ;
2026-09-12 22:14:24 +03:00
ts10 .Init ( 64 , " audit10-start " ) ;
2026-09-12 02:21:14 +03:00
CEventJournal js10 ;
2026-09-12 22:14:24 +03:00
js10 .Init ( 512 , " audit10-start " ) ;
2026-09-12 02:21:14 +03:00
LabSettings s10 ;
s10 . Zero ( ) ;
2026-09-12 22:14:24 +03:00
s10 . campaign_id = " tests " ;
s10 . experiment_id = LAB_EXP_E1 ;
s10 . series_id = 1 ;
s10 . symbol = " X " ;
s10 . magic = 1 ;
s10 . run_mode = LAB_RUN_LOCAL_ONLY ;
s10 . count_per_condition = 2 ;
s10 . warmup_per_condition = 0 ;
s10 . pause_ms = 1 ;
s10 . sample_capacity = 64 ;
s10 . event_capacity = 1024 ;
2026-09-12 02:21:14 +03:00
CExperimentRunner rs10 ;
2026-09-12 22:14:24 +03:00
rs10 .Init ( s10 , a10e ) ;
2026-09-12 02:21:14 +03:00
CAppController as10 ;
2026-09-12 22:14:24 +03:00
as10 . Configure ( & ts10 , & js10 , & rs10 , & cs10 ) ;
2026-09-12 02:21:14 +03:00
as10 . SetSettings ( s10 ) ;
2026-09-12 22:14:24 +03:00
const bool st1 = as10 . InitRun ( a10e ) ;
const bool st2 = as10 . StartRun ( a10e ) ;
const string ss10 = as10 . SessionId ( ) ;
const bool st3 = ( as10 . State ( ) = = LAB_STATE_FINISHED ) ;
const string ipath10 = " RequestLatencyLab \\ tests \\ " + ss10 + " \\ intents.csv " ;
string ic10 = " " ;
const bool il10 = CCsv : : LoadUtf8 ( ipath10 , ic10 , a10e ) ;
int iN = 0 ;
int srN = 0 ;
2026-09-12 02:21:14 +03:00
string ilines10 [ ] ;
2026-09-12 22:14:24 +03:00
const int iln10 = StringSplit ( ic10 , ' \n ' , ilines10 ) ;
for ( int i = 0 ; i < iln10 ; i + + )
2026-09-12 02:21:14 +03:00
{
2026-09-12 22:14:24 +03:00
string sl10 = ilines10 [ i ] ;
StringReplace ( sl10 , " \\ r " , " " ) ;
if ( StringFind ( sl10 , " INTENT " ) > = 0 )
2026-09-12 02:21:14 +03:00
iN + + ;
2026-09-12 22:14:24 +03:00
if ( StringFind ( sl10 , " SEND_RETURN " ) > = 0 )
2026-09-12 02:21:14 +03:00
srN + + ;
}
2026-09-12 22:14:24 +03:00
const bool pairs_ok = ( il10 & & iN > = 1 & & iN = = srN ) ;
const bool mt_ok = as10 . EnsureMarketTicksFile ( ) ;
const bool mt_ex = FileIsExist ( " RequestLatencyLab \\ tests \\ " + ss10 + " \\ market_ticks.csv " ) ;
const bool st4 = as10 . ResetForRestart ( ) ;
const bool st5 = ( as10 . State ( ) = = LAB_STATE_READY ) ;
const bool st6 = as10 . StartRun ( a10e ) ;
const bool st7 = ( as10 . State ( ) = = LAB_STATE_FINISHED ) ;
AddCheck ( " START-01 " , " start-run-restart-pilot " , " WARNING " ,
" StartRun(LOCAL_ONLY): синтетический E1 завершается FINISHED, создаёт intents.csv (пары) и market_ticks.csv; после финализации ResetForRestart и повторный запуск работают (R10-B1/R10-S1) " ,
StringFormat ( " init=%s st2=%s fin=%s il=%s pairsInt=%d pairsSr=%d mt=%s mtex=%s rest=%s ready=%s st6=%s fin2=%s " ,
( st1 ? " 1 " : " 0 " ) , ( st2 ? " 1 " : " 0 " ) , ( st3 ? " 1 " : " 0 " ) ,
( il10 ? " 1 " : " 0 " ) , iN , srN , ( mt_ok ? " 1 " : " 0 " ) , ( mt_ex ? " 1 " : " 0 " ) ,
( st4 ? " 1 " : " 0 " ) , ( st5 ? " 1 " : " 0 " ) , ( st6 ? " 1 " : " 0 " ) , ( st7 ? " 1 " : " 0 " ) ) ,
st1 & & st2 & & st3 & & pairs_ok & & mt_ok & & mt_ex & & st4 & & st5 & & st6 & & st7 , " audit-10-R10-B1 " ) ;
2026-09-12 02:21:14 +03:00
}
2026-09-12 06:20:31 +03:00
//+------------------------------------------------------------------+
2026-09-12 22:14:24 +03:00
//| Регрессии этапа 12 (R11) |
2026-09-12 06:20:31 +03:00
//+------------------------------------------------------------------+
2026-09-12 22:14:24 +03:00
void TestAudit11Regressions ( void )
2026-09-12 06:20:31 +03:00
{
2026-09-12 22:14:24 +03:00
//--- RUNN-01 (R11-B1): CExperimentRunner::Init обнуляет счётчики прогона.
//--- Повторный Init (новый прогон) НЕ накапливает отправки прежней сессии.
2026-09-12 06:20:31 +03:00
LabError a11e ;
LabSettings s11 ;
s11 . Zero ( ) ;
2026-09-12 22:14:24 +03:00
s11 . campaign_id = " tests " ;
s11 . experiment_id = LAB_EXP_E1 ;
s11 . series_id = 1 ;
s11 . symbol = " X " ;
s11 . magic = 1 ;
s11 . run_mode = LAB_RUN_LOCAL_ONLY ;
s11 . count_per_condition = 100 ;
s11 . warmup_per_condition = 10 ;
s11 . pause_ms = 1 ;
s11 . sample_capacity = 64 ;
s11 . event_capacity = 1024 ;
2026-09-12 06:20:31 +03:00
CExperimentRunner rr11 ;
2026-09-12 22:14:24 +03:00
const bool rinit1 = rr11 .Init ( s11 , a11e ) ;
2026-09-12 06:20:31 +03:00
rr11 . CountDispatched ( LAB_ROLE_MAIN ) ;
rr11 . CountDispatched ( LAB_ROLE_WARMUP ) ;
2026-09-12 22:14:24 +03:00
const ulong b_m = rr11 . MainDispatched ( ) ;
const ulong b_w = rr11 . WarmupDispatched ( ) ;
const bool rinit2 = rr11 .Init ( s11 , a11e ) ;
const bool reset_ok = ( rinit1 & & rinit2 & & b_m = = 1 & & b_w = = 1 & &
rr11 . MainDispatched ( ) = = 0 & & rr11 . WarmupDispatched ( ) = = 0 ) ;
AddCheck ( " RUNN-01 " , " init-resets-counters " , " WARNING " ,
2026-09-12 06:20:31 +03:00
" CExperimentRunner::Init обнуляет счётчики прогона - повторный запуск не удваивает MainDispatched/WarmupDispatched (R11-B1) " ,
StringFormat ( " init1=%s b_m=%I64u b_w=%I64u init2=%s m=%I64u w=%I64u " ,
2026-09-12 22:14:24 +03:00
( rinit1 ? " 1 " : " 0 " ) , b_m , b_w , ( rinit2 ? " 1 " : " 0 " ) ,
rr11 . MainDispatched ( ) , rr11 . WarmupDispatched ( ) ) ,
reset_ok , " audit-11-R11-B1 " ) ;
//--- START-02 (R11-B1): два самостоятельных запуска LOCAL_ONLY через
//--- StartRun/FINISHED/ResetForRestart - счётчики второй сессии независимы
//--- (200 MAIN / 20 WARMUP, а не накопленные 400/40); обе сессии FINISHED.
2026-09-12 06:20:31 +03:00
CTimeSource cs11 ;
cs11 . UseSynthetic ( 1000000 ) ;
CRequestTracker ts11 ;
2026-09-12 22:14:24 +03:00
ts11 .Init ( 64 , " audit11-start " ) ;
2026-09-12 06:20:31 +03:00
CEventJournal js11 ;
2026-09-12 22:14:24 +03:00
js11 .Init ( 512 , " audit11-start " ) ;
2026-09-12 06:20:31 +03:00
LabSettings s11b ;
s11b . Zero ( ) ;
2026-09-12 22:14:24 +03:00
s11b . campaign_id = " tests " ;
s11b . experiment_id = LAB_EXP_E1 ;
s11b . series_id = 1 ;
s11b . symbol = " X " ;
s11b . magic = 1 ;
s11b . run_mode = LAB_RUN_LOCAL_ONLY ;
s11b . count_per_condition = 100 ;
s11b . warmup_per_condition = 10 ;
s11b . pause_ms = 1 ;
s11b . sample_capacity = 256 ;
s11b . event_capacity = 4096 ;
2026-09-12 06:20:31 +03:00
CExperimentRunner rs11 ;
2026-09-12 22:14:24 +03:00
rs11 .Init ( s11b , a11e ) ;
2026-09-12 06:20:31 +03:00
CAppController as11 ;
2026-09-12 22:14:24 +03:00
as11 . Configure ( & ts11 , & js11 , & rs11 , & cs11 ) ;
2026-09-12 06:20:31 +03:00
as11 . SetSettings ( s11b ) ;
2026-09-12 22:14:24 +03:00
const bool st_i1 = as11 . InitRun ( a11e ) ;
const bool st_r1 = as11 . StartRun ( a11e ) ;
const ulong st_m1 = as11 . Runner ( ) . MainDispatched ( ) ;
const ulong st_w1 = as11 . Runner ( ) . WarmupDispatched ( ) ;
const bool st_f1 = ( as11 . State ( ) = = LAB_STATE_FINISHED ) ;
const bool st_rr = as11 . ResetForRestart ( ) ;
const bool st_r2 = as11 . StartRun ( a11e ) ;
const ulong st_m2 = as11 . Runner ( ) . MainDispatched ( ) ;
const ulong st_w2 = as11 . Runner ( ) . WarmupDispatched ( ) ;
const bool st_f2 = ( as11 . State ( ) = = LAB_STATE_FINISHED ) ;
const bool ok2 = ( st_i1 & & st_r1 & & st_f1 & & st_rr & & st_r2 & & st_f2 & &
st_m1 = = 200 & & st_w1 = = 20 & & st_m2 = = 200 & & st_w2 = = 20 ) ;
AddCheck ( " START-02 " , " restart-independent-counters " , " WARNING " ,
2026-09-12 06:20:31 +03:00
" повторный StartRun после финализации даёт независимые счётчики (200 MAIN / 20 WARMUP), а не накопленные 400/40 (R11-B1) " ,
StringFormat ( " i1=%s r1=%s m1=%I64u w1=%I64u f1=%s rr=%s r2=%s m2=%I64u w2=%I64u f2=%s " ,
2026-09-12 22:14:24 +03:00
( st_i1 ? " 1 " : " 0 " ) , ( st_r1 ? " 1 " : " 0 " ) , st_m1 , st_w1 , ( st_f1 ? " 1 " : " 0 " ) ,
( st_rr ? " 1 " : " 0 " ) , ( st_r2 ? " 1 " : " 0 " ) , st_m2 , st_w2 , ( st_f2 ? " 1 " : " 0 " ) ) ,
ok2 , " audit-11-R11-B1 " ) ;
2026-09-12 06:20:31 +03:00
}
2026-09-12 11:19:45 +03:00
//+------------------------------------------------------------------+
2026-09-12 22:14:24 +03:00
//| Регрессии этапа 13 (R13) |
2026-09-12 11:19:45 +03:00
//+------------------------------------------------------------------+
2026-09-12 22:14:24 +03:00
void TestAudit12Regressions ( void )
2026-09-12 11:19:45 +03:00
{
2026-09-12 22:14:24 +03:00
//--- START-03 (R13-B1): отказ ПЕРВОЙ durable-записи синтетического прогона
//--- не стирает контекст сессии: StartRun=true (направляет в финализацию
//--- частичного отчёта), FINISHED + termination_reason, session_id сохранён,
//--- фантомный резерв до T0 снят (не «выполненное наблюдение»); после
//--- ResetForRestart повторный запуск без инъекции завершается FINISHED.
2026-09-12 11:19:45 +03:00
LabError a13e ;
LabSettings s13 ;
s13 . Zero ( ) ;
2026-09-12 22:14:24 +03:00
s13 . campaign_id = " tests " ;
s13 . experiment_id = LAB_EXP_E1 ;
s13 . series_id = 1 ;
s13 . symbol = " X " ;
s13 . magic = 1 ;
s13 . run_mode = LAB_RUN_LOCAL_ONLY ;
s13 . count_per_condition = 10 ;
s13 . warmup_per_condition = 0 ;
s13 . pause_ms = 1 ;
s13 . sample_capacity = 64 ;
s13 . event_capacity = 1024 ;
2026-09-12 11:19:45 +03:00
CTimeSource c13 ;
c13 . UseSynthetic ( 1000000 ) ;
CRequestTracker t13 ;
2026-09-12 22:14:24 +03:00
t13 .Init ( 64 , " audit13-start03 " ) ;
2026-09-12 11:19:45 +03:00
CEventJournal j13 ;
2026-09-12 22:14:24 +03:00
j13 .Init ( 512 , " audit13-start03 " ) ;
2026-09-12 11:19:45 +03:00
CExperimentRunner r13 ;
2026-09-12 22:14:24 +03:00
r13 .Init ( s13 , a13e ) ;
2026-09-12 11:19:45 +03:00
CAppController a13 ;
2026-09-12 22:14:24 +03:00
a13 . Configure ( & t13 , & j13 , & r13 , & c13 ) ;
2026-09-12 11:19:45 +03:00
a13 . SetSettings ( s13 ) ;
2026-09-12 22:14:24 +03:00
const bool s13_i = a13 . InitRun ( a13e ) ;
2026-09-12 11:19:45 +03:00
a13 . TestSetFailIntentAfter ( 1 ) ; // отказ ПЕРВОГО INTENT
2026-09-12 22:14:24 +03:00
const bool s13_r1 = a13 . StartRun ( a13e ) ; // true: финализация частичного отчёта
const bool s13_f = ( a13 . State ( ) = = LAB_STATE_FINISHED ) ;
const bool s13_sid = ( StringLen ( a13 . SessionId ( ) ) > 0 ) ;
const bool s13_tr = ( StringLen ( a13 . TerminationReason ( ) ) > 0 ) ;
const bool s13_empty = ( t13 . Count ( ) = = 0 ) ; // резерв до T0 снят
const bool s13_rr = a13 . ResetForRestart ( ) ;
2026-09-12 11:19:45 +03:00
a13 . TestSetFailIntentAfter ( 0 ) ; // отказ снят - вторая сессия чистая
2026-09-12 22:14:24 +03:00
const bool s13_r2 = a13 . StartRun ( a13e ) ;
const bool s13_f2 = ( a13 . State ( ) = = LAB_STATE_FINISHED ) ;
const bool s13_ok = ( s13_i & & s13_r1 & & s13_f & & s13_sid & & s13_tr & &
s13_empty & & s13_rr & & s13_r2 & & s13_f2 ) ;
AddCheck ( " START-03 " , " abort-preserves-session " , " WARNING " ,
2026-09-12 11:19:45 +03:00
" отказ SyntheticDispatch (первый INTENT) сохраняет контекст сессии: StartRun=true, FINISHED+reason, резерв до T0 снят; после ResetForRestart повторный запуск чист (R13-B1) " ,
StringFormat ( " i=%s r1=%s fin=%s sid=%s reason=%s empty=%s rr=%s r2=%s fin2=%s " ,
2026-09-12 22:14:24 +03:00
( s13_i ? " 1 " : " 0 " ) , ( s13_r1 ? " 1 " : " 0 " ) , ( s13_f ? " 1 " : " 0 " ) ,
( s13_sid ? " 1 " : " 0 " ) , ( s13_tr ? " 1 " : " 0 " ) , ( s13_empty ? " 1 " : " 0 " ) ,
( s13_rr ? " 1 " : " 0 " ) , ( s13_r2 ? " 1 " : " 0 " ) , ( s13_f2 ? " 1 " : " 0 " ) ) ,
s13_ok , " audit-13-R13-B1 " ) ;
//--- START-04 (R13-B1): отказ записи SEND_RETURN (после T1) — прерванная
//--- попытка СОХРАНЯЕТСЯ как неопределённая запись (T0+T1, без T2), а не
//--- стирается и не считается выполненной сделкой; контекст сессии жив.
2026-09-12 11:19:45 +03:00
LabSettings s14 ;
s14 . Zero ( ) ;
2026-09-12 22:14:24 +03:00
s14 . campaign_id = " tests " ;
s14 . experiment_id = LAB_EXP_E1 ;
s14 . series_id = 1 ;
s14 . symbol = " X " ;
s14 . magic = 1 ;
s14 . run_mode = LAB_RUN_LOCAL_ONLY ;
s14 . count_per_condition = 10 ;
s14 . warmup_per_condition = 0 ;
s14 . pause_ms = 1 ;
s14 . sample_capacity = 64 ;
s14 . event_capacity = 1024 ;
2026-09-12 11:19:45 +03:00
CTimeSource c14 ;
c14 . UseSynthetic ( 1000000 ) ;
CRequestTracker t14 ;
2026-09-12 22:14:24 +03:00
t14 .Init ( 64 , " audit13-start04 " ) ;
2026-09-12 11:19:45 +03:00
CEventJournal j14 ;
2026-09-12 22:14:24 +03:00
j14 .Init ( 512 , " audit13-start04 " ) ;
2026-09-12 11:19:45 +03:00
CExperimentRunner r14 ;
2026-09-12 22:14:24 +03:00
r14 .Init ( s14 , a13e ) ;
2026-09-12 11:19:45 +03:00
CAppController a14 ;
2026-09-12 22:14:24 +03:00
a14 . Configure ( & t14 , & j14 , & r14 , & c14 ) ;
2026-09-12 11:19:45 +03:00
a14 . SetSettings ( s14 ) ;
2026-09-12 22:14:24 +03:00
const bool s14_i = a14 . InitRun ( a13e ) ;
2026-09-12 11:19:45 +03:00
a14 . TestSetFailIntentAfter ( 2 ) ; // отказ ПЕРВОГО SEND_RETURN (после T1)
2026-09-12 22:14:24 +03:00
const bool s14_r1 = a14 . StartRun ( a13e ) ;
const bool s14_f = ( a14 . State ( ) = = LAB_STATE_FINISHED ) ;
const bool s14_cnt = ( t14 . Count ( ) = = 1 ) ; // прерванная попытка сохранена
2026-09-12 11:19:45 +03:00
RequestMetadata s14_md ;
2026-09-12 22:14:24 +03:00
const bool s14_got = t14 . GetByIndex ( 0 , s14_md ) ;
const bool s14_t0 = ( s14_got & & ( s14_md . present_mask & ( uint ) LAB_MASK_T0 ) ! = 0 ) ;
const bool s14_t1 = ( s14_got & & ( s14_md . present_mask & ( uint ) LAB_MASK_T1 ) ! = 0 ) ;
const bool s14_no2 = ( s14_got & & ( s14_md . present_mask & ( uint ) LAB_MASK_T2 ) = = 0 ) ;
const bool s14_sid = ( StringLen ( a14 . SessionId ( ) ) > 0 ) ;
const bool s14_tr = ( StringLen ( a14 . TerminationReason ( ) ) > 0 ) ;
const bool s14_ok = ( s14_i & & s14_r1 & & s14_f & & s14_cnt & & s14_t0 & &
s14_t1 & & s14_no2 & & s14_sid & & s14_tr ) ;
AddCheck ( " START-04 " , " abort-keeps-uncertain " , " WARNING " ,
2026-09-12 11:19:45 +03:00
" отказ записи SEND_RETURN (после T1) оставляет прерванную попытку DISPATCH_UNCERTAIN (T0+T1, без T2), а не стирает её и не считает выполненной (R13-B1) " ,
StringFormat ( " i=%s r1=%s fin=%s cnt=%s t0=%s t1=%s not2=%s sid=%s reason=%s " ,
2026-09-12 22:14:24 +03:00
( s14_i ? " 1 " : " 0 " ) , ( s14_r1 ? " 1 " : " 0 " ) , ( s14_f ? " 1 " : " 0 " ) ,
( s14_cnt ? " 1 " : " 0 " ) , ( s14_t0 ? " 1 " : " 0 " ) , ( s14_t1 ? " 1 " : " 0 " ) ,
( s14_no2 ? " 1 " : " 0 " ) , ( s14_sid ? " 1 " : " 0 " ) , ( s14_tr ? " 1 " : " 0 " ) ) ,
s14_ok , " audit-13-R13-B1 " ) ;
//--- RESTART-01 (R13-B3): окна E3 прежней сессии НЕ переносятся в новый
//--- прогон — ResetForRestart и StartRun очищают массив/счётчик окон
//--- (иначе BuildMarketWindows пометил бы старые окна новой session_id).
2026-09-12 11:19:45 +03:00
LabSettings s15 ;
s15 . Zero ( ) ;
2026-09-12 22:14:24 +03:00
s15 . campaign_id = " tests " ;
s15 . experiment_id = LAB_EXP_E1 ;
s15 . series_id = 1 ;
s15 . symbol = " X " ;
s15 . magic = 1 ;
s15 . run_mode = LAB_RUN_LOCAL_ONLY ;
s15 . count_per_condition = 10 ;
s15 . warmup_per_condition = 0 ;
s15 . pause_ms = 1 ;
s15 . sample_capacity = 64 ;
s15 . event_capacity = 1024 ;
2026-09-12 11:19:45 +03:00
CTimeSource c15 ;
c15 . UseSynthetic ( 1000000 ) ;
CRequestTracker t15 ;
2026-09-12 22:14:24 +03:00
t15 .Init ( 64 , " audit13-restart " ) ;
2026-09-12 11:19:45 +03:00
CEventJournal j15 ;
2026-09-12 22:14:24 +03:00
j15 .Init ( 512 , " audit13-restart " ) ;
2026-09-12 11:19:45 +03:00
CExperimentRunner r15 ;
2026-09-12 22:14:24 +03:00
r15 .Init ( s15 , a13e ) ;
2026-09-12 11:19:45 +03:00
CAppController a15 ;
2026-09-12 22:14:24 +03:00
a15 . Configure ( & t15 , & j15 , & r15 , & c15 ) ;
2026-09-12 11:19:45 +03:00
a15 . SetSettings ( s15 ) ;
2026-09-12 22:14:24 +03:00
const bool s15_i = a15 . InitRun ( a13e ) ;
const bool s15_start1 = a15 . StartRun ( a13e ) ; // первая сессия FINISHED
2026-09-12 11:19:45 +03:00
MarketWindow s15_w ;
s15_w . Zero ( ) ;
2026-09-12 22:14:24 +03:00
s15_w . window_id = " old_window_e3 " ;
s15_w . sequence = 1 ;
s15_w . valid = true ;
2026-09-12 11:19:45 +03:00
a15 . TestAddMarketWindow ( s15_w ) ; // имитация сохранённого окна
2026-09-12 22:14:24 +03:00
const bool s15_has1 = ( a15 . MarketWindowCount ( ) = = 1 ) ;
const bool s15_rr = a15 . ResetForRestart ( ) ;
const bool s15_clear = ( a15 . MarketWindowCount ( ) = = 0 ) ;
const bool s15_start2 = a15 . StartRun ( a13e ) ; // новая сессия
const bool s15_clean = ( a15 . MarketWindowCount ( ) = = 0 ) ;
2026-09-12 11:19:45 +03:00
a15 . TestAddMarketWindow ( s15_w ) ; // новое окно новой сессии
2026-09-12 22:14:24 +03:00
const bool s15_has1b = ( a15 . MarketWindowCount ( ) = = 1 ) ;
const bool s15_ok = ( s15_i & & s15_start1 & & s15_has1 & & s15_rr & & s15_clear & &
s15_start2 & & s15_clean & & s15_has1b ) ;
AddCheck ( " RESTART-01 " , " e3-windows-reset " , " WARNING " ,
2026-09-12 11:19:45 +03:00
" ResetForRestart и StartRun очищают окна E3 прежней сессии - старые окна не переносятся в новый market_windows.csv (R13-B3) " ,
StringFormat ( " i=%s s1=%s has1=%s rr=%s clear=%s s2=%s clean=%s has1b=%s " ,
2026-09-12 22:14:24 +03:00
( s15_i ? " 1 " : " 0 " ) , ( s15_start1 ? " 1 " : " 0 " ) , ( s15_has1 ? " 1 " : " 0 " ) ,
( s15_rr ? " 1 " : " 0 " ) , ( s15_clear ? " 1 " : " 0 " ) , ( s15_start2 ? " 1 " : " 0 " ) ,
( s15_clean ? " 1 " : " 0 " ) , ( s15_has1b ? " 1 " : " 0 " ) ) ,
s15_ok , " audit-13-R13-B3 " ) ;
//--- CHECK-01 (R13-B2): контракт контроллера, на который опирается gate
//--- финализации в слое советника: BlockRun НЕ меняет уже FINISHED-состояние,
//--- а признак сохранности checkpoint доступен через CheckpointOk().
2026-09-12 11:19:45 +03:00
a15 . BlockRun ( " checkpoint write failed " ) ;
2026-09-12 22:14:24 +03:00
const bool ck1 = ( a15 . State ( ) = = LAB_STATE_FINISHED ) ;
2026-09-12 11:19:45 +03:00
a15 . SetCheckpointState ( false ) ;
2026-09-12 22:14:24 +03:00
const bool ck2 = ! a15 . CheckpointOk ( ) ;
2026-09-12 11:19:45 +03:00
a15 . SetCheckpointState ( true ) ;
2026-09-12 22:14:24 +03:00
const bool ck3 = a15 . CheckpointOk ( ) ;
const bool ck_ok = ( ck1 & & ck2 & & ck3 ) ;
AddCheck ( " CHECK-01 " , " finalize-gate-contract " , " WARNING " ,
2026-09-12 11:19:45 +03:00
" контракт слоя финализации: BlockRun не меняет FINISHED; сохранность checkpoint видна через CheckpointOk() (R13-B2) " ,
StringFormat ( " stays_finished=%s cp_false=%s cp_true=%s " ,
2026-09-12 22:14:24 +03:00
( ck1 ? " 1 " : " 0 " ) , ( ck2 ? " 1 " : " 0 " ) , ( ck3 ? " 1 " : " 0 " ) ) ,
ck_ok , " audit-13-R13-B2 " ) ;
2026-09-12 11:19:45 +03:00
}
2026-09-12 12:06:55 +03:00
//+------------------------------------------------------------------+
2026-09-12 22:14:24 +03:00
//| Регрессии этапа 14 (R14) |
2026-09-12 12:06:55 +03:00
//+------------------------------------------------------------------+
2026-09-12 22:14:24 +03:00
void TestAudit13Regressions ( void )
2026-09-12 12:06:55 +03:00
{
2026-09-12 22:14:24 +03:00
//--- SESS-01 (R14-B1): переустановка EA с теми же настройками и ТЕМИ ЖЕ
//--- синтетическими часами (1 000 000 = 0x000F4240) НЕ повторяет
//--- session_id — кандидат проверяется по существованию каталога;
//--- прежний отчёт (intents.csv первой сессии) не перезаписан.
2026-09-12 12:06:55 +03:00
LabError a14e ;
LabSettings s16 ;
s16 . Zero ( ) ;
2026-09-12 22:14:24 +03:00
s16 . campaign_id = " tests " ;
s16 . experiment_id = LAB_EXP_E1 ;
s16 . series_id = 1 ;
s16 . symbol = " X " ;
s16 . magic = 1 ;
s16 . run_mode = LAB_RUN_LOCAL_ONLY ;
s16 . count_per_condition = 10 ;
s16 . warmup_per_condition = 0 ;
s16 . pause_ms = 1 ;
s16 . sample_capacity = 64 ;
s16 . event_capacity = 1024 ;
//--- первая "установка"
2026-09-12 12:06:55 +03:00
CTimeSource c16 ;
c16 . UseSynthetic ( 1000000 ) ;
CRequestTracker t16 ;
2026-09-12 22:14:24 +03:00
t16 .Init ( 64 , " audit14-sess01a " ) ;
2026-09-12 12:06:55 +03:00
CEventJournal j16 ;
2026-09-12 22:14:24 +03:00
j16 .Init ( 512 , " audit14-sess01a " ) ;
2026-09-12 12:06:55 +03:00
CExperimentRunner r16 ;
2026-09-12 22:14:24 +03:00
r16 .Init ( s16 , a14e ) ;
2026-09-12 12:06:55 +03:00
CAppController a16 ;
2026-09-12 22:14:24 +03:00
a16 . Configure ( & t16 , & j16 , & r16 , & c16 ) ;
2026-09-12 12:06:55 +03:00
a16 . SetSettings ( s16 ) ;
2026-09-12 22:14:24 +03:00
const bool s16_i1 = a16 . InitRun ( a14e ) ;
const bool s16_r1 = a16 . StartRun ( a14e ) ;
const string s16_sidA = a16 . SessionId ( ) ;
//--- вторая "установка": новые объекты/часы при тех же настройках
2026-09-12 12:06:55 +03:00
CTimeSource c16b ;
c16b . UseSynthetic ( 1000000 ) ;
CRequestTracker t16b ;
2026-09-12 22:14:24 +03:00
t16b .Init ( 64 , " audit14-sess01b " ) ;
2026-09-12 12:06:55 +03:00
CEventJournal j16b ;
2026-09-12 22:14:24 +03:00
j16b .Init ( 512 , " audit14-sess01b " ) ;
2026-09-12 12:06:55 +03:00
CExperimentRunner r16b ;
2026-09-12 22:14:24 +03:00
r16b .Init ( s16 , a14e ) ;
2026-09-12 12:06:55 +03:00
CAppController a16b ;
2026-09-12 22:14:24 +03:00
a16b . Configure ( & t16b , & j16b , & r16b , & c16b ) ;
2026-09-12 12:06:55 +03:00
a16b . SetSettings ( s16 ) ;
2026-09-12 22:14:24 +03:00
const bool s16_i2 = a16b . InitRun ( a14e ) ;
const bool s16_r2 = a16b . StartRun ( a14e ) ;
const string s16_sidB = a16b . SessionId ( ) ;
const bool s16_diff = ( StringLen ( s16_sidA ) > 0 & & StringLen ( s16_sidB ) > 0 & &
s16_sidA ! = s16_sidB ) ;
const bool s16_keep = FileIsExist ( " RequestLatencyLab \\ tests \\ " + s16_sidA +
" \\ intents.csv " ) ;
const bool s16_ok = ( s16_i1 & & s16_r1 & & s16_i2 & & s16_r2 & & s16_diff & &
s16_keep ) ;
AddCheck ( " SESS-01 " , " reattach-unique-session " , " WARNING " ,
2026-09-12 12:06:55 +03:00
" переустановка EA с теми же часами/настройками даёт НОВЫЙ session_id и не перезаписывает каталог прежней сессии (R14-B1) " ,
StringFormat ( " i1=%s r1=%s sidA=%s i2=%s r2=%s diff=%s keep=%s " ,
2026-09-12 22:14:24 +03:00
( s16_i1 ? " 1 " : " 0 " ) , ( s16_r1 ? " 1 " : " 0 " ) , s16_sidA ,
( s16_i2 ? " 1 " : " 0 " ) , ( s16_r2 ? " 1 " : " 0 " ) ,
( s16_diff ? " 1 " : " 0 " ) , ( s16_keep ? " 1 " : " 0 " ) ) ,
s16_ok , " audit-14-R14-B1 " ) ;
//--- SESS-02 (R14-B1): повторный запуск ПОСЛЕ отказа первого INTENT
//--- (частичная сессия, часы не двигались) получает НОВЫЙ session_id —
//--- каталог неполной сессии не маскируется следующей сессией.
2026-09-12 12:06:55 +03:00
CTimeSource c17 ;
c17 . UseSynthetic ( 1000000 ) ;
CRequestTracker t17 ;
2026-09-12 22:14:24 +03:00
t17 .Init ( 64 , " audit14-sess02 " ) ;
2026-09-12 12:06:55 +03:00
CEventJournal j17 ;
2026-09-12 22:14:24 +03:00
j17 .Init ( 512 , " audit14-sess02 " ) ;
2026-09-12 12:06:55 +03:00
CExperimentRunner r17 ;
2026-09-12 22:14:24 +03:00
r17 .Init ( s16 , a14e ) ;
2026-09-12 12:06:55 +03:00
CAppController a17 ;
2026-09-12 22:14:24 +03:00
a17 . Configure ( & t17 , & j17 , & r17 , & c17 ) ;
2026-09-12 12:06:55 +03:00
a17 . SetSettings ( s16 ) ;
2026-09-12 22:14:24 +03:00
const bool s17_i = a17 . InitRun ( a14e ) ;
2026-09-12 12:06:55 +03:00
a17 . TestSetFailIntentAfter ( 1 ) ; // отказ ПЕРВОГО INTENT
2026-09-12 22:14:24 +03:00
const bool s17_r1 = a17 . StartRun ( a14e ) ; // частичная сессия FINISHED
const string s17_sidX = a17 . SessionId ( ) ;
const bool s17_f1 = ( a17 . State ( ) = = LAB_STATE_FINISHED ) ;
const bool s17_rr = a17 . ResetForRestart ( ) ;
2026-09-12 12:06:55 +03:00
a17 . TestSetFailIntentAfter ( 0 ) ; // отказ снят
2026-09-12 22:14:24 +03:00
const bool s17_r2 = a17 . StartRun ( a14e ) ; // полная сессия
const string s17_sidY = a17 . SessionId ( ) ;
const bool s17_f2 = ( a17 . State ( ) = = LAB_STATE_FINISHED ) ;
const bool s17_diff = ( StringLen ( s17_sidX ) > 0 & & StringLen ( s17_sidY ) > 0 & &
s17_sidX ! = s17_sidY ) ;
const bool s17_ok = ( s17_i & & s17_r1 & & s17_f1 & & s17_rr & & s17_r2 & &
s17_f2 & & s17_diff ) ;
AddCheck ( " SESS-02 " , " retry-after-fail-unique-session " , " WARNING " ,
2026-09-12 12:06:55 +03:00
" повторный запуск после отказа первого INTENT получает НОВЫЙ session_id - неполная сессия не маскируется (R14-B1) " ,
StringFormat ( " i=%s r1=%s sidX=%s f1=%s rr=%s r2=%s sidY=%s f2=%s diff=%s " ,
2026-09-12 22:14:24 +03:00
( s17_i ? " 1 " : " 0 " ) , ( s17_r1 ? " 1 " : " 0 " ) , s17_sidX ,
( s17_f1 ? " 1 " : " 0 " ) , ( s17_rr ? " 1 " : " 0 " ) , ( s17_r2 ? " 1 " : " 0 " ) ,
s17_sidY , ( s17_f2 ? " 1 " : " 0 " ) , ( s17_diff ? " 1 " : " 0 " ) ) ,
s17_ok , " audit-14-R14-B1 " ) ;
//--- WIN-02 (R14-S1): признак открытого измерительного окна учитывает и
//--- активный служебный cleanup — не только основную последовательность
//--- (иначе SyncPanelPhase перерисовывает график внутри окна cleanup).
2026-09-12 12:06:55 +03:00
CTimeSource c18 ;
c18 . UseSynthetic ( 1000000 ) ;
CRequestTracker t18 ;
2026-09-12 22:14:24 +03:00
t18 .Init ( 64 , " audit14-win02 " ) ;
2026-09-12 12:06:55 +03:00
CEventJournal j18 ;
2026-09-12 22:14:24 +03:00
j18 .Init ( 512 , " audit14-win02 " ) ;
2026-09-12 12:06:55 +03:00
CExperimentRunner r18 ;
2026-09-12 22:14:24 +03:00
r18 .Init ( s16 , a14e ) ;
2026-09-12 12:06:55 +03:00
CAppController a18 ;
2026-09-12 22:14:24 +03:00
a18 . Configure ( & t18 , & j18 , & r18 , & c18 ) ;
2026-09-12 12:06:55 +03:00
a18 . SetSettings ( s16 ) ;
2026-09-12 22:14:24 +03:00
const bool w18_i = a18 . InitRun ( a14e ) ;
const bool w18_idle = ! a18 . InMeasurementWindow ( ) ; // READY - окна нет
2026-09-12 12:06:55 +03:00
a18 . TestSetCleanupWindow ( true ) ;
2026-09-12 22:14:24 +03:00
const bool w18_cleanup = a18 . InMeasurementWindow ( ) ; // cleanup - окно открыто
2026-09-12 12:06:55 +03:00
a18 . TestSetCleanupWindow ( false ) ;
2026-09-12 22:14:24 +03:00
const bool w18_closed = ! a18 . InMeasurementWindow ( ) ;
const bool w18_ok = ( w18_i & & w18_idle & & w18_cleanup & & w18_closed ) ;
AddCheck ( " WIN-02 " , " measurement-window-includes-cleanup " , " WARNING " ,
2026-09-12 12:06:55 +03:00
" InMeasurementWindow учитывает и активный cleanup - принудительная перерисовка не выполняется внутри окна (R14-S1) " ,
StringFormat ( " i=%s idle=%s cleanup=%s closed=%s " ,
2026-09-12 22:14:24 +03:00
( w18_i ? " 1 " : " 0 " ) , ( w18_idle ? " 1 " : " 0 " ) ,
( w18_cleanup ? " 1 " : " 0 " ) , ( w18_closed ? " 1 " : " 0 " ) ) ,
w18_ok , " audit-14-R14-S1 " ) ;
2026-09-12 12:06:55 +03:00
}
2026-09-12 16:38:37 +03:00
//+------------------------------------------------------------------+
2026-09-12 22:14:24 +03:00
//| Регрессии этапа 15 (R15) |
2026-09-12 16:38:37 +03:00
//+------------------------------------------------------------------+
2026-09-12 22:14:24 +03:00
void TestAudit14Regressions ( void )
2026-09-12 16:38:37 +03:00
{
2026-09-12 22:14:24 +03:00
//--- SESS-03 (R15-B2): каталог с ЧАСТИЧНЫМИ остатками прерванного
//--- экспорта (только samples.csv/events.csv, БЕЗ intents/manifest/marker)
//--- не выдаётся новой сессии — прежняя пара побайтово неизменна.
2026-09-12 16:38:37 +03:00
LabSettings s20 ;
s20 . Zero ( ) ;
2026-09-12 22:14:24 +03:00
s20 . campaign_id = " audit15 " ;
s20 . experiment_id = LAB_EXP_E1 ;
s20 . series_id = 1 ;
s20 . symbol = " X " ;
s20 . magic = 1 ;
s20 . run_mode = LAB_RUN_LOCAL_ONLY ;
s20 . count_per_condition = 10 ;
s20 . warmup_per_condition = 0 ;
s20 . pause_ms = 1 ;
s20 . sample_capacity = 64 ;
s20 . event_capacity = 1024 ;
//--- первая установка: InitRun только назначает sid (StartRun не вызываем)
2026-09-12 16:38:37 +03:00
LabError s20e ;
CTimeSource c20 ;
c20 . UseSynthetic ( 1000000 ) ;
CRequestTracker t20 ;
2026-09-12 22:14:24 +03:00
t20 .Init ( 64 , " audit15-sess03 " ) ;
2026-09-12 16:38:37 +03:00
CEventJournal j20 ;
2026-09-12 22:14:24 +03:00
j20 .Init ( 512 , " audit15-sess03 " ) ;
2026-09-12 16:38:37 +03:00
CExperimentRunner r20 ;
2026-09-12 22:14:24 +03:00
r20 .Init ( s20 , s20e ) ;
2026-09-12 16:38:37 +03:00
CAppController a20 ;
2026-09-12 22:14:24 +03:00
a20 . Configure ( & t20 , & j20 , & r20 , & c20 ) ;
2026-09-12 16:38:37 +03:00
a20 . SetSettings ( s20 ) ;
2026-09-12 22:14:24 +03:00
const bool s20_i = a20 . InitRun ( s20e ) ;
const string s20_sidA = a20 . SessionId ( ) ;
//--- имитация прерванного экспорта: в каталоге sidA ТОЛЬКО пара
const string s20_dir = " RequestLatencyLab \\ audit15 \\ " + s20_sidA ;
2026-09-12 16:38:37 +03:00
FolderCreate ( s20_dir ) ;
2026-09-12 22:14:24 +03:00
const string s20_payload = " KEEP_SAMPLES \r \n " ;
const bool s20_mk = ( CCsv : : SaveUtf8 ( s20_dir + " \\ samples.csv " , s20_payload , s20e ) & &
CCsv : : SaveUtf8 ( s20_dir + " \\ events.csv " , " KEEP_EVENTS \r \n " , s20e ) & &
! FileIsExist ( s20_dir + " \\ intents.csv " ) & &
! FileIsExist ( s20_dir + " \\ manifest.csv " ) & &
! FileIsExist ( s20_dir + " \\ checkpoint.marker " ) ) ;
//--- повторное подключение с теми же часами — sidB должен отличаться
2026-09-12 16:38:37 +03:00
CTimeSource c20b ;
c20b . UseSynthetic ( 1000000 ) ;
CRequestTracker t20b ;
2026-09-12 22:14:24 +03:00
t20b .Init ( 64 , " audit15-sess03b " ) ;
2026-09-12 16:38:37 +03:00
CEventJournal j20b ;
2026-09-12 22:14:24 +03:00
j20b .Init ( 512 , " audit15-sess03b " ) ;
2026-09-12 16:38:37 +03:00
CExperimentRunner r20b ;
2026-09-12 22:14:24 +03:00
r20b .Init ( s20 , s20e ) ;
2026-09-12 16:38:37 +03:00
CAppController a20b ;
2026-09-12 22:14:24 +03:00
a20b . Configure ( & t20b , & j20b , & r20b , & c20b ) ;
2026-09-12 16:38:37 +03:00
a20b . SetSettings ( s20 ) ;
2026-09-12 22:14:24 +03:00
const bool s20_i2 = a20b . InitRun ( s20e ) ;
const string s20_sidB = a20b . SessionId ( ) ;
const bool s20_diff = ( StringLen ( s20_sidB ) > 0 & & s20_sidA ! = s20_sidB ) ;
//--- прежняя пара побайтово неизменна
2026-09-12 16:38:37 +03:00
string s20_back ;
LabError s20r ;
2026-09-12 22:14:24 +03:00
const bool s20_load = CCsv : : LoadUtf8 ( s20_dir + " \\ samples.csv " , s20_back , s20r ) ;
const bool s20_keep = ( s20_load & & s20_back = = s20_payload ) ;
const bool s20_ok = ( s20_i & & s20_mk & & s20_i2 & & s20_diff & & s20_keep ) ;
AddCheck ( " SESS-03 " , " partial-dir-not-reused " , " WARNING " ,
2026-09-12 16:38:37 +03:00
" каталог с частичными остатками прерванного экспорта (только samples/events) не выдаётся новой сессии; прежняя пара побайтово неизменна (R15-B2) " ,
StringFormat ( " i=%s mk=%s i2=%s sidA=%s sidB=%s diff=%s load=%s keep=%s " ,
2026-09-12 22:14:24 +03:00
( s20_i ? " 1 " : " 0 " ) , ( s20_mk ? " 1 " : " 0 " ) , ( s20_i2 ? " 1 " : " 0 " ) ,
s20_sidA , s20_sidB , ( s20_diff ? " 1 " : " 0 " ) ,
( s20_load ? " 1 " : " 0 " ) , ( s20_keep ? " 1 " : " 0 " ) ) ,
s20_ok , " audit-15-R15-B2 " ) ;
2026-09-12 16:38:37 +03:00
}
2026-09-12 22:14:24 +03:00
//+------------------------------------------------------------------+
//| Старт тестов |
//+------------------------------------------------------------------+
void OnStart ( void )
2026-09-09 01:19:31 +03:00
{
TestDataStructures ( ) ;
TestStatistics ( ) ;
TestParkMiller ( ) ;
TestCsv ( ) ;
TestMarket ( ) ;
TestTrackerBasics ( ) ;
2026-09-10 16:43:28 +03:00
TestAuditRegressions ( ) ;
2026-09-10 22:54:26 +03:00
TestAudit4Regressions ( ) ;
2026-09-11 04:09:32 +03:00
TestAudit5Regressions ( ) ;
2026-09-11 09:06:39 +03:00
TestAudit6Regressions ( ) ;
2026-09-11 15:17:38 +03:00
TestAudit7Regressions ( ) ;
2026-09-11 18:16:32 +03:00
TestAudit8Regressions ( ) ;
2026-09-11 21:20:36 +03:00
TestAudit9Regressions ( ) ;
2026-09-12 02:21:14 +03:00
TestAudit10Regressions ( ) ;
2026-09-12 06:20:31 +03:00
TestAudit11Regressions ( ) ;
2026-09-12 11:19:45 +03:00
TestAudit12Regressions ( ) ;
2026-09-12 12:06:55 +03:00
TestAudit13Regressions ( ) ;
2026-09-12 16:38:37 +03:00
TestAudit14Regressions ( ) ;
2026-09-12 22:14:24 +03:00
//--- сохранение checks.csv
2026-09-10 16:43:28 +03:00
LabError err ;
2026-09-12 22:14:24 +03:00
const string path = " RequestLatencyLab \\ tests \\ checks.csv " ;
const string out = CCsvReport : : BuildChecks ( ExtChecks , ExtCheckCount ) ;
if ( CCsv : : SaveUtf8 ( path , out , err ) & &
CCsv : : ReadBackVerifyFile ( path , ExtCheckCount + 1 , err ) )
2026-09-09 01:19:31 +03:00
Print ( StringFormat ( " RequestLatencyTests: checks.csv сохранён (%d проверок) " ,
2026-09-10 16:43:28 +03:00
ExtCheckCount ) ) ;
2026-09-09 01:19:31 +03:00
else
2026-09-12 22:14:24 +03:00
Print ( " RequestLatencyTests: SAVE FAILED " + err . message ) ;
2026-09-09 01:19:31 +03:00
PrintFormat ( " RequestLatencyTests: PASS=%d FAIL=%d TOTAL=%d " ,
2026-09-12 22:14:24 +03:00
ExtPassCount , ExtFailCount , ExtCheckCount ) ;
if ( ExtFailCount > 0 )
2026-09-09 01:19:31 +03:00
Print ( " RequestLatencyTests: есть FAIL — блокирующие проверки см. checks.csv " ) ;
2026-09-12 22:14:24 +03:00
}
//+------------------------------------------------------------------+