# DCA Ladder Monitoring — 阶梯买入自动监控

After screening candidates (see `dca-screener.md`), set up automated price monitoring so the user gets buy signals when prices hit ladder tiers.

## Architecture

```
dca_positions.json  ←  config (symbols, ladder prices, budget, status)
       ↓
dca_monitor.py      ←  reads config + fetches prices from LongPort
       ↓
cron jobs           ←  runs monitor on schedule, delivers alerts
```

## Step 1: Create Position Config

File: `~/.hermes/scripts/dca_positions.json`

```json
{
  "updated": "YYYY-MM-DD",
  "positions": {
    "SYMBOL.US": {
      "name": "Display Name",
      "yield": 10.5,
      "market": "US",
      "ladder": [
        {"tier": 1, "price": 15.28, "alloc_pct": 40, "status": "pending", "shares": 3, "cost_local": 45.84},
        {"tier": 2, "price": 15.07, "alloc_pct": 30, "status": "pending", "shares": 2, "cost_local": 30.14},
        {"tier": 3, "price": 13.03, "alloc_pct": 30, "status": "pending", "shares": 3, "cost_local": 39.09}
      ],
      "monthly_budget_hkd": 1071,
      "monthly_budget_local": 137,
      "notes": "PE8.5 科技BDC龙头"
    }
  },
  "budget": {
    "monthly_min_hkd": 6000,
    "monthly_max_hkd": 9000,
    "monthly_mid_hkd": 7500,
    "per_stock_hkd": 1071,
    "usd_hkd": 7.80
  },
  "alert_settings": {
    "trigger_pct": 2.0,
    "cooldown_hours": 24
  }
}
```

### Lot Calculation

Given monthly budget M and N stocks:
- `per_stock = M / N` (in HKD)
- For US stocks: `per_stock_local = per_stock / USDHKD`
- Per tier: `shares = floor(tier_budget / price)` where `tier_budget = per_stock_local * alloc_pct / 100`
- Update JSON with `shares`, `cost_local`, `cost_hkd` fields

### Status Tracking

When user confirms a purchase:
- Change `status` from `"pending"` to `"done"` in the ladder entry
- This prevents re-alerting on already-purchased tiers

## Step 2: Monitor Script

File: `~/.hermes/scripts/dca_monitor.py`

Key logic:
1. Load env vars from `~/.bashrc` (LONGBRIDGE_* → LONGPORT_*)
2. Load `dca_positions.json`
3. Fetch current prices via `ctx.quote(symbols)` in batches of 15
4. For each position, compare price to each pending ladder tier
5. If `current_price <= target * (1 + trigger_pct/100)`: emit alert
6. If no alerts triggered: output empty (silent — no notification sent)

### Alert Format

```
🔔 DCA买入信号 [YYYY-MM-DD HH:MM]

🚨 🇺🇸 HTGC.US Hercules Capital
  第1档目标: 15.28  现价: 15.20  已触达
  建议仓位: 40%  买入: 3股  股息率: 10.5%

🟡 🇺🇸 NLY.US Annaly Capital
  第2档目标: 21.07  现价: 21.22  差0.7%
  建议仓位: 30%  买入: 1股  股息率: 13.2%
```

### Pitfalls

- **SecurityQuote attribute**: `SecurityQuote` may not have `change_rate` on some data tiers. Use `CalcIndex.ChangeRate` via `calc_indexes` instead.
- **Price=0 on weekends**: LongPort returns 0 for `last_done` when markets are closed. The monitor will trigger all alerts on weekends — either skip weekends in cron schedule or handle in script.
- **HK stock codes in python3 -c**: Codes like `0728.HK` start with digits. Always write scripts to file, never use `python3 -c`.
- **Batch sizes**: quote 15/batch, calc_indexes 10/batch, static_info 20/batch.

## Step 3: Cron Jobs

Set up 5 jobs (all Beijing time):

| Schedule | Name | Purpose |
|----------|------|---------|
| `0 9 * * 1-6` | DCA每日晨报 | AI-driven summary with all positions status |
| `0 10 * * 1-5` | DCA港股盘中(上午) | HK market check (1hr after open) |
| `0 15 * * 1-5` | DCA港股盘中(下午) | HK market check (1hr before close) |
| `30 22 * * 1-5` | DCA美股盘中(晚间) | US market check (30min after open) |
| `0 2 * * 2-6` | DCA美股盘中(凌晨) | US market check (mid-session) |

### Cron Setup Pattern

**Script-only jobs** (no agent, just run monitor):
```python
cronjob(action='create', name='DCA港股盘中监控', 
        schedule='0 10 * * 1-5', no_agent=True, 
        script='scripts/dca_monitor.py', deliver='origin')
```

**AI-driven daily summary** (with agent for richer formatting):
```python
cronjob(action='create', name='DCA每日晨报',
        schedule='0 9 * * 1-6',
        prompt='Run dca_monitor.py, generate morning brief...',
        enabled_toolsets=['terminal'], deliver='origin')
```

## Step 4: User Interaction Commands

After deployment, user may say:

| User Says | Action |
|-----------|--------|
| "我买了XX T1" | Edit JSON: set tier status to `"done"` |
| "调整XX阶梯价位" | Edit JSON: update ladder prices |
| "设置预算XX万" | Recalculate lot sizes, update JSON |
| "加一只XX" | Add new position to JSON |
| "暂停DCA监控" | Pause cron jobs |
| "DCA状态" | Run monitor script, show all positions |

## Full Script Template

See `~/.hermes/scripts/dca_monitor.py` for the production script.

Key env loading pattern (required for all LongPort scripts):
```python
import os, re
env_vars = {}
with open(os.path.expanduser('~/.bashrc')) as f:
    for line in f:
        line = line.strip()
        if line.startswith('export LONGBRIDGE_') or line.startswith('export LONGPORT_'):
            parts = line.replace('export ', '').split('=', 1)
            if len(parts) == 2:
                env_vars[parts[0]] = parts[1]
for key, val in env_vars.items():
    if '${' not in val: os.environ[key] = val
for key, val in env_vars.items():
    if '${' in val:
        os.environ[key] = re.sub(r'\$\{(\w+)\}', lambda m: os.environ.get(m.group(1), ''), val)
```
