бэктест: подписи окна приведены к замеру, уверенность — к тому же прогону
All checks were successful
CI Trade-In / changes (pull_request) Successful in 12s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 14s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Successful in 1m21s
All checks were successful
CI Trade-In / changes (pull_request) Successful in 12s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 14s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Successful in 1m21s
Снимает три блокера ревью на этой же ветке.
1. ПОДПИСЬ ОКНА. Числа перевели на представительную выборку, а подпись
осталась от прежнего прогона: «сделки II квартала 2026 года». Автор честно
записал, что квартальный состав пересборки не смотрел, и оставил критерий
снятия — посчитать deal_date по выборке. Посчитал:
III кв 2025 — 106 (26,5 %) I кв 2026 — 88 (22,0 %)
IV кв 2025 — 110 (27,5 %) II кв 2026 — 96 (24,0 %)
Пересборка покрывает ЧЕТЫРЕ квартала, II квартал 2026 — четверть её. Прежняя
подпись для этих чисел была бы прямым враньём.
2. ЗНАМЕНАТЕЛЬ. Был 5 954 — сделки только II квартала. Для выборки по четырём
кварталам это завышало долю вчетверо: 5,5 % вместо 1,3 %. Замер тем же
фильтром, что у выборки: в окне 24 333, из них II квартал 2026 — 5 954. Оба
числа одним запросом.
3. УВЕРЕННОСТЬ. Плитка «325 из 327» была из прогона 29.08, а лид секции печатал
«замер 31.08» — старому числу приписывалась новая дата, и 325 в лиде совпадало
с 325 в плитке, читаясь как одна выборка. Заменено на «400 из 400» из ТОЙ ЖЕ
пересборки. Знаменатель 400 — запрошенные сделки, а не 325 сматчившихся:
уверенность считается на каждой оценке.
4. Оговорка уверенности не выводилась вовсе: AccuracyV3 собирал оговорки двух
величин из трёх. Плитка стояла без единого слова о том, почему уверенность
низкая. Добавлена третья.
Тесты проверены фальсификацией: возврат знаменатели одного квартала даёт
«expected 5954 to be 24333» и «expected 5.458 to be less than 2»; снятие
оговорки из вывода — «оговорка не выводится». 157 passed.
This commit is contained in:
parent
2864d80854
commit
79ae87019a
3 changed files with 117 additions and 28 deletions
|
|
@ -0,0 +1,68 @@
|
|||
/**
|
||||
* Подписи бэктеста обязаны описывать ТО окно, по которому числа посчитаны.
|
||||
*
|
||||
* 31.08.2026 числа перевели на представительную выборку (медиана трёх
|
||||
* пересборок), но подписи остались от прежнего прогона: «сделки II квартала
|
||||
* 2026 года» и знаменатель 5 954 (только этот квартал). Замер показал, что
|
||||
* пересборка покрывает ЧЕТЫРЕ квартала и II квартал 2026 — лишь 24 % её
|
||||
* сделок. Доля выборки при старом знаменателе выходила 5,5 % вместо 1,3 %,
|
||||
* то есть вчетверо льстила.
|
||||
*
|
||||
* Отдельно: плитка «уверенность низкая» показывалась БЕЗ своей оговорки —
|
||||
* `AccuracyV3` собирал оговорки только двух величин из трёх.
|
||||
*/
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { AccuracyV3 } from "../_components/v3/AccuracyV3";
|
||||
import {
|
||||
BACKTEST,
|
||||
BACKTEST_PERIOD_LABEL,
|
||||
BACKTEST_POPULATION,
|
||||
BACKTEST_SHARE_LABEL,
|
||||
} from "../landing-facts";
|
||||
|
||||
describe("подписи окна бэктеста", () => {
|
||||
it("окно названо целиком, а не одним кварталом из четырёх", () => {
|
||||
// Прежняя подпись называла II квартал 2026 — в представительной выборке
|
||||
// это 24 % сделок, а не все.
|
||||
expect(BACKTEST_PERIOD_LABEL).not.toMatch(/^сделки II квартала/u);
|
||||
expect(BACKTEST_PERIOD_LABEL).toMatch(/четыре квартала/u);
|
||||
});
|
||||
|
||||
it("знаменатель — того же окна, что и выборка", () => {
|
||||
// 5 954 — только II квартал 2026; окно целиком — 24 333 (замер 31.08.2026).
|
||||
expect(BACKTEST_POPULATION).toBe(24_333);
|
||||
});
|
||||
|
||||
it("доля выборки выводится и получается около 1,3 %, а не 5,5 %", () => {
|
||||
const share = (BACKTEST.priceError.sampleN / BACKTEST_POPULATION) * 100;
|
||||
expect(share).toBeGreaterThan(1.0);
|
||||
expect(share).toBeLessThan(2.0);
|
||||
expect(BACKTEST_SHARE_LABEL).toBe("1,3 %");
|
||||
});
|
||||
|
||||
it("уверенность посчитана на том же прогоне, что и остальные числа", () => {
|
||||
// «325 из 327» было из прогона 29.08 и под общей подписью «замер 31.08»
|
||||
// приписывало старому числу новую дату.
|
||||
expect(BACKTEST.confidenceLow.text).not.toMatch(/327/u);
|
||||
});
|
||||
});
|
||||
|
||||
describe("оговорки в интерфейсе", () => {
|
||||
it("на экране есть оговорки ВСЕХ ТРЁХ величин блока", () => {
|
||||
render(<AccuracyV3 stats={{}} showcase={null} />);
|
||||
for (const набор of [
|
||||
BACKTEST.priceError.caveats,
|
||||
BACKTEST.coverage.caveats,
|
||||
BACKTEST.confidenceLow.caveats,
|
||||
]) {
|
||||
for (const line of набор) {
|
||||
expect(
|
||||
screen.queryByText(line),
|
||||
`оговорка не выводится: ${line.slice(0, 48)}…`,
|
||||
).not.toBeNull();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
@ -114,9 +114,14 @@ export function AccuracyV3({
|
|||
});
|
||||
}
|
||||
|
||||
// Оговорки ВСЕХ ТРЁХ величин блока, а не двух. `confidenceLow` показывается
|
||||
// отдельной плиткой, но её оговорка сюда не попадала — то есть на экране
|
||||
// стояло «400 из 400 — уверенность низкая» без единого слова о том, почему.
|
||||
// Плитка без своей оговорки — ровно то, что этот блок и не должен делать.
|
||||
const caveats = [
|
||||
...BACKTEST.priceError.caveats,
|
||||
...BACKTEST.coverage.caveats,
|
||||
...BACKTEST.confidenceLow.caveats,
|
||||
];
|
||||
|
||||
const deals = showcase?.deals ?? [];
|
||||
|
|
|
|||
|
|
@ -97,14 +97,20 @@ export const BACKTEST: Readonly<Record<"priceError" | "coverage" | "confidenceLo
|
|||
],
|
||||
},
|
||||
confidenceLow: {
|
||||
text: "325 из 327",
|
||||
sampleN: 327,
|
||||
// Тот же прогон, что и остальные числа блока: пересборка «mera» от
|
||||
// 31.08.2026. Раньше здесь стояло «325 из 327» из прогона 29.08 — под
|
||||
// общей подписью «замер 31.08» это приписывало старому числу новую дату.
|
||||
// Знаменатель 400 — это ЗАПРОШЕННЫЕ сделки, а не 325 сматчившихся:
|
||||
// уверенность считается на каждой оценке, включая те, что не дошли до
|
||||
// сверки с ценой ДКП.
|
||||
text: "400 из 400",
|
||||
sampleN: 400,
|
||||
source: BACKTEST_CONFIDENCE_SOURCE,
|
||||
caveats: [
|
||||
"Оценки «уверенность высокая» в выборке не встретилось ни разу.",
|
||||
"Число — из прогона 29.08.2026 по 327 кластеризованным сделкам; на " +
|
||||
"представительных пересборках 31.08.2026 уверенность не пересчитывалась, " +
|
||||
"поэтому знаменатель здесь свой, а не 325 головных величин.",
|
||||
"«Уверенность высокая» за три пересборки по 400 оценок встретилась один " +
|
||||
"раз, «средняя» — 0, 6 и 9 раз. Это не скромность: порог требует " +
|
||||
"разброса цен среди сопоставимых объявлений не выше 24 %, а на замере " +
|
||||
"31.08.2026 по 60 случайным адресам его медиана — 29 %.",
|
||||
],
|
||||
},
|
||||
};
|
||||
|
|
@ -131,37 +137,47 @@ export const BACKTEST_CORRIDOR_LABEL = "±37 %";
|
|||
* 01.04.2026). Поэтому «квартал» — предельная точность, которую данные вообще
|
||||
* позволяют назвать; «сделки за апрель» было бы вторым враньём.
|
||||
*/
|
||||
export const BACKTEST_PERIOD_LABEL = "сделки II квартала 2026 года";
|
||||
export const BACKTEST_PERIOD_LABEL =
|
||||
"сделки за четыре квартала — с III квартала 2025 по II квартал 2026";
|
||||
|
||||
/**
|
||||
* ЧЕГО МЫ НЕ ЗНАЕМ ПРО ОКНО ПРЕДСТАВИТЕЛЬНЫХ ПЕРЕСБОРОК (записано 31.08.2026).
|
||||
* КВАРТАЛЬНЫЙ СОСТАВ ПРЕДСТАВИТЕЛЬНОЙ ВЫБОРКИ — ЗАМЕРЕН 31.08.2026.
|
||||
*
|
||||
* Подпись выше проверена на прогоне 29.08 (`ORDER BY id DESC`): там у всех
|
||||
* 327 сделок стояло `deal_date = 2026-04-01`. Пересборки 31.08 разносят
|
||||
* выборку по адресам, и квартальный состав их 325 сделок НИКТО не смотрел —
|
||||
* если разнесение идёт по всему окну `--since`, в выборку попадают и другие
|
||||
* три метки квартала, а тогда и знаменатель `BACKTEST_POPULATION` (5 954 —
|
||||
* только II квартал) назван не тот.
|
||||
* Здесь стояла оговорка «никто не смотрел» с критерием снятия: посчитать
|
||||
* `deal_date` по сделкам выборки. Посчитано, и ответ поменял подпись.
|
||||
*
|
||||
* КРИТЕРИЙ СНЯТИЯ, чтобы это не осталось вечной оговоркой: одним запросом
|
||||
* посчитать `deal_date` по сделкам представительной выборки. Один квартал —
|
||||
* подпись и знаменатель остаются как есть; больше одного — подпись назвать
|
||||
* окном целиком и взять знаменатель того же окна. До этого числа окна не
|
||||
* трогались: врать в другую сторону — та же ошибка, что чинилась здесь.
|
||||
* Прогон 29.08 (`ORDER BY id DESC`) брал последние ЗАГРУЖЕННЫЕ строки, и у
|
||||
* всех 327 сделок стояло `deal_date = 2026-04-01` — один квартал. Пересборка
|
||||
* вразброс покрывает ВСЁ окно `--since`, и её 400 сделок легли так:
|
||||
*
|
||||
* III кв 2025 — 106 (26,5 %) I кв 2026 — 88 (22,0 %)
|
||||
* IV кв 2025 — 110 (27,5 %) II кв 2026 — 96 (24,0 %)
|
||||
*
|
||||
* То есть II квартал 2026 — лишь четверть выборки. Прежняя подпись «сделки
|
||||
* II квартала 2026 года» для этих чисел была бы прямым враньём, а знаменатель
|
||||
* 5 954 (только II квартал) завышал бы долю вчетверо.
|
||||
*
|
||||
* Поэтому подпись называет окно целиком, а знаменатель взят того же окна.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Сколько сделок было В ТОМ ЖЕ окне — чтобы «мы сверили 327 сделок» не
|
||||
* читалось как «столько их и было». Замер 30.08.2026 на проде: годных под те
|
||||
* же фильтры выборки (`source='rosreestr'`, geom, ppm² 30 000..600 000, rooms,
|
||||
* area) сделок Екатеринбурга с `deal_date = 2026-04-01` — 5 954.
|
||||
* Сколько сделок было В ТОМ ЖЕ окне — чтобы «мы сверили 325 сделок» не
|
||||
* читалось как «столько их и было».
|
||||
*
|
||||
* ЗНАМЕНАТЕЛЬ БЕРЁТСЯ ИЗ ТОГО ЖЕ ОКНА, что и выборка. Соблазн подставить сюда
|
||||
* 24 333 (все сделки ЕКБ с июня 2025) даёт долю красивее — 1,3 % вместо
|
||||
* 5,5 %, — но это ровно та же ошибка, что чинится выше: доля от окна, которое
|
||||
* замер не покрывал.
|
||||
* Замер 31.08.2026 на проде, ровно те же фильтры, что у выборки бэктеста
|
||||
* (`source='rosreestr'`, geom, ppm² 30 000..600 000, rooms, area > 0,
|
||||
* `deal_date >= 2025-06-01`, город Екатеринбург): **24 333** сделки.
|
||||
*
|
||||
* ЗНАМЕНАТЕЛЬ БЕРЁТСЯ ИЗ ТОГО ЖЕ ОКНА, ЧТО И ВЫБОРКА — правило не изменилось,
|
||||
* изменилось окно. Раньше здесь стояло 5 954 (только II квартал 2026), и это
|
||||
* было верно для прогона 29.08, который в один квартал и укладывался. Для
|
||||
* пересборок вразброс, покрывающих четыре квартала, тот же знаменатель дал бы
|
||||
* долю вчетверо больше реальной — 5,5 % вместо 1,3 %.
|
||||
*
|
||||
* Обе величины проверены одним запросом: в окне 24 333, из них с
|
||||
* `deal_date = 2026-04-01` — 5 954.
|
||||
*/
|
||||
export const BACKTEST_POPULATION = 5_954;
|
||||
export const BACKTEST_POPULATION = 24_333;
|
||||
|
||||
/**
|
||||
* Доля выборки. ВЫВОДИТСЯ, а не вписывается: разъехаться с `sampleN` не может.
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue