← 返回博客列表

【量化系统从零构建 #19】策略装配与资产配置:多策略叠加权重·ETF 轮动·信号导出

2026年09月09日 08:51 · 智兔数服 · 量化系统从零构建

摘要:【量化系统从零构建 #19】策略装配与资产配置:多策略叠加权重·ETF 轮动·信号导出 系列:《量化系统从零构建》|连载项目 · 纯 GET 取数 · 仅依赖 requests 适用:

系列:《量化系统从零构建》|连载项目 · 纯 GET 取数 · 仅依赖 requests
适用:想把选股(#13)、择时(#15)、风控(#18)拼成「一个可下单组合」的读者;数据由智兔数服提供,本篇给多策略加权投票、ETF 轮动、股债配置与信号导出,不依赖任何行情终端,也不发起网络请求。所有数值均为算法演示,不构成收益承诺。

1. 你将得到什么

  • combine_strategies:多策略信号按权重投票,输出综合方向与分数。
  • etf_rotation:按动量在 ETF 池里挑前 N 只(轮动)。
  • asset_alloc:股债按波动倒数分权重(简化风险平价)。
  • export_signals:把当日信号导出为 JSON,交给下单/看板。
  • 本篇交付:装配与配置闭环,#20 看板在其上接信号做展示与调度。

2. 本篇取数约定

本篇是装配层,不发起网络请求。吃 #13 选股池打分、#15 择时信号、#18 风控权重,纯 Python 组合,不引 numpy/pandas。数据来自 智兔数服(www.zhituapi.com)。

3. 装配与配置组件

组件 作用
多策略投票 各策略信号×权重求和定方向
ETF 轮动 动量高的 ETF 优先持有
股债配置 波动低的多配,控制组合波动
信号导出 落盘为 JSON 供下游消费

4. 核心模板函数

import sys, json, os, tempfile


def combine_strategies(signal_map, weights):
    """多策略加权投票。signal_map: {名称: 1/-1/0}; weights: {名称: 权重}。
    返回 (综合分数, 综合方向): 分数=Σ(w*s)/Σw,方向=sign(分数)。"""
    total_w = 0.0
    score = 0.0
    for name, s in signal_map.items():
        w = weights.get(name, 0.0)
        score += w * s
        total_w += w
    if total_w == 0:
        return 0.0, 0
    score /= total_w
    return score, (1 if score > 0 else (-1 if score < 0 else 0))


def momentum(prices):
    """区间动量 = 末/首 - 1。"""
    if len(prices) < 2:
        return 0.0
    return prices[-1] / prices[0] - 1


def etf_rotation(prices_by_code, top_n=2):
    """按动量降序取前 top_n 只 ETF 代码(轮动)。prices_by_code: {code: [价序列]}。"""
    ranked = sorted(prices_by_code.items(),
                    key=lambda kv: momentum(kv[1]), reverse=True)
    return [code for code, _ in ranked[:top_n]]


def asset_alloc(vols, floor=0.05):
    """股债等风险预算:波动倒数权重,下限 floor 防极端。返回归一化权重 dict。"""
    inv = {c: (1.0 / v if v and v > 0 else 0.0) for c, v in vols.items()}
    s = sum(inv.values())
    if s == 0:
        n = len(vols)
        return {c: 1.0 / n for c in vols}
    raw = {c: max(floor, w / s) for c, w in inv.items()}
    tot = sum(raw.values())
    return {c: w / tot for c, w in raw.items()}


def export_signals(signals, path):
    """把信号(列表或 dict)导出为 JSON 文件,返回路径。"""
    with open(path, "w", encoding="utf-8") as f:
        json.dump(signals, f, ensure_ascii=False, indent=2)
    return path


def run_check():
    # 合成:两策略同多 -> 综合多
    sm = {"选股": 1, "择时": 1}
    w = {"选股": 0.6, "择时": 0.4}
    score, d = combine_strategies(sm, w)
    assert d == 1 and score > 0
    # 一多一空等权 -> 综合 0(持有)
    sm2 = {"选股": 1, "择时": -1}
    _, d2 = combine_strategies(sm2, {"选股": 1, "择时": 1})
    assert d2 == 0
    # ETF 轮动:B 动量最高
    prices = {"A": [10, 11], "B": [10, 13], "C": [10, 10.5]}
    assert etf_rotation(prices, top_n=1) == ["B"]
    # 股债配置:债波动低 -> 权重大
    alloc = asset_alloc({"股": 0.2, "债": 0.05})
    assert alloc["债"] > alloc["股"]
    # 信号导出可回读
    sigs = [{"code": "600000.SH", "signal": 1},
            {"code": "510300.SH", "signal": 1}]
    p = os.path.join(tempfile.gettempdir(), "_zt_sig.json")
    export_signals(sigs, p)
    loaded = json.load(open(p, encoding="utf-8"))
    assert loaded == sigs
    os.remove(p)
    print("校验通过")


if __name__ == "__main__":
    if len(sys.argv) > 1 and sys.argv[1] == "--check":
        run_check()
    else:
        print("本篇把选股/择时/风控装配成组合并导出信号,不发起网络;"
              "信号可接 #20 看板与定时调度。")

5. 跑通示例

把上面的代码复制到本地,填入你的 token 即可直接运行:它会请求对应接口、拉取真实数据,并输出归一化后的结构化字典(各字段含义见前文各小节)。

6. 坑与注意事项

  1. 权重要归一combine_strategies 已按总权重归一,但权重别全 0,否则退化成持有。
  2. 轮动有换手成本:ETF 轮动每次换仓都吃 #16 成本,频率别太高。
  3. 股债配置是长期视角:风险平价按历史波动算,波动突变会滞后,需定期再平衡。
  4. 信号导出要留痕:JSON 落盘便于复盘与下游对账,别只打印不存。
  5. 不承诺收益:装配出的组合仅为算法演示,不构成收益保证。

7. 小结与下篇预告

本篇把选股、择时、风控钉成一个组合:加权投票定方向、ETF 轮动做战术、股债配置控波动,信号导出闭环。装配层完整。

下一篇计划写 #20《看板与部署:Notebook 看板·定时任务·告警·一键运行》:把本篇装配出的信号接入看板展示、盘前/盘后定时调度,配告警与本地一键运行。

8. 免责声明

本文仅演示策略装配与资产配置的算法,所有代码示例均以合成数据校验,未含任何真实数据;文中示例数据仅作演示用途,不构成投资建议,亦不承诺收益。


免费领取证书
数据来自 智兔数服(www.zhituapi.com):零 SDK、纯 GET、免费版即可起步。

领取路径:进入 www.zhituapi.com → 点击「请求证书」→「证书获取」→「免费版」(邮箱验证 3 步即可拿到 token)。

把代码里的 你的智兔token 换成你拿到的真实 token,上面的脚本就能直接打印多策略装配信号与 ETF 轮动权重数据。

想亲自试一下?免费获取证书