mirror of
https://github.com/tiennm99/tiennm99bot.git
synced 2026-10-11 03:13:46 +00:00
4.6 KiB
4.6 KiB
phase, title, status, priority, effort, dependencies
| phase | title | status | priority | effort | dependencies | |
|---|---|---|---|---|---|---|
| 8 | Port trading + Firestore composite indexes | pending | P2 | 6h |
|
Phase 08: Port trading + Firestore composite indexes
Overview
Port the most complex module: VN-stocks paper trading. Original used D1 (relational SQL) for trades + leaderboards. Translate to Firestore document model with composite indexes for the leaderboard query path.
Requirements
- Functional:
/trade,/buy <ticker> <qty>,/sell …,/portfolio,/leaderboard, plus the daily price-update cron at0 17 * * *. - Non-functional: leaderboard query stays under 100ms warm. Daily cron fits within 50k-reads/20k-writes per-day cap (≤300 active users, ≤50 unique tickets traded).
Architecture
Firestore data model (replacing D1's trading_trades table):
collection: trading_users ← user state
doc id: <userID>
fields:
balanceVnd: number
createdAt: timestamp
lastTradeAt: timestamp
pnlVnd: number ← denormalized for leaderboard
subcollection: trades ← per-user trade log
doc id: <auto>
fields: { ticker, side, qty, priceVnd, ts }
subcollection: holdings ← current positions (one per ticker)
doc id: <ticker>
fields: { qty, avgCostVnd }
collection: trading_prices ← current ticker prices
doc id: <ticker>
fields: { priceVnd, updatedAt }
Composite index: trading_users on (pnlVnd DESC) for leaderboard. Single-field default indexes cover everything else.
Related Code Files
- Create:
internal/modules/trading/{module,buy,sell,portfolio,leaderboard,prices,cron_daily_update}.go - Create:
internal/modules/trading/store.go— direct Firestore access (bypassing KVStore for relational queries) - Create:
firestore.indexes.json(committed) — composite indexes deployed viagcloud firestore indexes composite create - Modify:
Depsto include*firestore.Client(already present) - Modify:
MODULESenv var in deploy yaml — addtrading
Implementation Steps
- Schema: Define structs
User,Trade,Holding,Priceinstore.go. Usefirestorestruct tags. - Buy flow:
- Read user balance + ticker price.
- Validate sufficient balance + qty > 0.
- In a Firestore
RunTransaction: decrement balance, increment holding (compute new avgCost), append trade, updatelastTradeAt.
- Sell flow:
- Symmetric. Realized PnL = (sellPrice - avgCost) * qty. Update
pnlVnddenorm.
- Symmetric. Realized PnL = (sellPrice - avgCost) * qty. Update
- Portfolio: list holdings + current prices (one read per ticker — typical user holds <10).
- Leaderboard:
Where(pnlVnd > 0).OrderBy(pnlVnd DESC).Limit(10). Requires composite index. - Daily price update cron:
- Triggered by Cloud Scheduler at
0 17 * * *(set up in Phase 09). - Fetches VN stock prices from existing data source (port URL/parsing from JS module).
- Writes ~50 ticker docs into
trading_prices. Stays under 20k writes/day cap easily.
- Triggered by Cloud Scheduler at
- One-time data import (optional, decided in Phase 12 cutover): script to read D1 dump, transform, write to Firestore. Skip if user opts to start fresh.
- Tests: emulator-based — buy → sell → portfolio → leaderboard parity with JS expectations.
- firestore.indexes.json: capture the composite index definition;
gcloud firestore indexes composite create --collection-group=trading_users --field-config=field-path=pnlVnd,order=descending.
Success Criteria
- Buy/sell round-trips correctly compute balance + avgCost
- Leaderboard query returns top 10 by pnl in <100ms
- Daily price cron runs (manual trigger via
/cron/trading-daily-updatefor now) - Composite index deployed and active
- Tests pass against emulator
Risk Assessment
- Risk: Firestore transactions have a 500-doc / 5MB / 10s limit. Trading transactions are tiny — fine.
- Risk: Leaderboard composite index requires explicit creation (Firestore prompts in console on first failed query). Mitigation: capture in
firestore.indexes.json+ deploy via gcloud in CI. - Risk: Denormalized
pnlVndcan drift if a sell update partially fails. Mitigation: always update inside transaction with the trade write. - Risk: Free tier 20k writes/day. Per active user, a buy+sell = 4 writes (user, trade, holding, price-touched). 300 users × 5 trades/day = 6k writes — well within.
- Risk: VN stock data source may be unstable. Mitigation: port the same source used by JS; if cron fails, retry on next run.
Rollback
Remove trading from MODULES. Existing data in trading_users collection persists harmlessly; no orphan refs since modules are isolated.