Files
goodgo-platform/docs/api/market-index-ticker-contract.md
2026-04-21 01:37:02 +07:00

13 KiB
Raw Blame History

Contract API Chỉ số Thị trường & Ticker (Goodgo Platform AI)

Liên quan: TEC-3036, TEC-3042, TEC-3043. Trạng thái: Draft v1 (chờ CTO + BE TechLead + FE TechLead duyệt). Chủ trì: API Architect. Phối hợp: Database Architect, Backend TechLead, Frontend TechLead.

Tài liệu này định nghĩa contract cho các endpoint mới phục vụ UI phong cách "sàn giao dịch" (trading-floor) của Goodgo Platform AI. Frontend chỉ gọi API thực, cấm mock. Các endpoint mới được thêm không breaking endpoint hiện có (/analytics/*, /listings/*, /search/*).

1. Nguyên tắc thiết kế

  • Base path: /api/v1/ (prefix chung toàn platform, bearer JWT khi cần).
  • Auth: Public cho các chỉ số tổng hợp (cache-able). JWT cho các API cá nhân hoá (watchlist ticker).
  • Caching: Cache-Control: public, max-age=30, stale-while-revalidate=60 cho chỉ số ticker. max-age=300 cho heatmap/price-trends.
  • Đơn vị:
    • Tiền tệ: VND (integer, không thập phân). Field nào dùng VND/m² phải ghi rõ unit: "VND_PER_SQM".
    • Thời gian: ISO-8601 UTC (2026-04-21T03:15:00Z). Client tự chuyển sang Asia/Ho_Chi_Minh.
    • % change: number (phần trăm, ví dụ 2.35 = +2.35%). delta là giá trị tuyệt đối cùng đơn vị với value.
  • Response envelope: thống nhất với module analytics hiện có:
    {
      "data": { ... },
      "meta": {
        "generatedAt": "2026-04-21T03:15:00Z",
        "ttlSeconds": 30,
        "source": "aggregation_v1",
        "baselinePeriod": "PT24H"
      }
    }
    
  • Lỗi: tuân theo docs/api-error-codes.md (ErrorResponse { code, message, details }).
  • Pagination (nếu có): page, pageSize (max 100), response có meta.pagination.
  • i18n: label/description phục vụ UI trả về tiếng Việt; field system (code, slug) dùng tiếng Anh.

2. Phân loại endpoint

Nhóm Mục đích UI Cache Auth
Market Index Header chỉ số GGI/khu vực 30s Public
Price Trends Biểu đồ candlestick/line 300s Public
District Volume Bar chart volume theo quận 120s Public
Listing Ticker Dải chạy real-time tin mới/giá mới 15s + SSE Public + (JWT cho watchlist)
Top Movers Bảng top tăng/giảm 60s Public
Heatmap Summary Overlay heatmap bản đồ 300s Public

3. Đặc tả chi tiết

3.1. GET /api/v1/analytics/market-index

Trả về chỉ số thị trường tổng hợp (Goodgo Market Index GGI) theo phân loại.

Query params

Name Type Required Default Mô tả
scope enum(national, city, district) no city Phạm vi tính chỉ số
cityCode string when scope≠national HCM Mã thành phố (HCM, HN, DN...)
districtCode string when scope=district Mã quận/huyện
propertyType enum(all, apartment, house, land, commercial) no all Loại hình
baseline enum(PT24H, P7D, P30D, P1Y) no PT24H Khoảng so sánh delta

Response 200

{
  "data": {
    "indexCode": "GGI-HCM-ALL",
    "indexLabel": "Chỉ số Goodgo TP.HCM - Tất cả",
    "value": 1284.35,
    "unit": "INDEX_POINT",
    "baseValue": 1000,
    "baselinePeriod": "PT24H",
    "delta": 29.62,
    "changePercent": 2.36,
    "direction": "up",
    "samples": 12845,
    "breakdown": [
      { "code": "apartment", "label": "Chung cư", "value": 1311.2, "changePercent": 3.1 },
      { "code": "house",     "label": "Nhà phố",  "value": 1198.4, "changePercent": 1.0 },
      { "code": "land",      "label": "Đất nền",  "value": 1342.7, "changePercent": 4.2 }
    ],
    "timestamp": "2026-04-21T03:15:00Z"
  },
  "meta": { "generatedAt": "2026-04-21T03:15:00Z", "ttlSeconds": 30, "source": "aggregation_v1", "baselinePeriod": "PT24H" }
}

Chuỗi giá trung bình theo thời gian (dạng nến OHLC hoặc line tuỳ granularity).

Query params

Name Type Required Default Mô tả
scope enum(city, district, ward) yes
scopeCode string yes Mã tương ứng
propertyType enum(...) no all
granularity enum(1h, 1d, 1w, 1m) no 1d Bucket thời gian
from ISO-8601 no now-30d
to ISO-8601 no now
series enum(avg, ohlc) no ohlc

Response 200

{
  "data": {
    "scope": "district",
    "scopeCode": "HCM-Q1",
    "unit": "VND_PER_SQM",
    "granularity": "1d",
    "points": [
      {
        "timestamp": "2026-04-20T00:00:00Z",
        "open": 95200000,
        "high": 96800000,
        "low": 94900000,
        "close": 96100000,
        "avg": 95600000,
        "volume": 184,
        "changePercent": 0.95
      }
    ]
  },
  "meta": { "generatedAt": "2026-04-21T03:15:00Z", "ttlSeconds": 300, "source": "listing_snapshot_v1", "baselinePeriod": "P30D" }
}

3.3. GET /api/v1/analytics/district-volume

Khối lượng tin đăng/giao dịch theo quận cho bar chart.

Query params

Name Type Required Default
cityCode string no HCM
metric enum(listings_new, listings_active, inquiries, transactions) no listings_new
window enum(PT1H, PT24H, P7D, P30D) no PT24H
limit int (1..50) no 24
sort enum(value_desc, value_asc, change_desc) no value_desc

Response 200

{
  "data": {
    "cityCode": "HCM",
    "metric": "listings_new",
    "window": "PT24H",
    "items": [
      {
        "districtCode": "HCM-Q7",
        "districtName": "Quận 7",
        "value": 312,
        "previousValue": 284,
        "delta": 28,
        "changePercent": 9.86,
        "rank": 1
      }
    ]
  },
  "meta": { "generatedAt": "2026-04-21T03:15:00Z", "ttlSeconds": 120, "source": "listing_stream_v1", "baselinePeriod": "PT24H" }
}

3.4. GET /api/v1/listings/ticker

Danh sách tick cho dải chạy real-time. Có cả poll (REST) và stream (SSE/WebSocket) version.

Query params

Name Type Required Default Mô tả
channel enum(new_listing, price_change, hot, featured, watchlist) no new_listing watchlist cần JWT
scope enum(national, city, district) no city
scopeCode string cond HCM
propertyType enum(...) no all
limit int(1..50) no 20
sinceTickId string no Lấy tick mới hơn ID này (polling incremental)

Response 200

{
  "data": {
    "channel": "new_listing",
    "ticks": [
      {
        "tickId": "tick_01J...",
        "listingId": "lst_01J...",
        "symbol": "HCM-Q7-APT-0934",
        "title": "Căn hộ 2PN view sông Phú Mỹ Hưng",
        "propertyType": "apartment",
        "districtCode": "HCM-Q7",
        "districtName": "Quận 7",
        "priceVnd": 5200000000,
        "pricePerSqmVnd": 68400000,
        "areaSqm": 76,
        "changePercent": -1.45,
        "direction": "down",
        "event": "price_change",
        "timestamp": "2026-04-21T03:14:57Z",
        "url": "/listings/lst_01J..."
      }
    ],
    "nextSinceTickId": "tick_01J...ZZ"
  },
  "meta": { "generatedAt": "2026-04-21T03:15:00Z", "ttlSeconds": 15, "source": "ticker_stream_v1" }
}

3.4.1. GET /api/v1/listings/ticker/stream (SSE)

  • Content-Type: text/event-stream.
  • Event types: tick, heartbeat, reset.
  • Payload data: giống một phần tử ticks[] phía trên.
  • Query params như REST, thêm heartbeatSeconds (default 15).

3.5. GET /api/v1/analytics/top-movers

Top tăng/giảm theo khu vực/loại hình.

Query params

Name Type Required Default
direction enum(gainers, losers, both) no both
scope enum(city, district, ward, project) no district
cityCode string no HCM
metric enum(price_per_sqm, index_value, volume) no price_per_sqm
window enum(PT24H, P7D, P30D) no P7D
limit int(1..50) no 10

Response 200

{
  "data": {
    "window": "P7D",
    "metric": "price_per_sqm",
    "gainers": [
      {
        "code": "HCM-Q9",
        "label": "TP Thủ Đức (Q9 cũ)",
        "value": 72400000,
        "previousValue": 68900000,
        "delta": 3500000,
        "changePercent": 5.08,
        "rank": 1,
        "sampleSize": 412
      }
    ],
    "losers": [ /* same schema */ ]
  },
  "meta": { "generatedAt": "2026-04-21T03:15:00Z", "ttlSeconds": 60, "source": "aggregation_v1", "baselinePeriod": "P7D" }
}

