Commit Graph
371 Commits
Author SHA1 Message Date
jackyu66gitandCursor 2cd50f1e01 主图增加笔/线段背驰与面积数字,并修正 SD99999 显示。
三周期分开关控制,只画数字不画图标;线段面积比沿用同向笔面积口径。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-10 04:33:40 +08:00
jackyu66git 6b72ba226b Merge remote-tracking branch 'origin/chan' into chan 2026-08-31 23:03:28 +08:00
jackandCursor 080ff7d20e 整点在线推 Telegram,并带上搬运管道的新鲜度
执行器活着不代表链路活着——搬运死了一样心跳正常、一样什么都不做。
所以每小时那条必须读 ship_alive.json:ssh 是否在连、文件是否还在刷。
连上/断开立刻落盘,不靠 5 分钟心跳,否则第一轮会误报上游断了。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-29 00:57:11 +08:00
jackandCursor 3bbd92784c 下单失败必须推 Telegram,并让心跳判读「做了却没建上」
实盘首日丢掉 4 个信号、白跑 5 小时,根因是 40774,但**没有任何推送**。
日志里一直在报,可外在表现和"没信号"一模一样——这正是整套通知设计要防的
那类静默经济损失,却恰好漏了下单失败这一条。

现在:入场被拒或止盈挂不上都推。按信号聚合成一条,不按腿推(避免一个信号
两条)。两腿全失败时说明"这个信号丢了"并带累计失败次数与常见原因;部分腿
失败时说明"收益结构已偏离设计"——那种情况仓位是半的,不是设计的两腿结构。

心跳原先只把数字并排列出来:"已做 4 · 在场 0/3" 那 5 小时一直在打,但没有
一处说这是异常。现在分开计 n_built 与 n_order_fail,做了却一次没建上直接
打 。只看 n_took 分不出"做了"和"建上了"。

status.sh 同样加判读:统计 entry_fail 次数并打出最后一条错误与码表。

tp_fail 现在也记进 live_trades.jsonl,原先只打日志不落盘。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-29 00:48:56 +08:00
jackandCursor 77056a8318 下单体去掉 tradeSide:账户是单向持仓,带它会被 40774 全拒
实盘首批 4 个信号全部下单失败,8 次尝试(4 信号 × 2 腿)都是
[40774] The order type for unilateral position must also be the unilateral
position type。投递链路其余部分完全正常:延后 0.0~0.2s、ssh 在线 255 分钟
零重连、去重 0、闸全过。纯粹是下单体语法。

官方文档:单向持仓下要忽略 tradeSide(side 取 buy/sell 即可),平仓用
side 取反 + reduceOnly=YES;而 reduceOnly 也只在单向模式下有效。双向持仓
才需要 tradeSide=open/close。我们发的是双向语法打到单向账户,是整体拒单
而不是部分降级。

三处下单体(entry_with_stop / tp_limit / close_market)都去掉 tradeSide。
reduceOnly 保留——它在单向模式下正是防止反手开出反向仓的那个参数。

另加 setup_position_mode():把模式钉成单向并读回核对,与既有的"每次启动
都设一遍逐仓和杠杆、不假设交易所侧状态"一致。调用点放在 reconcile 之后,
因为有持仓或挂单时交易所不允许切换。读回不是单向时推 Telegram 告警。

空跑不可能验到这个:dry=True 在发请求之前就返回假成功。这类"下单体字段
组合"的问题只有真单会暴露,和之前 oid 里 - 字符那条同一类。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-29 00:46:17 +08:00
jackyu66gitandCursor f14ef540c1 research: 订正——「额外信号 74%」是按条数统计的放大,按中枢是 52%
用户指出「原来的计算是没有问题的,是你算错了」。改用逐笔对账(不做统计,
把两边中间量全摆出来),结论一半一半。

引擎与我的口径都没问题:36 个额外信号 100% 归为 A 类(全量扫描区间不覆盖
入场根,即 available_ts 棘轮),0% 属于我的 bug;索引逐行对齐(5001=5001,
时间戳全同);额外信号的中枢在全量里全都存在且 (zg,zd) 完全一致,中枢不重画。
棘轮幅度实测 32~234 根。

但同一张表暴露了真正算错的地方:全量每个中枢恰好产出 1.00 个信号,回放里
同一中枢平均触发 2.12 次、最多 5 次。max_per_zone=1 只保证每次扫描返回一个,
但扫描起点随棘轮后移、越过旧入场点,同一中枢会重新产出「第一个」。

所以先前的「额外信号占 74%」不成立——那是按信号条数统计的,按中枢算是
17/33≈52%。这也把数量级对上了:§5.41 的 6.5% 分母是全部中枢,而会产出信号的
中枢里被改过的占比自然更高,两个数不矛盾,先前直接对比也是错的。

仍成立:棘轮机制、额外信号确实亏钱。已推翻:「实盘会多开 74% 的仓」。
真实倍数取决于执行层是否按中枢去重/冷却,该层从未审计,现已提为最高优先级
的前置项——查清前不要据 PF 0.73 动实盘参数。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-29 00:38:52 +08:00
jackyu66gitandCursor 7585491481 research: 订正额外信号的机制——不是重画,是中枢终点在实时不可知
我先前把 B4 的额外信号归因为「中枢重画」,用户两次指出后逐条查证,归因是错的:

① 中枢边界不重画(用户对)。zg/zd 由前三笔定死,verify_window_sens/step39 验过。

② 中枢只由已确认的笔构成,这是结构性保证:cal_bi_zs_list_pure 要求
   bi1/bi2/bi3.is_sure,延伸时要求 leave_bi/back_bi.is_sure。
   step70 实测 789 个信号全部 z_sure=True,用户说的浅色 B4 在研究路径不存在。
   因此我提的「补一道 is_sure 门」是空操作,实测 PF 0.73→0.73,已标记不要再提。

③ 真机制是 available_ts 棘轮,§5.41 早写明:改动不是已确认的笔被推翻,
   而是中枢又吸收了新笔、bis[-1] 换人。available_ts 取末笔 sure_time,
   于是往后棘轮,find_fast_bsp3 的 200 根扫描窗口整体右移。
   消失的不是笔,是中枢的终点。

顺带澄清一个伪问题:中枢跨度 > 200 根不会导致突破落不进窗口,
因为扫描起点是中枢结束(末笔确认)而非起点,此时价格已在离开中枢。

仍未对上的是数量级:中枢层面 6.5% vs 信号层面 75%。假设是 max_per_zone=1
的放大(起点棘轮越过旧入场点,同一中枢反复产出「第一个」)。
step69_mechanism.py 已写好判据但三次后台运行被中断,标为下一步前置项。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-29 00:31:46 +08:00
jackandCursor 1ee4c3d4e1 记 §3.6:scan=200 补测,意外地落在对的位置
scan 从未在 1m 加当前出场结构下被单独验过——step29 的网格里它和
pullback_win/tol 联动、跑在 15m 上用旧出场参数,分离不出单独效应。文档里
原先只有一行表格、没有依据。

澄清两个口径:scan 只约束找突破,入场由 pullback_win 单独限制且不受
scan_end 约束,实际触达 230 根;中枢跨度再长也不占窗口,因为 available_ts
取 bis[-1] 落在右边缘(1m 上跨度中位 135~169 根、31~40% 超 200 根)。

实测效应是砍掉约 30% 信号,但分层很陡:200-600 那桶 33 笔 PF 1.01、净均
0.21bp 等于零,>600 那桶 15 笔 PF 2.34 和保留的那批一样好。所以 200 正好
切掉了无效的中间桶,放宽到 600 只会稀释。>600 样本太少不足以动参数。

