mirror of
https://github.com/tiennm99/miti99bot.git
synced 2026-04-17 17:21:30 +00:00
Paper trading system with 5 commands (trade_topup, trade_buy, trade_sell, trade_convert, trade_stats). Supports VN stocks via TCBS, crypto via CoinGecko, forex via ER-API, and gold via PAX Gold proxy. Per-user portfolio stored in KV with 60s price caching. 54 new tests.
4.4 KiB
4.4 KiB
phase, title, status, priority, effort
| phase | title | status | priority | effort |
|---|---|---|---|---|
| 2 | Price Fetching + Caching | Pending | P2 | 1h |
Phase 2: Price Fetching + Caching
Context
- KV store interface —
putJSONsupportsexpirationTtl - Symbol registry
Overview
Fetches live prices from three free APIs, merges into a single cache object in KV with 60s TTL. All prices normalized to VND.
File: src/modules/trading/prices.js
Data shape — KV key prices:latest
{
ts: 1713100000000, // Date.now() at fetch time
crypto: { BTC: 2500000000, ETH: 75000000, SOL: 3500000 },
stock: { TCB: 25000, VPB: 18000, FPT: 120000, VNM: 70000, HPG: 28000 },
forex: { USD: 25400 }, // 1 USD = 25400 VND
others: { GOLD: 75000000 } // per troy oz in VND
}
Exports
fetchPrices(db)— fetch all APIs in parallel, merge, cache in KV, return merged objectgetPrices(db)— cache-first: read KV, if exists and < 60s old return it, else callfetchPricesgetPrice(db, symbol)— convenience: callsgetPrices, looks up by symbol + categorygetForexRate(db, currency)— returns VND equivalent of 1 unit of currency
API calls
-
Crypto + Gold (CoinGecko)
GET https://api.coingecko.com/api/v3/simple/price ?ids=bitcoin,ethereum,solana,pax-gold &vs_currencies=vndResponse:
{ bitcoin: { vnd: N }, ... }MapapiId -> VND priceusing SYMBOLS registry. -
Vietnam stocks (TCBS) For each stock symbol, fetch:
GET https://apipubaws.tcbs.com.vn/stock-insight/v1/stock/bars-long-term ?ticker={SYMBOL}&type=stock&resolution=D&countBack=1&to={unix_seconds}Response:
{ data: [{ close: N }] }— price in VND (already VND, multiply by 1000 for actual price per TCBS convention). Fetch all 5 stocks in parallel viaPromise.allSettled. -
Forex (Exchange Rate API)
GET https://open.er-api.com/v6/latest/USDResponse:
{ rates: { VND: N } }Store asforex.USD = rates.VND.
Implementation steps
- Create
src/modules/trading/prices.js - Implement
fetchCrypto()— single CoinGecko call, map apiId->VND - Implement
fetchStocks()—Promise.allSettledfor all stock symbols, extractclose * 1000 - Implement
fetchForex()— single call, extract VND rate - Implement
fetchPrices(db):Promise.allSettled([fetchCrypto(), fetchStocks(), fetchForex()])- Merge results, set
ts: Date.now() db.putJSON("prices:latest", merged)— no expirationTtl (we manage staleness manually)- Return merged
- Implement
getPrices(db):db.getJSON("prices:latest")- If exists and
Date.now() - ts < 60_000, return cached - Else call
fetchPrices(db)
- Implement
getPrice(db, symbol):- Get symbol info from registry
- Get prices via
getPrices(db) - Return
prices[category][symbol]
- Implement
getForexRate(db, currency):- If
currency === "VND"return 1 - If
currency === "USD"returnprices.forex.USD
- If
Edge cases
- Any single API fails ->
Promise.allSettledcatches it, use partial results + stale cache for missing category - All APIs fail -> if cache < 5 min old, use it; else throw with user-friendly message
- CoinGecko rate-limited (30 calls/min free tier) -> 60s cache makes this safe for normal use
- TCBS returns empty data array -> skip that stock, log warning
Failure modes
| Risk | Likelihood | Impact | Mitigation |
|---|---|---|---|
| CoinGecko rate limit | Low (60s cache) | Medium | Cache prevents rapid re-fetch; degrade gracefully |
| TCBS API changes response shape | Medium | Medium | Defensive access data?.[0]?.close; skip stock on parse failure |
| Forex API down | Low | Low | USD conversion unavailable; VND operations still work |
| All APIs down simultaneously | Very Low | High | Fall back to cache if < 5min; clear error message if no cache |
Security
- No API keys needed (all free public endpoints)
- No user data sent to external APIs
Success criteria
fetchPricescalls 3 APIs in parallel, returns merged objectgetPricesreturns cached data within 60s windowgetPricesrefetches when cache is stale- Partial API failure doesn't crash — missing data logged, rest returned
getPrice(db, "BTC")returns a number (VND)getForexRate(db, "VND")returns 1- File under 200 lines