Case: LLM as quant research assistant¶
Let Claude read your live market data and account state, have it write analysis. No order placement.
Requirements¶
- Claude Desktop can see real-time quotes + candles + fundamentals for Tencent / Apple / Tesla, etc.
- Claude can list your positions + today's orders + historical fills across all accounts.
- All reads go through MCP; Claude writes nothing.
- Full audit retained.
Key strategy¶
Read-only scopes only, no trade:*:
futucli gen-key \
--id claude-analyst \
--scopes qot:read,acc:read \
--expires 90d \
--bind-this-machine
- No trade scope → even if Claude decides to place an order,
futu_place_orderis rejected withmissing scope trade:real. - Machine-bound → copying
keys.jsonelsewhere doesn't work. - 90-day expiry → periodic rotation.
Start¶
# gateway
futu-opend \
--login-account 12345678 --login-pwd "$FUTU_PWD" \
--rest-port 22222 \
--rest-keys-file ~/.config/futu/keys.json
# MCP (stdio, spawned by Claude Desktop)
# see Claude Desktop config below
Claude Desktop config¶
~/Library/Application Support/Claude/claude_desktop_config.json
{
"mcpServers": {
"futu": {
"command": "/usr/local/bin/futu-mcp",
"args": [
"--gateway", "127.0.0.1:11111",
"--keys-file", "/Users/you/.config/futu/keys.json",
"--audit-log", "/Users/you/Library/Logs/futu-mcp-audit.jsonl"
],
"env": {
"FUTU_MCP_API_KEY": "fc_xxxxxxxxxxxxxxxxxx"
}
}
}
}
Try asking Claude¶
"Show me the latest quotes for Tencent and Apple, and walk me through today's market."
Claude will call futu_get_quote + futu_get_snapshot +
futu_get_kline, then synthesize the analysis.
"What do I hold? What trades executed today?"
Claude calls futu_list_accounts → futu_get_positions →
futu_get_deals.
"Place a buy order for 100 shares of Tencent."
Claude reports the tool call failed with missing scope trade:real.
Expected — this key is read-only.
Audit queries¶
# What did Claude look at this week?
jq 'select(.key_id=="claude-analyst" and .iface=="mcp")' \
~/Library/Logs/futu-mcp-audit.jsonl | tail -100
# Rejected calls (insufficient scope)
jq 'select(.key_id=="claude-analyst" and .outcome=="reject")' \
~/Library/Logs/futu-mcp-audit.jsonl
Upgrade path¶
Want Claude to place orders? Not recommended. If you insist:
- Generate a separate key with
trade:simulateonly, run for 1 week. - Configure very tight limits (
--max-order-value 10000--max-orders-per-minute 1). - Only after no surprises, consider
trade:real— and still keep strict limits. - Monitor
futu_auth_limit_rejects_total{key_id="claude-real"}with long-running alerts.
LLMs will make mistakes; guardrails are mandatory.