Initial commit: Hermes Agent skills collection
- Trading skills (OKX, dividend, lottery, quantitative) - Creative skills (ASCII art, diagrams, video) - Development skills (GitHub, debugging, TDD) - Research skills (arXiv, blog monitoring) - Productivity skills (email, documents, notes) - MCP integration skills - Custom user skills
This commit is contained in:
@@ -0,0 +1,259 @@
|
||||
---
|
||||
name: dividend-investing
|
||||
description: "Dividend stock research, analysis, and ex-dividend alerting across A/HK/US markets. Covers: dividend history analysis (yield, growth, payout ratio), pre-ex-dividend day alerts via cron job, yield-vs-financing-cost arbitrage calculations, and record date tracking. Not for short-term trading entries — this is the dividend-side analysis mindset."
|
||||
version: 1.0.0
|
||||
author: Hermes Agent
|
||||
license: MIT
|
||||
platforms: [linux, macos]
|
||||
metadata:
|
||||
hermes:
|
||||
tags: [trading, dividends, stocks, a-shares, hk-stocks, us-stocks, cron]
|
||||
related_skills: [tonghuashun, longbridge-python-sdk, stock-analysis]
|
||||
scripts:
|
||||
- dividend_alert.py: "python3 ~/.hermes/scripts/dividend_alert.py — daily cron job; runs via cronjob no_agent=true (script output delivered verbatim)"
|
||||
requires:
|
||||
- python3 + akshare (pip install akshare)
|
||||
- python3 + requests (stdlib)
|
||||
- For US stocks: internet access to api.nasdaq.com (no API key needed)
|
||||
- Cron job management (cronjob tool)
|
||||
---
|
||||
|
||||
# 股息投资 Skill — Dividend Investing
|
||||
|
||||
Dividend-focused stock analysis and pre-ex-dividend alerting. **Mindset is fundamentally different from trading:** focus on yield stability, growth trajectory, payout ratio, cash coverage, and tax implications — not technical entry points.
|
||||
|
||||
## Data Sources
|
||||
|
||||
| Market | Data Source | API Key? | Speed |
|
||||
|--------|-------------|----------|-------|
|
||||
| 🇨🇳 A股 | `akshare.news_trade_notify_dividend_baidu(date)` | Free | ~3s |
|
||||
| 🇭🇰 港股 | Same Baidu function (HK stocks included) | Free | ~3s |
|
||||
| 🇺🇸 美股 | `https://api.nasdaq.com/api/calendar/dividends?date=YYYY-MM-DD` | Free | ~2s |
|
||||
|
||||
## Cross-Market Alerting Cron Job
|
||||
|
||||
The script `~/.hermes/scripts/dividend_alert.py` runs daily and outputs a formatted dividend alert. Key design decisions:
|
||||
|
||||
### Core Logic
|
||||
|
||||
```python
|
||||
# 1. Find next trading day (skip weekends)
|
||||
def next_trading_day(d):
|
||||
while d.weekday() >= 5:
|
||||
d += timedelta(days=1)
|
||||
return d
|
||||
|
||||
# 2. A/HK: AKShare Baidu dividend calendar
|
||||
df = ak.news_trade_notify_dividend_baidu(date=target_date_str)
|
||||
# Returns: 股票代码, 除权日, 分红, 送股, 转增, 交易所, 股票简称, 报告期
|
||||
|
||||
# 3. US: Nasdaq API
|
||||
url = f'https://api.nasdaq.com/api/calendar/dividends?date={date_str}'
|
||||
# Returns: symbol, dividend_Rate (per-share), indicated_Annual_Dividend, record_Date, dividend_Ex_Date
|
||||
```
|
||||
|
||||
### Format Parsing
|
||||
|
||||
A-share dividend from Baidu is in **元/10股** format (e.g., "38.00元" = 3.80元/股).
|
||||
HK dividend from Baidu is in **港元/10股** format (e.g., "0.62港元").
|
||||
US dividend from Nasdaq is in **美元/股** format (e.g., 0.56).
|
||||
|
||||
### Cron Setup
|
||||
|
||||
```bash
|
||||
# Create the job (EDT server time, 20:30 = Beijing 08:30 next day)
|
||||
# Use no_agent=true for reliable script-only delivery
|
||||
cronjob action=create \
|
||||
name='股息登记日前一天提醒' \
|
||||
schedule='30 20 * * 1-5' \
|
||||
script='dividend_alert.py' \
|
||||
no_agent=true
|
||||
```
|
||||
|
||||
The `no_agent=true` mode delivers the script's stdout verbatim — no LLM token waste, no risk of the agent reformatting or truncating the message.
|
||||
|
||||
### Proxy Pitfall
|
||||
|
||||
AKShare AND the Nasdaq API BOTH break when system proxy env vars are set:
|
||||
```python
|
||||
import os
|
||||
for k in ['http_proxy','https_proxy','HTTP_PROXY','HTTPS_PROXY']:
|
||||
os.environ.pop(k, None)
|
||||
# Now import akshare and requests — they'll connect directly
|
||||
```
|
||||
|
||||
Always put this at the top of your dividend scripts. The proxy env vars are typically set by Hermes gateway or system-level VPN wrappers, and they prevent direct HTTPS connections to Chinese financial data API endpoints (ProxyError).
|
||||
|
||||
## Dividend Investor Mindset (vs Trader)
|
||||
|
||||
When the user says they want dividends (not trading), shift analysis completely:
|
||||
|
||||
| Dimension | Trader | Dividend Investor |
|
||||
|-----------|--------|-------------------|
|
||||
| **Focus** | Entry/exit price, momentum, MACD | Yield %, payout ratio, dividend growth CAGR |
|
||||
| **Key metric** | Buy point, stop loss, R:R | 股息率 vs 资金成本(如银行分期3%) |
|
||||
| **Timescale** | Days to weeks | Quarters to years |
|
||||
| **Data** | K-line, volume, ADR, MACD | Dividend history, cash flow, FCF, payout ratio |
|
||||
| **When to buy** | Technical breakout / support | Before ex-div date (登记日前一天 = last buy day) |
|
||||
| **Tax** | Short-term capital gains | Holding period tax rules (A股: 1月内20%, 1年以上免税) |
|
||||
|
||||
### Analysis Template
|
||||
|
||||
```
|
||||
股息率 = 全年每股分红 / 当前股价
|
||||
净息差 = 股息率 - 融资成本
|
||||
|
||||
分红增长率(5年CAGR) = (当年分红 / 5年前分红)^(1/5) - 1
|
||||
分红覆盖率 = 经营现金流 / 分红总额
|
||||
```
|
||||
|
||||
## Dividend Capture Analysis (Buy Before Ex-div, Sell After)
|
||||
|
||||
When the user asks about "收息后卖" (dividend capture), the math is NOT free money:
|
||||
|
||||
### The Core Equation
|
||||
|
||||
```
|
||||
Net P&L = Dividend_Net - (Buy_Price - Sell_Price) × Shares
|
||||
= Dividend × (1 - Tax_Rate) × Shares - Price_Drop × Shares
|
||||
```
|
||||
|
||||
### Why Dividend Capture Fails for Retail
|
||||
|
||||
| Scenario | Tax | Price Action | Net Result |
|
||||
|----------|-----|-------------|------------|
|
||||
| Sell at exact ex-div price | 20% (<1mo) | -div amount | **LOSE** (tax eaten) |
|
||||
| Sell at exact ex-div price | 10% (1mo-1yr) | -div amount | **LOSE** (tax eaten) |
|
||||
| Sell at exact ex-div price | 0% (>1yr) | -div amount | **BREAKEVEN** |
|
||||
| Stock recovers +2% (填权) | 20% | -div +2% | **SLIGHT GAIN** |
|
||||
| Stock fully fills gap | Any | -div +div | **GAIN = Dividend net** |
|
||||
|
||||
**Rule of thumb:** The stock MUST recover (填权) by at least the tax rate × dividend/price to break even. For A-shares with 20% tax, that's ~0.4% on a 2元 dividend on a 28元 stock.
|
||||
|
||||
### 填权 (Gap Fill) Timeline Analysis
|
||||
|
||||
Historical data for 华特达因 (000915):
|
||||
|
||||
```
|
||||
2025年: 除权日6/11收29.49 → 第10天31.15(+5.6%) → 第15天33.22(+12.6%) ✅ 填权
|
||||
2024年: 除权日5/16收33.48 → 第2天33.95(+1.4%) → 60天跌到26.89(-19.7%) ❌ 未填权(大盘差)
|
||||
```
|
||||
|
||||
**填权 probability depends primarily on:**
|
||||
1. **Stock's position in its range** — near 52-week low = higher fill probability (safety margin)
|
||||
2. **Broader market direction** — bull market = fast fill, bear market = may never fill
|
||||
3. **Stock quality** — strong fundamentals (growing dividends, cash-rich) = faster fill
|
||||
|
||||
### Dividend Capture Decision Matrix
|
||||
|
||||
```
|
||||
Q: "Can I buy today for the dividend and sell right after?"
|
||||
→ Show the math above. The answer is almost always NO unless the user can wait for 填权.
|
||||
|
||||
Q: "How long does it usually take to fill the gap?"
|
||||
→ Check historical 填权 data. For quality dividend stocks at low prices, typically 2-4 weeks.
|
||||
```
|
||||
|
||||
## Push Notification Formatting
|
||||
|
||||
For dividend alerts delivered to QQ/Telegram, use this card-style layout:
|
||||
|
||||
```
|
||||
📢 明日除权·红利提醒
|
||||
━━━━━━━━━━━━━━━━━━━━
|
||||
📅 今日 {date} 推送
|
||||
⏰ 明天 {next_date} ({weekday}) 除权除息
|
||||
💡 明天是登记日,今天买入仍享分红
|
||||
|
||||
────────────────────
|
||||
🇨🇳 A股 明日除权 TOP
|
||||
|
||||
⭐{code} {name}
|
||||
💰每10股派{d:.2f}元
|
||||
|
||||
💎{code} {name}
|
||||
💰每10股派{d:.2f}元
|
||||
|
||||
────────────────────
|
||||
🇭🇰 港股 明日除权 TOP
|
||||
{code} {name}
|
||||
💰每10股派{d:.2f}港元
|
||||
|
||||
────────────────────
|
||||
🇺🇸 美股 明日除权 TOP
|
||||
{code}
|
||||
💰${d:.2f}/股 | 年化${ann:.2f} | 年付{n}次
|
||||
📅登记日{rec_date}
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━
|
||||
📌 操作提示
|
||||
• 今天买入 → 明天登记 → 拿分红
|
||||
• A股持仓1年以上免税,1月内20%税
|
||||
━━━━━━━━━━━━━━━━━━━━
|
||||
🤖 Hermes 每日红利雷达
|
||||
```
|
||||
|
||||
Visual hierarchy rules:
|
||||
- ⭐ for very high dividend (≥10元/10股)
|
||||
- 💎 for good dividend (5-10元/10股)
|
||||
- No prefix for lower dividends
|
||||
- `━` for header/footer separators, `─` for section dividers
|
||||
- 2-space indent per card, newline between cards
|
||||
- Empty market sections are simply omitted (no "0 results" noise)
|
||||
|
||||
## Dividend Calendar Key Dates
|
||||
|
||||
**A股**:
|
||||
- 股权登记日 (Record date) = T day — buy on this day, still get dividend
|
||||
- 除权除息日 (Ex-div date) = T+1 trading day
|
||||
- **登记日前一天通知** → 用户登记日当天买入仍可拿分红
|
||||
|
||||
**港股**:
|
||||
- Generally same system as A-shares
|
||||
|
||||
**美股**:
|
||||
- 除权日 (Ex-div date) = cut-off. Buy on or after ex-div → no dividend
|
||||
- 登记日 (Record date) = often same day as ex-div
|
||||
- Notification should say "明天除权,今天是最后买入日"
|
||||
|
||||
## Current Price Fetching in dividend_alert.py
|
||||
|
||||
The `dividend_alert.py` script enriches each alert card with real-time prices via **LongPort SDK**. Unlike the old approach (AKShare for A, Sina for HK, LongPort for US), the current unified approach uses LongPort for ALL three markets in a single batch:
|
||||
|
||||
| Market | Symbol Mapping | LongPort Format |
|
||||
|--------|---------------|-----------------|
|
||||
| 🇨🇳 A股 | 603733 → 603733.SH, 000858 → 000858.SZ | `.SH`, `.SZ`, `.BJ` |
|
||||
| 🇭🇰 港股 | 01088 → 01088.HK, 5 → 00005.HK | `.HK` (5-digit padded) |
|
||||
| 🇺🇸 美股 | AAPL → AAPL.US | `.US` suffix |
|
||||
|
||||
**Batch all symbols in one LongPort call:**
|
||||
```python
|
||||
# One ctx.quote() call for all three markets
|
||||
all_syms = a_syms + hk_syms + us_syms
|
||||
for i in range(0, len(all_syms), 15):
|
||||
for q in ctx.quote(all_syms[i:i+15]):
|
||||
prices[q.symbol] = float(q.last_done)
|
||||
```
|
||||
|
||||
**⚠️ LongPort connection can be intermittent** — the SDK prints a permission table on first init and may timeout on high-load days. If LongPort fails, prices show as N/A but dividend data still outputs. The script retries on each run (cron runs daily), so a single failure self-recovers.
|
||||
|
||||
**Dividend yield formula:** Yield = (dividend_per_10shares / 10) / current_price * 100. The Baidu API returns dividend in 元/10股 format, so divide by 10 before calculating yield.
|
||||
|
||||
## Script Reference
|
||||
|
||||
See `scripts/dividend_alert.py` for the production alerting script.
|
||||
See `references/dividend-yield-arbitrage.md` for yield vs financing cost analysis.
|
||||
See `references/fill-gap-timing.md` for historical 填权 timing data and dividend capture analysis.
|
||||
|
||||
## Pitfalls
|
||||
|
||||
1. **Proxy environment variables** — always `unset` proxy vars before calling AKShare or Nasdaq API
|
||||
2. **Baidu dividend data is per-10-shares** for A/HK. Don't multiply by 10 again when displaying.
|
||||
3. **Nasdaq API rate limits** — fine for 1 query/day in a cron job, but don't query multiple times rapidly
|
||||
4. **Weekend/holiday handling** — `next_trading_day()` only skips Sat/Sun. For CN/HK holidays, you'd need a full trading calendar.
|
||||
5. **No backward-looking price fetching for yield** — current market price is available from `stock_zh_a_hist()` (1-2s per stock), but fetching for all alert stocks is slow (~15s for 8-10 stocks). Tradeoff: speed vs completeness.
|
||||
6. **Timezone confusion** — Server is usually EDT (UTC-4). Beijing is UTC+8. Cron schedule must account for this: 20:30 EDT = 08:30 BJT next day.
|
||||
7. **`stock_zh_a_spot_em()` downloads the full A-share market (~5000 stocks)** — Takes ~3-5s for a single call. This is **fine** for one-shot batch price lookups (as done in `dividend_alert.py`) but avoid calling it repeatedly in loops. For single-stock lookups, prefer `stock_zh_a_hist(code, period='daily', start_date=today, end_date=today, adjust='qfq')` instead (1-2s each).
|
||||
9. **AKShare Baidu dividend API is intermittent** — `ak.news_trade_notify_dividend_baidu()` may return 0 results on some runs despite having data on others. This is a server-side issue, not rate limiting. **Mitigation**: Added `time.sleep(0.5)` before the call to avoid cache issues. The cron job reruns daily, so a single failure self-recovers.
|
||||
10. **Yield formula: divide-by-10 trap** — The Baidu API returns dividend in **元/10股** format. When calculating dividend yield in percent, use `(dividend_per_10shares / 10) / current_price * 100`. A common bug is forgetting to divide by 10 (the "per 10 shares" unit). Verified correct formula: `d/10/p*100` where `d` is the Baidu dividend value and `p` is the stock price.
|
||||
11. **Variable name collisions in patch replacements** — When patching Python code that uses short variable names (`p`, `d`, `c`, `n`), find-and-replace patterns can accidentally match unrelated code. Always use 3+ lines of surrounding context for unique matching.
|
||||
@@ -0,0 +1,49 @@
|
||||
# 股息率套利分析 — Dividend Yield Arbitrage
|
||||
|
||||
## 核心逻辑
|
||||
|
||||
```
|
||||
净息差 = 股息率(税前) - 融资成本
|
||||
年套利收入 = 本金 × 净息差
|
||||
```
|
||||
|
||||
## 美的集团套利示例(2026年6月)
|
||||
|
||||
| 项目 | 数值 |
|
||||
|------|:----:|
|
||||
| 2025全年分红 | 4.30元/股(43元/10股) |
|
||||
| 当前价(除权后) | 77.27元 |
|
||||
| 股息率 | 5.56% |
|
||||
| 银行分期成本 | ~3% |
|
||||
| **净息差** | **~2.56%** |
|
||||
| 每100万套利收入 | **~2.56万/年** |
|
||||
|
||||
## 股息率随买入价变化(基于2025年分红4.30元/股)
|
||||
|
||||
| 买入价 | 股息率 | 净息差(3%成本) |
|
||||
|:-----:|:------:|:--------------:|
|
||||
| 82 | 5.24% | 2.24% |
|
||||
| 80 | 5.38% | 2.38% |
|
||||
| 77.27(现价) | 5.56% | 2.56% |
|
||||
| 75 | 5.73% | 2.73% |
|
||||
| 73 | 5.89% | 2.89% |
|
||||
| 70 | 6.14% | 3.14% |
|
||||
|
||||
## 股息增长对实际收益的影响
|
||||
|
||||
假设买入价77.27,分红按过去5年CAGR 22%增长:
|
||||
|
||||
| 年份 | 预测分红 | 对买入价的股息率 | 累计收益 |
|
||||
|:----:|:--------:|:---------------:|:--------:|
|
||||
| 2025(基准) | 4.30 | 5.56% | — |
|
||||
| 2026E | 4.50 (+5%保守) | 5.82% | 5.82% |
|
||||
| 2027E | 4.70 | 6.08% | 11.90% |
|
||||
| 2028E | 4.90 | 6.34% | 18.24% |
|
||||
|
||||
## 风险提示
|
||||
|
||||
1. **分红不保证** — 公司可能削减或取消分红
|
||||
2. **股价波动** — 除权后贴权会导致账面亏损
|
||||
3. **税率** — A股持仓<1月扣20%红利税,>1年免税
|
||||
4. **汇率风险** — 港股(港元)和美股(美元)有汇率波动
|
||||
5. **融资续贷风险** — 银行分期续贷不保证
|
||||
@@ -0,0 +1,98 @@
|
||||
# 填权 (Gap Fill) Timing Reference
|
||||
|
||||
## What is 填权?
|
||||
|
||||
After ex-dividend, the stock price drops by approximately the dividend amount. "填权" means the stock price recovers back to (or above) the pre-ex-dividend level over time.
|
||||
|
||||
**填权 ≠ immediate.** The market doesn't give away free money — the ex-div price drop is a mechanical adjustment. Whether and how fast the gap fills depends on ongoing supply/demand for the stock.
|
||||
|
||||
## Historical Fill Speeds for A-share Dividend Stocks
|
||||
|
||||
### 华特达因 (000915) — High-dividend healthcare stock
|
||||
|
||||
| Year | Ex-div | Dividend | Pre-close | Ex-close | Fill Time | Notes |
|
||||
|------|--------|----------|-----------|----------|-----------|-------|
|
||||
| 2025 | Jun 11 | 2.00元 | 32.39 | 29.49 | **~10 days** (31.15, +5.6%) | Fast fill; stock was in uptrend |
|
||||
| 2024 | May 16 | 2.00元 | 35.30 | 33.48 | **Did not fill in 60 days** (30→27) | Bear market dragged it down |
|
||||
|
||||
**Key insight:** 2025 filled fast because the stock was in a healthy trend. 2024 didn't fill because the overall market was falling. Stock quality matters but macro conditions dominate.
|
||||
|
||||
### 同仁堂 (600085) — Blue-chip TCM
|
||||
|
||||
(Add data here when available from analysis.)
|
||||
|
||||
## Factors That Determine Fill Speed
|
||||
|
||||
### 1. Stock Price Position (Most Important)
|
||||
|
||||
```
|
||||
Stock at 52-week low: High fill probability (already "cheap")
|
||||
Stock at 52-week high: Low fill probability (due for pullback)
|
||||
|
||||
Example: 华特达因 2025 → ex-div at 32 → near YTD high → fast fill anyway (good stock)
|
||||
华特达因 2024 → ex-div at 35 → at YTD high → no fill (bad timing + bad market)
|
||||
```
|
||||
|
||||
### 2. Market Direction
|
||||
|
||||
- **Bull market / 结构性牛市**: Most quality stocks fill within 1-3 weeks
|
||||
- **Bear market / 熊市**: Can take months or never — the dividend is "eaten" by the falling price
|
||||
- **Sideways market**: Depends on stock-specific catalysts
|
||||
|
||||
### 3. Dividend Size Relative to Price
|
||||
|
||||
| Dividend/Price ratio | Impact |
|
||||
|---------------------|--------|
|
||||
| < 2% | Small gap, easy to fill (days) |
|
||||
| 2-5% | Moderate, 1-3 weeks typical |
|
||||
| > 5% | Large gap, may take months; better to wait for natural dip before buying |
|
||||
|
||||
### 4. Company Fundamentals
|
||||
|
||||
- **Growing dividends** (e.g., 华特达因 2021: 0.35 → 2025: 2.50/share) → faster fill (market rewards increasing payouts)
|
||||
- **Stable/declining dividends** → slower fill
|
||||
- **High payout ratio** (>80%) → risk of cut → may never fill
|
||||
- **Cash-rich** (>30% market cap in cash) → faster fill (dividend is safe)
|
||||
|
||||
## Practical Rules for Dividend Capture
|
||||
|
||||
```
|
||||
If you MUST try dividend capture (buy pre-ex-div, sell post):
|
||||
|
||||
1. Only attempt on stocks near their 52-week LOW
|
||||
→ The ex-div gap is less damaging when already near support
|
||||
|
||||
2. Only attempt when market is in uptrend
|
||||
→ Check: is the SH/SZ index above its 50-day MA?
|
||||
|
||||
3. Plan to hold MINIMUM 2-4 weeks post-ex-div
|
||||
→ Selling the next day guarantees a loss (tax + gap)
|
||||
|
||||
4. Calculate your break-even price:
|
||||
BreakEven = ExDivPrice + (Tax_Rate × Dividend)
|
||||
|
||||
Example: 28元 stock, 2元 dividend, 20% tax:
|
||||
BreakEven = 26.00 + 0.40 = 26.40
|
||||
→ Stock must rally 1.5% from ex-div just to break even
|
||||
|
||||
5. Consider buying AFTER ex-div instead:
|
||||
- No dividend → no tax → no gap risk
|
||||
- Lower entry price → higher yield on cost
|
||||
- Same future dividends
|
||||
→ Often the better move for pure yield investors
|
||||
```
|
||||
|
||||
## Data Source
|
||||
|
||||
To calculate 填权 timing for any stock:
|
||||
|
||||
```python
|
||||
import akshare as ak
|
||||
|
||||
# Use unadjusted prices (adjust='') to see the real ex-div gap
|
||||
df = ak.stock_zh_a_hist(symbol='000915', period='daily',
|
||||
start_date='20250601', end_date='20251001', adjust='')
|
||||
|
||||
# Find ex-div day by looking for the big drop on the expected date
|
||||
# Then scan forward to see how many days to recover
|
||||
```
|
||||
Reference in New Issue
Block a user