M18 港美股微观结构与制度¶
层级:L1 策略与回测 | 优先级:★★★ | 预计学时:10 小时 对应原书:黑箱第 7–8 章的执行与数据,只作地图 前置知识:M03 的成交时点;M13 的印花税;M15 的常规时段限价。 读书笔记:时段与公司行动 状态:reviewed 数据截至:2026-10|最近复核:2026-10-28
学习目标¶
- 把港股开市前、持续交易、收市竞价和美股常规时段分开,并看出两边的常规时段没有重叠。
- 用手算未复权收益、总收益和预提税之后的收益。
- 用整手把 250 股收成 200 股,并把一个价位和印花税放在同一张基点图上。
- 说明 1 份 ADR 对 1 股、汇率 7.80 时,400 港元对 52 美元的价差不是无风险套利。
- 不把日内能卖出写成现金已经交收,也不写绕过券商风控或税务申报的做法。
- 按交易币种检查已交收现金,并把 PTP 预提、现金账户 GFV、港股价位和碎股从「总净值看起来正常」里分开。
本章知识拓扑图¶
flowchart TB
SIG["信号用到当日收盘"] --> NEXT["下一交易时段才能下单"]
HK["港股 9:30–12:00 与 13:00–16:00 撮合"] --> GAP["与美股常规时段不重叠"]
US["美股常规 9:30–16:00 美东"] --> GAP
ACT["拆股、股息"] --> TR["总收益向前累加"]
TR --> RAW["成交仍用未复权价"]
LOT["整手向下取整"] --> SEND["只发送取整后的数量"]
富途这条发送路径只接受常规时段的当日有效限价。盘前盘后不在这条路径里。
🟢 入门层:收盘价已经是过去的价¶
1. 两个市场的钟¶
港交所证券市场常见问题里的全日时间表是:开市前 9:00–9:30,早市 9:30–12:00,延长早市 12:00–13:00,午市 13:00–16:00,收市竞价从 16:00 到 16:08 与 16:10 之间的随机收市。同一页把持续撮合写成 9:30–12:00 和 13:00–16:00。延长早市出现在时间表上,但没有被写进这句持续撮合。半日市(圣诞、新年和农历新年前夕)没有延长早市和午市。2026 年的半日市通告写的是 2 月 16 日、12 月 24 日和 12 月 31 日。节假日和半日市仍以当时的交易时间页为准。台风和黑雨不再自动等于停市:港交所自 2024-09-23 起实施恶劣天气交易(Severe Weather Trading)。8 号或以上台风信号、黑色暴雨警告,以及特区政府公布的极端情况期间,证券市场维持正常开市,交易、清算和结算继续。港交所仍可在咨询政府和证监会后宣布例外停市。回测若跨过 2024-09,日历要分成两段:此前的真实停市日保留,不把没有成交的日子补成可交易;此后不把这类天气信号本身写成休市。函数见第 11 节。
开市前分成四段:9:00–9:15 可改可撤,竞价限价在前收的 ±15% 内;9:15–9:20 不可改撤;9:20 至多到 9:22 随机对盘;对盘之后到 9:30 是冻结。收市竞价的 2026 年 4 月说明写着:16:00–16:01 定参考价,16:01–16:06 输入,16:06–16:08 不可撤,16:08–16:10 随机收市。不是所有证券都进收市竞价。结构化产品的收市时间可以和正股不同。
美股常规时段是美东 9:30–16:00。本仓库把常规收盘记成 16:00,09:35 这根 5 分钟线覆盖 9:30–9:35。2018-12-24 是 13:00 的半日市,官方收盘和最后一根 5 分钟线可以不同,信号仍用官方日收盘。盘前盘后由券商和交易场所各自规定,不是一条全市场统一的 4:00–20:00。本仓库的发送路径不接受非常规时段。
香港没有夏令时。美东夏令时和标准时下,香港比美东快 12 或 13 小时。两种情况下,港股 16:00 的持续撮合结束,都早于美股当天常规开盘。不能在同一分钟里,一边打港股的卖盘、一边打美股常规时段的买盘。
2. 能当天卖出,不等于现金已经交收¶
港股和美股都可以在买入的同一天卖出。这和沪深的 T+1 交易限制不是同一条规则。美国多数证券的券商交收在 2024-05-28 起改为 T+1。交收日决定资金何时能取出、以及券商是否把未交收的卖出款算进可买资金。本仓库更严的一条是:卖出还没成交时,那笔金额不能用来买。两件事都要留着。
做空要先有券。借券费按空头名义和持有天数计算,不是印花税。腾讯实验室把 TCEHY 的空头融券写成年化 3%,并标明这是研究假设;标的在场外,券商不一定借得到,实验室也不发空单。本轮账户只做多、杠杆上限 1.0。港股卖空只覆盖指定证券,输入价还要满足当时的报价规则。券源在你下单时消失,回测里的空头就没有对应的成交。
港股下午 4 点持续撮合已经结束,美股常规时段还没开。综合账户上港元可以是负的,美元现金仍是正的,界面杠杆不超过 1,融资利息已经在走。 未复权价是柜台挂牌价,250 股、每手 100,只能发 200。前复权价每次新分红都改写更早的收盘,只适合看图。向前总收益指数在分红当天把再投资滚进去,历史点位定格,排名用它。 TCEHY 比港股折算价高 1.4% 时,两边不能同一分钟对敲。这是未实现的价差,不是落袋利润。 美股现金账户里,卖出款 T+1 才交收。用在途的钱买了新票又在到账前卖掉,是善意违规。卖出还没成交时,这笔金额更不能拿来买。 平价卖出某些合伙企业上市产品,价格没赚,预提仍可能按卖出总额扣一截。进阶层给利息、整手、价位和预提的手算。 收市竞价还没结束时的价格不是官方收盘。用它假装已经按收盘成交,是前视。
港股持续撮合是 9:30–12:00 和 13:00–16:00。美股常规时段是美东 9:30–16:00。香港没有夏令时,港股 16:00 收市时,美股当天常规时段还没开。 同一分钟里不能一边打港股卖盘、一边打美股常规买盘。综合账户上一种货币为负、另一种为正,界面杠杆仍可能不超过 1,融资利息已经在计。
250 股、每手 100,能发的是 200。目标金额除以前复权价,会得到交易所不接受的股数。排名用向前总收益指数,下单用未复权价。 TCEHY 相对港股折算价高出 1.4% 时,两边对不上同一分钟的成交。实验室把空头融券写成研究假设的年化 3%,本轮账户不发空单。
买入当天可以卖出。这和资金何时交收是两件事。美国多数证券自 2024-05-28 起按 T+1 交收。卖出还没成交时,这笔金额不能拿来买。 现金账户若用尚未交收的卖出款买入,又在到账前卖掉,记的是善意违规。保证金账户的购买力另算,本仓库仍不拿未成交的卖出款去买。
收市竞价从 16:00 走到 16:08 与 16:10 之间的随机收市。这段里的价格还不是官方日收盘。半日市没有午市,日历仍以当时的交易时间页为准。 2024-09-23 起,8 号台风或黑雨本身不再写成自动停市。跨过这个日期的回测要分段,不把此前真实停市的日子补成可交易。
开市前 9:00–9:15 可改可撤,竞价限价在前收的 ±15% 内。9:15–9:20 不可改撤。9:20 至多到 9:22 随机对盘,之后到 9:30 是冻结。 收市竞价在 16:00–16:01 定参考价,16:01–16:06 输入,16:06–16:08 不可撤。结构化产品的收市时间可以和正股不同。
2018-12-24 美股在 13:00 收市。官方收盘和最后一根 5 分钟线可以不同,信号仍用官方日收盘。09:35 这根线覆盖的是 9:30–9:35。 盘前盘后由券商各自规定。本仓库的发送路径不接受非常规时段。借券要先有券,券源消失时,回测里的空头没有对应的成交。
🟡 进阶层:复权、整手和价差¶
3. 总收益向前累加,成交用未复权价¶
未复权价是交易所挂出来的价,股数和整手按它算。 前复权价会在下一次分红时改写更早的收盘,图连得上,已经用过的信号会被改写。 向前总收益指数只在行为发生的当天把分红再投资进去,更早的点位保持原样,排名用这一列。 不能用第三列的价格去除目标金额,交易所会拒掉算出来的股数。
仓库的总收益因子是
fₜ = (Pₜ × sₜ + dₜ + cₜ) / Pₜ₋₁
| 符号 | 含义 |
|---|---|
| Pₜ | 当日未复权收盘 |
| sₜ | 拆股倍数,没有拆股时为 1 |
| dₜ | 每股现金股息 |
| cₜ | 分拆按前收估值、当成现金的部分 |
| 第一天 | 因子定为 1,不使用不存在的前收 |
手算:前收 100,除息日未复权收盘 98,股息 2,没有拆股。
f = (98 × 1 + 2) / 100 = 1
未复权收益是 98/100 − 1 = −0.020000。总收益因子是 1.000000,一日收益是 0。若把股息看成暴跌,信号会在除息日发出假的跌破。
非居民取得的美国来源股息,法定预提税率是 30%,税收协定可以更低。香港和美国没有全面所得税协定,不能把中国协定的税率套到香港居民身上。预提 30% 时,到手股息是 2 × 0.70 = 1.40,持有人的价值是 98 + 1.40 = 99.40,收益是 −0.006000。总收益指数没有扣这 0.60 个百分点。FeeSchedule 也不含个人税。W-8BEN 用来声明外国身份和协定税率,不是把 30% 自动改成 0。这不是税务意见。
同一套函数里,前收 100、未复权收盘 50、拆股倍数 2:
f = (50 × 2) / 100 = 1
因子是 1.000000。股数加倍、价格减半,持仓市值不变。用未复权价计算收益会得到 −50%,那是复权错误,不是行情。
4. 整手、印花税和价位¶
whole_lots 向下取整。想买 250 股、每手 100 股,发送 200 股,剩下 50 股不凑成一手。港交所常见问题写,持续交易时段单笔最多 3,000 手。某一只股票的手数以当时的证券资料为准,不把 100 写成所有港股的手数。
名义 20,000 港元、每侧印花税 0.1%(2023-11-17 起,买卖各自计算),一侧是 20.00 港元。一买一卖是 40 港元,占名义 20.0 个基点。这和 M13 的 2τ c 在单边换手为 1、每侧成本为 10 个基点时是同一个 0.20%。2021-08-01 至 2023-11-16 的税率是 0.13%。港股若以美元或人民币计价,印花税和若干交易征费使用港交所在当日 11:00 前公布的汇率,不是你自己随手取的即期汇率。
价格 0.40、价位 0.005 时,一个价位是
0.005/0.40 = 0.0125
即 125.0 个基点。它大于这 20 个基点的印花税。价位表会改,下单前对照当时的档位。
价位是价格的阶梯,不是一个固定的基点。价格落在哪一档,最小变动就换成那一档的金额。0.40 这一档的步长是 0.005,一步就是 125 个基点。价格若在更高的档,同样一步的金额更大,占价格的比例通常更小。所以低价股上,买卖价差的下限先由价位决定,印花税是另外一层。港交所把不同价格区间的步长列成价位表,区间边界会修订。本章只把已经手算的 0.40 和 0.005 写进图里,不把整张表抄成不会过期的清单。2025-08-04 与 2026-08-03 分阶段收窄的价格带写在第 14 节。0.25 至 0.50 这一档原来就是 0.005,不在这两次收窄里,图上的 125.0 个基点仍然有效。
5. 名义 20,000 港元的一侧,印花税之外还有四笔¶
印花税是政府税。交易所、证监会、会财局和结算公司另按成交金额收费。下面的比例来自港交所收费页和集团 2026 年上半年收入说明,都是每边、按成交金额。经纪佣金自 2003-04-01 起由券商和客户协商,不在这五行之内。投资者赔偿征费自 2005-12-19 起暂停,这一侧按 0 计。
| 项目 | 每边比例 | 20,000 港元的一侧 | 依据 |
|---|---|---|---|
| 印花税 | 0.1% | 20.00 | 2023-11-17 起,买卖各自计算 |
| 交易费 | 0.00565% | 1.13 | 港交所交易收费页,四舍五入到分 |
| 证监会交易征费 | 0.0027% | 0.54 | 2014-11-01 起 |
| 会财局交易征费 | 0.00015% | 0.03 | 2022-01-01 起 |
| 股票结算费 | 0.0042% | 0.84 | 交易所买卖;做市商的部分基金交易可以更低 |
| 合计 | 22.54 | 不含佣金 |
20000 × 0.0000565 = 1.13,20000 × 0.000027 = 0.54,20000 × 0.0000015 = 0.03,20000 × 0.000042 = 0.84。四笔与印花税相加是 22.54 港元。一买一卖是 45.08 港元,占名义
45.08/20000 = 0.002254
即 22.54 个基点。其中印花税仍是 20.0 个基点,其余四笔合计每边 1.27 个基点、来回 2.54 个基点。低价股上,一个价位的 125 个基点仍大于这整侧 22.54 港元所代表的 11.27 个基点。
结算费要分开两段用。集团 2026 年上半年收入说明写,股票结算费从 0.002% 调整到 0.0042%,并取消单笔最低和最高收费,适用于 2025-06-30 及之后执行的交易所交易。符合条件的交易所买卖基金做市交易可以仍按更低的比例。2025-06-30 之前的回测若沿用旧的 0.002% 和上下限,要在费用表里单独注明。美元或人民币计价的股票,印花税和这些按金额计算的征费使用港交所在当日 11:00 前公布的汇率。
FeeSchedule 收的是佣金、平台费和美股那一侧的清算与监管费,单位和舍入按那个表自己的规则。它不自动包含这张港股表。回测要把印花税和这四笔加在价目之外,否则港股换手的拖累会少掉大约来回 22.54 个基点里的法定部分。佣金仍以你和券商的约定另加。
6. 美股的买卖价先看全国最优,路由是另一层¶
全国最佳买卖价(NBBO)是同一只股票在各交易场所上最高的买价和最低的卖价。一笔可成交的订单若在劣于这个价差的价格上成交,就是穿过了全国最优报价。个人看到的券商界面,往往只显示一个中间价。真实的约束是这个价差,以及你的限价有没有落在价差里面。
订单路由决定你的单子被送到哪一个场所。有些零售券商把订单交给批发商,批发商向券商支付订单流对价(payment for order flow)。批发商可能在全国最优价差内部给你一个稍好的价格,这笔改善和它支付给券商的对价是同一笔生意的两边。改善了 1 个价位,不等于你看见了全部场所上更好的报价,也不等于研究里用收盘价成交的那一笔。本仓库的发送路径只发常规时段的限价单,不代你选择批发商,也不把盘后成交写进日线。
2026 年关于订单流对价和成交披露的规则文本还在变动。本章不把某一份提案写成已经生效的终态。要写进费用的,仍是你账单上的佣金、交易所收费和实际成交价相对决策价的差。M13 的实施缺口用这三项对账,不用「有没有订单流对价」这一句话代替。
7. 日线和 5 分钟线必须对上同一根收盘¶
bar_alignment.py 把每个交易日的日线开盘和 09:35 这根 5 分钟线的开盘比较,把日线收盘和该时段收盘那根线的收盘比较。常规时段的收盘时刻是 16:00。09:35 这根线覆盖的是 9:30 到 9:35。开盘量是 0 的 09:35 打印只是占位,不是一笔有成交的开盘。
未解释的价格缺口若超过 10 个基点,或者 5 分钟跨度里缺了线,这个代码会挡住该股票。1 个价位的四舍五入差被排除在外,美股这里的 1 个价位按 0.01 美元计。更大的缺口不能因为基点数看起来不大就抹掉。2018-12-24 是提前收市,官方收盘是 13:00。那天的日线收盘和最后一根 5 分钟成交可以不同,因为收盘竞价和连续成交不是同一笔。信号仍用官方日收盘,5 分钟线只作执行来源,不回写日线。代码里把这一天记成已经解释的例外,不因此挡住股票。
港股和美股不能共用一根「当天收盘」。香港没有夏令时,香港时间比美东快 12 或 13 小时。港股 16:00 的持续撮合结束时,美股当天的常规时段还没开。把两边的日线按日历日期对齐,是对齐两个交易日,不是对齐同一分钟。ADR 的 140 个基点价差,就发生在这两段不相交的时间里。
8. 供股不是拆股,return_factors 也不会自动补上¶
仓库的总收益因子处理三种公司行动:拆股倍数、现金股息,以及分拆里按前收估值、当成现金的部分。同一除权日若多于一种,函数直接拒绝,因为分不清价格里的哪一段属于哪一种。供股不在这三种里面。若除净日的价格下跌,而你没有把供股权的价值写进现金项,总收益会把「没参加供股」和「价格自然下跌」混在一起。
教学输入:前收 100,每 2 股旧股可以按 80 买 1 股新股。理论除权价是
(2 × 100 + 1 × 80) / 3 = 93.333333
相对 100 的未复权收益是 93.333333/100 − 1 = −0.066667。一份新股的认购权价值是 93.333333 − 80 = 13.333333。每 2 股旧股才有 1 份权,摊到每股旧股是 6.666667。持有人若把这项权利按理论价值记成现金,价值仍是 93.333333 + 6.666667 = 100,收益是 0。忽略供股权,就会把 −6.6667% 记成亏损。
这个 0 只在权利按理论价变现时成立。权利有交易时段、有整手,也可能乏人问津。实盘要记权利的成交价或明确放弃。放弃时,−0.066667 才是持有人的价格损失。return_factors 不会从「供股」这两个字推断出 6.666667。数据里若只有除净后的价格,就要另补一行现金或在样本说明里写明没有处理供股。
from decimal import Decimal
import pandas as pd
from quant.backtest.money import whole_lots
from quant.data.corporate_actions import return_factors
def hk_statutory_one_side(notional: Decimal) -> dict[str, Decimal]:
"""港股一侧法定费用。比例按收费页;交易费四舍五入到分。不含经纪佣金。"""
rates = {
"stamp": Decimal("0.001"),
"trading": Decimal("0.0000565"),
"sfc": Decimal("0.000027"),
"afrc": Decimal("0.0000015"),
"settlement": Decimal("0.000042"),
}
out = {name: (notional * rate).quantize(Decimal("0.01")) for name, rate in rates.items()}
out["total"] = sum(out.values(), Decimal(0))
return out
def theoretical_ex_rights(prev: Decimal, old_shares: int, new_shares: int, subscription: Decimal) -> Decimal:
"""供股理论除权价。权利价值要另记,函数本身不改写历史收盘。"""
gross = old_shares * prev + new_shares * subscription
return gross / (old_shares + new_shares)
# 250 股、每手 100,只发 200。供股不在 return_factors 认识的 kind 里。
sent = whole_lots(Decimal("250"), Decimal("100"))
terp = theoretical_ex_rights(Decimal("100"), 2, 1, Decimal("80"))
right_per_old = (terp - Decimal("80")) / 2
index = pd.to_datetime(["2024-01-02", "2024-01-03"])
close = pd.Series([100.0, 98.0], index=index)
actions = pd.DataFrame({"ex_date": [index[1]], "kind": ["dividend"], "value": [2.0]})
# 现金股息能使因子回到 1。kind 若写成 rights,过滤后当日没有任何行动,跌价会被当成亏损。
factor = float(return_factors(close, actions).iloc[1])
9. 1 比 1 的 ADR 仍有时差¶
实验室把 TCEHY 写成 1 份 ADR 对应 1 股港股普通股。下面的价格是教学输入,不是那份种子里的收盘。港股 400 港元,美元兑港元 7.80:
400/7.80 = 51.282051
美股报价 52 美元时,
52/51.282051 − 1 = 0.014000
价差 140 个基点。它不是无风险利润。港股持续撮合已经在 16:00 结束,美股常规时段还没开。中间有汇率变动、场外买卖价差、转换费用和借券。实验室的单边 15 个基点是给场外 ADR 用的研究假设,空头年化 3% 也是。正股一侧若成交,还要另付印花税。
🔴 深入层:规则改日历,不改前视的定义¶
10. 收盘竞价不是「用收盘价免费成交」¶
收市竞价的成交价由那一段的订单形成,随机落在 16:08 与 16:10 之间。用 16:00 之前的信息去预测这个收盘,和用已经公布的收盘去假装自己在 16:00 成交,是两件事。信号若用了当日收盘,成交只能放到下一交易时段。美股半日市把官方收盘提前到 13:00 时,仍用官方收盘做信号,不用最后一根 5 分钟线去改写它。
日内次数规则沿用 M04:Release 34-105226 在 2026-04-17 公布,生效日跟着 FINRA 的 Regulatory Notice,会员可以分阶段切换。次数限制放松,不取消保证金,也不取消单笔和单名上限。维持保证金的常见底线仍是 25%,券商可以更高。
借券费会在券源变紧时跳升。回测里写成常数 3% 的空头,在借不到券的那天应是不成交,而不是继续按 3% 持有。只做多的账户本来就没有这条空头。
11. 跨过 2024-09-23 的日历¶
第 1 节的制度事实要落到回测日历上,不能用「现在不停市」去改写旧样本,也不能用「以前台风就停」去关掉今天的模拟盘。
| 会话日 | 这一天算不算开市 | 依据 |
|---|---|---|
| 早于 2024-09-23 | 交易所当天实际停了,就保留停市 | 历史行情里没有的交易日,不要补一根 K 线 |
| 2024-09-23 及之后 | 8 号风球、黑雨、极端情况本身不停市 | 港交所通告 CT/148/24;例外停市另有公告 |
| 任何年份的半日市 | 开市,但收盘时刻提前 | 圣诞、新年和农历新年前夕仍要改时段 |
下面的函数不查询天文台,也不连接行情。调用方把已经核对过的事实传进来:旧样本用 historical_closure,现行运营用 exceptional_closure。天气信号在截止日期之后不再单独把会话关掉。
12. 双币种账户:净值杠杆不超过 1,仍会付融资利息¶
富途或盈透的综合账户、保证金账户把几种货币折成一个净值。闸门若只看这个折算净值,会漏掉子账本。教学例子:已交收现金是 100,000 美元和 0 港元,总净值按 7.80 折合也是这 100,000 美元。直接买入 234,000 港元的港股,没有先把美元换成港元。按 7.80,这 234,000 港元等于 30,000 美元,占总净值 30%。股票多头加上剩余美元现金,折算后的杠杆仍不超过 1.0。港元子账本却是 −234,000 港元。负现金按港元融资利率计息,美元现金不会自动去填这个洞。
教学利率年化 6.8%,港元按 365 天计息,持有 30 天:
234000 × 0.068 × 30/365 = 1307.84
港元。相对这 234,000 港元仓位,
1307.84/234000 = 0.005589
即 55.89 个基点。来回印花税在第 4 节的例子里是 20.0 个基点。这笔融资利息可以比显性佣金更贵,而且持有越久越贵。
换汇的点差是一次性的。教学区间取 15 至 30 个基点,不是某一家券商当天的牌价。每日融资拖累是 0.068/365,换成基点是每天 1.863 个基点。盈亏平衡持有天数是点差除以这个每日基点:
(15 × 365) / (0.068 × 10000) = 8.05, (30 × 365) / 680 = 16.10
大约 8.05 天和 16.10 天。短于这段,付一次点差比负现金更贵;长于这段,先换汇更便宜。函数在发单前只做这道币种现金检查。它不提交订单,也不读取交易密码。
13. 美股 PTP 的 10% 预提,以及现金账户的 GFV¶
PTP 预提是合伙企业税:不分盈亏,按卖出总金额硬扣一截。平价 10,000 美元卖出,价格盈亏是 0,10% 仍预提 1,000 美元。 善意违规是现金账户的空手套白条:卖出款还在 T+1 的路上,拿这张在途白条买了新票,又在交收到账前把新票卖掉。 保证金账户的购买力是另一套规则。卖出尚未成交时,本仓库连这笔金额都不拿来买。 总收益指数和 FeeSchedule 都不扣这 1,000 美元。名单会变,下单前以券商公布的 PTP 名单为准。
美国国内税收法 1446(f) 要求:非美税务居民转让公开交易合伙企业(PTP)权益时,经纪商按卖出总成交额预提,常见执行比例是 10%。税基是总金额,不是价差,也不是盈利。2023-01-01 起这是经纪商侧的扣缴。下面不是税务意见,名单也会变,下单前以券商公布的 PTP 名单为准。商品、波动率和能源一类里常被点名的例子包括 USO、UNG、UVXY、SVXY、DBC。它们进不进你的 ETF 轮动池,见 M03 第 12 节。
手算:10,000 美元买入,平价 10,000 美元卖出。价格盈亏是 0。预提 10000 × 0.10 = 1000 美元。税后净亏 1,000 美元,相当于这一次来回先亏 10%。总收益指数不扣这笔税。FeeSchedule 也不扣。
美股多数证券自 2024-05-28 起券商交收是 T+1。能在 T 日卖出,只说明成交发生了,不说明现金已经交收。现金账户的善意违规(Good Faith Violation, GFV)是另一条:用 T 日卖出、尚未交收的资金在 T 日买入另一只证券,并且在 T+1 交收完成前把这只新证券卖掉。保证金账户的购买力规则和这条不同,不能互相借用。本仓库更严的一条仍然有效:卖出还没成交时,那笔金额不能用来买。GFV 处理的是已经成交、尚未交收的那一段。
14. 价位对齐和碎股¶
align_hk_tick 把限价向下收到价位上。步长由调用方按交易日的价位表传入,函数不猜价格落在哪一档。向下取整是为了不把限价收到比原意更激进的一档。
港交所 2025–2026 年分阶段收窄部分适用证券的最低上落价位。适用证券包括股票、房地产投资信托和股权权证等;交易所买卖产品、债券、交易所期权和结构性产品不在这次收窄里,仍用原表。第一阶段 2025-08-04:10 至 20 港元的步长从 0.02 收到 0.01,20 至 50 港元从 0.05 收到 0.02。第二阶段 2026-08-03:0.50 至 10 港元从 0.01 收到 0.005。0.40 落在 0.25 至 0.50,这一档原来就是 0.005,图上的 125.0 个基点不变。
| 价格 | 当时步长 | 对齐结果 | 档位 |
|---|---|---|---|
| 0.403 | 0.005 | 0.400 | 0.25–0.50,两阶段都没改 |
| 12.015 | 0.02 | 12.00 | 10–20,2025-08-04 之前 |
| 12.015 | 0.01 | 12.01 | 同一档,第一阶段之后 |
| 25.03 | 0.05 | 25.00 | 20–50,第一阶段之前 |
| 25.03 | 0.02 | 25.02 | 同一档,第一阶段之后 |
| 3.007 | 0.01 | 3.00 | 0.50–10,第二阶段之前 |
| 3.007 | 0.005 | 3.005 | 同一档,2026-08-03 起 |
送红股、供股或并股之后,股数可能不成一手。250 股、每手 100,整手是 200,碎股是 50。碎股盘和整手盘不是同一本订单簿。碎股成交价相对整手买卖价常见 1% 到 5% 的折价,这是经验区间,不是交易所规定的固定扣减。按整手买一价给这 50 股估值,会把卖不掉的价格写进净值。
对账时把两类数量拆开。整手 200 股乘整手价格。碎股 50 股乘整手价格再乘 (1 − d),d 取 0.01 或 0.05。价格 10 港元时,5% 折价的碎股值 50 × 10 × 0.95 = 475,连同整手 2,000,合计 2,475,而不是 250 × 10 = 2, 500。1% 折价时碎股值 495,合计 2,495。whole_lots 发单时丢掉的那 50 股,要留在碎股列里,不能在下一笔整手里被悄悄加回。
# goal2-check
"""币种现金、PTP 预提、价位和碎股。教学函数,不连接网关,不发单。"""
from decimal import ROUND_DOWN, ROUND_HALF_EVEN, Decimal
CENT = Decimal("0.01")
SWT_START = "2024-09-23"
def securities_session_open(
session: str,
*,
historical_closure: bool,
exceptional_closure: bool,
) -> bool:
"""跨 2024-09-23 的开市判断。天气信号本身不作为截止日期之后的停市条件。"""
if session < SWT_START:
# 旧样本保留真实停市日,不用现行恶劣天气规则把历史休市补成开市。
return not historical_closure
# 现行运营:只有港交所的例外停市公告才关掉会话。
return not exceptional_closure
def check_currency_cash_sufficiency(
settled_cash: Decimal,
order_notional: Decimal,
currency: str,
financing_rate: Decimal,
holding_days: int,
day_count: int = 365,
fx_spread_bps_low: Decimal = Decimal("15"),
fx_spread_bps_high: Decimal = Decimal("30"),
) -> dict[str, Decimal]:
"""发单前按交易币种检查已交收现金。总净值够,不代表该币种现金够。"""
if day_count <= 0 or holding_days < 0:
raise ValueError("day_count and holding_days must be non-negative")
if settled_cash < 0 or order_notional <= 0 or financing_rate < 0:
raise ValueError("cash, notional and rate are out of range")
if not currency:
raise ValueError("currency is required")
# 只缺交易币种的那一段才融资。换汇之前,美元现金填不上港元负余额。
shortfall = order_notional - settled_cash
if shortfall < 0:
shortfall = Decimal(0)
interest = shortfall * financing_rate * Decimal(holding_days) / Decimal(day_count)
interest = interest.quantize(CENT, rounding=ROUND_HALF_EVEN)
drag_bps = (interest / order_notional * Decimal(10000)).quantize(
CENT, rounding=ROUND_HALF_EVEN
)
daily_bps = financing_rate / Decimal(day_count) * Decimal(10000)
return {
"shortfall": shortfall,
"interest": interest,
"drag_bps": drag_bps,
"breakeven_low": fx_spread_bps_low / daily_bps,
"breakeven_high": fx_spread_bps_high / daily_bps,
}
def align_hk_tick(price: Decimal, tick: Decimal) -> Decimal:
"""限价向下对齐到价位。tick 由调用方按当日价位表传入。"""
if price <= 0 or tick <= 0:
raise ValueError("price and tick must be positive")
units = (price / tick).to_integral_value(rounding=ROUND_DOWN)
return units * tick
def ptp_sale_withholding(
gross_proceeds: Decimal, rate: Decimal = Decimal("0.10")
) -> Decimal:
"""非美税务居民卖出 PTP 时,预提税按卖出总成交额,不按盈利。"""
if gross_proceeds < 0 or rate < 0:
raise ValueError("proceeds and rate must be non-negative")
return (gross_proceeds * rate).quantize(CENT, rounding=ROUND_HALF_EVEN)
def triggers_gfv(bought_on_sale_day: bool, sold_before_settlement: bool) -> bool:
"""T 日用未交收卖出款买入,且在 T+1 交收前卖掉新仓。"""
return bought_on_sale_day and sold_before_settlement
def odd_lot_value(
shares: Decimal, lot: Decimal, price: Decimal, discount: Decimal
) -> tuple[Decimal, Decimal]:
"""整手按盘口价,碎股按折价。返回 (碎股股数, 合计市值)。"""
if lot <= 0 or discount < 0 or discount > 1:
raise ValueError("lot and discount are out of range")
board_lots = (shares / lot).to_integral_value(rounding=ROUND_DOWN)
board = board_lots * lot
odd = shares - board
value = board * price + odd * price * (1 - discount)
return odd, value
pre = securities_session_open("2018-09-16", historical_closure=True, exceptional_closure=False)
live = securities_session_open("2025-09-16", historical_closure=False, exceptional_closure=False)
halt = securities_session_open("2025-09-16", historical_closure=False, exceptional_closure=True)
cash = check_currency_cash_sufficiency(
Decimal("0"), Decimal("234000"), "HKD", Decimal("0.068"), 30
)
withheld = ptp_sale_withholding(Decimal("10000"))
odd_shares, odd_5 = odd_lot_value(Decimal("250"), Decimal("100"), Decimal("10"), Decimal("0.05"))
_, odd_1 = odd_lot_value(Decimal("250"), Decimal("100"), Decimal("10"), Decimal("0.01"))
assert cash["interest"] == Decimal("1307.84")
assert cash["drag_bps"] == Decimal("55.89")
assert f"{cash['breakeven_low']:.2f}" == "8.05"
assert f"{cash['breakeven_high']:.2f}" == "16.10"
assert withheld == Decimal("1000.00")
print("pre_swt_typhoon_open", pre)
print("swt_weather_open", live)
print("swt_exception_open", halt)
print("interest_hkd", cash["interest"])
print("drag_bps", cash["drag_bps"])
print("breakeven_15_bps", f"{cash['breakeven_low']:.2f}")
print("breakeven_30_bps", f"{cash['breakeven_high']:.2f}")
print("ptp_withholding_usd", withheld)
print("ptp_net_pnl_usd", Decimal("0") - withheld)
print("gfv_round_trip", triggers_gfv(True, True))
print("gfv_hold_through_settle", triggers_gfv(True, False))
print("tick_0_40", align_hk_tick(Decimal("0.403"), Decimal("0.005")))
print("tick_12_old", align_hk_tick(Decimal("12.015"), Decimal("0.02")))
print("tick_12_p1", align_hk_tick(Decimal("12.015"), Decimal("0.01")))
print("tick_25_old", align_hk_tick(Decimal("25.03"), Decimal("0.05")))
print("tick_25_p1", align_hk_tick(Decimal("25.03"), Decimal("0.02")))
print("tick_3_old", align_hk_tick(Decimal("3.007"), Decimal("0.01")))
print("tick_3_p2", align_hk_tick(Decimal("3.007"), Decimal("0.005")))
print("odd_shares", odd_shares)
print("odd_5pct", odd_5)
print("odd_1pct", odd_1)
15. 双重上市:美股 ADR 与港股正股的定量选边¶
同一家公司可以同时有港股正股和美股 ADR。腾讯、阿里是常见的一对。两边不是同一分钟里可以互相对敲的同一个订单簿。第 9 节的 140 个基点价差仍然含着时差、汇率和转换摩擦。这一节只比持有和换仓的显性成本,不把价差写成套利。
港股正股:来回法定费用沿用案例 3 的 22.54 bps,另加来回滑点与佣金 20 bps,来回合计 42.54 bps。整手门槛见第 4 节。正股没有 ADR 托管费。美股 ADR:没有港股印花税,1 股起买。年度托管费按每股 0.02 至 0.05 美元这一档做教学假设,下面取 0.04 美元。场外买卖和转换另有摩擦,不进这道打平。
教学价格 50.00 美元。托管费 0.04 / 50.00 = 0.000800,即每年 8.00 bps。每次换仓,港股比 ADR 多付 42.54 − 20.00 = 22.54 bps。持有满一年,ADR 多付这 8.00 bps 托管费。打平的年换仓次数是
8.00 / 22.54 = 0.3549
次/年。倒数是 22.54 / 8.00 = 2.8175 年,约 2.82 年。换仓比 0.3549 次/年更少、持有更长时,少付的托管费盖过港股多出来的法定费用,正股这一侧更省。月频、季频,或者小资金要避开整手时,一年的换仓远多于 0.3549 次,高流动性 ADR 更省这道摩擦。托管费的真实费率以存托银行当时的收费为准,0.04 美元只服务这道除法。
# goal8-check
"""ADR 托管费与港股来回法定费用的换仓打平。离线,不下单。"""
from decimal import Decimal
price = Decimal("50.00")
fee_per_share = Decimal("0.04")
# 0.04 美元 / 50 美元,换成基点。
custody_bps = fee_per_share / price * Decimal("10000")
hk_round_trip = Decimal("22.54") + Decimal("20")
us_round_trip = Decimal("20.00")
# 每次换仓港股多付的部分,正好是法定的 22.54 bps。
extra_hk = hk_round_trip - us_round_trip
turns = custody_bps / extra_hk
hold_years = extra_hk / custody_bps
print(f"{custody_bps:.2f}")
print(f"{extra_hk:.2f}")
print(f"{turns:.4f}")
print(f"{hold_years:.2f}")
assert f"{turns:.4f}" == "0.3549"
assert f"{hold_years:.2f}" == "2.82"
真实案例¶
手算里的前收 100、股息 2、印花税 22.54 和价差 0.014000 仍留在正文和习题里。下面三节用实验室已经落盘的行情和成交,看同一套规则在真实路径上怎么入账。
案例 1:甲骨文除息日,未复权跌幅和总收益因子¶
technical_indicators_lab/seed_data/US.ORCL_actions.csv 在行情种子覆盖的 2006-09-22 至 2026-10-02、共 5038 个交易日里,有 69 行 dividend,没有 split。这段里最小的收盘比是 2025-01-27 的 0.8621,当天没有公司行动行,所以它是价格下跌,不是漏记的一拆二。
return_factors 在除息日用 (Pₜ + Dₜ) / Pₜ₋₁。2022-10-11 前收 62.57,收盘 62.41,股息 0.32。未复权收益 62.41/62.57 − 1 = −0.002557。总收益 (62.41 + 0.32) / 62.57 − 1 = +0.002557。价格少了 0.16 美元,股息 0.32 美元把因子从负的翻成正的。69 个除息日里有 12 天是未复权为负、总收益不为负。
2026-07-10 是另一张账单:前收 144.22,收盘 140.64,股息 0.50。未复权收益 −0.024823,总收益 −0.021356。股息只占前收的 0.003467,当天的下跌主要在价格上。全部除息日的股息/前收,中位数是 0.003610。整段未复权收盘的倍数是 8.1129,向前累加的总收益指数是 10.1783。多出来的是再投资股息。排名用这条指数,下单仍用未复权的 62.41 和 140.64。供股仍不进 return_factors。
案例 2:TCEHY 均值回归扣掉 15 bps 之后,仍不是 140 个基点的套利¶
腾讯 ADR 实验室的 S3_Daily_RSI_Long 只做 TCEHY:RSI(14) 跌破 30 做多,升破 55 离场。outputs/scheme_performance_summary.csv 在单边 15 bps 下:年化 4.21%,10 万美元变成 183,957 美元,最大回撤 38.46%,16 笔来回,在市时间 12.55%,年化换手 2.17 倍,成本一年拖走 0.33 个百分点,夏普 0.31。同一条权重、单边成本改成 0:年化 4.55%,期末 192,990 美元,最大回撤 36.97%,仍是 16 笔,夏普 0.33。
同一文件里的买入持有年化 19.45%,期末 1,379,133 美元,最大回撤 73.90%。15 bps 解释的是 19.3 万和 18.4 万的差额,解释不了策略为什么远低于买入持有。正文里 400 港元、汇率 7.80、报价 52 美元得到的 0.014000,是两地名义价差。这间实验室没有同时持有港股和 ADR,16 笔来回扣的是单边 15 bps 的买卖摩擦。港股 16:00 的撮合结束时,美股常规时段还没开。140 个基点没有被收成一笔无风险的现金。
案例 3:方案 4 的 56 笔来回,换成港股来回 22.54 个基点¶
technical_indicators_lab/outputs/m4_round_trip_trades.csv 里,方案 4 有 56 笔已平仓来回。第一笔进场 2008-01-25,最后一笔出场 2026-09-29,平均持有 50.1 天,大约一年 3.0 笔。trade_execution_summary.csv 的总成本是 79,487 美元,年化拖累 59.8 个基点。实验室对这只美股单边收 10 bps,一笔来回大约是成交名义的 20 个基点。
把每笔 cost_usd 除以 0.002 还原名义,再乘港股来回法定费用 22.54 个基点,这 56 笔合计 89,581 美元。同一条换手上,法定拖累大约是 59.8 × 22.54/20 = 67.4 个基点一年。甲骨文没有付过这笔印花税。一年大约 3 次来回时,22.54 个基点和美股教学摩擦的 20 个基点已经同一量级。标的若落到正文那种 0.40 港元,一个价位仍是 125.0 个基点,比这整趟法定费用更贵。换手再加快,67.4 个基点会按来回次数往上乘。
方法论来源¶
| 概念 / 模型 | 核心文献或规范出处 |
|---|---|
| 港股印花税 0.1% | 2023-11-17 起买卖各方;此前一段为 0.13% |
| 交易费、证监会征费、会财局征费、结算费 | 港交所收费页;结算费 0.0042% 用于 2025-06-30 及之后的交易所交易 |
| 价位 | 0.40 与步长 0.005 仍是图上的手算;2025-08-04 与 2026-08-03 的分阶段收窄见第 14 节 |
| 恶劣天气交易 | 港交所通告 CT/148/24,2024-09-23 起证券市场在 8 号风球或黑雨下维持开市 |
| PTP 预提 | 美国国内税收法 1446(f);按卖出总金额,不是按盈利 |
| 碎股 | 整手之外的余额单独入账;折价见第 14 节 |
| 美股 NBBO 与订单流对价 | 全国最佳买卖价;规则文本不写成已经生效的终态 |
| 整手 | src/quant/backtest/money.py 的 whole_lots |
| 总收益因子 | src/quant/data/corporate_actions.py 的 return_factors;供股不在其中 |
| 日线与 5 分钟 | 仓库对齐逻辑;2018-12-24 官方收盘 13:00 |
| 甲骨文除息 | Labs/technical_indicators_lab/seed_data/US.ORCL_actions.csv 与同目录行情;69 行股息、0 行拆股 |
| TCEHY 均值回归 | Labs/tencent_adr_lab/outputs/scheme_performance_summary.csv 的 S3_Daily_RSI_Long;0 成本一列用同一引擎把单边 bps 设为 0 |
| 方案 4 摩擦 | m4_round_trip_trades.csv 与 trade_execution_summary.csv;港股 22.54 个基点是法定来回,不是甲骨文的成交 |
💡 思考回顾与巩固¶
核心认知纠偏¶
| 误区 | 反例 | 用法 |
|---|---|---|
| 用了收盘价就可以按收盘价成交 | 收盘已经发生,下一时段才能下单 | 先看钟 |
| 港股和美股常规时段有一段重叠 | 16:00 香港时间早于美东 9:30 | 差 12 或 13 小时 |
| 当天能卖出,所以卖出款当天已交收 | 美国交收是 T+1;未成交的卖出在仓库里不能用来买 | 两道限制 |
| 除息日跌 2% 就是亏损 | 股息 2 时总收益因子是 1 | 未复权只用于下单 |
| 总收益已经扣了预提税 | 预提 30% 后收益是 −0.006 | 税在指数外面 |
| 250 股可以零碎发送 | 每手 100 时只发送 200 | 向下取整 |
| 印花税是低价股的最大成本 | 0.40 上一个价位是 125 个基点,买卖印花税是 20 | 先比基点 |
| 1 比 1 的 ADR 价差是套利 | 时段不重叠,还有汇率和转换 | 比率不是成交 |
| 3% 融券费是券商合同 | 实验室标明是研究假设,而且不发空单 | 借不到就不成交 |
| 盘前盘后可以走同一条限价路径 | 发送路径只要常规时段 | 非常规时段另算 |
| 印花税就是港股的全部法定成本 | 20,000 港元一侧合计 22.54,印花税是 20.00 | 交易费、两项征费和结算费另计 |
| 除净日下跌一定是亏损 | 供股未复权 −0.066667,权利入账后可以是 0 | 先看有没有权 |
| 5 分钟线的最后一笔就是日收盘 | 2018-12-24 官方收盘是 13:00 | 信号用日线 |
| 8 号风球一定停市 | 2024-09-23 起恶劣天气交易期间证券市场开市 | 旧样本保留真实停市日 |
| 总净值杠杆 ≤ 1 就不会付融资利息 | 0 港元现金买入 234,000 港元,30 天利息 1307.84 港元 | 按交易币种看已交收现金 |
| 平价卖出 PTP 不会亏 | 卖出总金额的 10% 被预提,10,000 美元平价卖出净亏 1,000 | 先对券商的 PTP 名单 |
| 碎股可以按整手买一价估值 | 50 股碎股按 5% 折价只值 475 | 整手和碎股分列 |
费曼自测¶
- 为什么港股 16:00 的收盘不能和美股常规时段的开盘对成一笔套利?
- 总收益因子是 1 时,持有人为什么仍可能亏 0.6%?
- 整手向下取整丢掉的 50 股,会不会在下一笔里被自动补上?
- 延长早市写在时间表上,为什么不能据此说 12:00–13:00 一定在持续撮合?
- 美元现金还有 100,000、港股只占净值的 30% 时,为什么港元子账本仍会按融资利率计息?
自测要点
1. 香港不实行夏令时。美东 9:30 对应香港时间 21:30 或 22:30。港股持续撮合在 16:00 已经结束。中间的价差可以来自汇率、新闻和场外价差,不是同一本订单簿上的买卖。 2. 因子 1 用的是毛股息 2。预提 30% 后只拿到 1.40,加上未复权价格 98,价值是 99.40。总收益指数不代替报税。 3. 不会。`whole_lots` 对这一笔向下取整,剩下的 50 股不是挂单。下一笔如果仍不足一手,继续是 0。 4. 常见问题把持续撮合写成 9:30–12:00 和 13:00–16:00。延长早市另有一行。两句同时成立时,不要把 12:00–13:00 写成和早市一样的撮合。半日市连这一行都没有。 5. 综合账户的折算净值把美元现金和港股市值加在一起。买单若没有先换成港元,港元现金是 −234,000。融资利息按这个负余额计算,不按「杠杆滑杆是否超过 1」计算。四维习题¶
A. 概念题¶
- 开市前的不可撤时段和收市竞价的不可撤时段,各自挡住的是什么?
- 未复权价和总收益指数,哪一个用来下单,哪一个用来排名?
- 美国 T+1 交收和「未成交卖出不能买」差在哪里?
- 为什么场外 ADR 的 1 比 1 不能推出可交易的价差策略?
B. 计算题¶
- 前收 100,除息日收盘 98,股息 2。未复权收益、总收益因子、预提 30% 后的收益各是多少?拆股倍数 2、收盘 50 时因子是多少?
- 每手 100,委托 250 股。实际发送多少?名义 20,000 港元、税率 0.1% 时,一侧印花税是多少?一买一卖占名义多少个基点?
- 价格 0.40、价位 0.005。一个价位是多少个基点?它是买卖印花税 20 个基点的多少倍?
- 港股 400 港元,汇率 7.80,ADR 报价 52 美元,比率 1 比 1。隐含美元价和价差是多少?
- 名义 20,000 港元。按交易费 0.00565%、证监会征费 0.0027%、会财局征费 0.00015%、结算费 0.0042%,加上印花税 0.1%,一侧合计多少港元?来回多少个基点?前收 100、每 2 股按 80 认购 1 股,理论除权价和忽略权利时的收益是多少?
- 已交收港元现金为 0,买入 234,000 港元且不换汇。港元融资年化 6.8%,一年 365 天,持有 30 天的利息是多少?占仓位多少个基点?一次性换汇点差 15 个基点和 30 个基点时,盈亏平衡持有天数各是多少?
- 10,000 美元买入一只 PTP,平价 10,000 美元卖出。非美税务居民按卖出总金额 10% 被预提时,价格盈亏和税后净盈亏各是多少?
C. 编程实战题¶
- 运行
figures/M18/plot_microstructure.py。核对lots 200、因子 1.000000、−0.006000、印花税 20.00、价位 125.0、隐含价 51.282051、价差 0.014000。 - 把手数改成 200,委托仍是 250。说明发送股数为什么变成 200 而不是 400。不要为了凑整手去加仓。
- 不要在开发机提交订单。时段以港交所和美股交易所当时的页面为准。半日市仍要改日历。2024-09-23 起,8 号或以上台风信号和黑色暴雨警告本身不停市。回测若跨过这一天,此前的真实停市日保留,此后不把这类天气写成停市;例外停市以公告为准。
- 运行第 14 节的代码。核对
interest_hkd 1307.84、drag_bps 55.89、breakeven_15_bps 8.05、breakeven_30_bps 16.10、ptp_withholding_usd 1000.00,以及 12.015 在步长 0.02 和 0.01 下分别对齐到 12.00 和 12.01。不要在开发机发单。
D. 个人实战思考题¶
- 你的信号在港股 16:05 用了收市竞价还没结束时的价格。下一笔最早能放在哪里?
- 券商显示美股卖出已成交,但购买力没有增加。你会不会用这笔钱立刻再买?
- 你看到
TCEHY比港股折算价高 1.4%。在只做多、不能同时交易两个时段的账户里,这 1.4% 是利润还是未实现的价差? - 账户折算净值的多头只有 30%,券商界面的杠杆显示不超过 1。你会不会跳过换汇,直接用美元现金去买港股?若一只 PTP 已经跌回成本价,平价卖出能不能当成「至少不亏」?
完整参考答案与解析
**A1.** 开市前 9:15–9:20 不可改撤,是为了让 9:20 至多到 9:22 的随机对盘有一个稳定的订单簿。收市竞价 16:06–16:08 不可撤,随机收市在 16:08–16:10。两段都阻止在对盘前一瞬间撤单。输入时段里的订单仍然可以不成交。 **A2.** 下单用未复权价,因为交易所撮合的是当时的报价,不是总收益指数。排名用向前累加的总收益,避免把拆股和除息当成暴跌。后来的公司行动不改写更早的指数点。 **A3.** T+1 说的是成交之后,资金何时完成交收。未成交卖出还没有价格,也没有数量可以入账,所以不能当作现金。成交之后是否能用未交收资金再买,看券商的购买力规则,不是交易所的一个总开关。 **A4.** 1 比 1 只换算份额。港股持续撮合结束时,美股常规时段尚未开盘。场外报价有自己的买卖价差,转换要时间和费用,空头还要券。实验室的 3% 融券费是假设,而且实验不发空单。 **B5.** 未复权收益 −0.020000。总收益因子 (98 + 2) / 100 = 1.000000。预提后 (98 + 1.40) / 100 − 1 = −0.006000。拆股因子 (50 × 2) / 100 = 1.000000。 **B6.** 发送 200 股,剩下 50。一侧印花税 20.00 港元。买卖合计 40 港元,占名义 40/20000 = 0.002,即 20.0 个基点。 **B7.** 0.005/0.40 = 0.0125,即 125.0 个基点。125/20 = 6.25 倍。价位表变了,这个倍数要重算。 **B8.** 400/7.80 = 51.282051 美元。价差 52/51.282051 − 1 = 0.014000,即 140 个基点。汇率或报价一变,价差就变。 **B9.** 一侧是印花税 20.00、交易费 1.13、证监会征费 0.54、会财局征费 0.03、结算费 0.84,合计 22.54 港元。来回 45.08 港元,占名义 22.54 个基点。理论除权价 (200 + 80) / 3 = 93.333333,忽略权利的收益是 −0.066667。权利摊到每股旧股 6.666667 并按理论价入账时,收益是 0。结算费 0.0042% 对应 2025-06-30 及之后执行的交易所交易。 **B10.** 234000 × 0.068 × 30/365 = 1307.84 港元。1307.84/234000 = 0.005589,即 55.89 个基点。每日融资是 0.068/365 × 10000 个基点。15 个基点对应 15 × 365/680 = 8.05 天,30 个基点对应 16.10 天。6.8% 和 15–30 个基点都是教学输入。美元现金仍是 100,000 时,只要没换成港元,港元子账本就是这 234,000 的负数。 **B11.** 价格盈亏是 0。预提 10000 × 0.10 = 1000.00 美元,税后净盈亏 −1000.00 美元,即 −10%。税打在卖出总金额上。这不是税务意见,也不是一份不会过期的 PTP 名单。 **C12–C14.** 下面这段自洽,不下载行情,也不发单。手数改成 200 时,250 仍不足两手。from decimal import Decimal
from quant.backtest.money import whole_lots
def theoretical_ex_rights(prev: Decimal, old_shares: int, new_shares: int, subscription: Decimal) -> Decimal:
# 理论价只服务复盘。仓库的 return_factors 不认识供股。
return (old_shares * prev + new_shares * subscription) / (old_shares + new_shares)
assert whole_lots(Decimal("250"), Decimal("200")) == Decimal("200")
assert theoretical_ex_rights(Decimal("100"), 2, 1, Decimal("80")) == Decimal("280") / 3
# goal2-check
from decimal import ROUND_HALF_EVEN, Decimal
interest = (Decimal("234000") * Decimal("0.068") * 30 / 365).quantize(
Decimal("0.01"), rounding=ROUND_HALF_EVEN
)
drag_bps = (interest / Decimal("234000") * Decimal(10000)).quantize(
Decimal("0.01"), rounding=ROUND_HALF_EVEN
)
days_15 = Decimal("15") * 365 / (Decimal("0.068") * 10000)
days_30 = Decimal("30") * 365 / (Decimal("0.068") * 10000)
withheld = (Decimal("10000") * Decimal("0.10")).quantize(Decimal("0.01"))
assert interest == Decimal("1307.84")
assert drag_bps == Decimal("55.89")
assert f"{days_15:.2f}" == "8.05"
assert f"{days_30:.2f}" == "16.10"
assert withheld == Decimal("1000.00")
print("interest_hkd", interest)
print("drag_bps", drag_bps)
print("breakeven_15_bps", f"{days_15:.2f}")
print("breakeven_30_bps", f"{days_30:.2f}")
print("ptp_withholding_usd", withheld)
print("ptp_net_pnl_usd", Decimal("0") - withheld)
📌 章节知识总结¶
一句话核心洞察¶
- 港股持续撮合是 9:30–12:00 和 13:00–16:00。美股常规时段是美东 9:30–16:00。两边不重叠。
- 除息日未复权收益可以是 −2%,总收益因子仍是 1。预提 30% 后,持有人收益是 −0.6%。
- 250 股、每手 100,只发送 200。买卖印花税在这个例子里是 20 个基点,一个低价价位是 125 个基点。
- 400 港元、汇率 7.80、报价 52 美元、比率 1 比 1,价差是 1.40%。时段不重叠,所以不是套利。
- 当天能卖出不等于现金已交收。未成交的卖出不能用来买。空头要有券,3% 只是实验室假设。
- 名义 20,000 港元的一侧法定费用是 22.54 港元。供股的理论除权可以让未复权 −6.6667% 在权利入账后变成 0。
return_factors不自动做这件事。
公式与样本¶
| 对象 | 数值 | 前提 |
|---|---|---|
| 开市前 | 9:00–9:15、9:15–9:20、9:20–9:22、至 9:30 | 港交所常见问题 |
| 持续撮合 | 9:30–12:00 与 13:00–16:00 | 同一页;延长早市另列 |
| 收市竞价 | 16:00–16:01、16:01–16:06、16:06–16:08、16:08–16:10 | 2026-04 说明;并非全部证券 |
| 美股常规 | 美东 9:30–16:00 | 2018-12-24 收盘 13:00 |
| 未复权 | −0.020000 | 100 到 98 |
| 总收益因子 | 1.000000 | 股息 2,或拆股倍数 2、价格 50 |
| 预提后 | −0.006000 | 30%,不是税务意见 |
| 整手 | 发送 200,剩下 50 | 250 股、每手 100 |
| 印花税 | 一侧 20.00 港元,买卖 20.0 个基点 | 名义 20,000,税率 0.1% |
| 价位 | 125.0 个基点 | 价格 0.40、价位 0.005 |
| ADR 价差 | 隐含 51.282051,价差 0.014000 | 400 港元、7.80、52 美元、1 比 1 |
| 融券 | 年化 3% | TCEHY 实验室假设,不发空单 |
| 交收 | 美国多数证券 T+1 | 2024-05-28 起;与未成交限制分开 |
| 法定费用一侧 | 22.54 港元,来回 22.54 个基点 | 20,000 港元;不含佣金 |
| 其中非印花税 | 每边 1.27 个基点 | 1.13+0.54+0.03+0.84 |
| 供股 | 除权价 93.333333,未复权 −0.066667 | 2 股旧股、认购价 80;权利入账后为 0 |
| 日线对齐 | 09:35 覆盖 9:30–9:35;常规收盘 16:00 | 2018-12-24 官方收盘 13:00;缺口容忍 10 个基点 |
| 恶劣天气交易 | 2024-09-23 起 8 号风球或黑雨下开市 | 此前真实停市日保留;例外停市另有公告 |
| 港元负现金 30 天 | 利息 1307.84 港元,55.89 个基点 | 234,000 港元、年化 6.8%、365 天计息 |
| 换汇盈亏平衡 | 15 个基点约 8.05 天,30 个基点约 16.10 天 | 教学点差,不是券商牌价 |
| PTP 平价卖出 | 预提 1000.00 美元,税后 −1000.00 | 卖出总金额的 10%;价格盈亏为 0 |
| 碎股 | 50 股;5% 折价合计市值 2475.00 | 250 股、每手 100、价格 10 港元;1% 折价为 2495.00 |
跨模块¶
| 结论 | 交给 | 位置 |
|---|---|---|
| 收盘不能当日成交 | 信号时点 | M03 |
| 印花税 0.1% 与 0.13% | 拖累 | M13 |
| 只做常规时段限价 | 发送 | M15 |
| 日内保证金 | 次数规则 | M04 |
| 总收益与整手 | 代码 | ../../../src/quant/data/corporate_actions.py,../../../src/quant/backtest/money.py |
| 日线与 5 分钟 | 对齐 | ../../../src/quant/data/bar_alignment.py |
| 1 比 1 与 3% 假设 | 单名实验室 | ../../Labs/tencent_adr_lab/README.md |
| 复算 | 图 | ../figures/M18/plot_microstructure.py |
| 币种现金、PTP、碎股 | 标的池与发单前检查 | M03 第 12 节;卡片 |
| 空仓现金不是融资利息 | 策略收益里的现金 | M02、M04 |
学习指引¶
概念卡片:港美股账户与税务陷阱
- 精读:港交所证券市场常见问题里的交易时间、开市前和收市竞价。时间表改了,以新页面为准。恶劣天气交易以 2024-09-23 的实施通告为准。
- 对照:因子、整手、印花税和价差以脚本打印为准。400 港元和 52 美元不是历史成交。
- 下一步:M19 写事件窗口时,除息日和公告日仍然要用这一章的时点:当天收盘之后的信息,不能用来在当天收盘成交。
自检清单¶
- 能说出港股持续撮合的两段,以及它和美股常规时段不重叠
- 能把 100、98、股息 2 算成未复权 −2%、总收益 0、预提后 −0.6%
- 能把 250 股收成 200 股,并把 20 个基点的印花税和 125 个基点的价位分开
- 能把 400/7.80 算成 51.282051,价差 1.40%
- 能说明未成交卖出不能买,以及 3% 融券费不是券商合同
- 能说明收市竞价的随机收市不是「用收盘价免费成交」
- 能把 234,000 港元、6.8%、30 天算成 1307.84 港元,并说出 8.05 天和 16.10 天的换汇盈亏平衡
- 能说明 PTP 平价卖出仍可能预提 1,000 美元,以及 GFV 发生在未交收资金被拿去当天买、交收前卖
- 能把 2024-09-23 前后的台风日历分开,并把 50 股碎股从整手盘口价里拆出去