样本只有本地 SOL/ETH,ETH 多数过不了 ATR 门控,故 118 笔以 SOL 为主,
ADA/DOGE/XRP 本地无数据。结论是"支持保留现状",不是"200 最优"。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-29 00:16:07 +08:00
jackyu66gitandCursor e21f1103c5 research: 1m 复验确认 B4 实盘口径 PF 0.72,并修正 ATR 门控的解读
用户指出 1m 参数与其他级别不同。核查:出场参数 SL2/3ATR减半/留损2/目标8ATR/48根
两边同形状,本轮用对了;但 ATR≥8bp 门控是照 1m 标定的,文档明确它在 5m 上惰性
(触发 2.6%,「无害也无用」)。所以 5m 那轮的「三道全开」实际只有两道在起作用,
先前那句「ATR 门控没用」不成立——它在 5m 上本来就不该起作用。

1m 复验(三道齐全)结论与 5m 一致:
  无过滤    712 笔 / 77% 额外 / PF 0.74
  三道全开  122 笔 / 72% 额外 / PF 0.72
  其中回测也有的 34 笔 PF 2.07,额外的 88 笔 PF 0.36 (t −2.73)

ATR 门控在 1m 上确实咬得凶(712→389,刷掉 45%),但对额外信号无区分力:
刷完额外占比仍是 77%。三道合计砍掉 83% 成交量,额外占比只从 77% 降到 72%。

新增两条前置待办:1m 关键分组只剩 34 笔,据此改实盘前应扩样本;
另外「额外信号是否真的每个都下单」取决于执行层,这一层尚未审计。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 23:57:53 +08:00
jackyu66gitandCursor 84ee59ecfb research: B4 时点干净,但实盘做的不是回测那批单(实盘口径 PF 0.73 vs 回测 4.21)
step63 因果回放:B4 的时点完全干净——100% 召回、100% 准时、零滞后,
一类那个把 PF 从 2.35 打到 0.92 的坑(§3.396)B4 没有。

但回放多产出 592 个全量口径里不存在的信号,PF 0.46 / t −5.03。
step68 按实盘口径复测(2001 根滚动窗口、每 500 根 init_stream、只做当根收盘,
逐行对齐 shadow_signal.py),并把三道滤网全测一遍:

  无过滤    789 笔,额外占比 75%,PF 0.64
  三道全开  195 笔,额外占比 74%,PF 0.73

滤网把成交量砍掉 75% 却几乎不改变额外信号占比——按同比例刷掉好的和坏的。
拆开看:回测里也有的 51 笔 PF 4.21/t+5.90,回测里没有的 144 笔 PF 0.26/t−6.21。

即回测报的 4.21 拿不到:那 51 笔要事后全量重算才能识别,实盘当下分不出来。
机制是重画——实时算出的中枢,数据变多后被修正掉。与 §3.396 同类:
一类错在时点,B4 错在存在性。

顺带闭掉一条待办:2001 根窗口与 2万→4万根增长窗口跑出完全相同的 789/197/592,
窗口左边界效应判为否。

限定:本轮是 5m/30m 而实盘跑 1m,需单独复验;同向过滤器被开了未来函数后门
(HTF 时间线用全量算),真实盘只会更差。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 23:46:19 +08:00
jackyu66gitandCursor 93ed315a4b research: 一类线门槛量化——全局只有一个变量,精度需 67~73%
把 §3.3992 的天花板换成实时可算的 ext_run 选样配结构止损:5m 0.51、15m 0.48,
对比上界 2.29/4.11,兑现不了。但数字对不上(Q4 真端点浓度已 44%),拆开后
得到本轮最有价值的一张表:

ext_run 在真端点**内部**毫无反向选样(5m 各档 2.53/1.80/2.61/2.22 持平,
15m 单调递增到 6.10),在非真端点内部也毫无区分力(恒在 0.11~0.18)。
全局只有「是不是真端点」这一个变量在起作用,其余特征都是它的噪声代理。

这也修正了 §3.399:ext_run 的符号翻转不是拟合,它确实提纯(28.7%→44%),
只是幅度远远不够。

于是 PF 退化为两组按精度 p 的混合,解出盈亏平衡精度:5m 72.6%、15m 67.1%。
即要求实时判「这是不是那个底」的准确率达七成,而现在是 28.7%。

顺带解释了为什么 B4 能做而一类不能:B4 是突破后的延续信号,不需要判断反转;
一类的全部难度集中在这一个二分类上。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 23:17:48 +08:00
jackyu66gitandCursor ed741c16b8 research: 一类线重开——病根是选样×止损的交互,不是没机会
用户问「修改 B1/B2 的识别规则呢」,查下来推翻了 §3.399 的关闭结论。

step65 先量机会本身:直接取 seg_list(不经过检测器),线段中位幅度 11.4 ATR,
扣掉 2.9 ATR 确认成本还剩 8.5,是止损的 4.26 倍,93% 的线段装得下。
五币四级别一致。**滞后不是瓶颈**,与原先预期相反。

但这与 §3.397 矛盾(空间在却拿不到),step66 给出解答:之前六次进攻每次只动
一半。完美选样配 2ATR 止损是 0.70/0.83,无选样配结构止损是 0.36,
**两个一起是 2.29(5m)/4.11(15m)**,胜率 36%→63% / 44%→70%。是交互不是叠加。

机制来自 MFE/MAE:真底那批逆向行程中位仅 2.24 ATR,2 ATR 止损打掉了 51~58%
的好单;非真底那批逆向行程中位 4.76,放宽只是亏更多。固定 2 ATR 同时做错两件事。

⚠️ 上界含两个未来函数(线段端点选样、sure_time 入场),是靶子不是策略。

顺带证伪一个我自己的猜测:step58 首轮用 r_scale=True 把 3.9 ATR 止损对应的
runner 目标推到 15.6 ATR(比整段行情还长)。加 --abs-targets 改绝对目标重跑,
PF 仍是 0.36,与 r_scale 版完全相同。首轮结论正确,只是理由错了。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 23:09:38 +08:00
jackyu66gitandCursor 380d6d61cd research: 二类买卖点定性为无信息,一二类线整体关闭
用户提出悖论:二类按定义依赖一类(引擎里 B2 确实被 first_bsp_bi_div 门控),
一类既已证否,二类凭什么好。当时有个值得测的反驳——二类多要求「确实反弹」
且「回踩守住 B1 低点」,这是「底是真的」的事后确认,而 §3.397 的诊断恰恰是
一类缺这个确认。若成立,二类反而是一类里被验证过的子集。

测下来悖论成立,但机制不同:二类不是继承了一类的错,是信息为零。
一类 t = −17~−22(强烈指反,有信息只是方向被滞后翻了面),二类原方向
t = −0.8~−2.4、反手 +0.2~+1.7,两边都贴着零,连反手都没有。

另一发现是几何:二类的天然止损位是 B1 的低点,但反弹加回踩之后入场价已在
其上方 4.87 ATR(一类 2.90),2 ATR 止损有 99~100% 落在结构内侧。
step58 在一类上放宽到结构位只把 PF 从 0.22 抬到 0.36,二类缺口大 68%。

⚠️ 报表里二类「命中线段顶点 0.0%」是我用错口径,已在文档标注:二类按定义
是反弹后的更高低点,与线段顶点不可能重合,该指标只对自称在极值的一类有效。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 22:54:31 +08:00
jackyu66gitandCursor 7292ef5dd4 research: 低滞后+质量过滤组合失败,过滤器符号翻转,一类线关闭
把 step62 的 ext_run 质量过滤接到 step56 的低滞后版上——两者各解决一半约束,
是唯一同时处理滞后和识别精度的路径。

失败,且失败方式本身是判据:过滤器符号反了。5m 上延伸度分档在滞后版是
Q1 0.12 / Q4 0.46(越延伸越好),在低滞后版是 Q1 0.58 / Q4 0.33(越短越好)。
叠加后 PF 从 0.37 掉到 0.30~0.33,比不过滤更差。div 同样翻转。

同一批信号、同一个特征,换个入场时点最优方向就反过来,说明这些过滤效果是
入场时点的交互产物而非信号的稳定属性,继续挑阈值就是拟合噪声。

