mql5-execution-microstructu.../CSV_SCHEMA.md

221 lines
21 KiB
Markdown

# 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`),
`<calibration_id>_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=<N>`; старый 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 (без обращения к текущему счёту).