写一个动量因子并不难,写一个反转因子也不难。真正让人犹豫的是:两个因子今天给出了相反的意见,资金应该听谁的?如果某个因子连续一段时间表现不好,是降低权重、暂时停用,还是把方向反过来?新发现的公式,又凭什么获得真实仓位?
这些问题很难靠增加几个指标解决。因子越多,越需要一套统一的考核和使用规则。
APFF,全称 Adaptive Perpetual Factor Factory,中文名称是“多品种永续合约自适应因子工厂策略”。它在一组永续合约之间比较相对强弱,让少量可解释因子共同形成多空组合,同时持续记录每个因子的前向表现,逐步调整权重,并让新候选经过观察和小权重试用。
这篇文章以已经完成运行修复的 FMZ JavaScript 版本为基础,讲清楚从数据、因子到实际订单的完整过程。对应版本为 v0.1.0,build 20260905-05,研究口径版本 2。目前的运行和模拟测试验证了主要工程流程,长期收益效果仍需后续样本检验。
一、先把交易问题变成“谁相对更强”
APFF 每次面对的是一个横截面:同一时刻的一组不同合约。
假设两个篮子各配置 3,000 USDT 的初始名义金额,一个做多,一个做空。在不考虑费用、资金费和调仓的简化情形下,多头篮子上涨 4%,空头篮子上涨 1%,价格盈亏就是:
text
多头盈利 = 3,000 × 4% = 120 USDT
空头亏损 = 3,000 × 1% = 30 USDT
合计盈亏 = 120 - 30 = 90 USDT
如果多头篮子下跌 2%,空头篮子下跌 5%,相同名义金额下,价格盈亏也可以是正的。这个例子说明,多空组合关心的是两组品种之间的收益差。
但多空金额接近,并不能消除市场风险。如果多头集中在高波动品种,空头集中在低波动品种,市场突然下跌时,两侧损益仍可能严重失衡。因此,APFF 在相对强弱排序之外,还需要波动率缩放、单品种上限和有限的 BTC Beta 对冲。
整个系统使用几个不同的时间尺度:
| 工作 | 当前节奏 | 作用 |
|---|---|---|
| 最优买卖价、标记价等行情 | WebSocket 持续接收 | 为定价和数据检查提供输入 |
| 基础价格因子 | 4 小时已完成 K 线 | 避免使用仍在变化的当前 K 线 |
| 组合决策及研究标签 | 8 小时 | 形成目标,积累下一期考核结果 |
| 交易池更新 | 每日 | 适应成交活跃度变化 |
| 因子考核和调权 | 每 7 天 | 降低追逐短期噪声的频率 |
| 候选因子生成 | 启动时及后续每 30 天调度 | 从固定模板中补充未注册候选 |
行情需要及时,因子权重则需要慢一些。盘口每次跳动,都触发一次权重重算,会把一个中低频组合变成追逐噪声的调仓系统。
二、交易池决定了因子在比较什么
当前版本只适配 Binance U 本位 USDT 线性永续合约,默认交易池规模为 30 个。
程序通过 GetMarkets 读取合约规格,通过 GetTickers 和实时行情维护成交额、价格及价差。基础筛选包括合约交易状态、有效报价、价差限制,以及能够取得上市时间时的 90 天上市要求。然后,按当前 24 小时成交额排序。
这里采用了排名缓冲。默认情况下,旧成员只要仍在符合条件品种的前 40 名内,就优先保留,再用排名靠前的其他合约补齐交易池。这样,两个成交额接近的品种不会因为每天交换一下排名,就反复进出组合。
BTC 被单独用作市场基准和对冲工具,进入交易池后也会从普通 Alpha 排名中排除。实际决策还要求至少有 15 个通过数据检查的非 BTC 品种,交易池配置数量和最终可用数量需要分开看。
这部分实现有明确边界:当前采用的是 24 小时成交额及基础过滤,参考方案中的 30 天平均成交额、历史时点交易池、连续数据完整率和盘口深度筛选还没有完整落地。上市时间缺失时,代码也不能独立证明该品种已上市 90 天。分析策略效果时,需要保留这些差异。
三、五个种子因子,各自提出一个可检验的假设
首批种子因子来自五个家族:动量、反转、资金费、溢价和持仓量。
下表中,RET 表示价格对数收益;RV 表示对应窗口内 4 小时对数收益的样本标准差,并非年化波动率;Z_TS 表示单品种时间序列标准分。
| 因子 | 当前计算思路 | 希望检验的关系 |
|---|---|---|
| 排除最近冲击的动量 | (RET_7D - RET_4H) / RV_7D | 剔除最近一根 K 线后,中期强势是否延续 |
| 短期反转 | -RET_4H / RV_24H | 短期相对过度下跌是否存在修复 |
| Funding 拥挤 | -Z_TS(按结算间隔折算的资金费率) | 资金费相对自身历史偏高,是否对应较弱的后续收益 |
| Premium 回归 | -Z_TS(Premium 序列) | 相对自身历史偏高的溢价是否回落 |
| OI 确认 | 价格方向 × Z_TS(OI 名义价值的 24H 对数变化) | 持仓扩张与价格方向的组合是否具有预测信息 |
OI 的价格方向项在 24 小时收益非负时取 +1,为负时取 -1。这些解释是研究假设,是否成立要由成熟样本来回答。比如,“资金费高”不意味着价格马上下跌,拥挤交易完全可能持续很久。
动量因子的核心可以直接从源码中看到:
javascript
var full = returnBars(prices, p.longBars)
var recent = returnBars(prices, p.skipBars)
var rv = realizedVol(prices, p.longBars)
return finite(full) && finite(recent) && rv > 0
? direction * (full - recent) / rv
: NaN
种子参数使用 42 根 4 小时 K 线表示 7 天,跳过最近 1 根。无效数据返回 NaN,随后退出有效值集合。
Funding 要先统一时间尺度
不同记录的资金费结算间隔可能不同。当前代码依据记录间隔,将费率折算到统一的 8 小时尺度,再计算时间序列标准分。种子使用约 30 天的时间窗口,并检查历史覆盖和最新记录是否足够新。
当期费率和历史结算费率也必须分开。FMZ 的 GetFundings 提供当前周期资金费率数据;APFF 把它保存为当期信息,研究收益和结算记账另外读取历史记录。FMZ GetFundings 文档
Binance 的 /fapi/v1/fundingRate 历史接口提供 fundingTime、fundingRate,以及与该次资金费对应的 markPrice,这是当前实现计算历史资金费现金流的依据。Binance 历史资金费文档
Premium 和 OI 要认清数据来源
生产环境的 Premium 使用交易所 Premium Index K 线历史;模拟环境则逐步积累 Mark / Index - 1 的快照代理序列。两种序列的定义并不完全相同,因此不能直接用模拟环境的因子表现推断生产环境效果。
OI 的生产历史只接受报价币名义价值口径。模拟快照则根据可获得的持仓量与价格积累,并按 4 小时时间桶保存,避免把六次分钟快照误当成六根 4 小时观察值。
辅助历史不足时,相应因子可以暂时缺省。对一套需要长期运行的程序而言,能够明确知道“这个因子暂时没有足够数据”,比勉强计算出一个数字更有意义。
四、统一排名,让不同因子能够共同投票
五个原始因子的量纲不同,不能直接相加。APFF 对每个因子做横截面处理:先过滤无效值,再按 5% 和 95% 分位缩尾,最后按排名映射到 [-1, +1]。
若排名从 0 开始,映射公式为:
text
标准分 = 2 × 排名 / (有效品种数 - 1) - 1
最高分表示该因子最看好的品种,最低分表示最不看好的品种。相同原始值获得平均名次;整个截面没有有效差异时,该因子判为无效,避免数组顺序凭空制造信号。
单个因子至少要覆盖当轮 80% 的品种。形成综合分数时,某个品种又必须覆盖至少 80% 的当轮有效因子权重。两道检查分别控制“因子是否有足够横向信息”和“这个品种是否有足够综合信息”。
对通过检查的品种,综合分数可写为:
text
Alpha_i = Σ(可用因子权重_j × 标准分_ij) / Σ(该品种可用因子权重_j)
这个分数负责相对排序。组合能够使用多少资金,还要看另一项:真正可用的因子预算。
五、缺少两个因子,为什么总仓位也应该缩小
初始五个种子因子各分配 20% 权重。假设动量、反转、Funding 可用,而 Premium 和 OI 还没有积累足够历史,此时有效因子预算为:
text
20% + 20% + 20% = 60%
如果把剩余三个因子都放大到 33.33%,虽然权重加起来重新变成了 100%,但同一组仓位实际上由更少的信息支撑,每个因子承担的风险也扩大了。
APFF 保留每个因子原有的预算含义,让尚未分配的部分闲置。组合初始总名义金额的计算为:
text
总名义预算 = 账户权益
× 配置总敞口比例
× 选择器置信系数
× 有效因子预算之和
× 回撤缩放系数
举一个纯粹用于说明计算方式的例子:账户权益 10,000 USDT,总敞口配置为 60%,有效因子预算之和为 60%,置信系数和回撤系数都为 1,那么:
text
总名义预算 = 10,000 × 60% × 1 × 60% × 1
= 3,600 USDT
初始多空两侧各分配约 1,800 USDT,随后还要接受单品种、对冲及换手约束。这个数字不代表最终一定成交到位,也不代表保证金占用比例。
当前有效权重还受到以下上限限制:
| 预算对象 | 上限 |
|---|---|
| 单个正式因子 | 25% |
| 同一家族合计 | 35% |
| 单个试用因子 | 5% |
| 全部试用因子合计 | 10% |
这些比例约束的是因子预算。它们会进一步影响组合名义敞口,不能直接解释为某个币占账户权益的比例。
对应代码分成两步:effectiveWeights 取出当轮有有效分数的正式、试用因子,再调用预算约束函数。下面摘出两处衔接的核心代码,第二部分位于 normalizeWeightTargets 的因子遍历内部:
javascript
function effectiveWeights(matrix) {
var raw = {}
factorIdsByStatus(["ACTIVE", "TRIAL"]).forEach(function(id) {
if (Object.keys(matrix[id] || {}).length)
raw[id] = Math.max(0, num(G.registry[id].weight, 0))
})
return normalizeWeightTargets(raw, G.registry, true)
}
// normalizeWeightTargets 内部的单因子约束片段
var cap = factor.status === "TRIAL"
? FIXED.trialFactorCap : FIXED.activeFactorCap
result[id] = Math.min(
cap,
Math.max(0, num(raw[id], 0))
/ (preserveBudget ? Math.max(1, total) : total)
)
关键是最后传入的 true,对应 preserveBudget。假设有效权重之和 total 只有 0.6,分母取 Math.max(1, 0.6),每个 0.2 就仍然是 0.2;若直接除以 0.6,它们就会被放大为约 0.3333。当合计预算超过 1 时,这个分母又会将其压回总预算范围。后续函数还会检查家族上限和试用合计上限,截掉的预算不会重新摊给其他因子。
六、先保存今天的判断,再等待明天的答案
自适应系统最容易犯的错误,是在考核时不知不觉改变了题目。
APFF 生成一轮决策时,会保存当时的交易池版本、入场参考价格、因子分数和覆盖率,并创建一个 8 小时后到期的研究样本。因子成绩只在这个样本成熟后更新。
初次启动后,状态栏出现“有效样本 0”可能完全正常。历史 K 线已经可以计算动量,和策略已经积累了完整的前向考核样本,是两个不同的条件。五个种子的 ACTIVE 表示初始运行身份,也不意味着它们已经通过了统计检验。
1. 固定样本成员和到期时间
每个因子的研究组合在入场时,根据当时评分选择顶部约 20% 做多、底部约 20% 做空,两侧各分配一半名义预算,侧内等权。分位边界上的并列成员共同纳入,再分配侧内权重。
到期后,不能因为某个品种缺少价格,就删除它、重新排名并换入其他品种。否则,恰好无法取到数据的亏损持仓可能从统计里消失。
当前实现最多允许 60 秒的期末价格采集窗口。采集到的期末价格一经固定,即使资金费历史晚到,也不再换成后来的价格。资金费最多再等待到原到期时刻之后 5 分钟;资料仍不完整,整期样本作废。
这使时间误差受到明确约束,但标签仍是中间价研究近似,不能等同于被动挂单在真实队列中的成交收益。
2. 价格特征和交易收益使用不同口径
对数收益适合计算动量和波动特征。固定数量的线性永续持仓,在计算损益时则应使用简单价格收益,并叠加资金费现金流。
设入场参考价格为 P0,期末价格为 P1,期间第 k 次结算费率为 f_k,结算标记价格为 M_k。单位初始名义本金的多头研究收益为:
text
r_long = P1 / P0 - 1 - Σ(f_k × M_k / P0)
代码在存在历史标记价时使用上述口径;缺少标记价时,以初始名义金额近似资金费影响。这里尚未扣除交易费用。
因子多空组合再按带符号权重汇总收益,并扣除估算换手成本:
text
换手量 = Σ |新权重_i - 旧权重_i|
净收益 = Σ(新权重_i × r_long_i) - 换手量 × 单边成本
例如,一个品种从 +5% 多头变成 -5% 空头,权重差绝对值已经是 10%,包含平多和开空两段交易。若再额外乘一次 2,就会重复计算成本。
七、让新因子经历观察、试用和退出
当前“因子工厂”从有限模板中生成候选。模板覆盖不同期限的动量、反转、Funding、Premium、OI,以及量价和低波动等方向。
每批最多补充 12 个未注册候选。公式类型、参数和方向组成固定身份,用于去重;模板耗尽后,后续批次可能不再产生新候选。当前没有在线调用大模型生成交易代码,也没有开放式公式搜索。
候选的主要路径是:
| 状态 | 含义 | 是否参与组合 |
|---|---|---|
OBSERVING | 积累前向研究成绩 | 否 |
TRIAL | 通过观察条件,开始独立试用考核 | 可以,但权重很小 |
ACTIVE | 正式参与组合 | 可以,受预算约束 |
DORMANT | 试用不通过或正式因子持续衰减 | 权重归零 |
REJECTED | 例如与现有因子高度重复而被拒绝 | 否 |
观察期至少需要 180 个有效的 8 小时样本,理想连续情况下约 60 天;进入试用后,还要单独积累至少 90 个有效样本,约 30 天。缺失样本和每周筛选节奏都可能让日历时间更长。
试用期成绩单独记录,不能把观察期积累的好成绩搬过来,凑满试用门槛。
考核看哪些东西
当前主要考核 Rank IC、成本后的多空收益、分段稳定性、分组单调性、换手率和数据覆盖率。
Rank IC 衡量入场分数排名与随后收益排名是否一致。为了减轻小样本的乐观偏差,均值会做一次收缩:
text
收缩后的平均 IC = n / (n + 60) × 平均 IC
稳定性则把可用收益序列分成四个时间段,统计多少段的平均净收益为正。这是四段检查,不能理解成已经经历四个真实季度或四种完整市场状态。
候选除了要求正的收缩 IC,还需要通过更严的成本检查:基础净收益已经扣过一次估算成本,晋升时再扣一次相同成本,检验成本加倍后是否仍为正。
系统还使用分块 bootstrap 观察结果稳定性,并单独在零均值假设下估计尾概率,再应用带依赖修正的多重比较阈值。两项统计不能混淆:状态栏中的 bootstrap 正收益频率,是重采样结果的描述,不是“未来盈利概率”。
这些规则用于抑制小样本、重复筛选和偶然胜出的影响。固定块长度、有限重采样次数和持续在线筛选仍有近似及选择偏差,不能据此宣称已经严格控制所有误判。
此外,候选与现有组合因子的同期研究收益绝对相关系数高于 0.85 时,会被视为高度重复。每个候选批次最多允许 2 个进入试用,同一批次同一家族最多 1 个;同时试用的因子最多 2 个,正式因子最多 6 个。
筛选中的基础准入函数如下。这里保留原函数逻辑,只将条件拆行并补充注释:
javascript
function factorPasses(factor, minimumSamples) {
var s = summarizeFactor(factor)
return s.samples >= minimumSamples
&& s.shrunkIc > 0
// meanNet 已扣过基础成本,这里再扣一次,检查双倍成本
&& s.meanNet - num(s.turnover, 0)
* G.cfg.oneWayCostBps / 10000 > 0
&& s.stability >= 0.75
&& s.pPositive >= 0.90
&& s.coverage >= 0.98
}
这里所有条件都要同时成立。stability >= 0.75 要求四个时间段中至少三个平均净收益为正;coverage >= 0.98 检查考核样本中的平均因子覆盖率,比单轮参与计算的 80% 门槛更严格。观察期调用时传入 180 个样本门槛,试用期则用独立的 trialMetrics 和 90 个样本门槛。这个函数通过后,调用方仍会检查相应的多重检验、相关性或名额限制,不能仅凭一次 true 就认定因子已经晋升。
八、自适应调权,需要克制地响应成绩
样本尚不足时,系统使用种子等权预算。达到自适应样本条件后,才根据各因子的考核结果形成目标权重。
实现中的证据分由经过缩放的 IC 得分、净收益得分、稳定性和单调性组成,再减去换手及缺失惩罚。它是程序内部的综合评分,不是经过校准的概率。
目标权重还会参考因子研究收益的波动。随后,旧权重和新目标进行平滑:
text
平滑权重 = 70% × 旧权重 + 30% × 新目标权重
普通每周调权将单个因子的变动限制在 5 个百分点以内,再重新施加各类预算上限。因子休眠和硬约束可以让权重下降得更快,不能把这个平滑限制理解为任何情况下都只能慢慢减仓。
正式因子至少积累 180 个有效样本后,如果最近 90 个样本的平均 IC 和平均净收益同时为负,会进入休眠、权重归零。当前逻辑会降低或停用因子,不会因为近期亏损就自动反转其交易方向。
方向反转本身就是一个新假设,应当重新接受考核。否则,系统很容易在一个持续无效的信号上,来回追逐正负两端的噪声。
九、从综合分数到可执行的目标仓位
实际组合与前面用于考核单因子的等权研究篮子有所区别。
实际组合按综合分数选择两端品种,新进入的目标规模参考前后约 20%,每侧最多 6 个。已有目标只要仍处于相应一侧约 35% 的排名区域,就优先保留,以减少边界附近的反复换仓。
侧内原始权重为:
text
原始权重_i ∝ max(0.05, |Alpha_i|) / RV_7D_i
相对强度越明显,原始权重越高;历史波动越大,分配受到的压低越明显。随后施加单品种上限,默认不超过账户权益的 8%。
BTC 对冲使用对齐时间戳后的 4 小时收益估计 Beta。当组合估算 Beta 敞口的绝对值超过权益的 2% 时,程序尝试用 BTC 调整;对冲量自身有上限,也继续受单品种和组合总敞口约束。经过截断后,组合仍可能保留 Beta,因此这是一种有限对冲。
最后处理换手。普通目标调仓名义金额默认以账户权益的 20% 为软上限,超过时按比例向新目标移动。若当前目标已超过风控硬上限,则必要降仓优先,不能为了限制换手继续保留超限风险。
所有对冲和换手处理完成后,还要重新检查一次单品种与总敞口。 前面通过检查的目标,经过后续加工仍可能重新超限,这一步必须放在最终目标形成之前。
由于实际组合有波动率权重、排名缓冲、BTC 对冲和换手处理,单因子研究收益不能直接相加成策略账户收益。研究层评价排序信息,执行层还要评价这些信息能否以合理成本转化为持仓。
十、订单成交以后,还要等持仓给出同一个答案
多品种策略一旦异步运行,行情、订单回包、成交记录和持仓快照就可能以不同顺序到达。
FMZ 的 exchange.Go 可以异步调用交易接口,并通过返回对象的 wait 方法取得结果。APFF 利用这一机制错峰安排数据和账户查询,当前并发任务上限为 8。FMZ exchange.Go 文档
异步执行需要额外回答一个问题:这次请求究竟有没有在交易所生效?
下单前先保存意图
每次发单前,程序先生成客户端订单号,记录品种、方向、数量、当时持仓和对应目标,再持久化这条意图。保存失败,就不继续发送。
每个品种最多保留一个活动意图,全局最多同时有四个。反向调仓先平原方向,再考虑建立反向仓位。普通执行使用 GTX 被动限价,新目标默认有 15 分钟执行窗口;窗口结束后停止继续增加风险,必要减仓和对账仍继续。
请求超时,先确认结果
明确拒单和结果未知必须分开。余额不足、精度错误等明确拒绝,可以按拒单处理;超时、连接中断或未知应答,则可能发生在交易所已经接受订单之后。
当前代码会按订单号或客户端订单号持续核对订单、挂单列表和历史订单。仅凭“挂单列表为空”或“已经过了几十秒”,不会解除未知意图并重复发单。
订单终态之后,还有持仓确认
假设原持仓为 100 张,卖出平仓订单累计成交 30 张,那么随后取得的新持仓快照应该确认剩余约 70 张。这次查询必须在订单终态之后新发起,旧请求迟到的回包不能代替确认。如果仍然返回 100 张,程序显示 WAIT_POSITION,继续查询并锁定该品种。
预期持仓按“初始持仓 + 带方向的累计成交量”计算,保存为 barrier.expected。收到持仓应答后,applyPositions 再检查能否解除确认锁。下面节选当前版本订单的核心路径,省略了诊断日志和旧版状态迁移分支;positions 是此前已解析好的持仓映射:
javascript
var snapshotAt = num(requestedAt, nowMs())
Object.keys(G.execution.positionBarriers || {}).forEach(function(symbol) {
var barrier = G.execution.positionBarriers[symbol]
// 必须是终态后新发起的查询;同品种双向持仓也不能解锁
if (snapshotAt <= barrier.at || (G.actual.hedgedSymbols || {})[symbol])
return
var spec = G.universe.markets[symbol]
var tolerance = Math.max(1e-12, num(spec && spec.step, 0) / 2)
if (finite(barrier.expected)
&& Math.abs(num(positions[symbol], 0) - barrier.expected) > tolerance)
return
delete G.execution.positionBarriers[symbol]
clearIssue("POSITION_RECONCILE_" + symbol)
})
snapshotAt 使用请求发起时间判断先后顺序;数量比较允许半个下单数量步长的误差,并设有极小的数值误差下限。任一检查未通过就提前返回,品种锁继续保留,执行层也会继续阻止该品种发单。只有检查通过,才删除 positionBarriers[symbol]。这避免了“订单已经成交,持仓接口暂时还没更新,于是又下了一遍”的重复交易。
一次已有的 Binance 模拟环境测试提供了具体例子:TAC 的一笔减仓单累计成交 17,990 张,原持仓从 37,062 张降至 19,072 张。期间撤单请求与成交发生竞态,出现一次 -2011 应答,随后通过订单查询在约 2.17 秒内确认剩余成交,结束对账。
这条测试记录说明的是该次竞态如何恢复。错误消息本身无法证明订单成交,也无法证明撤单成功,最终仍要让订单终态和持仓数量相互吻合。
十一、风控要在程序继续运行时发挥作用
APFF 按权益相对历史峰值的回撤,分级缩减目标预算:
| 权益回撤 | 目标预算缩放 |
|---|---|
| 小于 4% | 100% |
| 达到 4%、小于 7% | 70% |
| 达到 7%、小于 10% | 40% |
| 达到 10% | 目标清零,并暂停新增风险 |
4% 和 7% 档位会立即作用于当前目标,不必等下一次 8 小时决策。清零状态持续限制开仓,执行层在取得所需行情和账户状态后推进减仓。它约束的是程序目标及执行行为,不能保证极端行情中最大亏损恰好止于 10%。
交互按钮也区分了三种操作:PAUSE_NEW 暂停新增风险,RESUME 恢复组合执行,REDUCE_TO_ZERO 撤销本策略订单、完成对账,再平掉本策略管理的仓位。
策略通过自己的持仓归属标记识别管理范围。目标品种已经存在未归属本策略的持仓,或者检测到同品种同时有多空仓位时,会阻止该品种自动下单。
停止进程也不会自动清仓。希望清零时,应当让清零流程完成并核对结果,单纯停止机器人会保留持仓。对于多品种组合,这个区别直接关系到停止后账户还承担多少风险。
十二、在 FMZ 上运行时,怎样理解模式和状态栏
当前版本提供三种运行模式:
| 模式 | 行为 | 收益的含义 |
|---|---|---|
SHADOW | 读取公开行情,计算因子、研究成绩及组合目标 | 不模拟成交,没有交易账户收益 |
PAPER | 用实时 Bid/Ask 本地模拟成交,记录仓位、费用和资金费 | 本地模拟账本收益 |
LIVE | 读取所配置交易所账户并发送订单 | 所连接账户的权益变化口径 |
LIVE 走的是交易接口。如果交易所对象配置为 Binance 模拟环境,实际发生的是交易所模拟撮合;连接真实资金账户,才是真实资金交易。模式名称和账户环境要同时确认。
当前程序依赖实时 WebSocket,并在初始化时阻止进入 FMZ 历史回测环境。因此,PAPER 应通过实时运行积累前向结果,不能把它当成“选择一个历史区间即可回测”的模式。
导入完整策略文件、添加一个对应的 Binance 期货交易所对象后,主要界面参数如下:
| 参数 | 默认值 | 含义 |
|---|---|---|
RunMode | PAPER | 运行模式 |
UniverseSize | 30 | 交易池目标数量 |
GrossExposurePct | 60 | 多空绝对名义金额之和相对权益的配置预算 |
MaxSymbolPct | 8 | 单品种绝对目标名义金额占权益的上限 |
EstimatedOneWayCostBps | 4 | 单边估算成本,4 bps 即 0.04% |
LiveConfirm | 空 | LIVE 模式要求精确填写 ENABLE_APFF_LIVE |
这组数值是程序默认设置,没有经过收益最优参数认证。成本参数尤其需要按使用场景理解:研究层从参考价收益中扣除估算成本;PAPER 买入用 Ask、卖出用 Bid,已经体现买卖价差,再额外扣除该参数对应的费用。成本校准时应注意避免重复覆盖同一成本项。
观察运行情况时,可以按以下顺序阅读状态栏:
- 行情和数据门禁:两条行情连接是否持续更新,可用交易品种是否足够。
- 研究样本及成熟时间:当前只是数据预热完成,还是已经积累了有效前向样本。
- 配置权重和目标有效权重:哪些因子实际参与了这轮组合。
- 目标与持仓:偏差来自执行窗口、最小交易金额、交易所限制,还是尚待确认的成交。
- 订单意图和持仓确认锁:
UNKNOWN、WAIT_POSITION是否持续存在,相关查询是否继续推进。
PAPER 的账本会拆分已实现盈亏、未实现盈亏、资金费和估算费用。资金费按结算时的历史持仓补记,晚到的历史记录不会直接套用当前持仓。
LIVE 当前主要以账户权益相对基准权益的差额展示收益,尚未完整排除出入金和其他策略的影响,也未完整拆分实际已实现、未实现和资金费。未拆分项目显示 -,不能当作零值。
十三、当前验证到了哪里
截至本文所依据的构建,项目已完成基础逻辑检查、23 项订单回归检查和 34 项专项审核测试。专项测试包含 36 个加速决策周期,以及 500 组随机权重约束检查。
这些数字分别说明了什么,需要交代清楚:36 个周期使用合成数据和受控时钟,验证采样、成熟、记账及恢复流程,并不构成 12 天真实历史收益回测;随机约束检查验证预算上限,也不验证因子预测能力。
Binance 模拟环境中的运行复测,则覆盖了实际模拟撮合、重启恢复、撤单与成交竞态、明确拒单和持仓对账。这些结果让系统可以继续积累前向证据,但还不足以给出年化收益率、夏普比率或长期胜率结论。
后续值得优先补齐的,是历史时点数据和独立的滚动样本外验证、按品种及行情状态校准的成交成本、相关性分组仓位限制,以及预期收益覆盖交易成本的开仓门槛。这些内容在研究方案中已有方向,当前代码还没有全部实现。
APFF 已经把一个因子从产生、记录成绩,到获得预算、参与下单和退出组合的过程写进了程序。接下来最有价值的数据,是每一次当时留下的判断,以及八小时后按同一规则得到的答案:哪些信号持续有效,哪些只在某段行情里有效,又有多少优势在真正成交后仍然保得住。
- 1

