跳转至

M18 港美股微观结构与制度

层级:L1 策略与回测 | 优先级:★★★ | 预计学时:10 小时 对应原书:黑箱第 7–8 章的执行与数据,只作地图 前置知识:M03 的成交时点;M13 的印花税;M15 的常规时段限价。 读书笔记:时段与公司行动 状态:reviewed 数据截至:2026-10|最近复核:2026-10-28


学习目标

  1. 把港股开市前、持续交易、收市竞价和美股常规时段分开,并看出两边的常规时段没有重叠。
  2. 用手算未复权收益、总收益和预提税之后的收益。
  3. 用整手把 250 股收成 200 股,并把一个价位和印花税放在同一张基点图上。
  4. 说明 1 份 ADR 对 1 股、汇率 7.80 时,400 港元对 52 美元的价差不是无风险套利。
  5. 不把日内能卖出写成现金已经交收,也不写绕过券商风控或税务申报的做法。
  6. 按交易币种检查已交收现金,并把 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%,那是复权错误,不是行情。

左:未复权 −2%,总收益 0,预提 30% 后 −0.6%。右:一个价位 125 个基点,买卖印花税 20 个基点,假设价差 140 个基点。

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 整手和碎股分列

费曼自测

  1. 为什么港股 16:00 的收盘不能和美股常规时段的开盘对成一笔套利?
  2. 总收益因子是 1 时,持有人为什么仍可能亏 0.6%?
  3. 整手向下取整丢掉的 50 股,会不会在下一笔里被自动补上?
  4. 延长早市写在时间表上,为什么不能据此说 12:00–13:00 一定在持续撮合?
  5. 美元现金还有 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. 概念题

  1. 开市前的不可撤时段和收市竞价的不可撤时段,各自挡住的是什么?
  2. 未复权价和总收益指数,哪一个用来下单,哪一个用来排名?
  3. 美国 T+1 交收和「未成交卖出不能买」差在哪里?
  4. 为什么场外 ADR 的 1 比 1 不能推出可交易的价差策略?

B. 计算题

  1. 前收 100,除息日收盘 98,股息 2。未复权收益、总收益因子、预提 30% 后的收益各是多少?拆股倍数 2、收盘 50 时因子是多少?
  2. 每手 100,委托 250 股。实际发送多少?名义 20,000 港元、税率 0.1% 时,一侧印花税是多少?一买一卖占名义多少个基点?
  3. 价格 0.40、价位 0.005。一个价位是多少个基点?它是买卖印花税 20 个基点的多少倍?
  4. 港股 400 港元,汇率 7.80,ADR 报价 52 美元,比率 1 比 1。隐含美元价和价差是多少?
  5. 名义 20,000 港元。按交易费 0.00565%、证监会征费 0.0027%、会财局征费 0.00015%、结算费 0.0042%,加上印花税 0.1%,一侧合计多少港元?来回多少个基点?前收 100、每 2 股按 80 认购 1 股,理论除权价和忽略权利时的收益是多少?
  6. 已交收港元现金为 0,买入 234,000 港元且不换汇。港元融资年化 6.8%,一年 365 天,持有 30 天的利息是多少?占仓位多少个基点?一次性换汇点差 15 个基点和 30 个基点时,盈亏平衡持有天数各是多少?
  7. 10,000 美元买入一只 PTP,平价 10,000 美元卖出。非美税务居民按卖出总金额 10% 被预提时,价格盈亏和税后净盈亏各是多少?

C. 编程实战题

  1. 运行 figures/M18/plot_microstructure.py。核对 lots 200、因子 1.000000、−0.006000、印花税 20.00、价位 125.0、隐含价 51.282051、价差 0.014000。
  2. 把手数改成 200,委托仍是 250。说明发送股数为什么变成 200 而不是 400。不要为了凑整手去加仓。
  3. 不要在开发机提交订单。时段以港交所和美股交易所当时的页面为准。半日市仍要改日历。2024-09-23 起,8 号或以上台风信号和黑色暴雨警告本身不停市。回测若跨过这一天,此前的真实停市日保留,此后不把这类天气写成停市;例外停市以公告为准。
  4. 运行第 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. 个人实战思考题

  1. 你的信号在港股 16:05 用了收市竞价还没结束时的价格。下一笔最早能放在哪里?
  2. 券商显示美股卖出已成交,但购买力没有增加。你会不会用这笔钱立刻再买?
  3. 你看到 TCEHY 比港股折算价高 1.4%。在只做多、不能同时交易两个时段的账户里,这 1.4% 是利润还是未实现的价差?
  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
第 5 节的 `hk_statutory_one_side(Decimal("20000"))` 合计是 22.54。供股仍不进入 `return_factors`。第 14 题的日历:2018 年的真实台风停市保留为不开市;2024-09-23 及之后,天气信号本身不再关掉会话,例外停市看公告。半日市改的是收盘时刻,不是这条开市开关。 **C15.** 与第 14 节同一段代码。利息 1307.84,拖累 55.89 个基点,盈亏平衡 8.05 天和 16.10 天,预提 1000.00 美元。`align_hk_tick(Decimal("12.015"), Decimal("0.02"))` 是 12.00,步长 0.01 时是 12.01。0.403 对 0.005 仍是 0.400,第 4 节的 125.0 个基点不改。开发机不提交订单。
# 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)
**D16.** 16:05 还在收市竞价的输入时段里,价格不是最终收盘。最早的下一笔是竞价结束并得到官方收盘之后的下一个可交易时段。若当天已经没有持续撮合,就是下一交易日的开市前或早市,而且仍要遵守你的券商允许的订单类型。用未完成的竞价价去假装收盘成交,是前视。 **D17.** 不自动再买。先分清是购买力规则、未交收,还是订单状态仍是未知。M15 里未知状态要停新单。购买力没有增加时,把屏幕上的成交当成现金,会在卖出款不能用的情况下发出买单。 **D18.** 是未实现的价差。只做多的账户不能同时卖贵的一边、买便宜的一边。两个市场的常规时段又不重叠。1.4% 在下一时段开盘前可以被汇率和新闻吃掉。它不是已经落袋的利润。 **D19.** 不跳过换汇。30% 的多头只说明折算净值没有超过 1,港元现金已经是负的。持有超过大约 8 至 16 天时,6.8% 的融资利息会超过一次性 15 至 30 个基点的换汇点差。平价卖出 PTP 不是「至少不亏」:预提按 10,000 美元的 10% 扣,价格盈亏为 0 时税后仍少 1,000 美元。卖之前先看这只代码在不在券商的 PTP 名单上。

📌 章节知识总结

一句话核心洞察

  1. 港股持续撮合是 9:30–12:00 和 13:00–16:00。美股常规时段是美东 9:30–16:00。两边不重叠。
  2. 除息日未复权收益可以是 −2%,总收益因子仍是 1。预提 30% 后,持有人收益是 −0.6%。
  3. 250 股、每手 100,只发送 200。买卖印花税在这个例子里是 20 个基点,一个低价价位是 125 个基点。
  4. 400 港元、汇率 7.80、报价 52 美元、比率 1 比 1,价差是 1.40%。时段不重叠,所以不是套利。
  5. 当天能卖出不等于现金已交收。未成交的卖出不能用来买。空头要有券,3% 只是实验室假设。
  6. 名义 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 股碎股从整手盘口价里拆出去