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:
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
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:
curl http://127.0.0.1:5000/api/v1/health
Run the dashboard
In a second terminal:
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:
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:
GET /api/v1/data-health
GET /api/v1/vintages?as_of=<ISO-8601 timestamp>
GET /api/v1/replay/tourism?vintage_id=<vintage_id>
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/:
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
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 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.