бэктест: подписи окна приведены к замеру, уверенность — к тому же прогону
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 = [
|
const caveats = [
|
||||||
...BACKTEST.priceError.caveats,
|
...BACKTEST.priceError.caveats,
|
||||||
...BACKTEST.coverage.caveats,
|
...BACKTEST.coverage.caveats,
|
||||||
|
...BACKTEST.confidenceLow.caveats,
|
||||||
];
|
];
|
||||||
|
|
||||||
const deals = showcase?.deals ?? [];
|
const deals = showcase?.deals ?? [];
|
||||||
|
|
|
||||||
|
|
@ -97,14 +97,20 @@ export const BACKTEST: Readonly<Record<"priceError" | "coverage" | "confidenceLo
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
confidenceLow: {
|
confidenceLow: {
|
||||||
text: "325 из 327",
|
// Тот же прогон, что и остальные числа блока: пересборка «mera» от
|
||||||
sampleN: 327,
|
// 31.08.2026. Раньше здесь стояло «325 из 327» из прогона 29.08 — под
|
||||||
|
// общей подписью «замер 31.08» это приписывало старому числу новую дату.
|
||||||
|
// Знаменатель 400 — это ЗАПРОШЕННЫЕ сделки, а не 325 сматчившихся:
|
||||||
|
// уверенность считается на каждой оценке, включая те, что не дошли до
|
||||||
|
// сверки с ценой ДКП.
|
||||||
|
text: "400 из 400",
|
||||||
|
sampleN: 400,
|
||||||
source: BACKTEST_CONFIDENCE_SOURCE,
|
source: BACKTEST_CONFIDENCE_SOURCE,
|
||||||
caveats: [
|
caveats: [
|
||||||
"Оценки «уверенность высокая» в выборке не встретилось ни разу.",
|
"«Уверенность высокая» за три пересборки по 400 оценок встретилась один " +
|
||||||
"Число — из прогона 29.08.2026 по 327 кластеризованным сделкам; на " +
|
"раз, «средняя» — 0, 6 и 9 раз. Это не скромность: порог требует " +
|
||||||
"представительных пересборках 31.08.2026 уверенность не пересчитывалась, " +
|
"разброса цен среди сопоставимых объявлений не выше 24 %, а на замере " +
|
||||||
"поэтому знаменатель здесь свой, а не 325 головных величин.",
|
"31.08.2026 по 60 случайным адресам его медиана — 29 %.",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
@ -131,37 +137,47 @@ export const BACKTEST_CORRIDOR_LABEL = "±37 %";
|
||||||
* 01.04.2026). Поэтому «квартал» — предельная точность, которую данные вообще
|
* 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 разносят
|
* `deal_date` по сделкам выборки. Посчитано, и ответ поменял подпись.
|
||||||
* выборку по адресам, и квартальный состав их 325 сделок НИКТО не смотрел —
|
|
||||||
* если разнесение идёт по всему окну `--since`, в выборку попадают и другие
|
|
||||||
* три метки квартала, а тогда и знаменатель `BACKTEST_POPULATION` (5 954 —
|
|
||||||
* только II квартал) назван не тот.
|
|
||||||
*
|
*
|
||||||
* КРИТЕРИЙ СНЯТИЯ, чтобы это не осталось вечной оговоркой: одним запросом
|
* Прогон 29.08 (`ORDER BY id DESC`) брал последние ЗАГРУЖЕННЫЕ строки, и у
|
||||||
* посчитать `deal_date` по сделкам представительной выборки. Один квартал —
|
* всех 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 сделок» не
|
* Сколько сделок было В ТОМ ЖЕ окне — чтобы «мы сверили 325 сделок» не
|
||||||
* читалось как «столько их и было». Замер 30.08.2026 на проде: годных под те
|
* читалось как «столько их и было».
|
||||||
* же фильтры выборки (`source='rosreestr'`, geom, ppm² 30 000..600 000, rooms,
|
|
||||||
* area) сделок Екатеринбурга с `deal_date = 2026-04-01` — 5 954.
|
|
||||||
*
|
*
|
||||||
* ЗНАМЕНАТЕЛЬ БЕРЁТСЯ ИЗ ТОГО ЖЕ ОКНА, что и выборка. Соблазн подставить сюда
|
* Замер 31.08.2026 на проде, ровно те же фильтры, что у выборки бэктеста
|
||||||
* 24 333 (все сделки ЕКБ с июня 2025) даёт долю красивее — 1,3 % вместо
|
* (`source='rosreestr'`, geom, ppm² 30 000..600 000, rooms, area > 0,
|
||||||
* 5,5 %, — но это ровно та же ошибка, что чинится выше: доля от окна, которое
|
* `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` не может.
|
* Доля выборки. ВЫВОДИТСЯ, а не вписывается: разъехаться с `sampleN` не может.
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue