# CSV-контракт RequestLatencyLab **Статус: черновик. Не утверждено. Без номера версии.** Приложение к `Docs/RequestLatencyLab_TZ_RU.md` (раздел 8). Имена файлов и формат — проектные предложения спецификации `RequestLatencyLab_Specification_RU.md`. Содержательно: один файл отчёта — одна ответственность; таблицы задают полный порядок колонок; поля LatencySample сохраняются под исходными именами. ## Общие правила - UTF-8 без BOM при записи; один BOM допускается при чтении (реализация `CsvStorage.mqh`: запись — UTF-8 без BOM, чтение допускает BOM). - Разделитель — запятая `,`; десятичная точка; окончания строк CRLF. - Поля с запятой/кавычкой/переводом строки заключаются в кавычки; кавычка удваивается. - Содержательная пустая строка пишется как `""`; отсутствующее значение — пустое поле. Парсер сохраняет это различие (`CCsv::ParseRow`). - uint32/uint64/int32/int64 — точные десятичные целые без преобразования через double. - Bool — 0/1. Double — конечное число, не менее 16 значащих цифр для вычисляемых показателей; NaN/Inf не допускаются. - Неизвестное значение не подменяется нулём; причина — код статуса/presence. - JSON-ячейки экранируются целиком как CSV-ячейка. ## Основные файлы ### manifest.csv Паспорт запуска. Ключи (полный реестр): `project_name, document_status, campaign_id, session_id, experiment_id, series_id, configuration_id, protocol_fingerprint, schema_fingerprint, schema_version, application_identifier, source_commit, source_dirty, source_package_sha256, dependencies_lock_sha256, schedule_sha256, data_origin, terminal_build, compiler_build, server_name, account_alias, account_margin_mode, symbol, point_size, price_tick_size, price_digits, volume_min, volume_max, volume_step, market_filling, pending_filling, deviation_points, pending_distance_ticks, magic, count_per_condition, warmup_per_condition, outcome_timeout_ms, late_grace_ms, pause_ms, timer_requested_ms, feature_window_sec, feature_max_age_ms, quote_max_age_ms, calibration_id, collection_windows, calendar_time_source, calendar_time_scale, start_calendar, end_calendar, generator_name, seed, cleanup_mode, cleanup_logging, cleanup_max_attempts, sample_capacity, event_capacity, error_reserve_capacity, volume_units_tolerance, address_read_budget, history_scan_budget, run_status, stop_reason, n_main_dispatched, n_warmup_dispatched, n_cleanup_dispatched, n_pilot_dispatched, n_dispatch_uncertain, records_attempted, records_accepted, records_persisted, records_lost, buffer_high_watermark, outstanding_objects, last_checkpoint_event_sequence, ping_before_run_us, ping_after_run_us, input_files, output_files`. Реализация: `CCsvReport::BuildManifest` (подмножество реестра текущей версии). ### schedule.csv `session_id,row_id,row_kind,slot_id,experiment_id,series_id,condition_id,block_id,role, operation,side,mode,logging_mode,planned_order,sequence,parent_sequence,status, reason_code`. ### samples.csv **Первые 10 полей — LatencySample в исходном порядке**: `request_id, sequence, send_start_us, send_return_us, request_event_us, order_event_us, first_deal_us, final_event_us, retcode, completed`. Далее: `campaign_id, experiment_id, series_id, session_id, configuration_id, condition_id, slot_id, parent_sequence, cleanup_attempt, role, is_warmup, operation, mode, logging_mode, symbol, side, correlation_status, correlation_method, order_ticket, position_ticket, position_identifier, last_deal_us, deal_count, executed_volume, callback_volume, history_volume, deal_coverage_complete, present_mask, recovered, conflict, measurement_interrupted, scenario_deviation, outcome, outcome_at_final, final_source, trading_state_known, confirmation_invalidated, confirmation_record_id, current_confirmation_record_id, observation_deadline_us, collection_deadline_us, collection_close_us, collection_closed, deadline_exceeded, market_regime, calibration_id, bid_at_send, ask_at_send, spread_at_send, tick_time_msc, actual_type, actual_filling, deviation_points, comment, request_price, callback_coverage_complete, market_window_id, market_window_seq`. Аудит-4 (R4-B8): отсутствующее значение timestamps/retcode/объёмов пишется ПУСТЫМ полем (а не 0); `actual_type/actual_filling/deviation_points/comment/request_price` фиксируют фактический контракт запроса (для cleanup — контракт закрытия/удаления). Аудит-5 (R5-S2): `actual_type`/`actual_filling` — допустимые нулевые коды (`ORDER_TYPE_BUY=0`, `ORDER_FILLING_FOK=0`) записываются как `0`; пустое поле означает, что фактический контракт не зафиксирован (`actual_known=false`). `configuration_id` всегда заполняется после Preflight и попадает в manifest/samples. Аудит-8 (R8-S2): формат расширен до 68 колонок — `market_window_id`/`market_window_seq` (связь строки с исходным окном E3, соответствие `market_windows.csv` по `sequence`; `market_window_seq=0`/пустой id — запись без E3-контекста или до режима). Аудит-5 (R5-B4): `callback_coverage_complete` — отдельный признак полноты ЛОКАЛЬНЫХ callback-меток сделок (1 = все сделки ордера наблюдались через DEAL_ADD; сделка, найденная только в истории, не даёт полного T5). `deal_coverage_complete` — признак торгового результата (сумма объёмов равна запрошенному). T5-метрика использует `callback_coverage_complete`. ### events.csv Append-only: `session_id,event_sequence,record_kind,local_time_us,origin_sequence, target_sequence,ref_event_sequence,transaction_type,request_id,order_ticket,deal_ticket, position_ticket,handler_enter_us,handler_exit_us,payload_json,quality_code`. ### deals.csv `session_id,account_alias,deal_ticket,order_ticket,owner_sequence,symbol,deal_type,entry, reason,magic,position_ticket,position_identifier,callback_first_us,callback_event_id, has_callback,callback_volume,history_volume,history_price,deal_time_msc, history_read_event_id,recovered,correction_count,deleted,quality_codes`. ### summary.csv `experiment_id,condition_id,series_id,metric_id,outcome_group,population,observation_unit, segment_key,source_set_id,unit,n_attempted,n_applicable,n_not_applicable, n_applicability_unknown,n_valid,n_missing,n_late,n_conflict,n_interrupted,n_used,minimum, mean,median,p90,p95,p99,p999,maximum,stddev,percentile_method,method_fingerprint, p95_tail_expected_n,p99_tail_expected_n,p999_tail_expected_n,tail_warning_codes, n_deadline_exceeded,deadline_denominator,deadline_rate,n_timestamp_missing_at_deadline, missing_timestamp_rate,conditional_distribution`. ### histogram.csv `experiment_id,condition_id,series_id,metric_id,outcome_group,population,segment_key, bin_index,lower_us,upper_us,count,denominator,share`. Только неотрицательные интервалы ([0,1),[1,2),[2,4),[4,8),[8,16),[16,32),[32,64),[64,128),[128,256),[256,+∞) мс). Знаковая метрика T2−T4 в histogram НЕ включается (отдельный offset_signs.csv); в summary группа исходов `outcome_group` = ALL | SUCCESS | REJECTED (SUCCESS — базовые успешные исходы: не REJECTED и не UNKNOWN; ALL включает отказы); CLEANUP-роль и warmup из summary/histogram исключаются; статистики считаются при n_valid>=1 (min/mean/median/квантили), `stddev` при n_valid<2 остаётся пустым (выборочное n-1 неприменимо). Категория LATE в online и offline определяется по фактическому КОНЦУ метрики относительно `observation_deadline_us`, а не по флагу `deadline_exceeded` (R5-B3): ранние точки остаются VALID даже при позднем T6. ### market_windows.csv, checks.csv, experiment_manifest.csv `market_windows.csv` — сохраняемые окна E3 (не более 4096): `session_id,dataset_id,window_id,sequence,calibration_id,symbol,from_msc,to_msc, anchor_tick_msc,n_returned,n_valid_info,quote_frequency_hz,mid_range_ticks,regime,valid, quality_codes,ticks_checksum`. Аудит-8 (R8-S2): `ticks_checksum` — FNV-1a 64 по `(time_msc,bid,ask)` валидных INFO-тиков окна; контрольная сумма используется как ПРОВЕРКА при повторном чтении (`CopyTicksRange(from_msc,to_msc)` того же диапазона), а НЕ как источник данных. Аудит-9 (R9-S3): сырые тики окна удерживаются В ПАМЯТИ (`ComputeWindowRawTicks`) и выгружаются в `market_ticks.csv` в safe-фазе ПОСЛЕ закрытия окна измерения и в ExportRun; аудит-10 R10-B4: файл имеет ЗАГОЛОВОК `session_id,window_id,window_seq,time_msc,bid,ask,flags,volume`, строки завершаются НАСТОЯЩИМ CRLF (не литеральным `\r\n`), bid/ask - точность `%.17g` (полный round-trip double); заголовок пишется при создании/пустом файле; для сессии файл с заголовком гарантируется (EnsureMarketTicksFile) синхронная файловая запись внутри измерительной фазы запрещена (аудит-8 R8-B3), поэтому выгрузка перенесена на post-close/export. Зарезервированы контрактом: признаки/калибровка, результаты проверок, связь пяти экспериментов (E4 — ссылки на E1, `reuse=1`, без копирования samples). ## Дополнительные файлы `comparisons.csv` (A_vs_B: n/mean/median/p95 для A и B, delta = B−A и относительное изменение (B−A)/A по каждой метрике — ТЗ §7, стр. 419; sign дельты: B медленнее A — положительная), `offset_signs.csv` (NEGATIVE/ZERO/POSITIVE для T2−T4, вне latency-гистограммы), `market_ticks.csv` `market_ticks.csv` (аудит-9 R9-S3: сырые тики окон E3, append-only; строки: `session_id,window_id,window_seq,time_msc,bid,ask,flags,volume`; выгрузка из памяти в safe-фазе после закрытия окна и в ExportRun; порядок одинаковых миллисекунд (R10-B4: заголовок 8 имён, реальный CRLF, %.17g) сохраняется; сбой выгрузки — fail-closed, новые отправки блокируются), `calibration.csv` (реестр порогов; `trading_session_count` = число отдельных календарных дат валидных окон, генератор считает реальные сессии и loader требует >=5; аудит-5: генератор НЕ сохраняет набор с <5 сессиями или <1000 окон — явный INSUFFICIENT_DATA; добавлены ключи `period_from_msc`/`period_to_msc`), `_windows.csv` (исходные окна калибровки: `minute_start_msc,quote_frequency_hz,mid_range_ticks` — для независимой проверки отсутствия пересечения калибровки и эксперимента и пересчёта порогов), `summary_rebuilt.csv`/`histogram_rebuilt.csv`/`offset_signs_rebuilt.csv`/ `comparisons_rebuilt.csv` (offline-пересчёт: `summary_rebuilt.csv` использует ЕДИНУЮ 41-колоночную схему онлайн-summary; число строк контрольного чтения — от реально созданной таблицы с уникальными ключами; повреждённый/усечённый samples.csv отвергается, а не пересчитывается молча; группы ALL|SUCCESS|REJECTED, категории LATE по концу метрики, T5 по `callback_coverage_complete`), `intents.csv` (устойчивый журнал намерений: строки `session_id,sequence,kind, local_time_us`; `INTENT` пишется ДО T0; INTENT без SEND_RETURN после сбоя/перезапуска учитывается как `n_dispatch_uncertain` в manifest — восстановление неопределённых попыток. Аудит-7 R7-S1/B1: запись — ровно n-1 полезный байт с начала без терминального NUL (`FileWriteArray(handle,array, start,count)`); cleanup-отправки пишутся в тот же журнал; синхронная запись SEND_RETURN перенесена ВНЕ измеряемой фазы T0..T1; при нечитаемости/повреждении журнала `n_dispatch_uncertain` принимает sentinel ULONG_MAX (неопределённость не сужается до нуля; ветвь недоступности различима и НЕ преобразуется в обычный счётчик), повреждённая строка — +1 неопределённость. Аудит-10 R10-B2: INTENT_CANCELLED снимает неопределённость РОВНО с одной попытки того же sequence (хронологическая пара): повторный INTENT после отмены без SEND_RETURN остаётся неопределённым. Аудит-8 R8-B3: `SEND_RETURN` НЕ дописывается синхронно между T1 и обработкой callbacks (один поток исполнения советника): факт возврата буферизуется в памяти и дописывается в safe-фазе после закрытия окна (`FlushPendingSendReturns`); при аварии до этого попытка честно остаётся DISPATCH_UNCERTAIN. Аудит-8 R8-B4: подтверждённые ключи (sequence) набираются динамически без предела 256 — полностью парный запуск 5x100+очистки даёт 0 неопределённых, а не ложные `INTENT`-без-возврата; короткое чтение `FileReadArray != FileSize` также даёт sentinel), `checkpoint.marker` (маркер согласованности завершённого checkpoint-набора samples/events; пишется последним и содержит ПОКОЛЕНИЕ экспорта `gen=`; старый marker удаляется ДО перезаписи — при сбое между фазами остаётся отсутствующий marker, а не устаревший «успех»; checkpoint выполняется только в безопасной фазе `CanCheckpoint` и его ошибка блокирует новые отправки) — `summary_rebuilt_e4.csv` (E4: pooled статистика из 500 первичных ASYNC-записей ПЯТИ РАЗНЫХ завершённых E1-серий + per-series строки со ссылками на исходные session_id; без копирования samples и без усреднения серийных медиан; аудит-5: проверяются 5 разных каталогов, уникальные session_id, ровно `expected_main` MAIN B-ASYNC записей на серию (по умолчанию 100), единый символ серий. Аудит-6: manifest.csv источника ОБЯЗАТЕЛЕН (отсутствие = отказ), допускается только `run_status=FINISHED` с `n_main_dispatched>=expected_main`; нормализованная конфигурация (`configuration_id`) и символ совпадают МЕЖДУ всеми пятью сериями; каждая строка принадлежит сессии серии; последовательности (sequence) уникальны внутри файла; pooled/per-series статистика считаются той же метрической семантикой валидности, что и остальные отчёты (R6-B2/B7). Аудит-7 (R7-B6): обычный E4 принимает только допустимое происхождение `data_origin` (по умолчанию `DEMO`; `SYNTHETIC`-серии отвергаются code 41 вне отдельного синтетического режима); `configuration_id` СТРОК сверяется с manifest серии (code 42); столбец `data_origin` присутствует во всех строках; добавлены показатели полноты `n_attempted, n_applicable, n_not_applicable, n_applicability_unknown, n_missing, n_late, n_conflict, n_interrupted, n_deadline_exceeded, deadline_denominator, deadline_rate` по сериям и pooled (числа нельзя вывести из одного n после фильтрации VALID). ## Порядок и контроль - Порядок строк — по первичному ключу; дубликат ключа — ошибка. - Чтение samples.csv (rebuild-путь, аудит-6 R6-S1, аудит-7 R7-B4, аудит-8 R8-S1): полная проверка заголовка (все 68 имён по порядку, перестановка колонок — отказ), числовая валидация полей времени/масок (нечисловой токен — отказ), дубликаты sequence — отказ. Деление файла на ЛОГИЧЕСКИЕ записи CSV: перевод строки внутри кавычек — данные, а не разделитель; терминатор \r записи снимается один раз (CR внутри данных не вырезается); пустые числовые ячейки сверяются с `present_mask` (бит есть, ячейка пуста — отказ; бита нет, значение есть — отказ), т.е. несогласованный файл не заменяется нулями и не пропускается. Ёмкость `capacity` — число НАБЛЮДЕНИЙ; заголовок её НЕ расходует (головка + ровно capacity строк допустимы). Контрольное чтение (ReadBackVerifyFile) считает те же ЛОГИЧЕСКИЕ записи, что и reader (встроенный перевод строки в кавычках — данные), поэтому валидный multiline-экспорт не отвергается собственной проверкой. - Единая семантика online/offline (R6-B2): offline-путь восстанавливает запись в RequestMetadata и считает категории/применимость/срок той же статической `CRequestTracker::EvaluateMetric`; WARMUP-роль исключается одинаково во всех путях; deadline-счётчики summary совпадают с онлайн-экспортом. - Хеши наборов — SHA-256 по отсортированному списку путей (реализация — `CryptEncode(CRYPT_HASH_SHA256)` в контрольных точках). - Генерационное время не участвует в числовом сравнении; пересчёт одних исходных байтов даёт точные целые поля и double в допуске max(1e-9, 1e-12*|x|). - Повторный расчёт — только из архивных CSV (без обращения к текущему счёту).