Files
miti99bot/plans/260415-1010-d1-cron-infra/phase-03-trading-history.md

85 lines
2.7 KiB
Markdown

# Phase 03 — Trading Trade History
**Priority:** P1
**Status:** Complete
**Depends on:** Phase 01
## Overview
Persist every buy/sell in `trading_trades` table. Add `/history [n]` command to show last N trades for the caller (default 10, max 50).
## Requirements
**Functional**
- Every successful buy/sell inserts a row: `(id, user_id, symbol, side, qty, price_vnd, ts)`.
- `/history` → last 10 trades (newest first).
- `/history 25` → last 25 (clamp 1..50).
- Rendered as compact table (HTML-escaped).
- **No inline cap enforcement** — cleanup cron (Phase 04) handles it.
**Non-functional**
- Insert is fire-and-forget from the user's perspective but must complete before `ctx.reply` (use `await`).
- Failure to persist does NOT fail the trade — log + swallow (portfolio KV is source of truth for positions).
## Architecture
```
src/modules/trading/
├── index.js # export crons[] + commands[] (unchanged shape, new cron + new command)
├── handlers.js # buy/sell call recordTrade() after portfolio update
├── history.js # NEW — recordTrade(), listTrades(), /history handler, format
├── migrations/
│ └── 0001_trades.sql # NEW
```
### Schema (`trading_trades`)
```sql
CREATE TABLE trading_trades (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
symbol TEXT NOT NULL,
side TEXT NOT NULL CHECK (side IN ('buy','sell')),
qty INTEGER NOT NULL,
price_vnd INTEGER NOT NULL,
ts INTEGER NOT NULL -- unix ms
);
CREATE INDEX idx_trading_trades_user_ts ON trading_trades(user_id, ts DESC);
CREATE INDEX idx_trading_trades_ts ON trading_trades(ts); -- for global FIFO trim
```
## Related Code Files
**Create**
- `src/modules/trading/history.js`
- `src/modules/trading/migrations/0001_trades.sql`
- `tests/modules/trading/history.test.js`
**Modify**
- `src/modules/trading/index.js` — register `/history` command, accept `sql` in init
- `src/modules/trading/handlers.js` — call `recordTrade()` on buy/sell
## Todo List
- [x] Migration SQL
- [x] `recordTrade(sql, { userId, symbol, side, qty, priceVnd })`
- [x] `listTrades(sql, userId, n)`
- [x] `/history` command handler + HTML formatter
- [x] Wire into buy/sell handlers
- [x] Tests (Miniflare D1): record + list + max-cap clamp
## Success Criteria
- Buy + sell produce rows in `trading_trades`.
- `/history` returns last 10 newest-first.
- `/history 50` returns 50. `/history 999` clamps to 50. `/history 0` falls back to default.
- Persistence failure is logged but does not break the trade reply.
## Risks
- D1 write latency inside a user-facing handler — measured in tests; if >300ms, consider `ctx.waitUntil(insert)` non-blocking.
## Next Steps
- Phase 04 adds the retention cron consuming this table.