mirror of
https://github.com/tiennm99/miti99bot.git
synced 2026-04-17 15:20:58 +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.
3.7 KiB
3.7 KiB
phase, title, status, priority, effort
| phase | title | status | priority | effort |
|---|---|---|---|---|
| 1 | Symbol Registry + Formatters | Pending | P2 | 45m |
Phase 1: Symbol Registry + Formatters
Context
Overview
Two pure-data/pure-function files with zero side effects. Foundation for all other phases.
File: src/modules/trading/symbols.js
Requirements
- Export
SYMBOLS— frozen object keyed by uppercase symbol name - Export
CURRENCIES— frozen Set of supported fiat:VND,USD - Export helper
getSymbol(name)— case-insensitive lookup, returns entry orundefined - Export helper
listSymbols()— returns formatted string of all symbols grouped by category
Data shape
export const SYMBOLS = Object.freeze({
// crypto
BTC: { category: "crypto", apiId: "bitcoin", label: "Bitcoin" },
ETH: { category: "crypto", apiId: "ethereum", label: "Ethereum" },
SOL: { category: "crypto", apiId: "solana", label: "Solana" },
// stock (Vietnam)
TCB: { category: "stock", apiId: "TCB", label: "Techcombank" },
VPB: { category: "stock", apiId: "VPB", label: "VPBank" },
FPT: { category: "stock", apiId: "FPT", label: "FPT Corp" },
VNM: { category: "stock", apiId: "VNM", label: "Vinamilk" },
HPG: { category: "stock", apiId: "HPG", label: "Hoa Phat" },
// others
GOLD: { category: "others", apiId: "pax-gold", label: "Gold (troy oz)" },
});
Implementation steps
- Create
src/modules/trading/symbols.js - Define
SYMBOLSconstant with all entries above - Define
CURRENCIES = Object.freeze(new Set(["VND", "USD"])) getSymbol(name)—SYMBOLS[name.toUpperCase()]with guard for falsy inputlistSymbols()— group by category, format asSYMBOL — Labelper line- Keep under 60 lines
File: src/modules/trading/format.js
Requirements
formatVND(n)— integer, dot thousands separator, suffixVND. Example:15.000.000 VNDformatUSD(n)— 2 decimals, comma thousands, prefix$. Example:$1,234.56formatCrypto(n)— up to 8 decimals, strip trailing zeros. Example:0.00125formatStock(n)— integer (Math.floor), no decimals. Example:150formatAmount(n, symbol)— dispatcher: looks up symbol category, calls correct formatterformatCurrency(n, currency)— VND or USD formatter based on currency string
Implementation steps
- Create
src/modules/trading/format.js formatVND:Math.round(n).toLocaleString("vi-VN")+VND— verify dot separator (or manual impl for CF Workers locale support)formatUSD:n.toFixed(2)with comma grouping +$prefixformatCrypto:parseFloat(n.toFixed(8)).toString()to strip trailing zerosformatStock:Math.floor(n).toString()formatAmount: switch ongetSymbol(sym).categoryformatCurrency: switch on currency string- Keep under 80 lines
Edge cases
formatVND(0)->0 VNDformatCrypto(1.00000000)->1formatAmountwith unknown symbol -> return raw number string- CF Workers may not have full locale support — implement manual dot-separator for VND
Failure modes
| Risk | Likelihood | Impact | Mitigation |
|---|---|---|---|
toLocaleString not available in CF Workers runtime |
Medium | Medium | Manual formatter fallback: split on groups of 3, join with . |
Success criteria
SYMBOLShas 9 entries across 3 categoriesgetSymbol("btc")returns BTC entry (case-insensitive)getSymbol("NOPE")returnsundefinedformatVND(15000000)==="15.000.000 VND"formatCrypto(0.001)==="0.001"(no trailing zeros)formatStock(1.7)==="1"- Both files under 200 lines