3.6. GET /api/v1/analytics/heatmap-summary

Dữ liệu tóm tắt heatmap cho overlay bản đồ.

Query params

Name Type Required Default Mô tả
cityCode string no HCM
metric enum(price_per_sqm, volume, demand_index, change_percent) no price_per_sqm
resolution enum(district, ward, h3_r7, h3_r8) no district H3 cell khi cần granularity cao
window enum(PT24H, P7D, P30D) no P30D
bbox string minLon,minLat,maxLon,maxLat no Bounding box bản đồ

Response 200

{
  "data": {
    "metric": "price_per_sqm",
    "unit": "VND_PER_SQM",
    "resolution": "district",
    "legend": {
      "buckets": [
        { "min": 0,          "max": 40000000,  "color": "#1a9850", "label": "Thấp" },
        { "min": 40000000,   "max": 80000000,  "color": "#fee08b", "label": "Trung bình" },
        { "min": 80000000,   "max": 140000000, "color": "#d73027", "label": "Cao" }
      ]
    },
    "cells": [
      {
        "code": "HCM-Q1",
        "label": "Quận 1",
        "centroid": { "lat": 10.776, "lon": 106.700 },
        "value": 128400000,
        "changePercent": 1.8,
        "sampleSize": 532,
        "polygonRef": "geo/districts/HCM-Q1.geojson"
      }
    ]
  },
  "meta": { "generatedAt": "2026-04-21T03:15:00Z", "ttlSeconds": 300, "source": "aggregation_v1", "baselinePeriod": "P30D" }
}

