Hi5个人魔改版投资策略与自动化执行手册

Hi5个人魔改版投资策略与自动化执行手册

作者: shisaq 日期: February 3, 2026

这份笔记旨在参考雷公的原策略基础上,客观记录“新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 (标普等权) 的表现判断市场情绪:

  1. 一级信号:RSP 单日跌幅达到 -1%
    • 操作:当月实施第一次额外增持。
  2. 二级信号:RSP 月度累计跌幅达到 -5%
    • 操作:实施第二次大额增持(黄金坑)。
  3. 保底机制
    • 若当月未触发上述任何信号,在第三个星期五收盘前强制实施一次增持。

2.3 极端行情应对 (Humanity’s Extreme)

  • 触发条件:VIX 指数 > 35 或 市场宽度 < 15%。
  • 操作
    • 暂停所有常规 DCA 和 BTD 信号
    • 卖出 SGOV (或 FLOT) 变现。
    • 差异化配置:超配 IWY/RSP (各30%),标配 SCHD/SGOV (各15%),低配 SPMO (10%)。

:低配 SPMO 是因为在市场急转弯(V型反转)时,动量因子容易崩盘。

2.4 信号优先级

当多个条件同日满足时,按以下优先级执行:

  1. 年度再平衡 > 一切(8月1-5日无条件执行)
  2. VIX > 35 > 所有常规操作(进入狙击模式,DCA/BTD 全部暂停)
  3. 月跌 -5% > 日跌 -1%(同日触发时,只执行 -5% 的大额加仓,不重复加仓)
  4. 月度 DCABTD 可叠加(1号且日跌1%时,两笔都执行)
  5. 保底机制 仅在本月无任何买入时触发

3. 过渡期建仓策略 (Transition Strategy)

针对当前持有大量 FLOT (浮动利率债) 且市场处于高位的现状,采用“非对称加速建仓”方案。

目标:将 FLOT 转换为 New Hi5 组合,保留 20% 资金进入 SGOV。

周期:预计 15-18 个月(视市场波动情况自动缩短)。

资金参数配置 (BUILD_RULES):

场景 触发条件 投入金额 (USD) 资金来源 倍数
基础定投 每月 1 日 $6,000 FLOT 1x
轻微回调 RSP 日跌 -1% $6,000 FLOT 1x
深度回调 RSP 月跌 -5% $18,000 FLOT 3x
极端恐慌 VIX > 35 All-in FLOT (剩余全部)

FLOT 与 SGOV 的关系:建仓期间,FLOT 充当 SGOV 的角色(两者都是低波动现金等价物)。因此建仓期买入只分4份(IWY/SPMO/RSP/SCHD),不额外买 SGOV。等 FLOT 耗尽、建仓完成后,新增 DCA 资金才分5份,其中20%进入 SGOV。

操作提示:当建仓完成(FLOT 耗尽)后,需在代码中将 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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
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

# --- 环境变量 (兼容现有 Secrets) ---
EMAIL_SENDER = os.environ.get("MAIL_USER")
EMAIL_PASSWORD = os.environ.get("MAIL_PASS")
EMAIL_RECEIVER = os.environ.get("MAIL_USER")

# ==============================================================================
# 🏗️ 建仓模式配置 (BUILDING PHASE CONFIG)
# 当你的 FLOT 耗尽,建仓完成后,请将 IS_BUILDING_PHASE 改为 False
# ==============================================================================
IS_BUILDING_PHASE = True  

