chore: pre-existing in-tree work (event-study/research/vintages/prices + migration script + integrity docs)

Committing the prior uncommitted working-tree state that predates this session's
data-source work (was already modified/untracked at session start) so the tree
is clean before push. Includes: event-study + research report integrity/forward
observation work, prices tests, research hash migration script, and the
2026-08-23/24 engineering-log + test-evidence notes. Verified green as part of
the full 362-test suite.
This commit is contained in:
Kunthawat Greethong
2026-08-29 09:19:24 +07:00
parent dbb787c50a
commit ead9aeb25c
22 changed files with 2326 additions and 66 deletions

View File

@@ -20,7 +20,7 @@ No external webhook receiver and no live MT5 execution are enabled.
```bash
python -m venv .venv
.venv/bin/pip install -r backend/requirements.txt
PAPER_WRITE_TOKEN=local-paper-token TOURISM_SOURCE=fixture PYTHONPATH=backend .venv/bin/python backend/run.py
PAPER_AUTH_MODE=demo HOST=127.0.0.1 TOURISM_SOURCE=fixture PYTHONPATH=backend .venv/bin/python backend/run.py
```
Health check:
@@ -41,18 +41,20 @@ npm run dev -- --host 127.0.0.1
Open `http://127.0.0.1:5173`.
The frontend reads the live API through Vite's `/api` proxy. Paper writes require the operator to unlock an HttpOnly browser session using the backend `PAPER_WRITE_TOKEN`; the token is never embedded in the frontend bundle. The paper-entry action records an assumed fill in the local paper ledger only; it never sends an order.
The frontend reads the live API through Vite's `/api` proxy. The local command above uses explicit loopback-only `PAPER_AUTH_MODE=demo`: paper writes do not require a session token, and the dashboard shows a demo warning. This mode is for a single local operator only; it must not be bound to a network interface or used for live execution. For shared or network access, use `PAPER_AUTH_MODE=token`, set `PAPER_WRITE_TOKEN` in the server environment, and require the HttpOnly browser session; the token is never embedded in the frontend bundle. The paper-entry action records an assumed fill in the local paper ledger only; it never sends an order.
For HTTPS/non-local deployment, set `PAPER_COOKIE_SECURE=1`. Paper entries persist atomically under `backend/data/paper/ledger.json` by default. Sessions remain in-memory and single-process; use a shared session store before running multiple workers or replicas.
For HTTPS/non-local deployment, use protected token mode and set `PAPER_COOKIE_SECURE=1`. Paper entries persist atomically under `backend/data/paper/ledger.json` by default. Sessions remain in-memory and single-process; use a shared session store before running multiple workers or replicas.
## Run with the real BOT Tourism source
Use the BOT-backed adapter when network access is available:
```bash
PAPER_WRITE_TOKEN=local-paper-token TOURISM_SOURCE=bot PYTHONPATH=backend .venv/bin/python backend/run.py
PAPER_AUTH_MODE=demo HOST=127.0.0.1 TOURISM_SOURCE=bot PYTHONPATH=backend .venv/bin/python backend/run.py
```
For a shared/network BOT deployment, replace `PAPER_AUTH_MODE=demo` with `PAPER_AUTH_MODE=token` and provide `PAPER_WRITE_TOKEN` through the server environment. The backend rejects demo mode when `HOST`/`PAPER_BIND_HOST` is not loopback.
At startup the adapter performs a read-only GET/POST against the BOT Tourism Indicators report, parses the available monthly history, computes the latest year-over-year arrival observation against a trailing 12-point baseline, and stores the raw HTML plus normalized snapshot under `backend/data/` (ignored by git). The dashboard labels provisional BOT data as `provisional`, not `high`.
Data-health and replay endpoints:
@@ -107,10 +109,12 @@ cd frontend && npm run build
- No webhook receiver yet
- No MT5 bridge yet
The next implementation step is replacing or supplementing revised vendor history with a point-in-time daily price source, while continuing to collect independent BOT releases.
The PIT archive contract is implemented, but a real provider archive and release-time evidence are still required. Continue collecting independent BOT releases and do not promote revised vendor history by configuration alone.
The event-study gate is now exposed through `/api/v1/backtest/tourism`. It returns HTTP `409` with `status=blocked` when the independent-vintage minimum is not met, and it explicitly reports that a point-in-time daily price series is still required. The pure engine accepts events, daily prices, benchmark prices, event windows, and cost assumptions; it does not fetch or invent market prices.
The accepted PIT contract is `pit-daily-v1`. It requires explicit provider release/evidence metadata, an IANA market timezone per series, timezone-aware per-bar `session_date`/`known_at`/OHLC/adjusted-close/volume, and immutable raw/normalized/manifest hash binding. PIT event-study mode rejects missing or market-locally future-known prices. The current Yahoo collector intentionally remains `quality=revised_vendor_history` and `point_in_time=false`.
## Run a frozen research check
The research runner persists either a deterministic event-study result or a blocked report. A blocked report is still useful: it records the exact vintage IDs, hashes, price snapshot, configuration, and gate reason that prevented the study.
@@ -135,6 +139,15 @@ GET /api/v1/research/tourism/latest
The default live state intentionally returns `status=blocked`: the current archive has one independent BOT release and the Yahoo price snapshot is marked `point_in_time=false`. The runner never converts that data into a backtest by inference.
Research reports written before integrity hashes were introduced are intentionally not auto-migrated. Migrate a trusted local research store explicitly before loading it:
```bash
PYTHONPATH=backend .venv/bin/python backend/scripts/migrate_research_report_hashes.py \
--root backend/data/research
```
The migration validates report identity and manifest metadata, writes the canonical report and manifest-entry hashes, and fails closed on malformed or partially migrated entries.
## Collect daily price snapshots
The initial research price provider uses Yahoo Finance Chart API with SET ticker mappings. It stores OHLCV plus adjusted close for the eight exposure names and `^SET.BK` as `SET50`. This is **revised vendor history**, not point-in-time market data, so the snapshot is visible for data plumbing but cannot unlock the backtest gate: