268 lines
9.9 KiB
Python
268 lines
9.9 KiB
Python
#!/usr/bin/env python3
|
||||
|
|
"""
|
|||
|
|
对象标识测试:验证同一 Rc 底层数据通过不同路径访问时,
|
|||
|
|
Python 侧始终返回相同的 PyObject(`is` 比较为 True)。
|
|||
|
|
|
|||
|
|
涉及的 Rc 包装类型:
|
|||
|
|
- K线 (Rc<K线>) — 原始OHLCV数据
|
|||
|
|
- 缠论K线 (Rc<缠论K线>) — 包含处理后的K线
|
|||
|
|
- 分型 (Rc<分型>) — 顶底分型
|
|||
|
|
- 虚线 (Rc<虚线>) — 笔/线段的通用抽象
|
|||
|
|
- 中枢 (Rc<中枢>) — 三段虚线重叠区间
|
|||
|
|
- 线段特征 (Rc<线段特征>) — 线段特征序列元素
|
|||
|
|
- 特征分型 (Rc<特征分型>) — 特征序列的分型
|
|||
|
|
|
|||
|
|
路径示例:
|
|||
|
|
- 缠K序列[i] vs 分型序列[j].中 (同一根缠K)
|
|||
|
|
- 分型序列[i] vs 笔序列[j].文 (同一个分型)
|
|||
|
|
- 笔序列[i] vs 中枢[k].基础序列[m] (同一条虚线)
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
import chanlun
|
|||
|
|
import math
|
|||
|
|
|
|||
|
|
|
|||
|
|
def create_observer(symbol="btcusd", period=14400, n_bars=500):
|
|||
|
|
"""创建观察者并喂入模拟K线数据。"""
|
|||
|
|
cfg = chanlun.缠论配置()
|
|||
|
|
obs = chanlun.观察者(symbol, period, cfg)
|
|||
|
|
|
|||
|
|
for i in range(n_bars):
|
|||
|
|
trend = i * 3
|
|||
|
|
wave = math.sin(i * 0.05) * 2000
|
|||
|
|
mid = 68000.0 + trend + wave
|
|||
|
|
high = mid + abs(math.cos(i * 0.3)) * 400 + 100
|
|||
|
|
low = mid - abs(math.sin(i * 0.5)) * 400 - 100
|
|||
|
|
k = chanlun.K线(
|
|||
|
|
标识=symbol,
|
|||
|
|
周期=period,
|
|||
|
|
时间戳=1771675200 + i * period,
|
|||
|
|
开盘价=mid - 50,
|
|||
|
|
高=high,
|
|||
|
|
低=low,
|
|||
|
|
收盘价=mid + 50,
|
|||
|
|
成交量=abs(math.sin(i)) * 1000,
|
|||
|
|
)
|
|||
|
|
obs.增加原始K线(k)
|
|||
|
|
|
|||
|
|
return obs
|
|||
|
|
|
|||
|
|
|
|||
|
|
class Test缠K身份:
|
|||
|
|
"""缠论K线: 从序列、分型、笔端点、中枢等不同路径访问。"""
|
|||
|
|
|
|||
|
|
def test_序列重复获取(self):
|
|||
|
|
"""同一序列获取两次,元素应相同。"""
|
|||
|
|
obs = create_observer()
|
|||
|
|
seq1 = obs.缠论K线序列
|
|||
|
|
seq2 = obs.缠论K线序列
|
|||
|
|
for i in range(min(len(seq1), 10)):
|
|||
|
|
assert seq1[i] is seq2[i], f"缠K序列[{i}] 身份不一致"
|
|||
|
|
|
|||
|
|
def test_分型中K(self):
|
|||
|
|
"""分型.中 与 缠K序列 对应元素应相同。"""
|
|||
|
|
obs = create_observer()
|
|||
|
|
seq = obs.缠论K线序列
|
|||
|
|
分序 = obs.分型序列
|
|||
|
|
for fx in 分序[:10]:
|
|||
|
|
中 = fx.中
|
|||
|
|
for ck in seq:
|
|||
|
|
if ck.时间戳 == 中.时间戳:
|
|||
|
|
assert ck is 中, f"分型.中 (ts={中.时间戳}) 与序列中元素不匹配"
|
|||
|
|
break
|
|||
|
|
|
|||
|
|
def test_笔端点钟K(self):
|
|||
|
|
"""笔的端点分型的中间K线应与序列元素相同。"""
|
|||
|
|
obs = create_observer()
|
|||
|
|
seq = obs.缠论K线序列
|
|||
|
|
for bi in obs.笔序列:
|
|||
|
|
for nm, getter in [("文", lambda b=bi: b.文), ("武", lambda b=bi: b.武)]:
|
|||
|
|
ep = getter()
|
|||
|
|
if ep is None:
|
|||
|
|
continue
|
|||
|
|
中 = ep.中
|
|||
|
|
for ck in seq:
|
|||
|
|
if ck.时间戳 == 中.时间戳:
|
|||
|
|
assert ck is 中, f"笔.{nm}.中 (ts={中.时间戳}) 与序列中元素不匹配"
|
|||
|
|
break
|
|||
|
|
|
|||
|
|
def test_getter重复调用(self):
|
|||
|
|
"""同一getter调用两次返回同一对象。"""
|
|||
|
|
obs = create_observer()
|
|||
|
|
for fx in obs.分型序列[:5]:
|
|||
|
|
中1 = fx.中
|
|||
|
|
中2 = fx.中
|
|||
|
|
assert 中1 is 中2, "分型.中 两次调用返回不同对象"
|
|||
|
|
|
|||
|
|
|
|||
|
|
class Test分型身份:
|
|||
|
|
"""分型: 从分型序列、笔/线段端点、买卖点等不同路径访问。"""
|
|||
|
|
|
|||
|
|
def test_序列重复获取(self):
|
|||
|
|
"""同一序列获取两次,元素应相同。"""
|
|||
|
|
obs = create_observer()
|
|||
|
|
seq1 = obs.分型序列
|
|||
|
|
seq2 = obs.分型序列
|
|||
|
|
for i in range(min(len(seq1), 9)):
|
|||
|
|
assert seq1[i] is seq2[i], f"分型序列[{i}] 身份不一致"
|
|||
|
|
|
|||
|
|
def test_笔端点与序列(self):
|
|||
|
|
"""笔.文 / 笔.武 应与分型序列中对应元素相同。"""
|
|||
|
|
obs = create_observer()
|
|||
|
|
分序 = obs.分型序列
|
|||
|
|
for bi in obs.笔序列:
|
|||
|
|
for nm in ["文", "武"]:
|
|||
|
|
ep = getattr(bi, nm)
|
|||
|
|
if ep is None:
|
|||
|
|
continue
|
|||
|
|
matched = False
|
|||
|
|
for fx in 分序:
|
|||
|
|
if fx.时间戳 == ep.时间戳 and fx.结构 == ep.结构:
|
|||
|
|
assert fx is ep, f"笔.{nm} (ts={ep.时间戳}) 与分型序列中元素不匹配"
|
|||
|
|
matched = True
|
|||
|
|
break
|
|||
|
|
assert matched, f"笔.{nm} (ts={ep.时间戳}) 在分型序列中未找到"
|
|||
|
|
|
|||
|
|
def test_段端点与序列(self):
|
|||
|
|
"""段.文 / 段.武 应与分型序列中对应元素相同。"""
|
|||
|
|
obs = create_observer()
|
|||
|
|
分序 = obs.分型序列
|
|||
|
|
for duan in obs.线段序列:
|
|||
|
|
for nm in ["文", "武"]:
|
|||
|
|
ep = getattr(duan, nm)
|
|||
|
|
if ep is None:
|
|||
|
|
continue
|
|||
|
|
matched = False
|
|||
|
|
for fx in 分序:
|
|||
|
|
if fx.时间戳 == ep.时间戳 and fx.结构 == ep.结构:
|
|||
|
|
assert fx is ep, f"段.{nm} (ts={ep.时间戳}) 与分型序列中元素不匹配"
|
|||
|
|
matched = True
|
|||
|
|
break
|
|||
|
|
assert matched, f"段.{nm} (ts={ep.时间戳}) 在分型序列中未找到"
|
|||
|
|
|
|||
|
|
def test_getter重复调用(self):
|
|||
|
|
"""同一getter调用两次返回同一对象。"""
|
|||
|
|
obs = create_observer()
|
|||
|
|
for bi in obs.笔序列:
|
|||
|
|
文1 = bi.文
|
|||
|
|
文2 = bi.文
|
|||
|
|
assert 文1 is 文2, "笔.文 两次调用返回不同对象"
|
|||
|
|
武1 = bi.武
|
|||
|
|
武2 = bi.武
|
|||
|
|
assert 武1 is 武2, "笔.武 两次调用返回不同对象"
|
|||
|
|
break # 只测第一笔
|
|||
|
|
|
|||
|
|
|
|||
|
|
class Test虚线身份:
|
|||
|
|
"""虚线(笔/线段): 从笔序列、线段序列、中枢内部序列等不同路径访问。"""
|
|||
|
|
|
|||
|
|
def test_笔序列重复获取(self):
|
|||
|
|
obs = create_observer()
|
|||
|
|
seq1 = obs.笔序列
|
|||
|
|
seq2 = obs.笔序列
|
|||
|
|
for i in range(min(len(seq1), 8)):
|
|||
|
|
assert seq1[i] is seq2[i], f"笔序列[{i}] 身份不一致"
|
|||
|
|
|
|||
|
|
def test_线段序列重复获取(self):
|
|||
|
|
obs = create_observer()
|
|||
|
|
seq1 = obs.线段序列
|
|||
|
|
seq2 = obs.线段序列
|
|||
|
|
for i in range(min(len(seq1), 5)):
|
|||
|
|
assert seq1[i] is seq2[i], f"线段序列[{i}] 身份不一致"
|
|||
|
|
|
|||
|
|
def test_多个扩展序列(self):
|
|||
|
|
"""扩展线段的不同序列获取同一虚线应相同。"""
|
|||
|
|
obs = create_observer()
|
|||
|
|
s1 = obs.扩展线段序列
|
|||
|
|
s2 = obs.扩展线段序列_线段
|
|||
|
|
s3 = obs.扩展线段序列_扩展线段
|
|||
|
|
# 这些序列可能包含不同的虚线,但如果同一个 Rc 出现在两个序列中应该相同
|
|||
|
|
for d1 in s1:
|
|||
|
|
for d2 in s2:
|
|||
|
|
if d1.序号 == d2.序号:
|
|||
|
|
assert d1 is d2, f"扩展线段序列[{d1.序号}] 跨序列身份不一致"
|
|||
|
|
break
|
|||
|
|
|
|||
|
|
|
|||
|
|
class TestK线身份:
|
|||
|
|
"""原始K线: 从序列、买卖点、缠K标的等不同路径访问。"""
|
|||
|
|
|
|||
|
|
def test_序列重复获取(self):
|
|||
|
|
obs = create_observer()
|
|||
|
|
seq1 = obs.普通K线序列
|
|||
|
|
seq2 = obs.普通K线序列
|
|||
|
|
for i in range(min(len(seq1), 10)):
|
|||
|
|
assert seq1[i] is seq2[i], f"普K序列[{i}] 身份不一致"
|
|||
|
|
|
|||
|
|
|
|||
|
|
class Test中枢身份:
|
|||
|
|
"""中枢: 从中枢序列、分型关联、笔中枢/线段中枢等不同路径访问。"""
|
|||
|
|
|
|||
|
|
def test_序列重复获取(self):
|
|||
|
|
obs = create_observer(period=3600, n_bars=800)
|
|||
|
|
seq1 = obs.中枢序列
|
|||
|
|
seq2 = obs.中枢序列
|
|||
|
|
for i in range(min(len(seq1), 5)):
|
|||
|
|
assert seq1[i] is seq2[i], f"中枢序列[{i}] 身份不一致"
|
|||
|
|
|
|||
|
|
def test_笔中枢与线段中枢(self):
|
|||
|
|
obs = create_observer(period=3600, n_bars=800)
|
|||
|
|
笔中 = obs.笔_中枢序列
|
|||
|
|
段中 = obs.线段_中枢序列
|
|||
|
|
扩展中 = obs.扩展中枢序列
|
|||
|
|
# 验证同一次获取内的身份
|
|||
|
|
for zs in 笔中:
|
|||
|
|
文1 = zs.文
|
|||
|
|
文2 = zs.文
|
|||
|
|
assert 文1 is 文2, f"笔中枢.文 两次调用不同"
|
|||
|
|
break
|
|||
|
|
for zs in 段中:
|
|||
|
|
文1 = zs.文
|
|||
|
|
文2 = zs.文
|
|||
|
|
assert 文1 is 文2, f"段中枢.文 两次调用不同"
|
|||
|
|
break
|
|||
|
|
|
|||
|
|
|
|||
|
|
class Test整体身份:
|
|||
|
|
"""跨类型综合身份测试。"""
|
|||
|
|
|
|||
|
|
def test_买卖点分型(self):
|
|||
|
|
"""验证买卖点的关联分型身份。"""
|
|||
|
|
obs = create_observer(period=3600, n_bars=800)
|
|||
|
|
# 尝试访问可用的结构
|
|||
|
|
分序 = obs.分型序列
|
|||
|
|
笔序 = obs.笔序列
|
|||
|
|
assert len(分序) >= 0 and len(笔序) >= 0 # 至少不崩溃
|
|||
|
|
|
|||
|
|
def test_全链路一致性(self):
|
|||
|
|
"""缠K → 分型 → 笔 → 段 链路中所有对象身份一致。"""
|
|||
|
|
obs = create_observer()
|
|||
|
|
seq = obs.缠论K线序列
|
|||
|
|
|
|||
|
|
for bi in obs.笔序列:
|
|||
|
|
# 笔的端点分型
|
|||
|
|
for nm, getter in [("文", lambda b=bi: b.文), ("武", lambda b=bi: b.武)]:
|
|||
|
|
ep = getter()
|
|||
|
|
if ep is None:
|
|||
|
|
continue
|
|||
|
|
# ep 中的 中 是一根缠K,应能在序列中找到相同对象
|
|||
|
|
中 = ep.中
|
|||
|
|
for ck in seq:
|
|||
|
|
if ck.时间戳 == 中.时间戳:
|
|||
|
|
assert ck is 中
|
|||
|
|
break
|
|||
|
|
# 左也应该是可访问的
|
|||
|
|
左 = ep.左
|
|||
|
|
if 左 is not None:
|
|||
|
|
for ck in seq:
|
|||
|
|
if ck.时间戳 == 左.时间戳:
|
|||
|
|
assert ck is 左
|
|||
|
|
break
|
|||
|
|
# 右也应该是可访问的
|
|||
|
|
右 = ep.右
|
|||
|
|
if 右 is not None:
|
|||
|
|
for ck in seq:
|
|||
|
|
if ck.时间戳 == 右.时间戳:
|
|||
|
|
assert ck is 右
|
|||
|
|
break
|