tradingSystem/MACRO_TIMING_PLAN.md

25 KiB
Raw Blame History

tradingSystem 宏观择时接入方案V3 可读版)

状态已定稿可以开工。日期2026-08-18。 本版把 V2.1 按项目输出风格重写(风格文件在 .claude/output-styles/readable-chinese.md。技术内容与 V2.1 一致,没有新增决定。 参数初值的依据是当天在桥机实跑的标定报告,报告存档在仓库根目录的 MACRO_CALIB_2026-08-18.md。


一、这件事为最终目标买什么

我们的最终目标是管好整体仓位。现在系统只会执行你手动下的仓位命令。大盘整体是偏热还是偏冷,系统自己不看,也不动。

这次要加一个宏观择时层。它每天算一个指标,判断大盘处在偏热、偏冷还是中性阶段。偏冷阶段自动提高仓位。偏热阶段先管住买入,等热度退潮时再降低仓位。

买到的东西有三样。第一,极端行情里仓位会自动往安全方向走,不再完全依赖人盯。第二,系统的自动买入不会在过热阶段火上浇油。第三,每次动作都有数据依据,事后可以复盘打分。

这一层动仓位只有一种方式,就是像你一样在命令台下命令。方案生成、合规检查、择时、下单、对账,全部走现有链路。现有代码的行为一行都不改。框架还预留了位置,将来接入别的宏观信号时,决策和执行部分不用再改。

二、这个指标是什么

指标叫股汇对冲指数。它回答一个问题最近二十个交易日A 股和人民币汇率是不是在同向共振。

股市涨,同时人民币贬值,这叫股强汇弱,指数走高。股市跌,同时人民币升值,指数走低。计算时还会减去一点资金利率的干扰,再做标准化,变成一个可以横向比较的分数。

分数的含义是:当前的共振强度,相对最近四十个交易日处在多极端的位置。分数放大了十倍,所以正负二十几就是很极端的读数。今天的读数是正十四点七,处在中性区。

数据来自三张表,都在 153 代理数据库里,已经实测确认。上证指数日线在表 zs_day_data。美元兑离岸人民币汇率在表 gp_fx_daily。上海银行间市场一个月期的拆借利率在表 gp_shibor。完整公式和列名对照放在附录一可不读。

三、总共做哪几件事

一共七件事,全部在持仓系统 tradingSystem 里做。其他四个系统一个字不动。

  1. 每个交易日九点三十五分算一次指数,结果存进一张新表,页面能看历史。
  2. 指数进入偏冷区时,当天就自动下加仓命令,或者先出建议等你点头。
  3. 指数进入偏热区时,当天先停掉系统的自动买入。停的范围包括自主提议的四类买入动作,也包括个股策略的买入开仓腿。
  4. 指数从偏热极值回落、共振破裂时,自动下降仓命令,或者先出建议。
  5. 管理页面加一块宏观择时面板。指数走势、所处区域、当日建议、闸的状态都在上面。
  6. 所有阈值和开关登记成运行参数,页面可调,默认整层关闭。
  7. 配一批单元测试,按步骤上线。每一步都有能亲手运行的验证命令。

四、关键规则,每条讲清做什么和为什么

总开关和三个档位。 有一个总开关和三个档位。第一档只算不动。第二档只出建议,你在页面点头才变成命令。第三档全自动下命令。当前是模拟仓,用户已拍板不设观察期,所以部署默认就是总开关打开加全自动档。任何时候你都能在页面收权降档。将来转实盘之前,这两个默认值要重新评估一次。

偏冷时进区当天就动。 指数低过负二十,当天就触发加仓。为什么不等确认:标定数据显示,冷区的反弹就发生在极值当口。进区当天动,之后五天大盘平均涨百分之二点一五,胜率九成二。多等一天,或者等指数回升再动,收益都明显变差。

偏热时先停买,回落才卖。 指数高过正二十五,当天只做一件事,就是停掉系统的自动买入。降仓命令要等指数从极值回落、跌破正十五才下。为什么不进区就卖:标定数据显示,这段样本里极热之后大盘平均还在涨。进区就卖等于和趋势作对,二十天要付出接近百分之三的机会成本。等共振破裂再卖,机会成本降到百分之一。停买这件事不用等,因为不买入没有机会成本,是便宜的保险。

动多少,按对数曲线定。 极值越深,调整越多,但加码越来越慢。这样第一步就有分量,又不会因为一个罕见的极端读数把仓位一次打空。降仓的幅度按本轮热度的峰值深度算,热得越狠,回落时降得越多。参数已按五年历史标定:常见深度对应大约五个点仓位,罕见的深极值对应大约十二个点,单个周期封顶二十个点。

四道防抖。 同一个极值周期里,指数不再创新深度就不再加码。同方向两次动作至少间隔三个交易日。每个信号每个交易日最多动一次。上一条宏观命令还没执行完时,不叠加新命令。

两条底线。 自动降仓最低降到总仓位百分之二十,再往下必须你亲自下命令。自动加仓不会突破你设定的总仓上限。

永远给用户和安全让路。 和你在途的命令方向冲突时这一轮就放弃记一笔账绝不强行执行。休假模式下整层不跑。你手动暂停买入期间不自动加仓。组合刹车期间不自动加仓。个股策略的卖出和平仓回补不受闸影响做T的买回腿也不会被拦。

数据拿不到就什么都不做。 任何一张源表取不到、样本不足或者数据过期,指数记为不可用。不可用时不下命令,也不落闸,页面亮黄条提醒。宁可不动,不猜方向。

每一步都留痕。 指数每天一行存进新表。每次动作,和每次想动但被拦,都写进评审账本。宏观层下的命令在署名栏写 macro和你下的命令一眼就能分开。

五、为什么不会破坏现有系统

宏观层动仓位只有一个出口,就是调用现有的命令下达函数(代码名 command_service.issue。它不直接写方案表、指令表和持仓账本也不碰执行器。

需要修改的现有文件一共十二处。每一处都只增加内容,不改动已有行为。其中两处是测试哨兵:项目在测试里钉了"表的总数"和"调度位的集合"两枚哨兵,加表和加调度位时按哨兵自己的注释指引各加一行。新增文件五个。把新增文件全部删掉,系统就回到今天的样子。决策系统、选股桥、择时层、行情侧,零改动。

总开关随时可以在页面一键关闭,关闭后系统行为立刻回到没有这一层的样子。当前是模拟仓,部署默认开启全自动档。转实盘前把默认改回关闭加只出建议,改的是两个参数初值,不用动代码结构。

修改点的完整清单在附录四,可不读。

六、上线步骤和验证

下面所有命令都在桥机 factorevaluation 上运行。每一步都写了预期读数。读数不对就停下来,不进入下一步。

第一步,标定。已完成。 2026 年 8 月 18 日已在桥机实跑。三张表的位置和列名已确认,参数初值已按报告回填本方案。报告在仓库根目录的 MACRO_CALIB_2026-08-18.md。标定脚本保留在仓库里供以后每月重跑校准。

第二步,写代码和单元测试。 代码写完后跑全量单测。

make test

预期最后一行是 ALL SUITES PASS。清单里应出现新批次 test_batch14_units.py并且没有任何一条 SUITE MISSING。

第三步,收盘后部署和建表。 改动打进镜像,所以必须重建容器。

make deploy
make check

预期 make check 的表清单里比部署前多出一张 pms_macro_signal。此时打开页面的参数设置应出现一批 PMS_MACRO 开头的参数,总开关显示开启,档位显示全自动。因为是收盘后部署,当天不会再有扫描,首次真实运行在次日九点三十五分。

第四步,试算一次。 试算只计算不落表,也不下命令。

curl -s -X POST 'http://127.0.0.1:38100/api/ops/macro-scan?dry_run=true' | python3 -m json.tool --no-ensure-ascii

预期返回里能看到指数当前值和所处区域。拿它和标定脚本同一天算出的值对照,两者应当一致。

第五步,全自动上线后的头几个交易日,看四样东西。 用户已拍板:现在是模拟仓,不设观察期,部署即全自动。所以这一步不是等待期,是上线后的日常核对清单。第一,每个交易日新表里多一行指数记录,面板的值和区域正常刷新。第二,指数进入偏冷区的日子,命令台自动出现署名 macro 的加仓命令,并正常展开成方案和指令。第三,指数进入偏热区的日子,自主买入和策略买开腿的跳过记录里出现宏观闸的字样;等指数回落穿出退出带,命令台自动出现降仓命令。第四,每次宏观动作在评审账本里都有对应的一行。

第六步,冲突演练。 先手动下一条加仓命令。趁它还在执行中,手动触发一次宏观扫描。

curl -s -X POST 'http://127.0.0.1:38100/api/ops/macro-scan' | python3 -m json.tool --no-ensure-ascii

预期评审账本里出现一条宏观让路的拒绝记录,命令台没有新增宏观命令。演练完撤掉手下的那条加仓命令。

第七步,首条自动命令的判收。 等到第一次真实触发,确认这条命令走完从下达、方案、指令、择时出手到进度回执的全程。模拟仓阶段允许它自然发生,不需要人为催熟。

回滚方法。 任何时候在页面把总开关改回关,整层立即停止。命令、闸、建议全停,系统回到现状,不需要重新部署。

七、已拍板的决定记录

以下六条是 2026 年 8 月 18 日与用户逐条确认的结果。写在这里,将来查依据不用翻对话。

  1. 仓位调整的形态用对数映射。不用固定步长,也不用一步调到位。
  2. 偏热侧等极值回落再降仓。依据是标定里这种做法机会成本最低。
  3. 偏冷侧进区当天就加仓,进入阈值取负二十。依据是标定里这一档样本更多,事后反弹也更强。
  4. 个股层面也受宏观控制。范围是自主提议的四类买入动作,加上个股策略的买入开仓腿。策略的卖出和平仓回补不受影响。
  5. 自动加仓让位于组合刹车。这是全方案唯一未经你直接确认的假设项,参数随时可以放开。
  6. 三张数据表都在 153 代理,已实测钉死。
  7. 当前是模拟仓,不设观察期。部署默认总开关开启、档位全自动。转实盘前重新评估这两个默认值。

八、风险和限制,先说在前面

这个指数是慢变量。它抓的是阶段性的冷热,不是每天的涨跌。牛市里它会显得慢半拍,这是设计使然,不是故障。

偏热降仓在标定样本里是保险,不是赚钱工具。样本覆盖 2021 年到 2026 年,尾段是大牛市,极热之后大盘平均还在涨。这套逻辑的价值要等下一轮真正的顶部才能兑现。

标定样本很小。极值一年只出现几次,统计上不显著。冷区的高胜率里有一部分水分,因为牛市里逢跌买入本来就容易对。汇率数据从 2021 年 6 月才开始,没有覆盖过深熊。

所以月度复盘是方案的一部分,不是可选项。数据每多一段,就用标定脚本重跑一次,参数跟着校准。模拟仓不设观察期是用户的拍板,正好让真实触发来检验参数,代价由模拟资金承担。


附录(以下全部可不读,供实现和排查时查阅)

附录一、指标计算公式与数据列名

计算分四步。第一步算二十日对数收益:股的一边用上证指数收盘价,汇的一边用美元兑离岸人民币,汇率上涨代表人民币贬值。汇率的日期先加一个自然日,再对齐到国内交易日历,当天没有值就沿用最近的前值。第二步把两个收益相加,得到共振值(代码名 spread。第三步减去利率修正修正量等于 0.02 乘以一个月期拆借利率的二十日变化(代码名 spread_adj。第四步对修正后的值做四十日滚动标准化再乘以十得到股汇对冲指数代码名 hedge_index

stock_ret  = ln(close_t / close_{t-20})
fx_ret     = ln(fx_t / fx_{t-20})
spread     = stock_ret + fx_ret
spread_adj = spread - 0.02 * (shibor1m_t - shibor1m_{t-20})
hedge_index = (spread_adj - mean40) / std40 * 10     # std 用样本标准差
数据源 日期列 取值列 过滤条件 备注
zs_day_data proxy(153) timestamp close symbol='000001.SH' 盘中最新行是前一交易日,与九点三十五分用前一日数据的设计一致
gp_fx_daily proxy(153) trade_date bid_close ts_code='USDCNH.FXCM' 整表单一品种,历史从 2021 年 6 月起
gp_shibor proxy(153) trade_date rate_1m 无,宽表 单位是百分数,例如 1.4165

数据守卫有三条。有效样本少于六十五个交易日,判不可用。任何一个数据源的最新日期落后当前交易日超过三个交易日,判不可用(天数参数是 PMS_MACRO_STALE_TDAYS。标准差为零判不可用。序列缺口用前值补齐补齐的天数记进当日明细。

附录二、区域判定与决策流程的精确定义

区域(代码名 zone四种取值。偏热HOT指数大于进入阈值PMS_MACRO_HOT_TH初值 25回落到退出带PMS_MACRO_EXIT_BAND初值 15以内算离区。偏冷COLD指数小于进入阈值PMS_MACRO_COLD_TH初值 -20回升到负退出带以内算离区。其余为中性NEUTRAL。数据守卫未过为不可用UNAVAILABLE。进出阈值不同是为了防止指数在阈值附近来回穿越造成反复触发。

对数映射:超额深度 e 等于指数绝对值减去进入阈值。周期累计目标 target(e) = min(S0 * ln(1 + e / k), SHIFT_MAX),标定回填 S0=0.049k=1.6SHIFT_MAX=0.20。偏冷侧(触发口径 zone_enter按步进追踪本次步长等于 target(当前深度) 减去周期内已执行量,确认当日首步保底 STEP_MIN0.02);只有指数更深才有新步。偏热侧(触发口径 zone_exit整个周期只动一次回落穿出退出带时触发步长取 target(周期峰值深度),同样受 STEP_MIN 和 SHIFT_MAX 约束。周期结束后累计量和峰值深度清零。

冷却与去重的规则如下。同方向两次生效动作至少间隔三个交易日(参数 PMS_MACRO_COOLDOWN_TDAYS。生效的定义是所发命令没有被立刻取消。因为排不出方案而被取消的命令不占冷却次日可以重试。每个信号每个交易日至多动一次。存在署名 macro 的在途命令时不叠加新命令。宏观降仓不把总仓位降到百分之二十以下(参数 PMS_MACRO_MIN_PCT用户自己的命令不受此限。

加仓方向的额外让路全局暂停买入开关PMS_GLOBAL_BUY_HALT生效时不发组合刹车生效且 PMS_MACRO_RESPECT_BRAKE 为真时不发。这两个状态在参数表而不在命令表,命令冲突检测看不见它们,引擎必须自查。

调度时点定在九点三十五分而不是盘前,原因是一个已核实的机制:盘前取不到实时价时,持仓快照会用成本价顶替并打上价格不可靠标记,方案生成器会排除这类票。盘前下的降仓命令会在一分钟内被排成零方案并取消。九点三十五分行情已就位,也在八点四十分拉取候选池之后,加仓有票可选。指数用的是前一交易日的数据,几点计算数值都一样。

建议(第二档产出)的承载:写在当日 pms_macro_signal 行里,当日有效,隔日自动作废。采纳走独立接口,采纳前重查一遍冲突和开关,采纳后署名记为用户。特意不复用个股提议表 pms_proposal因为它的采纳语义是生成单只股票的指令装不下组合级动作。

宏观闸的时点与降仓命令刻意不同步:闸在进入偏热区当天就生效,回落穿出退出带解除;降仓命令回落时才下。时间线是先停增量,热度破裂后再减存量。闸的实现落点有三处,全部与既有检查并排:自主提议扫描入口一处,自主建仓扫描入口一处,个股策略运行器的买入开腿判定点一处(与既有的买入暂停集合同一个判定位置,天然继承只挡开腿不挡平仓的语义)。闸不写任何暂停标记,闸解除后下一轮自动恢复,不影响你手动设置的暂停记录。闸读不到宏观状态时不拦,因为买入本身另有规则闸把关,不能让宏观层故障把自主引擎整体摁死。

多信号扩展宏观服务内有一张信号注册表V1 只有股汇对冲指数一项。将来接入决策系统的宏观水温或者择时层的日线指标,只需要新增注册表项和该信号自己的参数,参数命名空间约定为 PMS_MACRO_信号名_参数名。多信号合成规则 V1 定死为最保守者优先:任一信号偏热则整体偏热,无偏热但有偏冷则整体偏冷。

附录三、参数清单

全部登记进运行参数中心,页面可调,五秒生效。总开关的文件初值现在是开(模拟仓拍板),所以要把它加进参数中心的安全名单(代码名 FAIL_CLOSED参数表读不到的时候按关闭处理宁可少动一轮不在基础设施故障期间自动下命令。附带说明参数表和三张数据源表在同一个数据库真断了的时候取数环节也会先判不可用这条名单是第二道保险。

参数 初值 说明
PMS_MACRO_ENABLED True 宏观择时总开关。关闭时调度位空转,不取数不计算。模拟仓拍板默认开,转实盘前改回 False
PMS_MACRO_AUTONOMY full 档位off 只算不动propose_only 出建议full 自动下命令。模拟仓拍板默认 full转实盘前改回 propose_only
PMS_MACRO_SIGNALS hedge_fx 启用的信号清单,逗号分隔
PMS_MACRO_STOCK_GATE True 个股宏观闸。偏热确认期间暂停自主买入与策略买入开腿
PMS_MACRO_HOT_TH 25.0 偏热进入阈值。标定约每年 1.4 段,平均持续 5.6 天
PMS_MACRO_COLD_TH -20.0 偏冷进入阈值。标定约每年 4 段,用户确认取 -20
PMS_MACRO_EXIT_BAND 15.0 迟滞退出带。偏热侧它同时是降仓命令的触发线
PMS_MACRO_CONFIRM_DAYS 1 偏冷进区连续天数要求。标定显示等第二天反而更差
PMS_MACRO_TRIGGER_HOT zone_exit 偏热触发口径,回落再动。可切 zone_enter
PMS_MACRO_TRIGGER_COLD zone_enter 偏冷触发口径,进区即动。可切 zone_exit
PMS_MACRO_LOG_S0 0.049 对数映射系数,标定回填
PMS_MACRO_LOG_K 1.6 对数映射尺度,标定回填
PMS_MACRO_SHIFT_MAX 0.20 单个极值周期累计调整封顶,占规模的百分点
PMS_MACRO_STEP_MIN 0.02 裁剪后步长小于此不动,也是确认当日的首步保底
PMS_MACRO_COOLDOWN_TDAYS 3 同方向两次动作的最小间隔,交易日
PMS_MACRO_MIN_PCT 0.20 宏观降仓地板。用户命令不受此限
PMS_MACRO_MAX_PCT 0.0 宏观加仓天花板。零表示不单设,由总仓上限兜底
PMS_MACRO_WINDOW_TDAYS 3 宏观命令的执行窗口
PMS_MACRO_RESPECT_BRAKE True 组合刹车期间不自动加仓
PMS_MACRO_STALE_TDAYS 3 数据日龄超此判不可用
PMS_MACRO_RET_WIN 20 收益计算窗口
PMS_MACRO_Z_WIN 40 标准化窗口
PMS_MACRO_SHIBOR_BETA 0.02 利率修正系数

附录四、新增与触碰文件清单

新增文件五个,互相独立,全部删除即回到现状。

文件 内容
app/core/macro_rules.py 纯逻辑:对齐与四步计算、区域迟滞判定、对数映射决策。无外部依赖,可单测
app/repo/macro_repo.py 三张源表的单表取数,加新表 pms_macro_signal 的读写
app/services/macro_service.py 编排:守卫、取数、计算、落表、决策、分流、留痕。对外提供状态、采纳、闸状态三个函数
scripts/calibrate_macro_signal.py 标定脚本,已交付并实跑。保留供月度复盘
scripts/test_batch14_units.py 单测约二十五例,内容见附录六

触碰的既有文件十二处,全部只加不改(末两行是测试哨兵,按哨兵注释的指引各加一行)。

文件 加什么
config/settings.py 一段 PMS_MACRO 开头的参数字段
app/services/param_store.py 参数说明、取值范围、合法值校验各加宏观项
app/scheduler.py 一个新调度任务,加调度表一行
app/services/proposal_service.py 两处扫描入口各加一个宏观闸检查,与既有的休假检查并排
app/services/strategy_runner.py 买入开腿判定点加一个宏观闸并列条件,与既有买入暂停集合同点
app/web/main.py 三个新接口:状态查询、手动扫描、建议采纳
app/web/static/index.html 一块宏观择时面板
ddl_pms_v1.sql 第十八张表的建表语句
scripts/check_db.py 自检表清单加 pms_macro_signal顺带发现 pms_strategy 和 pms_op_log 也不在清单里,属既有欠账,按不夹带纪律单独提)
scripts/run_tests.py 登记新测试批次
scripts/test_batch6_units.py DDL 表数哨兵 17 改 18哨兵注释明言"加表就要来这里加一"
scripts/test_wiring.py 调度表哨兵集合加 macro_scan

三条铁律的对照。命令至上:宏观动作本身就是命令,冲突让位用户,可撤可改,闸只拦系统自主动作。分工不越权:宏观层只回答整体该多重和增量该不该停,逐股怎么买卖仍由方案生成器、择时和规则闸决定,其他系统零改动。先记账后动作加故障即守成:信号先落表再决策,命令走先落表的既有路径,任何取数计算失败都不产生新指令也不落闸。

附录五、新表结构

CREATE TABLE IF NOT EXISTS pms_macro_signal (
  id          BIGINT PRIMARY KEY AUTO_INCREMENT,
  signal_key  VARCHAR(32)  NOT NULL COMMENT '信号名, 如 hedge_fx',
  trade_date  INT          NOT NULL COMMENT '北京交易日 YYYYMMDD',
  value       DECIMAL(12,4)         COMMENT 'hedge_index 值',
  zone        VARCHAR(16)  NOT NULL DEFAULT 'NEUTRAL' COMMENT 'HOT/COLD/NEUTRAL/UNAVAILABLE',
  detail_json TEXT                  COMMENT '中间量: 收益/共振值/超额深度/各源末日/补齐天数/周期已执行量',
  action      VARCHAR(32)  NOT NULL DEFAULT 'NONE' COMMENT 'NONE/ADVICE_REDUCE/ADVICE_INCREASE/CMD_ISSUED/BLOCKED/GATED',
  ref_id      VARCHAR(64)  NOT NULL DEFAULT '' COMMENT '关联 command_id (下达或采纳后回填)',
  note        VARCHAR(500) NOT NULL DEFAULT '' COMMENT '被拦原因/建议文案/闸状态',
  updated_at  DATETIME COMMENT '写入或重扫更新时间',
  UNIQUE KEY uk_sig_date (signal_key, trade_date)
) COMMENT='宏观信号日快照与动作记录 (每信号每交易日一行, 重扫就地更新)';

这一行数据有四个用途:历史序列供面板画图;第二档的建议就写在当日行里;冷却和周期已执行量从历史行回查,不另设状态存储;宏观闸的当日状态也从这行读,进程内缓存五秒。

附录六、接口、面板与单测清单

新增接口三个。查询状态GET /api/macro/status返回各信号的当前值、区域、近二十日序列、数据日龄、当日建议、闸状态、最近宏观命令进度和冷却剩余。手动扫描POST /api/ops/macro-scan带 dry_run=true 时只算不落表不下达。建议采纳POST /api/macro/adopt采纳前重查冲突和开关采纳幂等隔日作废。

命令下达的调用形态如下,供实现对照。

command_service.issue(
    "REDUCE_EXPOSURE",
    {"pct": step, "window_tdays": 3},
    issued_by="macro",
    note="宏观择时: 股汇对冲指数回落穿出退出带, 峰值深度 6.2, 本次降 8.6 个点")

单测批次十四的覆盖清单:四步计算对照手算值;汇率加一日对齐和缺口沿用前值;样本不足和数据停更判不可用;区域迟滞的进出和横跳;对数映射的单调性、同深度不重复加码、周期清零、封顶;偏冷首步保底;偏热按峰值深度一次动作;确认天数、冷却、每日一次;零方案命令不占冷却;冲突让路、暂停买入让路、刹车让路(打桩);宏观闸拦自主买入不拦保垫减仓、拦策略买开腿不拦平回腿、读不到不拦、档位关不拦;建议采纳幂等与隔日作废。

页面面板内容:指数当前值和区域色带(热红、冷蓝、中性灰、不可用黄)、近二十日迷你走势、当日建议卡带采纳与忽略按钮、宏观闸状态条、最近一条宏观命令的进度。参数不用做界面,登记后自动出现在参数设置页。