221 lines
No EOL
21 KiB
Markdown
221 lines
No EOL
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 (без обращения к текущему счёту). |