一类线六次独立进攻全部止步 1.0 以下:引擎原生 0.24、反手 0.92(因果回放后)、
实时重写 0.41、结构止损 0.36、用未来函数选样 0.83、质量过滤 0.54、
低滞后+过滤 0.33。第五项尤其说明问题——即使选样做到完美也只到 0.83。

判定关闭。病根是入场价已在结构底上方 2.9 ATR、实盘再晚 15 根,来自「等笔确认」
机制本身而非参数。识别可以优化(精度能翻倍),但识别从来不是瓶颈。

fast_bsp1 顺带补 ext_atr 字段,用 ATR 归一才与 step62 的 ext_run 同口径。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 22:03:59 +08:00
jackyu66gitandCursor 5695d8e983 research: 趋势末端识别(step62),ext_run 单调区分但 PF 仍不过 1
用户指出很多一二类实际在趋势中途被识别而非末期,若真在末期即使有延迟也该走出
行情。用 step60 的线段顶点当标签找实时可算的区分特征。

ext_run(极值越过中枢边界几个 ATR)单调有效:5m 上四分位的命中率是
12.0/22.1/37.0/43.8%,PF 0.12/0.11/0.22/0.46。短延伸那批就是趋势中途被识别的,
占一半且 PF 仅 0.11。最佳组合 ext_run≥P75 且 div≥中位:命中率 47.9%、PF 0.54,
相对基准 28.7%/0.22 精度接近翻倍。

两个反直觉结果:背驰越强反而越差(div Q1 命中 17.0%/PF 0.13,Q4 37.5%/0.28),
是对 MACD 面积判据的直接证伪;趋势级数无区分力(命中率 28.5/30.6/28.6/25.5%
基本持平)。

另修正一个我先前的猜测:以为引擎漏了缠论「趋势 vs 盘整」前提,实测该条件在
2504 笔上恒为 True——B1 要求 enter_bi.dir == leave_bi.dir == DOWN,中枢向下进
向下出本身就定义了它嵌在下跌趋势里,引擎已隐含强制,过滤器无从添加。
zs_count 也不可用,它是全局中枢序号而非趋势内序号。

结论:识别可优化且幅度不小,但不是瓶颈,瓶颈是入场时点。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 22:00:52 +08:00
jackyu66git 78708ddeee research: 极值点波动率(step61),观察成立但既不解释滞后也不解释亏损
用户提出一二类长在极值点、该区域波动大、这可能是确认慢的原因。拆成三条
子命题分别测。

① 成立:5m 上 B1 的 atr_z 中位 1.224、74% 高于基准,而所有笔端点的基准恰好
落在 50%(构造正确的旁证)。但二类是镜像——B2/S2 的 atr_z 中位 0.94~0.97、
仅四成高于基准,长在低波动区,因为它是首轮反转冲动之后的回抽。所以
「一二类都在极值点」对一类成立、对二类不成立。

② 只沾边:corr(atr_z, lag_bars) 仅 +0.10,滞后中位在四个 atr_z 分档里是
8/7/8/9 根,几乎不动。滞后是结构性的,笔要等分型确认,与波动率基本无关。

③ 回落属实但不是死因:入场后 48 根平均 ATR 是入场时的 0.896,目标确实按虚高
ATR 定、绝对价格高估约 10%。但一类只有 9.4% 走到 3ATR 减仓(三类 30.7%),
81.3% 直接止损、超时仅 17.9%。若死因是目标够不着,超时占比该很高。所以一类
不是走不动,是入场后立刻反向——与 §3.397 的诊断一致。
2026-08-28 21:51:59 +08:00
jackyu66gitandCursor e137d92b50 research: 因果回放推翻一类反手(step59),线段顶点验证识别有效但不够(step60)
step59 逐根回放:init_stream 预热后逐根 append_bar,每根重算笔中枢与 bsp,
记录信号首现根并按它入场。信号身份用「类型+极值KLC时刻」而非 sure_time,
后者正是会被重画的字段。必须逐根,分段重建等于多给信息。

结果把 §3.395 推翻了:召回 100%、幻影 0,即存在性是因果的;但首现根比全量
sure_time 晚中位 15 根、P90 31 根,0% 能准时拿到。按真实首现根入场,
B1 反手 PF 2.35→0.92、S1 2.75→1.30,t 0.20/0.64 完全不显著。

教训:sure_time 是引擎事后标注的确认时刻,不等于可执行时刻。任何拿它当
entry 的回测都要先过逐根回放。

step60 用线段终点当标准答案验证识别本身。对照组取所有同向笔端点——B1 按构造
就长在笔低点上,不设这个基准任何绝对命中率都无法解读。5m 上 B1 命中 30.3%
对基准 15.0%,提升 2.02 倍,S1 1.81 倍;B3/S3 恰为 0%,符合三类长在趋势
中段的预期,两者互为标签有效性旁证。

所以识别是对的,但精度只有 30%,且那 70% 噪声 PF 只有 0.08。更关键的是即使
用未来函数把精度提到 100%,命中组也只有 PF 0.70~0.83,仍不赚钱——因为入场价
已在结构底上方 2.90 ATR。一类线三层逐层否定后到此为止。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 21:38:59 +08:00
jackyu66gitandCursor 9efbae6ade research: 结构止损对照(step58),止损确实太紧但不是主因
用户提出 B1 做多的止损可能挂在 B2 低点上方,于是在那次正常回抽处被打掉。
观察成立且比预估严重:入场价到结构极值中位 2.90 ATR,2ATR 止损落在结构位
内侧 0.90 ATR,88.6% 的一类做多价格不用回踩到前低就已出局。

修掉有改善但救不活:结构位外侧 1.5ATR 止损把胜率从 16.7% 抬到 33.1%、
PF 从 0.22 到 0.36,仍深度为负。同口径下反手做空 PF 2.15~2.53。

做这个对照时目标必须随止损等比放大(恒定 1.5R 减半/4R 收尾),否则放宽止损
却不放大目标会把盈亏比压到 1 以下,测出来的「放宽无效」是自证的。故
walk_exits 不能用,自带了逐笔止损的模拟器。

最有说服力的读法是结构+1.5 那档:止损宽达 4.4 ATR 仍有 67% 被打掉,即
三分之二的一类信号会在 48 根内跌破结构低点再多走 1.5 ATR。缠论里「B2 回踩
不破前低」在本市场多数时候不成立,趋势确实越过第二类买卖点继续走原方向。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 20:31:06 +08:00
jackyu66gitandCursor be3602f5ba research: 一类反手(step57),六年五币双向全正,PF 2.3~3.0
用户问一类二类的识别是不是错了。查下来是代码没写错、信号该反着用。

先排除一个错误猜测:check_bi_div 只比 MACD 面积、不检查创新极值,等于把
缠论背驰定义丢了一半。看着像致命缺陷,实测 93% 的离开笔本来就创了新极值,
解释不了 PF 0.2。

真正的线索是胜率方向:B1 胜率 18.6%,而 SL2/TP3 随机入场约 40%,远低于
随机说明样本带信息只是指反了。拿相反方向重跑出场(路径依赖,不能取负号),
5m/15m 上 PF 2.35~3.03、t +10~+13。

step57 三关全过:5 币全正、前后半段 2.81/2.81 与 2.55/2.51 几乎不变、
2021~2026 连续六年全正、多空两边都正。余量 15m 33~76bp,B4 只有约 3bp。

机理是滞后把信号变成了相反的交易:信号在 leave_bi.sure_time 发出,中位滞后
8~9 根,此时价格已从低点反弹 1.68 ATR。引擎想说抄底,它给的时间戳对应的却是
反弹已走完——在下跌趋势里于此处做空是标准的顺势回调入场。与 B4 的突破延续
是不同的入场原型,可能互补。

三项验证未做完前不许上实盘:因果性(中枢右边缘已知会重画,需流式回放)、
消融(是不是反弹就做空都赚)、与 B4 的重叠度。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 20:12:11 +08:00
jackyu66gitandCursor 409dc2f020 research: 实时版一类(step56),证明滞后不是病根,一类没有边
step55 判定引擎 B1/S1 不可用后,照 B3→B4 的路子做了实时版:把「中枢已成 /
向下离开 / 背驰 / 触发」四步全换成当根可判的代理,滞后从 8~9 根压到 ≤1 根。
背驰能实时判的关键是进入段面积在中枢确认时已是历史,只有离开段需要逐根累加。