4. Nguồn dữ liệu & aggregation (phối hợp Database Architect)

Endpoint Nguồn Aggregation chính Refresh
market-index Listing, Transaction, PriceSnapshot Weighted avg price/m² chuẩn hoá về base 2025-01-01 = 1000 1 phút (materialized view)
price-trends PriceSnapshot (daily), Listing.price_history OHLC theo bucket 5 phút
district-volume Listing.createdAt, Inquiry, Transaction COUNT + window function 1 phút
listings/ticker Redis stream listings:events + DB fallback Event bus (create/price_change/feature) real-time
top-movers PriceSnapshot % change theo bucket + ranking 5 phút
heatmap-summary Listing + District/H3Cell precomputed PostGIS aggregate theo polygon/H3 15 phút

Database Architect xác nhận: (i) thêm materialized views mv_market_index_*, mv_price_snapshot_*; (ii) Redis stream listings:events dùng cho ticker/SSE; (iii) bảng H3Cell cho heatmap r7/r8.

5. Phân việc cho Backend TechLead (TEC-3042)

Endpoint Đề xuất cấp độ BE Ghi chú
market-index Senior Cần design materialized view + cache invalidation
price-trends Middle Query trên view sẵn có
district-volume Middle Window function + Redis cache
listings/ticker (REST+SSE) Senior Pub/sub, backpressure, auth watchlist
top-movers Middle Dựa trên view price-trends
heatmap-summary Senior PostGIS + H3, geojson ref
DTO/OpenAPI sync Junior Viết DTO, Swagger decorators, update docs/api-endpoints.md

6. OpenAPI & Swagger

  • DTO mới ở apps/api/src/modules/analytics/presentation/dto/apps/api/src/modules/listings/presentation/dto/ (ticker).
  • Controller:
    • analytics.controller.ts: thêm @Get('market-index'), @Get('price-trends'), @Get('district-volume'), @Get('top-movers'), @Get('heatmap-summary').
    • listings.controller.ts: thêm @Get('ticker'), @Get('ticker/stream').
  • Tag Swagger: Analytics - Market, Listings - Ticker.
  • Cập nhật docs/api-endpoints.md (phần Analytics & Listings) sau khi merge.

7. Versioning & rollout

  • Tất cả endpoint mới nằm dưới /api/v1/ hiện tại — không bump major.
  • Feature flag (nếu cần) dùng FEATURE_MARKET_TICKER cho ticker SSE.
  • SLO: p95 < 250ms cho REST; SSE duy trì kết nối ≥ 5 phút, heartbeat 15s.

8. Checklist duyệt

  • CTO phê duyệt phạm vi & SLO.
  • BE TechLead xác nhận phân rã task & timeline.
  • FE TechLead xác nhận response schema đủ cho UI sàn giao dịch.
  • Database Architect ký nguồn dữ liệu & materialized views.
  • DTO/OpenAPI/Swagger & docs/api-endpoints.md cập nhật.

Revision 1 — 2026-04-21 — API Architect (TEC-3043).