这份笔记旨在参考雷公的原策略基础上,客观记录“新Hi5”投资组合的构建邏輯、交易规则、建仓策略及自动化监控系统的技术实现。内容去除了情绪化表达,仅保留核心参数与操作指南。
核心理念: 核心-卫星策略 (Core-Satellite) + 平均成本法 (DCA) + 逆势布局 (BTD)
风险偏好: 进取型(含防御机制)
1. 资产配置 (Target Allocation)
组合采用等权配置(Equal Weight),每年8月强制再平衡。
| 代码 | 资产类型 | 角色定位 | 权重 | 备注 |
|---|---|---|---|---|
| IWY | 罗素Top200成长 | Alpha (矛) | 20% | 大盘成长,捕捉科技巨头收益 |
| SPMO | 标普500动量 | Alpha (风) | 20% | 动量因子,捕捉市场最强趋势 |
| RSP | 标普500等权 | Beta (锚) | 20% | 均值回归,市场宽度的物理化身 |
| SCHD | 红利增长 | Shield (盾) | 20% | 防御性价值,替代地产/高波动的VNQ |
| SGOV | 超短债/现金 | Cash (粮) | 20% | 极低波动,危机时刻的抄底弹药 |
注:原策略中的 PFF (优先股) 和 VNQ (地产) 因流动性风险及利率敏感度被剔除,分别由 SGOV 和 SCHD 替代。
2. 交易规则 (Trading Rules)
2.1 常态操作 (DCA)
-
时间:每月 1 日。
-
操作:固定金额买入,等权分配至上述 5 只 ETF。
2.2 逆势加仓 (BTD - Buy The Dip)
基于 RSP (标普等权) 的表现判断市场情绪:
- 一级信号:RSP 单日跌幅达到 -1%。
- 操作:当月实施第一次额外增持。
- 二级信号:RSP 月度累计跌幅达到 -5%。
- 操作:实施第二次大额增持(黄金坑)。
- 保底机制:
- 若当月未触发上述任何信号,在第三个星期五收盘前强制实施一次增持。
2.3 极端行情应对 (Humanity’s Extreme)
- 触发条件:VIX 指数 > 35 或 市场宽度 < 15%。
- 操作:
- 暂停常规 DCA。
- 卖出 SGOV (或 FLOT) 变现。
- 配置调整:超配 IWY/RSP,低配 SPMO (防范动量崩盘风险)。
3. 过渡期建仓策略 (Transition Strategy)
针对当前持有大量 FLOT (浮动利率债) 且市场处于高位的现状,采用“非对称加速建仓”方案。
目标:将 FLOT 转换为 New Hi5 组合,保留 20% 资金进入 SGOV。
周期:预计 15-18 个月(视市场波动情况自动缩短)。
资金参数配置 (BUILD_RULES):
| 场景 | 触发条件 | 投入金额 (USD) | 资金来源 |
|---|---|---|---|
| 基础定投 | 每月 1 日 | $6,000 | FLOT |
| 轻微回调 | RSP 日跌 -1% | $6,000 | FLOT |
| 深度回调 | RSP 月跌 -5% | $20,000 | FLOT |
| 极端恐慌 | VIX > 35 | All-in | FLOT (剩余全部) |
操作提示:当建仓完成(FLOT 耗尽或 SGOV 达标)后,需在代码中将
IS_BUILDING_PHASE置为False。
4. 自动化系统架构 (Technical Implementation)
系统基于 GitHub Actions 和 Python 实现无服务器自动监控。
-
运行时间:每个交易日 UTC 21:30 (美股收盘后)。
-
数据源:Yahoo Finance (
yfinance)。 -
通知方式:SMTP 邮件推送。
4.1 目录结构
1
2
3
4
5
6
.
├── .github/
│ └── workflows/
│ └── hi5_us_check.yml # 调度配置文件
├── monitor_hi5.py # 核心策略脚本
└── ...
4.2 核心代码 (monitor_hi5.py)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
import yfinance as yf
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.header import Header
import datetime
import os
import pandas as pd
# --- 环境配置 ---
EMAIL_SENDER = os.environ.get("MAIL_USER")
EMAIL_PASSWORD = os.environ.get("MAIL_PASS")
EMAIL_RECEIVER = os.environ.get("MAIL_USER")
# --- 策略配置 ---
IS_BUILDING_PHASE = True # 建仓期开关
BUILD_RULES = {
"monthly_base": 6000, # 基础定投
"dip_1pct": 6000, # 日跌幅加速
"dip_5pct": 20000, # 月跌幅加速
"source_asset": "FLOT" # 资金池
}
def get_market_data():
rsp = yf.Ticker("RSP")
rsp_hist = rsp.history(period="1y")
vix = yf.Ticker("^VIX")
vix_hist = vix.history(period="1y")
return rsp_hist, vix_hist
def calculate_vix_percentile(current_val, history, days):
if len(history) < days: return 0.0
recent_data = history['Close'].tail(days)
count_lower = (recent_data < current_val).sum()
return (count_lower / len(recent_data)) * 100
def analyze_strategy(rsp_hist, vix_hist):
signals = []
today_date = datetime.datetime.now().date()
today_close = rsp_hist['Close'].iloc[-1]
prev_close = rsp_hist['Close'].iloc[-2]
# MTD & YTD Calculation
curr_month = today_date.strftime("%Y-%m")
month_data = rsp_hist[rsp_hist.index.astype(str).str.contains(curr_month)]
mtd_change = (today_close - month_data['Open'].iloc[0]) / month_data['Open'].iloc[0] if not month_data.empty else 0.0
curr_year = today_date.strftime("%Y")
year_data = rsp_hist[rsp_hist.index.astype(str).str.contains(curr_year)]
ytd_change = (today_close - year_data['Open'].iloc[0]) / year_data['Open'].iloc[0] if not year_data.empty else 0.0
current_vix = vix_hist['Close'].iloc[-1]
daily_change = (today_close - prev_close) / prev_close
# --- 信号逻辑 ---
# 1. 年度再平衡
if today_date.month == 8 and 1 <= today_date.day <= 5:
signals.append("📅【年度再平衡】8月已至,请将 Hi5 组合恢复至等权 (20% each)。")
# 2. 定投 (DCA)
if today_date.day == 1:
if IS_BUILDING_PHASE:
amt = BUILD_RULES['monthly_base']
signals.append(f"🏗️【建仓-月度定投】卖出 ${amt} {BUILD_RULES['source_asset']} -> 买入 Hi5 各 ${amt/4:,.0f}。")
else:
signals.append("💰【DCA 定投日】每月1号,执行固定金额增持。")
# 3. BTD (-1% Daily)
if daily_change <= -0.01:
if IS_BUILDING_PHASE:
amt = BUILD_RULES['dip_1pct']
signals.append(f"📉【建仓-加速(小)】RSP 跌 {daily_change:.2%}。卖出 ${amt} {BUILD_RULES['source_asset']} -> 买入 Hi5。")
else:
signals.append(f"📉【BTD 机会】RSP 单日跌幅 {daily_change:.2%},建议第一次额外增持。")
# 4. BTD (-5% Monthly)
if mtd_change <= -0.05:
if IS_BUILDING_PHASE:
amt = BUILD_RULES['dip_5pct']
signals.append(f"🕳️【建仓-加速(黄金坑)】RSP 月跌 {mtd_change:.2%}。卖出 ${amt} {BUILD_RULES['source_asset']} -> 买入 Hi5。")
else:
signals.append(f"🕳️【黄金坑】RSP 本月跌幅 {mtd_change:.2%},建议第二次大额增持。")
# 5. VIX Extreme
if current_vix > 35:
if IS_BUILDING_PHASE:
signals.append(f"🔥【建仓-狙击模式】VIX {current_vix:.2f}。暂停定投,卖出剩余 {BUILD_RULES['source_asset']} All-in。")
else:
signals.append(f"🔥【人性之极】VIX {current_vix:.2f}。暂停定投,All-in 现金抄底。")
# 6. 保底机制
if today_date.weekday() == 4 and 15 <= today_date.day <= 21:
signals.append("🛡️【保底检查】本月第三个周五。若无操作,请强制买入。")
stats = {
'rsp_price': today_close, 'rsp_mtd': mtd_change, 'rsp_ytd': ytd_change,
'vix_val': current_vix,
'vix_p120': calculate_vix_percentile(current_vix, vix_hist, 120)
}
return signals, stats
def send_email(signals, stats):
if not EMAIL_SENDER: return
msg = MIMEMultipart()
msg['From'] = Header("Hi5 Strategy", 'utf-8')
msg['To'] = Header("Investor", 'utf-8')
msg['Subject'] = Header(f"【Hi5 日报】{datetime.datetime.now().strftime('%Y-%m-%d')}", 'utf-8')
signal_html = "<h3>🚀 行动指令:</h3><ul>" + "".join([f"<li style='color:red;'>{s}</li>" for s in signals]) + "</ul><hr>" if signals else "<h3>✅ 无操作信号</h3><hr>"
if IS_BUILDING_PHASE: signal_html = f"<p style='color:blue;'>🏗️ 建仓模式中</p>" + signal_html
body = f"""
<html><body>
{signal_html}
<table border="1" style="border-collapse:collapse;width:300px;">
<tr><td>RSP 价格</td><td>${stats['rsp_price']:.2f}</td></tr>
<tr><td>RSP 本月涨跌</td><td>{stats['rsp_mtd']:.2%}</td></tr>
<tr><td>VIX 指数</td><td>{stats['vix_val']:.2f}</td></tr>
<tr><td>VIX 120日分位</td><td>{stats['vix_p120']:.0f}%</td></tr>
</table>
</body></html>
"""
msg.attach(MIMEText(body, 'html', 'utf-8'))
try:
server = smtplib.SMTP_SSL("smtp.gmail.com", 465)
server.login(EMAIL_SENDER, EMAIL_PASSWORD)
server.sendmail(EMAIL_SENDER, EMAIL_RECEIVER, msg.as_string())
server.quit()
except: pass
if __name__ == "__main__":
rsp, vix = get_market_data()
signals, stats = analyze_strategy(rsp, vix)
send_email(signals, stats)
4.3 配置文件 (.github/workflows/hi5_us_check.yml)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
name: Hi5 US Strategy Check
on:
schedule:
- cron: '30 21 * * 1-5' # UTC 21:30 (美股收盘后)
workflow_dispatch:
jobs:
run_hi5:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with: { python-version: '3.9' }
- run: pip install yfinance pandas requests
- run: python monitor_hi5.py
env:
MAIL_USER: $
MAIL_PASS: $