没救回来。5m 上 fastB1 一买 PF 0.38 / 一卖 0.36,15m 同量级,比引擎版的
0.20~0.24 有改善但离 1.0 很远。

真正的价值在对照组和滞后分档两处:
- 同数据同出场同管线,fastB3 跑出 PF 1.83~2.06 / t +8.5~+10.7,排除了
  「管线接错所以为负」。
- lag ≤1 根(≈买在极值上)那一档 PF 仍只有 0.40。即滞后清零也救不活,
  §3.393 里「等 8 根导致几何劣势」的解释只对了一半。

结论与 §3.4 消融、§3.391 的 mom60 相互印证:这族信号的 alpha 在顺势延续上,
不在反转上。把反转信号提前,它还是反转信号。一类到此为止。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 19:55:23 +08:00
jackyu66gitandCursor 30592889aa research: 一二类买卖点可行性探针(step55),结论为不可用
用户提出把 B4 的研究思路搬到第一/第二类买卖点,并指定 5m/15m 测。

滞后先于收益测,结论是用户判断正确:B1/B2/B3 滞后中位都是 8~9 根,
三类共用 find_all_bsp 的「中枢 is_sure + 笔 is_sure + sure_time」,
滞后不是区分它们的变量。

收益全负,一类最差:5m 上 B1 PF 0.24 / 胜率 18.6% / t −17.3,
S1 PF 0.20 / 胜率 14.8% / t −22.3,15m 同量级。B3 跑出 0.64/0.76、
HANDOFF §4 记的是 0.66,口径校验通过,所以 B1/B2 的数可信。

一类烂得彻底是几何决定的:等 8 根后价格已朝上跑 1.68 ATR,新入场价往下
2 ATR 的止损落在比原始低点还低 0.3 ATR 处,几乎贴着极值。同样的滞后在
顺势突破上只是追高,在逆势反转上是加倍惩罚。

探针里修掉两个会静默出错的地方:cdf.date 是 datetime64[ms] 而
Timestamp.value 是纳秒,手工转 int64 比较会让 searchsorted 全部落到末尾
且不报错(这是之前跑出 0 条的原因);速率对照未按币归一,拿 5 币的数去
比 10 币的 B4 基线等于凭空打对折。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 19:48:59 +08:00
jackyu66gitandCursor 6d93975e67 用 7 年长样本复核 step54:时段坐实无效,周日效应符号翻转、撤回
用户追问"你用的是长时间周期分析的?"。原答案是 1m / 18.5 个月 / 3941 笔,
而上一轮的结论是"没效果"——没效果最怕样本不够,这个追问戳到了点子上。

改用 step41 那份 5m/15m/30m 复核:2019-09 → 2026-08,2525 天,8168 笔。
口径能对上——keep = (h1_agree==1) & push 就是深色过滤,出场配置
s2_so8_k2_m48 正是实盘那套 2/3/8/2/48(cfg_name 的分批命名,scale_at 默认 3)。
唯一差异是没加 ATR≥8bp 门控,而 §3.5 实测它在这些周期上几乎不触发
(30m 0%、15m 0.16%、5m 2.6%)。

① 时段坐实无效。段间毛R极差的置换 p:5m 0.6185、15m 0.6687、30m 0.3105、
合并 0.6445,全部远离显著。1m 那个 p=0.0895 现在看清楚了,就是零分布里运气
略好的一条尾巴。

② 周末效应没复现,而且符号翻转。1m 上周末−工作日是 -0.132(p=0.0246),
长样本上 5m +0.014、15m +0.142、30m -0.006、合并 +0.041;周日从 -0.199
(p=0.0079)变成 +0.033。这不是严格的样本外复现——1m 与 5m 是不同的信号
总体——但若"周末流动性薄所以吃亏"是真的市场结构效应,它没有理由只在 1m 上
出现、在 5m 上还反号。

所以上一提交里"周日效应统计上真实"那句要撤回。本步一共跑了约 35 个分组比较
(24 小时 + 两套时段定义 + 7 个星期 + 周末/周日),冒出一个 p=0.008 恰是多重
比较的期望产物。HANDOFF 里已把该结论标为撤回并记下两条教训:报告"无效"之前
先确认样本量撑得起这个"无";在几十个分组里挑出的最显著那个,默认它是噪声,
除非能在另一个总体上复现。

实践结论不变且更硬:不加任何时间维度的过滤,mom60≥7 仍是唯一值得上的开关。

复核走 --long,复用 step41 已有 feather,未重跑采集。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 19:29:26 +08:00
jackyu66gitandCursor 1ffc4fbb18 时段(亚/欧/美)是噪声,周日效应真实但与 mom60 过滤不可加
用户问哪个时段更容易盈利。答案分两半:时段这个切法本身无效,换成星期几才
有东西,而那东西不该做成新过滤。

① 亚/欧/美三等分(UTC 0/8/16),毛R 1.127 / 1.006 / 1.079,段间极差 0.122,
置换检验 p=0.0895——随便把 24 小时切三份,9% 的概率能切出这么大的差。更要命
的是美盘两个时期反号:样本外 1.158(最好)→ 发现期 0.885(最差),噪声的
典型指纹。按真实开盘时刻切五段、把欧美重叠单列,结论一样。

这里不是输给 ATR 混淆。亚盘 ATR 中位确实最低(12.5 vs 14.1),本来最该是
混淆源,但控 ATR 后段间极差 0.101/0.108,和无条件的 0.122 几乎一样——时段
不是 ATR 的代理,它本来就小。

② 星期几有信号,集中在周日:毛R 0.893、胜率 66.6%、PF 2.76、余量 13.08bp,
对照周五 1.228 / 74.7% / 4.78 / 23.44。置换检验周日 p=0.0079、周末 p=0.0246,
两个时期方向一致,10 个币里 7 个周末更差(BTC 最甚 -0.336)。但幅度在发现期
塌了大半(-0.168 → -0.039)。

③ 关键在重叠。施加 step53 的 mom60<7 之后,周末差从 -0.132 缩到 -0.058、
周日从 -0.199 缩到 -0.104。重叠不在笔数上(mom60≥7 在周末占 22.9%、工作日
21.3%,几乎一样),是伤害重叠:周末真正亏钱的是那些追已走完行情的单子。
周末流动性薄,追高的代价被放大——这和 §3.391「势不能过头」是同一件事在另一
个维度上的投影。

④ 所以不加。决策表(发现期总R)显示 mom60≥7 + 周日 在 0~20bp 每一档都输给
mom60≥7 单用(5bp: 574 vs 658;10bp: 343 vs 394;15bp: 113 vs 131),叠加
只是白丢 10% 笔数。单用砍周日也要 12bp 以上才赢过等权。

这一步的价值是排除。「美盘流动性好该更赚」这种直觉很难自证伪,跑完才知道它
连随机切分都跑不赢;而顺手捞到的周日效应统计上真实,却因与已有过滤重叠而
不可加——显著和值得做是两件事,中间隔着一张决策表。

分析全部复用 step53 的 feather,未重跑采集。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 19:23:39 +08:00
jackyu66gitandCursor 17fe965a7c HANDOFF 跟上服务端:5.72 标题回退到旧结论,四条待办已完成
拉取后对表,文档有几处和代码脱节:

1. §5.72 标题还写「inner_ms 的 3.3 倍是争抢」,而正文经 af029bc 已更正为
   1.49x。扫标题的人会被带偏,改成 30ms 地板 + 37ms 缓存浪费 + 1.5x 争抢。

2. 「实盘信号计算改用增量」已上线(§5.7,清空十币 560→247ms),标完成,
   并把「重建不要用 init_stream」这个坑记在同一条上。

