Files
miti99bot/plans/260414-1457-trading-module/phase-04-commands.md
tiennm99 c9270764f2 feat: add fake trading module with crypto, stocks, forex and gold
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.
2026-04-14 15:16:53 +07:00

4.7 KiB

phase, title, status, priority, effort, depends_on
phase title status priority effort depends_on
4 Command Handlers + Module Entry Pending P2 1.5h
1
2
3

Phase 4: Command Handlers + Module Entry

Context

  • Module pattern — reference for init, command shape
  • Registry typesBotModule typedef
  • Phases 1-3 provide: symbols, prices, portfolio, format

Overview

Thin index.js wiring five trade_* commands. Each handler: parse args -> validate -> call data layer -> format reply.

File: src/modules/trading/index.js

Module shape

const tradingModule = {
  name: "trading",
  init: async ({ db: store }) => { db = store; },
  commands: [
    { name: "trade_topup",   visibility: "public", description: "...", handler: handleTopup },
    { name: "trade_buy",     visibility: "public", description: "...", handler: handleBuy },
    { name: "trade_sell",    visibility: "public", description: "...", handler: handleSell },
    { name: "trade_convert", visibility: "public", description: "...", handler: handleConvert },
    { name: "trade_stats",   visibility: "public", description: "...", handler: handleStats },
  ],
};
export default tradingModule;

Command implementations

trade_topup <amount> [currency=VND]

  1. Parse: ctx.match.trim().split(/\s+/) -> [amountStr, currencyStr?]
  2. Validate: amount > 0, numeric; currency in CURRENCIES (default VND)
  3. Get portfolio, add currency
  4. If currency !== VND: fetch forex rate, add amount * rate to totalvnd
  5. If currency === VND: add amount to totalvnd
  6. Save portfolio
  7. Reply: Topped up {formatCurrency(amount, currency)}. Balance: {formatCurrency(balance, currency)}

trade_buy <amount> <symbol>

  1. Parse args: amount + symbol
  2. Validate: amount > 0; symbol exists in SYMBOLS
  3. If stock: amount must be integer (Number.isInteger(parseFloat(amount)))
  4. Fetch price via getPrice(db, symbol)
  5. Cost = amount * price (in VND)
  6. Deduct VND from portfolio; if insufficient -> error with current balance
  7. Add asset to portfolio
  8. Save, reply with purchase summary

trade_sell <amount> <symbol>

  1. Parse + validate (same as buy)
  2. Deduct asset; if insufficient -> error with current holding
  3. Fetch price, revenue = amount * price
  4. Add VND to portfolio
  5. Save, reply with sale summary

trade_convert <amount> <from> <to>

  1. Parse: amount, from-currency, to-currency
  2. Validate: both in CURRENCIES, from !== to, amount > 0
  3. Deduct from currency; if insufficient -> error
  4. Fetch forex rates, compute converted amount
  5. Add to currency
  6. Save, reply with conversion summary

trade_stats

  1. Get portfolio
  2. Fetch all prices
  3. For each category, compute current VND value
  4. Sum all = total current value
  5. P&L = total current value + currency.VND - totalvnd
  6. Reply with formatted breakdown table

Arg parsing helper

Extract into a local parseArgs(ctx, specs) at top of file:

  • specs = array of { name, required, type: "number"|"string", default? }
  • Returns parsed object or null (replies usage hint on failure)
  • Keeps handlers DRY

Implementation steps

  1. Create src/modules/trading/index.js
  2. Module-level let db = null; set in init
  3. Implement parseArgs helper (inline, ~20 lines)
  4. Implement each handler function (~25-35 lines each)
  5. Wire into commands array
  6. Ensure file stays under 200 lines. If approaching limit, extract parseArgs to a helpers.js file

Edge cases

Input Response
/trade_buy (no args) Usage: /trade_buy <amount> <symbol>
/trade_buy -5 BTC Amount must be positive
/trade_buy 0.5 NOPE Unknown symbol. Supported: BTC, ETH, ...
/trade_buy 1.5 TCB Stock quantities must be whole numbers
/trade_buy 1 BTC (no VND) Insufficient VND. Balance: 0 VND
/trade_sell 10 BTC (only have 5) Insufficient BTC. You have: 5
/trade_convert 100 VND VND Cannot convert to same currency
API failure during buy Could not fetch price. Try again later.

Failure modes

Risk Likelihood Impact Mitigation
File exceeds 200 lines Medium Low Extract parseArgs to helpers.js
Price fetch fails mid-trade Low Medium Catch, reply error, don't modify portfolio
User sends concurrent commands Low Low Last write wins; acceptable for paper trading

Success criteria

  • All 5 commands registered as public
  • Each command validates input and replies helpful errors
  • Buy/sell correctly modify both VND and asset balances
  • Convert works between VND and USD
  • Stats shows breakdown with P&L
  • File under 200 lines (or split cleanly)