# SET50 Alternative Data Platform Tourism-first vertical slice for a deterministic SET50 alternative-data research system. The app can run against a clearly-labelled fixture or fetch a point-in-time Tourism Indicators vintage from the Bank of Thailand report backed by the Ministry of Tourism and Sports. Current scope: ```text fixture or BOT source observation → Tourism Pulse surprise → versioned exposure score → ranked target weights → English dashboard → internal paper ledger ``` No external webhook receiver and no live MT5 execution are enabled. ## Run the backend ```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 ``` Health check: ```bash curl http://127.0.0.1:5000/api/v1/health ``` ## Run the dashboard In a second terminal: ```bash cd frontend npm install 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 in-memory paper ledger only. For HTTPS/non-local deployment, set `PAPER_COOKIE_SECURE=1`. The M0 session store is intentionally 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 ``` 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: ```text GET /api/v1/data-health GET /api/v1/vintages?as_of= GET /api/v1/replay/tourism?vintage_id= GET /api/v1/backtest/tourism?min_events=12 ``` Source: `https://app.bot.or.th/BTWS_STAT/statistics/ReportPage.aspx?reportID=875&language=eng` ## Collect a vintage manually The collector is intentionally one-shot and idempotent. Run it after a source update; it preserves raw bytes, normalized snapshots, and a manifest under `backend/data/`: ```bash PYTHONPATH=backend .venv/bin/python backend/scripts/collect_tourism_vintage.py --root backend/data ``` Repeated collection of the same source hash keeps one `vintage_id` and increments `seen_count` without changing `first_seen_at`. A new hash for the same publication timestamp is recorded as a separate `revised` vintage. ## Tests and build ```bash PYTHONPATH=backend .venv/bin/python -m unittest discover -s backend/tests -v cd frontend && npm run build ``` ## Current M2 boundary - English UI and analysis vocabulary - Research mode and paper mode only - Tourism Pulse fixture adapter and BOT Tourism Indicators adapter - Data lineage: source, publication time, retrieval time, vintage - Raw response hash and normalized snapshot persistence - Immutable vintage manifest with first-seen/revision metadata - Read-only data-health, vintage timeline and vintage replay endpoints - Deterministic event-study engine with benchmark and cost inputs - Backtest readiness gate that blocks without independent vintages and prices - Deterministic surprise × exposure × confidence score - Paper ledger endpoint - No LLM call yet; the deterministic result is the source of truth - No webhook receiver yet - No MT5 bridge yet The next implementation step is the event-study/backtest layer using only vintages whose `published_at` is known at each test date. 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.