3. 「shadow_report 的 BUDGET_BP」和「live 补中枢阶梯 + ATR 门控」都已做完
   (前者改为从 lib/shadow_budget import,后者见 shadow_signal.py:11-18),
   标完成并保留当初的理由,那两条的判断过程比结论有用。

4. §5.72 新发现的两个杠杆之前只在正文里,没进待办,补上:按币绑 worker
   (省 inner_ms 三分之一)与 add_indicators 增量化,并写明二者是叠加不是
   二选一,以及后者的拦路石是 Wilder RSI 的 avg_gain/avg_loss 状态。

另外给 mom60≥7 那条待办补一句现状:live 路径的三道过滤是同向 + 阶梯 +
ATR 门控,mom60 连算都没算,要上得先在信号侧补出这个字段。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 19:23:39 +08:00
jackandCursor 881cc5e77d 补上「一个币一个仓位、账户专用」这两个没落在代码里的前提
三处都源于同一个隐含假设从未被写成代码。

① 同币并发会让 pnl_day 翻倍。blocks() 只查幂等键与三条计数闸,没有按币的
占用检查。同币开两笔时交易所净成一个仓位,于是 watch() 按 pair 判出场会让
两个 key 同时进 gone,_match_hist 给它们返回同一条历史记录,realized() 被
调两次。而 pnl_day 正是 MAX_DAY_LOSS 读的数:亏损翻倍提前停机,盈利翻倍让
闸变迟钝。sweep() 也会在第一笔截止时平掉合并后的整个仓位。

合并后的行为(一个止损、两个不同价位的止盈、超时一锅端)不是任何一版回测
建模的东西,所以在 on_signal 里跳过第二个信号,是最接近安全的近似。期望并发
0.18 笔,损失极小。另外给 _match_hist 加 positionId 独占认领,让这个不变量
在记账处本地成立,而不是依赖两百行外的检查——花钱的路径值得两道。

② positions() 返回账户全部仓位,三个调用点都没过滤。账户上任何第三方仓位
都会被 reconcile 在重启时市价平掉,而 watch() 会因该 symbol 一直在场而永不
结算,MAX_OPEN 名额泄漏、pnl_day 不再更新。加 PAIRS 过滤与 my_positions()。
残留局限记在注释里:同币上的第三方仓位仍分不出来,账户仍应专用。

③ oid_of 把非字母数字换成下划线,理由是 : 和 + 未必被接受,但调用方又拼了
"-tp" 把 - 加回去,自相矛盾。真被拒时止盈单会全部挂不上,而那条路径只告警
不停机,收益结构静默退化成「只有止损 + 超时」,且空跑验不到(dry 返回假
成功)。改用 _tp,并把字符集约束写进 oid_of 的文档。

实测:同币第二个信号被挡、别币放行、独占认领不重复计账、PAIRS 排除
PEPEUSDT、全部 clientOid 只含字母数字下划线。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 18:58:47 +08:00
jackandCursor 5ffbadb0b2 setup_symbols 不再无条件报"已设好",失败要说出来
那行"已设 N 个币为逐仓 10x"原先无条件打印,20 次调用全失败也照样这么说。
日志里写假话比不写更糟。而且这段只在真跑模式执行,空跑从没碰过它,第一次
运行就是上实盘的那一刻。

杠杆设失败是有经济后果的:仓位大小由名义额算、与杠杆无关,但保证金要求会
变。若交易所侧实际是 1x,每笔需 100 USDT 保证金,第 2、3 笔必然失败,且
可能只成一条腿、静默变成半仓——收益结构从「50% 在 3 ATR + 50% 在 8 ATR」
变成别的东西。

现在按失败项数如实报告,并推一条 Telegram。不硬性阻止启动:真正的失败会在
下单时暴露,但日志必须诚实。

实测全部成功/一项失败/全部失败三种情形。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 18:41:37 +08:00
jackandCursor 668ebe1e44 探针把 40014 判为最小权限的正常表现,并翻译 parentId/权限/白名单
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 18:36:10 +08:00
jackandCursor f62b4f62a5 加只读探针定位资金所在账户:子账户/现货/币本位/USDC 本位一次看清
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 18:29:23 +08:00
jackandCursor c513455998 余额不足时算出能开几笔,并点明半仓风险:两条腿分别下单,第二条失败会静默改掉收益结构
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 18:25:35 +08:00
jackandCursor 7435cef61f 空跑打全余额字段:逐仓下管用的是 isolatedMaxAvailable,只看 available 会误判
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 18:23:57 +08:00
jackandCursor 9562b8c598 空跑补一次带签名请求,否则密钥与白名单根本没被验到
dryrun 第 5 步声称"会真实验到密钥与白名单",但 start() 在空跑下于密钥检查
之前就 return,全程只发了 contracts() —— 那是公开端点,不验签。于是空跑会
"通过"却什么都没测到。这种假保证比不测更糟:等第一个真信号来时才暴露,而
信号那时正在过期,没有从容排查的余地。

现在空跑返回前发一次 account()(只读),失败即退出并给出码表(40018 白名单
/ 40037 key 不存在 / 40001,40009 secret,passphrase / 40099 权限)。顺带报
可用余额,并在不足 MAX_OPEN 笔并发所需保证金时告警。

两处 SystemExit 会绕过 run() 的收尾,退出前不关 aiohttp 会话,日志尾部一串
Unclosed client session 会把真正的报错顶出视野。都补上了 close()。

BITGET_PASSPHRASE 现在也接受 BITGET_API_PASSPHRASE:另两项都带 API_,只有
它不带,很容易顺手写错,而后果是"像是填了"但签名一直失败。我自己就踩了。

启动时打一行 Telegram 是否启用。配错 token 的表现原本是完全静默。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 18:17:53 +08:00
jackandCursor d797adc77e dryrun: timeout 套进 sudo 内层,否则 SIGTERM 未必传到 ssh
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 18:09:29 +08:00
jackandCursor 040810d48d known_hosts 校验命令补 sudo:.ssh 是 700 chan,ubuntu 读不了
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 18:02:54 +08:00
jackandCursor d884de2c36 dryrun 的 ssh 探测适配强制命令,并分清指纹与认证两种失败
采集机那侧的 authorized_keys 用了强制命令,把这把 key 锁成只能跑 tail,
拿不到 shell。副作用是任何请求都变成 tail -F 而它永不返回,原先
`ssh <host> 'echo ok'` 的探测会永久挂住——ConnectTimeout 只管建连,不管
命令时长。改成照 ship_signals 的真实用法读流:tail -c +0 先吐出整个文件,
数行数就等于对端总线条数,之后由 timeout 收掉。

失败诊断原先一律说"装 key",但 Host key verification failed 是 known_hosts
空的,装 key 修不了,会把人引到错方向。现在分三类:指纹未确认、认证被拒、
其他,各给对应做法。指纹那条明确不建议 StrictHostKeyChecking=no——这条链路
上跑的是下单信号。

install.sh 的第 2 步补上 known_hosts:原先只说装 key,而服务跑起来会撞同一
个墙(BatchMode=yes 不允许交互确认)。

实测:强制命令下故意请求错路径,仍拿到真总线内容且 stderr 干净。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 18:01:01 +08:00
jackandCursor b6ed5c8b50 on_signal 校验字段,畸形记录只丢一条而不是停掉交易
on_signal 直接取 r["emit_ms"],总线上一条字段不全的记录就会抛 KeyError
打死 poll 任务,进而整个执行器停止交易。一行坏数据换全面停摆,代价不对等。
搬运侧已挡半行,但挡不住字段级的不全。

现在缺 key/sym/emit_ms/direction/entry_px/atr_pct 任一项即丢弃该条,记日志
并推一条 Telegram(这类事应当可见,否则只是少做几笔,统计上看不出来)。

