{"id":50677239,"url":"https://github.com/ash-datapro/ts-explorer","last_synced_at":"2026-06-08T16:05:56.304Z","repository":{"id":328903310,"uuid":"1117275178","full_name":"ash-datapro/ts-explorer","owner":"ash-datapro","description":"A leakage-safe, walk-forward time-series forecasting stack.","archived":false,"fork":false,"pushed_at":"2025-12-16T06:06:16.000Z","size":33829,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-12-19T15:39:26.566Z","etag":null,"topics":["backtesting","docker-compose","forecasting","mlops","model-monitoring","plumber","postgresql","shiny","tidymodels","time-series","xgboost"],"latest_commit_sha":null,"homepage":"https://github.com/ash-datapro/ts-explorer/blob/main/media/demo.gif","language":"R","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ash-datapro.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-12-16T04:47:34.000Z","updated_at":"2025-12-16T15:10:34.000Z","dependencies_parsed_at":null,"dependency_job_id":"03ed3449-91fd-4a0d-a177-df4586c8b77a","html_url":"https://github.com/ash-datapro/ts-explorer","commit_stats":null,"previous_names":["ash-datapro/ts-explorer"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/ash-datapro/ts-explorer","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ash-datapro%2Fts-explorer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ash-datapro%2Fts-explorer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ash-datapro%2Fts-explorer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ash-datapro%2Fts-explorer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ash-datapro","download_url":"https://codeload.github.com/ash-datapro/ts-explorer/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ash-datapro%2Fts-explorer/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34069529,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-08T02:00:07.615Z","response_time":111,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["backtesting","docker-compose","forecasting","mlops","model-monitoring","plumber","postgresql","shiny","tidymodels","time-series","xgboost"],"created_at":"2026-06-08T16:05:56.238Z","updated_at":"2026-06-08T16:05:56.297Z","avatar_url":"https://github.com/ash-datapro.png","language":"R","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Time-Series Explorer \u0026 Forecast Monitor\n\nA production-style, end-to-end time-series ML system: **Postgres → leakage-safe feature engineering → rolling-origin backtests → deployable inference API (Plumber) → Shiny monitoring UI**.\n\nBuilt to look and behave like a real ML service: reproducible runs, honest evaluation, auditable artifacts, and clear separation of **data / training / serving / monitoring**.\n\n\u003c!-- Badges--\u003e\n\n![R](https://img.shields.io/badge/R-%E2%89%A54.2-276DC3?logo=r\u0026logoColor=white)\n![tidymodels](https://img.shields.io/badge/tidymodels-ML%20workflow-1f77b4)\n![xgboost](https://img.shields.io/badge/xgboost-forecasting-orange)\n![Plumber](https://img.shields.io/badge/Plumber-API%20serving-0f766e)\n![Shiny](https://img.shields.io/badge/Shiny-dashboard-0b7285)\n![bslib](https://img.shields.io/badge/bslib-UI-334155)\n![Postgres](https://img.shields.io/badge/PostgreSQL-data%20store-336791?logo=postgresql\u0026logoColor=white)\n![Docker](https://img.shields.io/badge/Docker-containerized-2496ED?logo=docker\u0026logoColor=white)\n![Compose](https://img.shields.io/badge/Docker%20Compose-multi--service-2496ED?logo=docker\u0026logoColor=white)\n\n\u003c!-- ========================= --\u003e\n\u003c!-- Badges: Engineering       --\u003e\n\u003c!-- ========================= --\u003e\n\n![Leakage Safe](https://img.shields.io/badge/leakage-safe-brightgreen)\n![Backtesting](https://img.shields.io/badge/eval-rolling--origin-blue)\n![Baseline](https://img.shields.io/badge/baseline-zero--return-lightgrey)\n![Artifacts](https://img.shields.io/badge/artifacts-auditable-blueviolet)\n![API](https://img.shields.io/badge/interface-HTTP%20JSON-black)\n![Observability](https://img.shields.io/badge/monitoring-Shiny%20UI-64748b)\n\n**Quick links:** Demo · Architecture · Quickstart · API · ML pipeline · Dashboard · Configuration · Testing · Production notes\n\n---\n\n## Demo\n\nShiny dashboard for EDA + forecast calls + diagnostics, with a modular UI layout.\n\n![](media/demo.gif)\n\n---\n\n## Why this exists\n\nTime-series ML systems fail in predictable ways: leakage, unrealistic evaluation, unclear ownership of artifacts, and “demo-only” UIs.\n\nThis is a reference implementation of **correct time-series discipline** packaged as a **deployable service**:\n\n* **Leakage-safe features** (only information available at time *t*)\n* **Rolling-origin evaluation** (simulates live usage)\n* **Hard baseline** (“do nothing / zero return”) so models must earn their keep\n* **Auditable artifacts** (metrics + plots + model bundle)\n* **Separation of concerns** (training ≠ serving ≠ monitoring)\n\n---\n\n## What’s included\n\n### Core components\n\n* **Postgres** — canonical source of truth for the time series\n* **Training (R / tidymodels + xgboost)**\n  Leak-free features, rolling-origin CV, tuning per horizon, artifact generation\n* **Inference API (R / Plumber)**\n  Loads a serialized model bundle (`backend/model.rds`) and serves predictions via HTTP (`/predict`)\n* **Monitoring UI (R / Shiny + bslib)**\n  Overview metrics, forecasting interface, and training diagnostics rendered from artifacts\n\n### High-level architecture\n\n```text\nPostgres (source of truth)\n        |\n        v\nTraining (tidymodels + xgboost)\n  - leakage-safe features\n  - rolling-origin backtests\n  - tuning per horizon\n  - writes artifacts + model bundle\n        |\n        v\nbackend/model.rds + backend/reports/*\n        |\n        +--\u003e Plumber API (/predict)\n        |\n        +--\u003e Shiny UI (Overview / Forecast / Diagnostics / Explore / About)\n```\n\n---\n\n## Quickstart\n\n### Prerequisites\n\n* Docker + Docker Compose\n\n### 1) Configure environment\n\nCreate a `.env` in the repo root:\n\n```bash\nPOSTGRES_USER=ts_user\nPOSTGRES_PASSWORD=ts_password\nPOSTGRES_DB=ts_db\n```\n\n### 2) Start the stack\n\n```bash\ndocker compose up --build\n```\n\nServices:\n\n* Postgres: `localhost:5432`\n* Backend API: `http://localhost:8000`\n* Shiny UI: `http://localhost:8501`\n\n---\n\n## Repository layout\n\nKey locations you’ll interact with:\n\n* `backend/` — Plumber API + model bundle + training artifacts\n* `backend/model.rds` — serialized deployable model bundle\n* `backend/reports/` — metrics/plots used by the Diagnostics tab\n* `frontend/` — Shiny application\n\n---\n\n## API\n\n### `POST /predict`\n\nForecasts **log return** at horizon `h` from an `as_of` date.\n\n**Request**\n\n```json\n{\n  \"as_of\": \"2019-05-14\",\n  \"horizon\": 5\n}\n```\n\n**Response (example)**\n\n```json\n{\n  \"as_of\": \"2019-05-14\",\n  \"current_price\": 1840.12,\n  \"prediction\": {\n    \"h5\": -0.0022\n  }\n}\n```\n\n**Interpretation**\n\n* Target is **log return** for horizon `h`\n* UI converts to implied percent move via `exp(r) - 1`\n\n**Smoke test**\n\n```bash\ncurl -X POST http://localhost:8000/predict \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"as_of\":\"2019-05-14\",\"horizon\":5}'\n```\n\n---\n\n## Local development (no Docker)\n\n### Postgres\n\nRun Postgres however you prefer. Ensure env vars are set:\n\n```bash\nexport DB_USER=ts_user\nexport DB_PASSWORD=ts_password\nexport DB_HOST=127.0.0.1\nexport DB_PORT=5432\nexport DB_NAME=ts_db\n```\n\n### Backend API (Plumber)\n\nFrom `backend/`:\n\n```r\nplumber::pr_run(plumber::plumb(\"api/main.R\"), host = \"0.0.0.0\", port = 8000)\n```\n\n### Frontend (Shiny)\n\nFrom `frontend/`:\n\n```r\nshiny::runApp(\".\", host = \"0.0.0.0\", port = 8501)\n```\n\nThe UI calls the backend via:\n\n* `PREDICT_API_URL` (default: `http://127.0.0.1:8000/predict`)\n* In Docker Compose this is typically: `http://backend:8000/predict`\n\n---\n\n## ML pipeline\n\nThis project demonstrates “time-series ML done right”:\n\n1. **Reproducible run** — fixed seed, explicit horizons, rolling-backtest settings\n2. **Leakage-safe features** — lagged returns, rolling stats, momentum-style sums, simple calendar signals\n3. **Direct multi-horizon forecasting** — one supervised problem per horizon (e.g., 1/5/20 days)\n4. **Rolling-origin CV** — repeatedly train on past, test on next window, moving forward through time\n5. **Hard baseline** — predicts zero return and reports RMSE/MAE so the model must beat “do nothing”\n6. **Production-style preprocessing** — imputation/encoding, drop constant cols, numeric scaling\n7. **Tuning inside evaluation** — fixed-budget random search; select by RMSE and report MAE\n8. **Final training + artifacts** — final per-horizon training, then save model bundle + reports\n9. **Sanity diagnostics** — backtest RMSE by horizon + actual-vs-pred plots\n\n**Artifacts**\n\n* `backend/reports/`\n* `backend/model.rds`\n\n---\n\n## Dashboard guide\n\n### Overview\n\n* KPI tiles: price / daily return / 20d annualized vol / max drawdown\n* Core plots: price, returns, vol, drawdown\n* Recent observations table\n\n### Forecast\n\n* Sends request to `/predict`\n* Shows predicted return and implied move context\n\n### Diagnostics\n\nRenders training outputs from `backend/reports/`:\n\n* `backtest_rmse_by_horizon.png`\n* `actual_vs_pred.png`\n* `backtest_metrics.csv`\n\n\u003e In Docker Compose, this works because the frontend container mounts `./backend/reports` read-only.\n\n### Explore (bring your own CSV)\n\nUpload a time-series CSV for:\n\n* date parsing + missingness + duplicates\n* coverage + frequency hints\n* returns/vol regimes, ACF, basic diagnostics\n* “safe defaults” that are useful without overclaiming\n\n---\n\n## Configuration\n\n### Environment variables\n\nBackend:\n\n* `DB_USER`, `DB_PASSWORD`, `DB_HOST`, `DB_PORT`, `DB_NAME`\n\nFrontend:\n\n* `PREDICT_API_URL` (default: `http://127.0.0.1:8000/predict`)\n* `DB_*` (only if the Shiny app loads directly from Postgres)\n\n---\n\n## Testing\n\nIf present, run from each folder:\n\n* `frontend/test-frontend.R`\n* `backend/test-api.R`\n\nYou can always smoke-test the API with the curl example in the API section.\n\n---\n\n## Production notes\n\nIf you were taking this beyond localhost:\n\n* Add structured logging (request IDs, latency, model version, feature coverage)\n* Add model registry semantics (metadata + schema + training hash)\n* Add monitoring: prediction distribution drift, null rates, feature ranges, endpoint SLOs\n* Add CI: lint + unit tests + minimal integration test (compose spin-up + API call)\n* Add auth + rate limiting for public exposure\n* Consider offline/online parity checks if you scale beyond a single series\n\n---\n\n## Disclaimer\n\nForecasting returns is inherently noisy. This project is for engineering/learning purposes and is not financial advice.\n\n---\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fash-datapro%2Fts-explorer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fash-datapro%2Fts-explorer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fash-datapro%2Fts-explorer/lists"}