TW Trading Bot
What It Is
The real-money sibling of AI Trading PK. That project runs LLM agents on paper; this one places actual orders on my 永豐 (SinoPac) account through the Shioaji API — and it is deliberately tiny. Odd-lot orders, literally one share at a time, hard-capped at NT$10,000 per order. The first real order — one share of TSMC, 盤中零股 — filled on 2026-07-03.
It is semi-automatic on purpose. The agent screens the market and writes proposals; I review them in a web portal; a terminal script with a typed-YES gate places the order. No strategy code ever touches the broker directly.
Guards Before Strategy
The safety layer shipped before any strategy existed — Task 2 of the plan, right after "can we log in." Four guards run on every order inside a pure evaluator with no broker calls and no network (the same pattern as my portfolio manager's option-invalidation module: pure function in, verdict out, trivially unit-testable):
- Kill switch — a flag file on disk. If it exists, everything halts.
- 漲跌停 price band — an order priced outside the daily band is blocked. Missing band data? Also blocked.
- Size caps — 10 shares and NT$10,000 per order. An unknown lot type is assumed to be 整股 (1,000 shares), so ambiguity over-counts and blocks.
- Daily loss cap — past −NT$5,000 for the day, new buys are blocked and the guard trips the kill switch itself. Can't read today's P&L? New buys are blocked too.
Every failure mode defaults to blocking. The only path to the broker is one choke-point function that runs the evaluator first; if any guard objects, nothing reaches the exchange and the reasons are printed in Chinese.
The Human Holds the Trigger
Clicking 核准 in the portal does not place an order — it only marks the proposal as approved. Execution is a separate script I run in a terminal: it re-checks every guard, shows the exact order (code, limit price, shares, notional), and requires a typed YES before calling the broker.
That split was a deliberate architecture decision: the web backend never holds the power to fire a real order from an HTTP endpoint. A portal bug, a stray request, a hijacked session — none of them can trade. The portal is the review desk; the terminal is the trigger.
Agent 1 · 動能派
The first agent brain screens ~50 large caps for momentum. A candidate must pass all three:
- 爆量 — today's volume ≥ 1.5× the 20-day average
- 突破 — close above the prior 20-day high
- 外資買超 — foreign net buying > 0, from TWSE T86 data (which settles in the evening — run it too early and nothing passes, a very Taiwan-market gotcha)
Daily bars come from yfinance, chips data from TWSE's open endpoints — no broker calls in the screening path either. Gemini writes each pick's rationale in the persona's voice; without an API key it falls back to templates rather than skipping the proposal. Position sizing stays inside the guard caps by construction, and each agent trades a virtual NT$50,000 book reconciled against the real account.
The portal also grew a 個股 page along the way: a sortable, searchable daily table of 789 TWSE names with price and institutional data, seeded and refreshed incrementally.
Built Spec-First
The whole system went from empty folder to first real fill through eight SDD cycles in two days — 提案 → 實作 → 歸檔, each feature a written proposal I reviewed and amended before any code: order guards, the portal, the agent brain, proposals-in-portal, the approve flow, the stocks data layer. The bot enforces discipline on my trading; the workflow enforces the same thing on the building.