实测:两条字段不全 + 一条非法 JSON + 一条正常,前三条分别被丢弃/被
read_all 吞掉,正常那条照常下单,进程存活。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 17:57:24 +08:00
jackandCursor 999ad924ad install.sh 报出模板新增的配置项,并给 --sync-env 追加
live.env 装着密钥,所以安装时刻意不覆盖。但这样一来,往 live.env.example
里加配置,已有部署会永远拿不到、且毫无提示——静默漂移,等到某个开关根本
没生效才发现。这次加 TG_TOKEN 就撞上了。

现在重复安装会 diff 两边的变量名:报模板多出的项(提示跑 --sync-env),
也报配置里已被模板删掉的项(可能已废弃)。

--sync-env 独立成一个模式而不是塞进安装流程:追加要改一个装着密钥的文件,
这种事应当由人显式发起。只追加缺的项、连它上面的注释一起,不动已有任何
一行,先备份。实测原有内容逐行未变且重复跑幂等。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 17:45:48 +08:00
jackandCursor 15f4aa088e 加 Telegram 通知,并修好一道死掉的闸
接 Telegram 时查出 Guard.realized() 定义了但全仓库没有调用点——pnl_day
恒为 0,MAX_DAY_LOSS 完全不生效。三条硬约束里最重要的一条是死的。

根因是止损与止盈都挂在交易所侧成交,本进程收不到通知,而 sweep 只在 48
分钟到点才查持仓。连带第二个后果:execs 条目不清,MAX_OPEN 把已出场的
仓位继续算在场,新信号被白挡到截止时刻。

补 watch() 循环(10s):持仓消失即判出场,去 history-position 取
netProfit(= pnl + 资金费 + 开平手续费)记回闸并释放名额。盈亏取交易所的
数而不自己按标记价估——估会漏掉费用且方向总偏乐观。历史未落库时留到下轮,
不会漏记。字段名按文档与官方 TS 类型的差异同时兼容 ctime/cTime。

Telegram(live/tg.py,stdlib + aiohttp):推开仓、平仓带已实现盈亏、被硬
约束挡住、报错、对账平仓、跨日结算、启动与停机。不推信号过期跳过(常态,
搬运重连会重放旧信号)与心跳,否则真事会被淹掉。启动那条兼作通道自检。
研究侧 tg_notify.send 改为复用生产的传输层,方向与 signal_bus 一致。

status.sh 增加一条判读:开过仓但 pnl 仍为 0 就是 watch() 出了问题。

实测:8 类消息渲染、_match_hist 的过早/方向不符/币不符/驼峰字段/取最近
五种情形、100 USDT 下 SOL 与 ADA 的端到端空跑(两腿等量,50/50 精确)。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 17:30:11 +08:00
jackandCursor 3e3d579e35 文档与实现对齐:停机是 SIGTERM 且会平仓,补充崩溃/主动停机的差异
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 17:13:56 +08:00
jackandCursor 1642b1bbfc 补上停机处理:SIGTERM 撤挂单平仓再退,并关掉 aiohttp 会话
README 里原先写「执行器收到 SIGINT 会先平掉在场仓位再退出」,但代码里
完全没有信号处理——asyncio.run 外面连 except KeyboardInterrupt 都没有。
断言了一个不存在的行为,现在把它实现。

为什么停机要平仓而崩溃不用:崩溃后 systemd/docker 几秒内重启,reconcile
接着清掉遗留仓位,空窗期有交易所侧止损兜着。而主动停机后没人重启,仓位会
一直挂到止损或止盈,48 分钟超时腿丢了,跑的就不是回测那个出场结构。

必须显式挂 SIGTERM:docker stop 与 systemd stop 默认发的都是它,而 Python
对 SIGTERM 不抛 KeyboardInterrupt,不挂就是直接消失、没有任何清理。挂上后
systemd 单元里 KillSignal=SIGINT 那个绕法也去掉了。

主循环因异常退出时同样走停机流程,不把仓位留给已经没人管的进程。
顺带修掉 "Unclosed client session"(bitget_rest 本有 close() 但没人调,
Restart=always 下会漏 socket)。

实测:发 SIGTERM 后走完停机流程、无泄漏警告。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 17:12:41 +08:00
jackandCursor 335d891478 生产与研究分家:实盘执行器独立成 live/ 子树,加 AWS 部署
实盘要跑在 AWS(API key 绑了 IP 白名单),而信号在新加坡那台算。借这次
把生产从研究侧摘出来,四条具体代价里第一条已经咬过:

1. live_state.json 原先落在 research/out/,而那里 shadow_hb 会自动 rename
   归档、研究脚本会写、人也手工清过。那文件装的是 MAX_DAY_LOSS 累计与已
   处理信号键,被清掉不报错,只是两道闸静默失效。改到 LIVE_HOME。
2. 采集器十币清空 300~560ms 直接叠在信号到达执行器的延迟上。
3. 研究侧探针 OOM 过一次(14.9GB),当时若有仓位在场会连坐执行器。
4. 为读两个常量 import 研究侧 step43,把 numpy/pandas/pyarrow 拖进实盘
   进程。抽出 stdlib-only 的 live/exit_params.py,install.sh 加断言挡回归。

新增 live/ship_signals.py:AWS 侧 ssh tail 拉总线,每次重连从文件头重放
+ 按幂等键去重,断线期间的信号自愈;旧信号由 staleness 闸挡掉不补做。
带时钟倒流检测——两机时钟不同步会让那道闸静默放宽。

部署件:systemd 两单元(搬运挂了执行器仍管在场仓位的超时平仓)、
install.sh、dryrun.sh(验密钥/白名单/时钟/ssh/取整)、status.sh、README。

验证:live_exec 重构后端到端空跑,SOL 多头与 ADA 空头的止损/两级止盈/
数量取整逐项核对正确,isolated + post_only + reduceOnly 都在;搬运的去重、
重启不重复追加、脏数据跳过、断线重连重放均已测。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 17:03:03 +08:00
jackandCursor af029bc26e 更正上一提交:争抢是 1.5x 不是 3.3x,并发现按币绑 worker 可省三分之一
上一提交的 3.3x 基线是废的:离线每次喂同一窗口,append_bar 一根没追
(实测 6 次调用 stream 2001→2001 零增长),测的只是信号链地板 30ms,
拿它比在场 inner_ms 又是一次口径不对齐——和刚撤回的 22/86 同一类错。

对齐后:在场每次平均追 2.81 根(2 worker 各存一份缓存、各自漏掉对方
处理过的根),离线同口径 67.3ms,在场 100ms → 争抢 1.49x。
顺带修正「重建占 21.5%」:真重建只有 0.31%,此前把换 worker 的小幅
回退误判成重建。

新杠杆:symbol 固定到同一 worker,每次只追 1 根,离线 67.3 → 45.0ms,
在场约省 33ms/币(inner_ms 三分之一)。心跳判定改指这一刀。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 16:23:02 +08:00
jackandCursor cef894376c 定位 inner_ms 的 3.3 倍:是争抢不是算法档,撤回 22/86 拆分
服务端跑 probe_inner.py 与本机对表:分档占比一致(TF_DF 两腿 68~74%,
信号链 16~21ms),此前报的「chan 构建 22ms / 信号链 86ms」是无效减法
——孤立环境的 1m append 减在场十币的 inner_ms,还漏了 5m 腿。

真实构成:inner_ms 100ms = 约 30ms 计算 × 3.3 倍争抢。逐个排除
_rebuild(1.6ms)、币种差异、周期重建(21.5%/1.5x)、批内次序(相关-0.083)
后,用外生的到达密集度确认(67→117ms),并以注入合成负载复现
(2 个满载进程 2.68x,3 个 2.86x,在场实测 2.24~3.34x)。

心跳判定不再推荐「继续压算法」,改为加核或减币。

