10年模拟投资计划

10年模拟投资计划

作者: shisaq 日期: January 3, 2026

假如小H是一个基金经理,他负责管理100w元的财产,管理年限是10年,最大回撤是12w。

文档日期:2026年1月5日

初始本金:1,000,000 RMB

投资周期:10年

预期目标:年化收益 5.5% ~ 7.0%

风控红线:最大回撤控制在 ~12% (约12万元) 以内

策略核心:哈利·布朗永久组合改良版(哑铃型配置 + 股债金互补)


1. 资产配置模型表

雪球组合链接https://xueqiu.com/P/ZH3566600

账户分层 资产标的 代码 权重 金额 角色定义
防守-收益 红利低波100 159307 20% 200,000 现金奶牛 / 底仓股息
防守-对冲 30年国债 511090 20% 200,000 危机对冲 / 票息收益
进攻-全球 标普500 159655* 30% 300,000 核心增长 (场外联接)
进攻-中国 沪深300 510300 15% 150,000 估值修复
极端保险 黄金ETF 518660 15% 150,000 法币对冲

2. 建仓执行路径

阶段一:一次性建仓(T日完成)

对象“生息类”资产

逻辑:既然是收息资产,早一天买入,早一天享受票息;且红利低波本身具备防守属性。

  • 操作指令
    1. 买入 红利低波100 (159307)200,000 元。
    2. 买入 30年国债 (511090)200,000 元。
  • 阶段一合计支出:400,000 元。

阶段二:定投准备(T日完成)

对象:定投储备资金。

  • 操作指令
    1. 将剩余 600,000 元买入货币ETF(511880)或国债逆回购。
    2. 此账户为“定投弹药库”。

阶段三:定投执行(T+1月 至 T+10月)

对象“高波动/无息类”资产

逻辑:黄金、美股、A股波动大,且黄金不生息,定投可平滑成本,降低“买在高点”的懊悔感。

  • 执行周期:共10个月。
  • 操作指令:每月固定日期(如15日),从“弹药库”赎回 60,000 元,按以下分配:
标的 每月定投金额 调整理由
标普500 (场外) 30,000 核心增长,平滑波动
黄金ETF (518660) 15,000 规避高位站岗风险
沪深300 (510300) 15,000 周期波动大,定投更佳
每月合计 60,000 10个月耗尽60万

3. 阈值再平衡机制(应对极端波动)

除了年度定期再平衡外,引入 “±5% 绝对偏离阈值” 机制,以捕捉市场极端机会。

  • 触发条件:当任一资产的当前权重偏离目标权重超过 5% (绝对值) 时。

  • 监控频率:月度概览或重大市场事件发生时。

  • 操作指令:一旦触发,立即对全组合进行再平衡,恢复至初始目标比例。

阈值速查表

  • 标普500 (30%):低于 25% 补仓 / 高于 35% 止盈
  • 红利/国债 (20%):低于 15% 补仓 / 高于 25% 止盈
  • 沪深/黄金 (15%):低于 10% 补仓 / 高于 20% 止盈

逻辑:此机制通常在牛市泡沫破裂前(强制卖出)或危机恐慌底(强制买入)时触发,是提升长期收益率的关键“胜负手”。


4. 风险提示

  • 红利回撤风险:虽然红利低波是一次性买入,但若遇极端行情短期仍可能浮亏 5-10%。请记住它是你的“生息底仓”,只要股息在发,股价波动不仅无害,反而是再平衡的机会

  • 黄金踏空风险:将黄金改为定投后,若下个月发生地缘黑天鹅导致金价暴涨,你可能只有 1/10 的仓位在车上。这是为了平稳而付出的代价(但你有30年国债作为另一个保险,所以此风险可控)。

5. 部署自动化程序

获取邮箱 SMTP 授权码(以QQ邮箱为例,Gmail/163同理)

  1. 登录网页版QQ邮箱 -> 设置 -> 账号。
  2. 找到 POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务
  3. 开启 POP3/SMTP服务
  4. 点击“生成授权码”,复制那串英文代码。这是脚本发邮件的“密码”。

在 GitHub 创建仓库

  1. 登录 GitHub,点击右上角 + -> New repository

  2. Repository name 填 xueqiu-monitor,勾选 Private(私有,保护你的隐私),点击 Create。

配置密钥 (Secrets)

为了不把你的Cookie和密码明文写在代码里,我们需要用 GitHub Secrets。

  1. 在你的仓库页面,点击上方的 Settings -> 左侧侧边栏 Secrets and variables -> Actions

  2. 点击 New repository secret,依次添加以下 3 个变量:

Name (变量名) Secret (填入的内容)
XUEQIU_COOKIE 第一步里复制的那一长串 Cookie
MAIL_USER 你的邮箱地址 (如 123456@qq.com)
MAIL_PASS 第二步里获取的授权码

创建自动运行脚本

  1. 回到仓库首页,点击 Actions 标签。

  2. 点击 set up a workflow yourself

  3. 将文件名 main.yml 改为 daily_check.yml

  4. 清空 编辑框里的所有内容,复制粘贴以下代码:

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
name: Daily Portfolio Check

on:
  schedule:
    # 北京时间每天下午 15:30 运行 (UTC 07:30),确保收盘后检查
    - cron: '30 7 * * *'
  workflow_dispatch:

jobs:
  run_script:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v3

      - name: Set up Python
        uses: actions/setup-python@v4
        with:
          python-version: '3.9'

      - name: Install dependencies
        run: pip install requests

      - name: Run Monitor
        env:
          MAIL_USER: $
          MAIL_PASS: $
        run: python monitor.py

