服务端把增量上线后回传两个热点: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>
163 lines
5.1 KiB
Python
163 lines
5.1 KiB
Python
from decimal import Decimal
|
|
import chanlun.core.ChanKLC as ChanKLC
|
|
from chanlun.core.ChanEnum import Chan_BI_DIR
|
|
class ChanBI():
|
|
def __init__(self, klc: ChanKLC, index, ddir=Chan_BI_DIR.UP):
|
|
self.start_klc = klc
|
|
self.end_klc = klc
|
|
self.next = None
|
|
self.pre = None
|
|
self.dir = ddir
|
|
self.index = index
|
|
self.is_sure = False
|
|
self.high = klc.high
|
|
self.low = klc.low
|
|
self.sure_time = None
|
|
self.klc_list = []
|
|
self.klc_list.append(klc)
|
|
self._klc_idx = {klc.index}
|
|
self.end_time = klc.end_time
|
|
self.start_time = klc.start_time
|
|
self._macd_hist = 0
|
|
self._macd_div = 0
|
|
self._macd_dirty = False
|
|
self.seg = None
|
|
self.height = 0
|
|
self.width = 0
|
|
self.slop = 0
|
|
self.fib_list = []
|
|
self.seg_index = 0
|
|
self.bi_zs = None
|
|
self.seg_zs = None
|
|
def set_bi_zs(self, bi_zs):
|
|
for klc in self.klc_list:
|
|
klc.set_bi_zs(bi_zs)
|
|
def set_seg(self, seg):
|
|
self.seg = seg
|
|
self.seg_index = len(seg.bi_list)-1
|
|
# macd_hist / macd_div 改为惰性。原来 add_klc 每加一根 KLC 就把整笔的
|
|
# 所有 KLU 重新累加一遍,是 O(k²);而这两个值只有背驰判定(bsp.py)在读,
|
|
# lean 模式下 bsp 根本不算,等于全程白算。这里只标脏,取值时才算。
|
|
# 语义不变:klc_list 只增不减(set_start_klc 会重置并同时清脏),
|
|
# dir 在 klc 累加期间固定,所以延后到读取时算与逐次重算结果相同。
|
|
@property
|
|
def macd_hist(self):
|
|
if self._macd_dirty:
|
|
self.cal_macdhist()
|
|
return self._macd_hist
|
|
|
|
@macd_hist.setter
|
|
def macd_hist(self, v):
|
|
self._macd_hist = v
|
|
self._macd_dirty = False
|
|
|
|
@property
|
|
def macd_div(self):
|
|
self.cal_macd_div()
|
|
return self._macd_div
|
|
|
|
@macd_div.setter
|
|
def macd_div(self, v):
|
|
self._macd_div = v
|
|
|
|
def set_macdhist(self, macd_hist):
|
|
self.macd_hist = macd_hist
|
|
def set_macd_div(self, macd_div):
|
|
self.macd_div = macd_div
|
|
def cal_macd_div(self):
|
|
self._macd_div = 0.0
|
|
if self.pre and self.pre.pre:
|
|
if self.pre.pre.macd_hist == 0:
|
|
self._macd_div = 0.0
|
|
else:
|
|
self._macd_div = self.macd_hist / self.pre.pre.macd_hist
|
|
#print(self.start_time, self.end_time, self.macd_hist, self.pre.pre.macd_hist, self.macd_div)
|
|
def cal_macdhist(self):
|
|
self._macd_dirty = False
|
|
self._macd_hist = 0
|
|
up = self.dir == Chan_BI_DIR.UP
|
|
acc = 0
|
|
for klc in self.klc_list:
|
|
for klu in klc.klu_list:
|
|
h = klu.macdhist
|
|
if up:
|
|
if h > 0:
|
|
acc += h
|
|
elif h < 0:
|
|
acc -= h
|
|
self._macd_hist = acc
|
|
def check_bi_zs_overlap(self):
|
|
if self.next and self.next.next:
|
|
if self.dir == Chan_BI_DIR.UP:
|
|
return self.low < self.next.next.high
|
|
else:
|
|
return self.high > self.next.next.low
|
|
else:
|
|
return False
|
|
def check_overlap(self):
|
|
if self.next and self.next.next and self.next.next.is_sure:
|
|
if self.dir == Chan_BI_DIR.UP:
|
|
return self.high > self.next.low and self.high < self.next.next.high
|
|
else:
|
|
return self.high > self.next.high and self.low > self.next.next.low
|
|
else:
|
|
return False
|
|
def set_end_klc(self, klc, sure_klc):
|
|
if self.dir == Chan_BI_DIR.UP and klc.high > self.high:
|
|
self.high = klc.high
|
|
if self.dir == Chan_BI_DIR.DOWN and klc.low < self.low:
|
|
self.low = klc.low
|
|
self.end_klc = klc
|
|
self.set_is_sure(True, sure_klc.end_time)
|
|
self.end_time = klc.end_time
|
|
self.cal_properties()
|
|
#print(self.start_time, klc.fx, "This bi is ended", len(self.klc_list), klc.index - self.start_klc.index)
|
|
def cal_properties(self):
|
|
if self.is_sure:
|
|
self.height = float(format(self.high - self.low, ".2f"))
|
|
self.width = self.end_klc.index - self.start_klc.index
|
|
self.slop = float(format(self.height / self.width, ".2f"))
|
|
fib_list = [0.0, 0.236, 0.382, 0.5, 0.618, 0.786, 1.0]
|
|
for fib in fib_list:
|
|
self.fib_list.append(float(format(self.height * fib + self.low, ".2f")))
|
|
#print(self.end_time, self.height, self.width, self.slop, self.fib_list)
|
|
def set_is_sure(self, is_sure, time):
|
|
self.is_sure = is_sure
|
|
self.sure_time = time
|
|
def set_start_klc(self, klc, ddir):
|
|
self.start_klc = klc
|
|
self.klc_list = []
|
|
self.klc_list.append(klc)
|
|
# klc_list 被整个换掉,去重集合与惰性缓存都要跟着重置,
|
|
# 否则后续 add_klc 会以为旧下标还在里面而漏加
|
|
self._klc_idx = {klc.index}
|
|
self._macd_dirty = True
|
|
self.high = klc.high
|
|
self.low = klc.low
|
|
self.dir = ddir
|
|
def set_pre(self, bi):
|
|
self.pre = bi
|
|
def set_next(self, bi):
|
|
self.next = bi
|
|
def add_klc(self, klc):
|
|
# 去重原本是对 klc_list 线性扫描,配合下面每次全量重算的 macdhist,
|
|
# 让「往一笔里加 k 根 KLC」变成 O(k²)。改用下标集合,O(1)。
|
|
if klc.index not in self._klc_idx:
|
|
self._klc_idx.add(klc.index)
|
|
self.klc_list.append(klc)
|
|
self.end_klc = klc
|
|
self.end_time = klc.klu_list[-1].time
|
|
self._macd_dirty = True
|
|
def append_klc_list(self, klc_list):
|
|
self.klc_list.append(klc_list)
|
|
def get_decimal(self, value):
|
|
return Decimal("{:.2f}".format(value))
|
|
def update_bi(self, klc):
|
|
self.end_klc = None
|
|
if self.dir == Chan_BI_DIR.UP and klc.high > self.high:
|
|
self.high = klc.high
|
|
if self.dir == Chan_BI_DIR.DOWN and klc.low < self.low:
|
|
self.low = klc.low
|
|
self.is_sure = False
|
|
self.sure_time = None
|
|
#print(self.start_time, klc.start_time, klc.fx, "This bi is extended") |