第一版用 t_signal-inner_ms 反推并发区间得相关 0.533,是循环构造,
已废弃并在 §5.72 记下这个坑。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 16:19:25 +08:00
jack be442783a1 Merge branch 'chan' of ssh://git.jackyu66.com:2222/jack/chan into chan 2026-08-28 16:07:28 +08:00
jackandCursor f11c6507b0 实盘执行改走 Bitget v2 REST,止损挂到服务端
丢掉 Hummingbot 的 PositionExecutor。它的连接器只暴露 LIMIT / LIMIT_MAKER /
MARKET,没有触发单,于是 control_stop_loss() 只能本地盯价、触发时才发市价单
——**进程一死仓位就是裸的**。而交易所本身支持 place-order 带
presetStopLossPrice,下单时就把止损挂到服务端。绕过连接器不是图省事,是为了
消掉一整类故障。顺带 TripleBarrierConfig 只有单级止盈,装不下两级,自己写更短。

## 三条出场腿各自挂在哪

    止损   交易所侧(presetStopLossPrice,随入场单一起到)→ 进程死了仍在
    止盈   交易所侧(post_only reduce-only 限价)        → 进程死了仍在
    超时   本进程,48 分钟到点市价平

所以进程死掉只会让持仓超过 48 根,不会变成裸仓,退化是良性的。

## 止盈不能用 presetStopSurplusPrice

它触发后按市价执行,而成本模型里止盈是 maker——那 60% 的出场不吃滑点、按
maker 费率计(LEG_IS_TAKER)。用 preset 会让这部分变成 taker,预算就不成立。
所以止盈单独挂 post_only + reduceOnly 限价单。止损反过来必须市价:stop-limit
在急跌里可能不成交,损失远大于省下的费。

## clientOid 是交易所级幂等,但要小心两个坑

信号键形如 SOL:1787904388411:+1,`:` 和 `+` 未必被接受,带过去直接拒单——而
拒单发生在入场腿上,等于这笔信号静默漏掉。

清洗时不能简单把非字母数字换成下划线:那样 `+1` 和 `-1` 都变成 `_1`,同一根上
的多空信号得到相同 oid,第二笔被当重复拒掉。方向显式编码为 L/S。

用 clientOid 而非只靠本地去重,是因为「已发出但没收到回复」这种情况本地判不了,
重试就会开两次仓。

## 空跑要走完 open_position

第一版在 on_signal 里 `if dry: return`,结果数量取整、价位对齐 tick、请求体
构造全都没被验到。现在空跑走完整条路径,不下真单由 Bitget(dry=True) 负责。

实测两笔:SOL 多头 entry 106.706 / ATR 11bp → 止损 106.471、止盈 107.058 与
107.645;BTC 空头 → 止损在上方 79747.5、止盈在下方。半仓精确一半(SOL 2.3+2.3、
BTC 0.0031+0.0031)。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 16:07:28 +08:00
jackyu66gitandCursor bfdb2f2e2a 引擎四处「算了没人要的东西」,append_bar 13.9ms → 6.6ms
服务端把增量上线后回传两个热点:add_indicators 为加一根重算全表(占 34%)、
cal_bi_list 整表重扫(51%)。顺着查下来四处都不是算法慢,是算了没人读的结果。

1. cal_trend 挂到 lean 下。它不是笔的依赖(bi.py:221 在它自己的循环里读自身
   序列状态),服务端 verify_incr_parity 三币 1800 根已对拍定论。web 走非
   lean,klc_trend 图层不受影响。

2. check_fx_pattern 删掉拼完就丢的字符串。它把 klu.to_string() 拼成 p 只为
   一行注释掉的 print——2000 根上近 3 万次 f-string 加 6 万次 enum 格式化,
   而且在 cal_bi_list 内层。klu.pattern 只被 cal_klu_pattern 自己的双K/三K
   判定读,不出模块不进 web,所以整个调用在 lean 下也跳过。

3. ChanBI.add_klc 去二次方。去重原本线性扫 klc_list,且每加一根就把整笔所有
   KLU 的 macdhist 重累一遍,往一笔加 k 根是 O(k²)。改成下标集合加
   macd_hist/macd_div 惰性求值。这两个值只有背驰判定(bsp.py)读,lean 下
   bsp 根本不算。

4. add_indicators 批量挂列。2001 行上 TA 计算合计只有 2.5ms,而 30 多次
   df['x']= 要 3.6ms——开销大头是 BlockManager 逐列插入不是计算,改为一次
   concat。cal_volume_ratio 里为算一列 rolling 而 copy() 整张 40 列表,一并去掉。

实测(本机,2001 根窗口。服务端基线 21.8ms 是另一台机器,别直接比绝对值):
  append_bar        13.9 → 6.6ms
  └ rebuild_bi_zs    8.7 → 2.8ms
  └ add_indicators   4.4 → 3.5ms
  TF_DF lean        49.8 → 32.9ms
  TF_DF full        72.7 → 64.9ms

对拍用 git worktree 检出改动前的提交,同一份 BTC 1m 4000 根跑 38 项指纹:
full 模式 19 项全部一致(web 那条路没动);lean 模式差 2 项,正是设计要它差
的 klc.trend 和 klu.pattern,而 lean 下 bi/zs/seg/bsp/dataframe 全部一致——
这就是「这两个字段没人读」的实测证据:打空它们,下游一位不变。

瓶颈已经换位置了。新增 probe_inner.py 拆 inner_ms 分档:本机 TF_DF 两条腿占
70%、build_htf_zones 13%、htf_fx_timeline 6%,而服务端报的是 chan 构建 22ms /
信号链 86ms,机器差解释不了这个四倍差距。曾怀疑是 payload 反序列化,实测
_rebuild 只有 1.0ms,假设不成立。两边跑同一探针对分档表才能定位。

HANDOFF 顺带修掉一处 5.6 重号(增量落地那节改为 5.7,本节挂 5.71)。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 16:01:26 +08:00
jackyu66gitandCursor 06928d1d5f 开仓前那段:量要小、势要有但不能过头,换掉 vr60 那个开关
用户提出看开仓之前的量与趋势方向。这是第三个位置——step50 管信号根、
step52 管持仓中,这里管入场前。step48 采集端补 vpre10/vpre30/mom10/mom60。

先纠正一个错的心智模型(我和用户都以为的):B4/S4 不是回抽后进场。mom10
中位 +3.47 ATR,为负的只占 6%。信号触发时价格在前 10 根里已经顺着你的方向
走了三个多 ATR,2 ATR 止损是架在一段已经走完的行情后面。这也解释了 step50:
信号根是突破根,放量 = 追在最后一棒上。

① 入场前的量单调,越小越好(样本外毛R,Q1→Q4):1.474 / 1.266 / 1.156 /
0.573。控 vr60 后仍成立(低量层 −0.284、高量层 −0.541),与 vr60 相关只有
+0.301,不是同一件事换个说法。

② 入场前的动量是驼峰形,不是单调(mom60 样本外毛R,Q1→Q4):1.252 /
1.473 / 1.141 / 0.603。势要有——完全没动过的 Q1 也不如 Q2;但不能过头——
Q4 在发现期余量只剩 1.44bp,等于不能做。

驼峰形意味着中位数二分法会把它测没:控制表里 mom10 的毛R差是 +0.044,
看着无效,那是二分把 Q1+Q2 和 Q3+Q4 各自平均了。对非单调因子不要用中位数
分层做检验。

③ 砍 mom60≥7 全面优于 §3.39 定的砍 vr60≥4(发现期):

  等权          保留 100%  盈亏平衡 13.5bp  R夏普 0.325  回撤 16.8  总R 596.6
  砍 vr60≥4     保留  69%  盈亏平衡 15.6bp  R夏普 0.411  回撤 10.8  总R 514.9
  砍 mom60≥7    保留  78%  盈亏平衡 17.5bp  R夏普 0.476  回撤  7.7  总R 657.7

每一项都赢,还多留 9 个点的笔数。更要紧的是总R 比不砍还高——被砍掉那 22%
期望为负,砍掉不是花钱买稳健,是纯赚。样本外同向。所以这个开关不需要等
影子测量:它在 0~20bp 每一档都不输等权。

附滑点决策表:5~12bp 区间砍 mom60≥7 通吃,只有 ≥15bp 才该上「三个都砍」,
而那时策略本身已在生死线上。