上传 Python 核心代码

  1. 回到仓库首页 (Code 标签),点击 Add file -> Create new file

  2. 文件名填 monitor.py

  3. 复制粘贴以下 Python 代码:

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
import os
import requests
import smtplib
from email.mime.text import MIMEText
from email.header import Header

# ================= 配置区域 (每年再平衡后需更新此处) =================
# 填写你“建仓/再平衡”那一天,各个标的的收盘价(大概即可)
# 格式: "代码": [权重, 基准价格(Cost_Price), 名称]
# 注意:代码前要加 sh (上海) 或 sz (深圳)
# 红利低波网址:
# https://quotes.sina.cn/hs/company/quotes/view/sz159307?from=nbsearchresult

PORTFOLIO_CONFIG = {
    "sz159307": {"target": 0.20, "base_price": 1.061, "name": "红利低波"},  # 示例价格,请按实际修改
    "sh511090": {"target": 0.20, "base_price": 113.967, "name": "30年国债"},
    "sz159655": {"target": 0.30, "base_price": 1.824, "name": "标普500"},  # 场内代码作为价格锚点
    "sh510300": {"target": 0.15, "base_price": 4.825, "name": "沪深300"},
    "sh518660": {"target": 0.15, "base_price": 9.478, "name": "黄金ETF"}
}
# ===================================================================

def get_current_prices(codes):
    """
    从新浪财经获取无需Cookie的实时行情
    """
    url = f"http://hq.sinajs.cn/list={','.join(codes)}"
    headers = {"Referer": "http://finance.sina.com.cn/"}
    
    try:
        resp = requests.get(url, headers=headers)
        if resp.status_code != 200:
            return None
        
        # 解析数据
        prices = {}
        # 返回格式: var hq_str_sz159307="名字,开盘,昨收,现价....";
        lines = resp.text.split('\n')
        for line in lines:
            if len(line) < 10: continue
            code = line.split('="')[0].split('_str_')[1]
            data = line.split('="')[1].split(',')
            current_price = float(data[3]) # 第4个字段是当前价格
            
            # 如果停牌或未开盘(现价为0),用昨日收盘价(第3个字段)代替
            if current_price == 0:
                current_price = float(data[2])
                
            prices[code] = current_price
        return prices
    except Exception as e:
        print(f"获取行情失败: {e}")
        return None

def calculate_weights(current_prices):
    """
    根据基准价格和现价,模拟计算当前权重
    """
    # 1. 假设初始总资金为 10000 元 (方便计算)
    initial_total = 10000
    
    # 2. 计算各资产当前的模拟市值
    simulated_values = {}
    total_market_value = 0
    
    for code, config in PORTFOLIO_CONFIG.items():
        if code not in current_prices:
            continue
            
        # 初始持仓数量 = (总资金 * 目标比例) / 基准价格
        initial_shares = (initial_total * config['target']) / config['base_price']
        
        # 当前市值 = 初始数量 * 当前价格
        current_val = initial_shares * current_prices[code]
        
        simulated_values[code] = current_val
        total_market_value += current_val
        
    # 3. 计算当前权重
    current_weights = {}
    for code, val in simulated_values.items():
        current_weights[code] = val / total_market_value
        
    return current_weights

def send_email(subject, content):
    mail_user = os.environ.get("MAIL_USER")
    mail_pass = os.environ.get("MAIL_PASS")
    # 如果没有配置环境变量,直接返回(方便本地测试)
    if not mail_user or not mail_pass:
        print("未配置邮箱,跳过发送")
        return

    msg = MIMEText(content, 'plain', 'utf-8')
    msg['Subject'] = Header(subject, 'utf-8')
    msg['From'] = Header("Portfolio Monitor", 'utf-8')
    msg['To'] = Header("Investor", 'utf-8')

    try:
        # 使用Gmail邮箱服务器
        server = smtplib.SMTP_SSL("smtp.gmail.com", 465)
        server.login(mail_user, mail_pass)
        server.sendmail(mail_user, [mail_user], msg.as_string())
        server.quit()
        print("邮件发送成功")
    except Exception as e:
        print(f"邮件发送失败: {e}")

def main():
    codes = list(PORTFOLIO_CONFIG.keys())
    prices = get_current_prices(codes)
    
    if not prices:
        print("无法获取价格数据")
        return

    weights = calculate_weights(prices)
    alerts = []
    report = []
    
    print(f"{'名称':<8} | {'现价':<8} | {'基准价':<8} | {'当前权重':<8} | {'目标权重':<8} | {'偏差'}")
    print("-" * 70)
    
    for code, real_weight in weights.items():
        info = PORTFOLIO_CONFIG[code]
        name = info['name']
        target = info['target']
        curr_price = prices[code]
        base_price = info['base_price']
        diff = real_weight - target
        
        line = f"{name:<8} | {curr_price:<8.3f} | {base_price:<8.3f} | {real_weight*100:<7.2f}% | {target*100:<7.2f}% | {diff*100:+.2f}%"
        print(line)
        report.append(line)
        
        # 触发阈值:偏差绝对值 > 5% (即 0.05)
        if abs(diff) >= 0.05:
            action = "卖出止盈" if diff > 0 else "买入补仓"
            alerts.append(f"⚠️ 【{name}】 偏差 {diff*100:+.2f}% (现权重 {real_weight*100:.1f}%) -> 建议{action}")

    # 发送逻辑
    if alerts:
        content = "检测到资产配置偏离阈值!\n\n" + "\n".join(alerts) + "\n\n详细数据:\n" + "\n".join(report)
        send_email("【警报】资产配置再平衡提醒", content)
    else:
        print("所有资产均在正常范围内。")

if __name__ == "__main__":
    main()