# 建仓期资金规则 (单位: 美元)
BUILD_RULES = {
    "monthly_base": 6000,  # 每月1号固定投入 (DCA)
    "dip_1pct": 6000,       # RSP日跌1% 额外投入 (BTD小)
    "dip_5pct": 18000,      # RSP月跌5% 额外投入 (BTD大, 3x)
    "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) - 使用 pd.Timestamp 过滤,避免字符串匹配问题
    month_start = pd.Timestamp(today_date.replace(day=1), tz=rsp_hist.index.tz)
    month_data = rsp_hist[rsp_hist.index >= month_start]
    mtd_change = (today_close - month_data['Open'].iloc[0]) / month_data['Open'].iloc[0] if not month_data.empty else 0.0
    
    # 今年涨跌幅 (YTD)
    year_start = pd.Timestamp(today_date.replace(month=1, day=1), tz=rsp_hist.index.tz)
    year_data = rsp_hist[rsp_hist.index >= year_start]
    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

    # ========== 策略逻辑 (按优先级排列) ==========
    #
    # 优先级: 年度再平衡 > VIX极端 > BTD月跌5% > BTD日跌1% > 月度DCA > 保底检查
    # VIX > 35 时,所有常规信号(DCA/BTD)被抑制,进入狙击模式
    # 月跌5% 触发时,日跌1% 信号被抑制(避免同日双重触发)

    is_vix_extreme = current_vix > 35
    is_monthly_dip = mtd_change <= -0.05
    is_daily_dip = daily_change <= -0.01
    is_dca_day = today_date.day == 1
    is_rebalance = today_date.month == 8 and 1 <= today_date.day <= 5
    is_safety_net = today_date.weekday() == 4 and 15 <= today_date.day <= 21

    # 1. 年度再平衡 (最高优先级,不受 VIX 影响)
    if is_rebalance:
        signals.append("📅【年度再平衡】现在是8月,请强制将 Hi5 组合恢复至等权 (20% each)。")

    # 2. VIX 极端模式 (抑制所有常规 DCA/BTD)
    if is_vix_extreme:
        src = BUILD_RULES['source_asset']
        if IS_BUILDING_PHASE:
            signals.append(f"🔥【建仓-狙击模式】VIX {current_vix:.2f} > 35。⚠️ 暂停常规定投!")
            signals.append(f"   👉 将剩余 {src} 全部卖出!")
            signals.append(f"   👉 差异化抄底配置:")
            signals.append(f"       IWY  30% (超配,科技龙头抄底)")
            signals.append(f"       RSP  30% (超配,均值回归)")
            signals.append(f"       SCHD 15% (标配,红利防御)")
            signals.append(f"       SGOV 15% (标配,保留弹药)")
            signals.append(f"       SPMO 10% (低配,动量崩盘风险高)")
        else:
            signals.append(f"🔥【人性之极】VIX 飙升至 {current_vix:.2f}。暂停定投。")
            signals.append(f"   👉 卖出 SGOV,差异化抄底:IWY/RSP 各30%, SCHD/SGOV 各15%, SPMO 10%。")
    else:
        # --- 常规模式 (VIX <= 35) ---

        # 3. BTD 月跌 -5% (黄金坑,优先于日跌)
        if is_monthly_dip:
            if IS_BUILDING_PHASE:
                amt = BUILD_RULES['dip_5pct']
                each = amt / 4
                src = BUILD_RULES['source_asset']
                signals.append(f"🕳️【建仓-加速(黄金坑)】RSP 本月累跌 {mtd_change:.2%}。请卖出 ${amt:,} {src}。")
                signals.append(f"   👉 买入 IWY, SPMO, RSP, SCHD 各 ${each:,.0f}。")
                if is_daily_dip:
                    signals.append(f"   ℹ️ 今日同时触发日跌信号 ({daily_change:.2%}),已合并至黄金坑操作中,不重复加仓。")
            else:
                signals.append(f"🕳️【黄金坑】RSP 本月跌幅 {mtd_change:.2%} (低于-5%),建议第二次大额增持。")

        # 4. BTD 日跌 -1% (仅在未触发月跌时生效)
        elif is_daily_dip:
            if IS_BUILDING_PHASE:
                amt = BUILD_RULES['dip_1pct']
                each = amt / 4
                src = BUILD_RULES['source_asset']
                signals.append(f"📉【建仓-加速(小)】RSP 跌幅 {daily_change:.2%}。请卖出 ${amt:,} {src}。")
                signals.append(f"   👉 买入 IWY, SPMO, RSP, SCHD 各 ${each:,.0f}。")
            else:
                signals.append(f"📉【BTD 机会】RSP 单日跌幅 {daily_change:.2%},建议第一次额外增持。")

        # 5. 月度定投 (DCA)
        if is_dca_day:
            if IS_BUILDING_PHASE:
                amt = BUILD_RULES['monthly_base']
                each = amt / 4
                src = BUILD_RULES['source_asset']
                signals.append(f"🏗️【建仓-月度定投】每月1号。请卖出 ${amt:,} {src}。")
                signals.append(f"   👉 买入 IWY, SPMO, RSP, SCHD 各 ${each:,.0f}。")
            else:
                signals.append("💰【DCA 定投日】每月1号,请执行固定金额增持 Hi5 各 ETF。")

        # 6. 保底增持 (第三个周五)
        if is_safety_net:
            signals.append("🛡️【保底检查】本月第三个周五。若本月尚未有任何买入动作,请强制执行一次月度定投。")

    stats = {
        'rsp_price': today_close, 'rsp_mtd': mtd_change, 'rsp_ytd': ytd_change,
        'vix_val': current_vix,
        'vix_p20': calculate_vix_percentile(current_vix, vix_hist, 20),
        'vix_p60': calculate_vix_percentile(current_vix, vix_hist, 60),
        'vix_p120': calculate_vix_percentile(current_vix, vix_hist, 120)
    }
    return signals, stats

def send_email(signals, stats):
    if not EMAIL_SENDER or not EMAIL_PASSWORD:
        print("未配置邮箱 Secrets,跳过发送")
        return

    msg = MIMEMultipart()
    msg['From'] = Header("Hi5 Strategy", 'utf-8')
    msg['To'] = Header("Master Investor", 'utf-8')
    msg['Subject'] = Header(f"【Hi5 美股日报】{datetime.datetime.now().strftime('%Y-%m-%d')}", 'utf-8')

    # 根据是否有信号调整颜色
    header_color = "red" if signals else "green"
    status_text = "需要行动 (Action Required)" if signals else "市场平静 (No Action)"

    signal_html = ""
    if signals:
        signal_html = "<h3>🚀 行动指令:</h3><ul>" + "".join([f"<li style='color:#d9534f;font-weight:bold;font-size:16px;margin-bottom:8px;'>{s}</li>" for s in signals]) + "</ul><hr>" 
    else:
        signal_html = "<h3>✅ 今日无操作信号,持仓躺平。</h3><hr>"
    
    # 增加建仓进度提示
    phase_info = ""
    if IS_BUILDING_PHASE:
        phase_info = f"<p style='color:blue; font-weight:bold;'>当前处于:🏗️ 建仓模式 (资金来源: {BUILD_RULES['source_asset']})</p>"

    body = f"""
    <html><body>
        {phase_info}
        {signal_html}
        <h3>📊 市场快照</h3>
        <table border="1" style="border-collapse: collapse; width: 450px; text-align: left;">
            <tr style="background-color: #f2f2f2;"><th>指标</th><th>数值 / 状态</th></tr>
            <tr><td>RSP 价格</td><td>${stats['rsp_price']:.2f}</td></tr>
            <tr><td>RSP 本月涨跌</td><td style="color:{'red' if stats['rsp_mtd']<0 else 'green'}"><b>{stats['rsp_mtd']:.2%}</b></td></tr>
            <tr><td>RSP 今年涨跌</td><td style="color:{'red' if stats['rsp_ytd']<0 else 'green'}">{stats['rsp_ytd']:.2%}</td></tr>
            <tr><td>VIX 指数</td><td><b>{stats['vix_val']:.2f}</b></td></tr>
            <tr><td>VIX 历史分位(120日)</td><td>{stats['vix_p120']:.0f}% (越高越恐慌)</td></tr>
        </table>
        <br>
        <div style="font-size:12px; color:gray;">
            <p>Strategy Rules:</p>
            <ul>
                <li>DCA: 每月1号定投 ${BUILD_RULES['monthly_base']:,}</li>
                <li>BTD-1: RSP日跌1% 加投 ${BUILD_RULES['dip_1pct']:,}</li>
                <li>BTD-2: RSP月跌5% 加投 ${BUILD_RULES['dip_5pct']:,}</li>
            </ul>
            <p><em>System generated by GitHub Actions.</em></p>
        </div>
    </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()
        print("邮件发送成功")
    except Exception as e:
        print(f"邮件失败: {e}")

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: $