阈值 7 和 1.5 是贴着 Q4 边界取的整数,不是搜出来的,但也不是完全无关于数据
(看过分位表才取的整),上线前应确认阈值附近没有断崖敏感。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 15:55:49 +08:00
jackyu66gitandCursor 431e776905 持仓中放量不是出场信号,恰恰是最好那批单子的标记
用户问:既然 step50 证明入场根放量是接盘,那持仓中出现放量根是不是也说明
这一波走完了、该直接平掉?测下来方向是反的,且比入场那条还干净。

规则:持仓期间任一根 vr60 ≥ 阈值就收盘市价平掉(taker)。同根内优先级
止损(盘中)> 目标(盘中)> 放量平仓(收盘)。10 币 3941 笔:

  基线(不看量)  毛R 1.068  R夏普 0.627  PF 3.80  余量 18.45bp  均持仓 30.0
  vr60≥3 就平    毛R 0.617  R夏普 0.459  PF 2.81  余量  7.81bp  均持仓 11.1
  vr60≥5 就平    毛R 0.858  R夏普 0.568  PF 3.37  余量 12.85bp  均持仓 19.8
  vr60≥8 就平    毛R 1.009  R夏普 0.617  PF 3.69  余量 16.68bp  均持仓 26.3

阈值越高、触发越少就越接近基线——这条曲线的最优点是「永不触发」,规则纯扣分。
「浮盈才平」的变体把胜率抬到 74.4%(基线 71.7%)而毛R 掉到 0.655,是过早
止盈的教科书特征:胜率上升、期望下降。

根因:vr60≥5 触发的 1769 笔若不平,止盈率 45.7%、止损率 16.6%,而全体基线
是 30.1% / 33.1%。持仓中的放量根标记的是最好的那批单子,平掉每笔让出 +0.484R。
幸存者偏差已控——按基线持仓 ≥K 根分层后 5/5 档同向,放量组止盈率约为无量组
两倍(K=5 时 41.2% vs 19.8%)。

同一个事件入场为负、持仓为正,区别只在站在它的哪一边:入场那根放量你是买方,
持仓中那根是资金来接你的货。用户「有资金的趋势才是好趋势」的直觉成立,
作用点在持仓期而非入场点。

自带逐根模拟器不走 walk_exits,所以先与它对拍基线(毛收益差 <1e-12、出场
原因零分歧),10/10 币通过才往下算。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 15:55:49 +08:00
jackandCursor 360e4e1c41 自动化实盘执行器:信号总线 + 两个半仓分解 + 四条硬约束
## 架构:影子发信号,实盘执行,两个进程

不让实盘自己算信号。前两个理由是资源与隔离(2 核上再来一份十币计算会把清空
从 247ms 推到 500ms+;实盘崩溃不该影响正在采的数据集),第三个最要紧:
**实盘交易的必须是影子测量的那一个信号**。各算一份会让两边悄悄分叉,之后就
没法把实盘实际成交和影子测的滑点曲线对照——而那个对照是整件事的目的。

总线用 append-only JSONL + fsync:崩溃安全,且「影子在跑、实盘没在跑」会表现
为信号在攒着,而不是静默丢弃。

## 出场结构精确分解成两个半仓

TripleBarrierConfig 只有单级止盈,装不下 3ATR 减半 + 8ATR 目标。但已核实
exit_model.py:151 的 runner_stops 是从**入场价**算的(ret = (entry-low)/a),
且 RUNNER_STOP == SL == 2.0,所以两半共用同一个不动的止损,可精确分解为:

    半仓 A  市价入场 · TP 3ATR · SL 2ATR · 48min
    半仓 B  市价入场 · TP 8ATR · SL 2ATR · 48min

止损先到则两半都在 -2ATR 出场;3ATR 先到则 A 出场、B 继续且止损仍在 2ATR。
与回测逐情形一致。assert_decomposable() 在启动时挡住 RUNNER_STOP != SL 的
改动,否则实盘会跑另一个收益结构且不报错。

## 硬约束是这个文件的重点

一笔止损只亏约 1 USDT,所以「亏损可控」对单笔成立。但三类故障的代价**不随
仓位缩小**,必须显式封住:失控下单(MAX_OPEN=3 / MAX_DAY=15)、亏损累积
(MAX_DAY_LOSS=20)、裸仓(重启对账)。状态落盘且原子替换——不落盘的话反复
重启就等于反复重置日上限,而失控下单恰好常伴随反复重启。

已验:幂等去重、并发上限、日开仓上限、日亏损上限、跨日归零且保留 done 键、
重启后计数不清零、状态文件损坏时不抛异常。

## 重启对账选择平掉而非接管

崩溃重启后交易所可能还有仓位,而 executor 全没了,那些仓位没有任何止损在盯。
接管需要重建入场价/ATR/剩余半仓状态/已过根数,任一项猜错就让出场结构变成
另一个东西;平掉的代价只是一笔小额亏损,且行为确定。

## 已知风险:止损在机器人侧

Bitget 连接器只支持 LIMIT / LIMIT_MAKER / MARKET,无触发单。
PositionExecutor.control_stop_loss() 是本地盯价、触发时才发市价单,所以进程
一死仓位就是裸的。10x 下强平需逆向 10%(约 100 个 ATR),48 分钟内极不可能,
单次代价仍封在保证金内。下一步用 Bitget 服务端 TP/SL 计划单做兜底。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 15:52:03 +08:00
jackandCursor f716ddd149 推送带杠杆与保证金,名义额默认抬到 500
杠杆只影响占用保证金,**不改**名义敞口、手续费、滑点、盈亏绝对值——费与滑点
都按名义额收。所以「用杠杆所以可以很小额」这个方向要说清:杠杆省的是本金
占用,不是成本。

它真正的用处是让「抬名义额」几乎免费,而抬名义额正好压掉步长取整。实测
取整到步长偶数倍后最差币的名义偏差:100U → 6.7%(SOL)、500U → 1.8%、
1000U → 0.6%。所以默认名义改 500、杠杆 10x,每笔占 50 USDT 保证金。

止损在 2 ATR ≈ 0.2%,而 10x 的强平约需逆向 10% = 100 个 ATR,差 50 倍。
这个止损紧到杠杆几乎不引入强平风险,所以这里没有「杠杆换风险」的取舍。
仍建议逐仓,让每笔最大损失被保证金封住。

推送里加上占用保证金与「触止损亏多少 USDT / 占保证金百分之几」,手工执行时
不必自己换算。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 15:44:39 +08:00
jackandCursor c57adc8cb0 推送按合约规则取整,减半腿精确一半
100 USDT 的仓位在 Bitget 上不是最小下单量的问题(minTradeUSDT=5、
minTradeNum 极小,10U 都能下),是**步长取整**的问题:

  SOL  步长 0.1 币 ≈ 10.7U → 50U 腿只能取 0.4 币 = 42.7U,偏 14.6%
  LINK 步长 1 币 ≈ 11.7U   → 4 币 = 46.8U,偏 6.4%
  BTC  步长 0.0001 ≈ 8.0U  → 0.0006 = 47.8U,偏 4.4%

减半腿变成全仓的 43% 而非 50%,剩下 57% 暴露在 8ATR 目标上,而回测的收益
结构假设 50/50。小额下这不影响机制验证,但会让 P&L 读不出回测那个结构。

解法是入场量取到**步长的偶数倍**,这样一半天然落在步长上。实测十个币减半腿
全部精确 50%,名义额落在 93.6~106.7 USDT——小额实盘无所谓。

同时把价位对齐到 tick(priceEndStep × 10^-pricePlace),否则限价单会被拒。
规则拉一次缓存;拉不到就退化为不取整并打日志,不阻断推送。

费率一事已核实无需改动:预算假设的挂牌 taker 0.040% / maker 0.016% 正是
Bitget VIP2 的官方档(返 50% 后 2.0 / 0.8bp)。合约接口返的 6bp/2bp 是 VIP0
基础档,不适用。8bp 门控阈值不变。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 15:42:17 +08:00