From 154462d3f83dc30d01d07a088a7254bdeeba9d78 Mon Sep 17 00:00:00 2001 From: jackyu66git Date: Sat, 6 Sep 2025 01:01:51 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0macd=E5=92=8Csignal=E5=BD=A2?= =?UTF-8?q?=E6=80=81=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ChanKLC.py | 2 +- ChanKLU.py | 108 ++++++++++++++++++++++++++++++++++----------- ChanMACD.py | 4 +- ChanMACDHistSet.py | 22 ++++----- ChanMACDUnitTF.py | 11 ++--- 5 files changed, 104 insertions(+), 43 deletions(-) diff --git a/ChanKLC.py b/ChanKLC.py index 655799a..7f78429 100644 --- a/ChanKLC.py +++ b/ChanKLC.py @@ -42,7 +42,7 @@ class ChanKLC(): self.strength = klu.strength self.last_top_klc = None self.last_bottom_klc = None - self.bb_out = False + self.bb_out = True self.macd = 0 self.signal = 0 def set_last_top_klu(self, last_top_klc): diff --git a/ChanKLU.py b/ChanKLU.py index f3b7ecf..b33217b 100644 --- a/ChanKLU.py +++ b/ChanKLU.py @@ -641,39 +641,67 @@ class ChanKLU: #self.cal_macd_state() def cal_macd_state(self): # 按定义精简实现:优先级 CROSS0 > 位置(HIGH/HE/RETURN_ZERO) > NEAR0 > UNKNOWN - # 1) 首条或缺前一根 + # 首条或缺前一根 if not hasattr(self, 'pre') or self.pre is None: self.macd_state = Chan_MACD_STATE.START return self.macd_state - # 2) 基本校验 - if self.macd == 0 and self.signal == 0 and self.macdhist == 0: + # 基本校验 + if (self.macd == 0 and self.signal == 0 and self.macdhist == 0) or self.ema52 == 0: self.macd_state = Chan_MACD_STATE.UNKNOWN return self.macd_state - # 3) CROSS0 仅以 Signal 穿越零轴判定 + # CROSS0 仅以 Signal 穿越零轴判定 if self.pre.signal >= 0 and self.signal < 0: self.macd_state = Chan_MACD_STATE.CROSS0_DOWN return self.macd_state if self.pre.signal <= 0 and self.signal > 0: self.macd_state = Chan_MACD_STATE.CROSS0_UP return self.macd_state - # 6) 趋近零轴:任一线接近零轴即可,无限接近零轴,EMA52没有加 + # 穿零轴后的形态:缠绕/倒挂(基于前一状态为CROSS0_*) + if self.pre.macd_state == Chan_MACD_STATE.CROSS0_UP or self.pre.macd_state == Chan_MACD_STATE.CROSS0_DOWN: + direction = 1 if self.pre.macd_state == Chan_MACD_STATE.CROSS0_UP else -1 + hist_same_dir = (self.macdhist * direction) > 0 + hist_decreasing = abs(self.macdhist) < abs(self.pre.macdhist) + lines_tight = abs(self.macd - self.signal) <= 12 + # 倒挂:能量柱衰减且黄白线相对方向不利/出现反向能量释放 + if hist_decreasing and (((self.macd - self.signal) * direction) < 0 or not hist_same_dir): + self.macd_state = Chan_MACD_STATE.CROSS_REV + return self.macd_state + # 缠绕/粘合:紧贴能量柱运行,无反向能量释放 + if lines_tight and hist_same_dir: + self.macd_state = Chan_MACD_STATE.CROSS_OS + return self.macd_state + # 趋近零轴:细化 NEAR0_* 判定 NEAR0_EPS = 15 - if abs(self.macd) <= NEAR0_EPS or abs(self.signal) <= NEAR0_EPS: + lines_near_zero = abs(self.macd) <= NEAR0_EPS or abs(self.signal) <= NEAR0_EPS + touch_52 = (self.ema52 != 0) and ((abs(self.close - self.ema52) <= NEAR0_EPS) or (self.low <= self.ema52 <= self.high)) + touch_24 = (self.ema24 != 0) and ((abs(self.close - self.ema24) <= NEAR0_EPS) or (self.low <= self.ema24 <= self.high)) + # 完美形态:白线接近零轴 + 价格触碰/轻破EMA52 + 黄线不穿零轴 + if abs(self.macd) <= NEAR0_EPS and touch_52 and (not (self.pre.signal >= 0 and self.signal < 0)) and (not (self.pre.signal <= 0 and self.signal > 0)): + self.macd_state = Chan_MACD_STATE.NEAR0_PERFECT + return self.macd_state + # EMA24 附近 + if lines_near_zero and touch_24: + self.macd_state = Chan_MACD_STATE.NEAR0_24 + return self.macd_state + # EMA52 附近 + if lines_near_zero and touch_52: + self.macd_state = Chan_MACD_STATE.NEAR0_52 + return self.macd_state + # 白线接近零轴但价格未至EMA52 + if abs(self.macd) <= NEAR0_EPS and not touch_52: + self.macd_state = Chan_MACD_STATE.NEAR0_DIFF + return self.macd_state + # 一般近零轴 + if lines_near_zero or touch_52: self.macd_state = Chan_MACD_STATE.NEAR0 return self.macd_state - if self.macd > 0 and abs(self.low - self.ema52) <= NEAR0_EPS: - self.macd_state = Chan_MACD_STATE.NEAR0 - return self.macd_state - if self.macd < 0 and abs(self.high - self.ema52) <= NEAR0_EPS: - self.macd_state = Chan_MACD_STATE.NEAR0 - return self.macd_state - # 4) 穿零轴后离开零轴 - if self.pre.macd_state == Chan_MACD_STATE.CROSS0_UP and self.macd > self.pre.macd and self.signal < self.macdhist: + # 穿零轴后离开零轴 + if self.pre.macd_state == Chan_MACD_STATE.CROSS0_UP and ((self.macd >= self.pre.macd and self.signal >= self.pre.signal) or (abs(self.macdhist) >= abs(self.pre.macdhist))): self.macd_state = Chan_MACD_STATE.UP return self.macd_state - if self.pre.macd_state == Chan_MACD_STATE.CROSS0_DOWN and self.macd < self.pre.macd and self.signal > self.macdhist: + if self.pre.macd_state == Chan_MACD_STATE.CROSS0_DOWN and ((self.macd <= self.pre.macd and self.signal <= self.pre.signal) or (abs(self.macdhist) >= abs(self.pre.macdhist))): self.macd_state = Chan_MACD_STATE.DOWN return self.macd_state if self.pre.macd_state == Chan_MACD_STATE.UP and self.macd > self.pre.macd and self.signal > self.pre.signal: @@ -682,8 +710,24 @@ class ChanKLU: if self.pre.macd_state == Chan_MACD_STATE.DOWN and self.macd < self.pre.macd and self.signal < self.pre.signal: self.macd_state = Chan_MACD_STATE.DOWN return self.macd_state - # 5) 高位状态的位置状态, 高位,高位空,归零轴 - if (self.pre.macd_state == Chan_MACD_STATE.UP or self.pre.macd_state == Chan_MACD_STATE.HIGH or self.pre.macd_state == Chan_MACD_STATE.RZ_UP) and self.macd > 0: + # 趋势兜底:强势同步上行/下行直接进入 UP/DOWN + if self.macd > 0 and self.signal > 0 and (self.macd >= self.pre.macd and self.signal >= self.pre.signal): + self.macd_state = Chan_MACD_STATE.UP + return self.macd_state + if self.macd < 0 and self.signal < 0 and (self.macd <= self.pre.macd and self.signal <= self.pre.signal): + self.macd_state = Chan_MACD_STATE.DOWN + return self.macd_state + # 峰值:白线高位出现局部顶 + if hasattr(self.pre, 'pre') and self.pre and self.pre.pre and self.macd > 0: + if self.pre.macd > self.pre.pre.macd and self.pre.macd > self.macd: + self.macd_state = Chan_MACD_STATE.PEAK + return self.macd_state + # 高位状态的位置状态, 高位,高位空,归零轴 + if (self.pre.macd_state == Chan_MACD_STATE.UP or self.pre.macd_state == Chan_MACD_STATE.HIGH or self.pre.macd_state == Chan_MACD_STATE.RZ_UP or self.pre.macd_state == Chan_MACD_STATE.PEAK or self.pre.macd_state == Chan_MACD_STATE.HIGH_EMPTY) and self.macd > 0: + # 高位空(正区间):能量柱衰减且黄白线间距较大 + if abs(self.pre.macdhist) > 0 and abs(self.macdhist) < abs(self.pre.macdhist) and abs(self.macd - self.signal) > 5: + self.macd_state = Chan_MACD_STATE.HIGH_EMPTY + return self.macd_state if abs(self.pre.macd - self.macd) < 10: self.macd_state = Chan_MACD_STATE.HIGH return self.macd_state @@ -694,7 +738,11 @@ class ChanKLU: elif self.macd < self.pre.macd and self.signal < self.pre.signal: self.macd_state = Chan_MACD_STATE.RETURN_ZERO return self.macd_state - if (self.pre.macd_state == Chan_MACD_STATE.DOWN or self.pre.macd_state == Chan_MACD_STATE.HIGH or self.pre.macd_state == Chan_MACD_STATE.RZ_DOWN) and self.macd < 0: + if (self.pre.macd_state == Chan_MACD_STATE.DOWN or self.pre.macd_state == Chan_MACD_STATE.HIGH or self.pre.macd_state == Chan_MACD_STATE.RZ_DOWN or self.pre.macd_state == Chan_MACD_STATE.PEAK or self.pre.macd_state == Chan_MACD_STATE.HIGH_EMPTY) and self.macd < 0: + # 高位空(负区间):能量柱衰减且黄白线间距较大 + if abs(self.pre.macdhist) > 0 and abs(self.macdhist) < abs(self.pre.macdhist) and abs(self.macd - self.signal) > 5: + self.macd_state = Chan_MACD_STATE.HIGH_EMPTY + return self.macd_state if abs(self.pre.macd - self.macd) < 10: self.macd_state = Chan_MACD_STATE.HIGH return self.macd_state @@ -706,26 +754,36 @@ class ChanKLU: self.macd_state = Chan_MACD_STATE.DOWN return self.macd_state - # 7) 离开0轴开始上涨或者下跌阶段,高位之前的 + # 离开0轴开始上涨或者下跌阶段,高位之前的 if self.macd > 0 and self.pre: - if (self.pre.macd_state == Chan_MACD_STATE.NEAR0 or self.pre.macd_state == Chan_MACD_STATE.RZ_UP) and (self.signal > self.pre.signal or self.close > self.ema52): + if (self.pre.macd_state == Chan_MACD_STATE.NEAR0 or self.pre.macd_state == Chan_MACD_STATE.RZ_UP or self.pre.macd_state == Chan_MACD_STATE.CROSS0_UP) and (self.signal > self.pre.signal or self.close > self.ema52): self.macd_state = Chan_MACD_STATE.RZ_UP return self.macd_state elif self.macd < 0 and self.pre: - if (self.pre.macd_state == Chan_MACD_STATE.NEAR0 or self.pre.macd_state == Chan_MACD_STATE.RZ_DOWN) and (self.signal < self.pre.signal or self.close < self.ema52): + if (self.pre.macd_state == Chan_MACD_STATE.NEAR0 or self.pre.macd_state == Chan_MACD_STATE.RZ_DOWN or self.pre.macd_state == Chan_MACD_STATE.CROSS0_DOWN) and (self.signal < self.pre.signal or self.close < self.ema52): self.macd_state = Chan_MACD_STATE.RZ_DOWN return self.macd_state - # 8) 归零轴走势 + # 归零轴走势 if self.pre.macd_state == Chan_MACD_STATE.RETURN_ZERO: if self.macd > 0: - if self.pre.macd > self.macd: + if self.pre.macd > self.macd or abs(self.macdhist) <= abs(self.pre.macdhist) or self.signal <= self.pre.signal: self.macd_state = Chan_MACD_STATE.RETURN_ZERO return self.macd_state else: - if self.pre.macd < self.macd: + if self.pre.macd < self.macd or abs(self.macdhist) <= abs(self.pre.macdhist) or self.signal >= self.pre.signal: self.macd_state = Chan_MACD_STATE.RETURN_ZERO return self.macd_state - # 9) 其余情况 + # 从 NEAR0 收敛到零轴的归零轴承接(正负两侧) + if self.pre.macd_state == Chan_MACD_STATE.NEAR0: + # 正区间朝零轴收敛 + if self.macd > 0 and self.pre.macd > 0 and self.macd <= self.pre.macd and self.signal <= self.pre.signal: + self.macd_state = Chan_MACD_STATE.RETURN_ZERO + return self.macd_state + # 负区间朝零轴收敛 + if self.macd < 0 and self.pre.macd < 0 and self.macd >= self.pre.macd and self.signal >= self.pre.signal: + self.macd_state = Chan_MACD_STATE.RETURN_ZERO + return self.macd_state + # 其余情况 if self.pre.macd_state == Chan_MACD_STATE.UNKNOWN: if self.macd > 0 and self.close > self.ema52 and self.pre.pre and (self.pre.pre.macd_state == Chan_MACD_STATE.UP or self.pre.pre.macd_state == Chan_MACD_STATE.RZ_UP): self.macd_state = self.pre.pre.macd_state diff --git a/ChanMACD.py b/ChanMACD.py index e6b7d8d..dfe8b0a 100644 --- a/ChanMACD.py +++ b/ChanMACD.py @@ -128,7 +128,9 @@ class ChanMACD(): if not last_unittf.is_end: last_unittf.add_klu(klu) last_seg.add_klu(klu) - last_klu = klu + last_klu = klu + klu.cal_macd_state() + print(klu.time, klu.macd_state, klu.continue_div, klu.separate_div, klu.macd, klu.signal, klu.macdhist, klu.ema24, klu.ema52, klu.close) if last_histset: last_histset.set_end_klu(last_klu) if last_unittf: diff --git a/ChanMACDHistSet.py b/ChanMACDHistSet.py index b85dc75..9a760a4 100644 --- a/ChanMACDHistSet.py +++ b/ChanMACDHistSet.py @@ -18,18 +18,22 @@ class ChanMACDHistSet(): self.last_klu = start_klu self.start_klu = start_klu self.peak_div_list = [] + self.middle_area = 0 def set_next(self, next_histset): self.next = next_histset def set_pre(self, pre_histset): self.pre = pre_histset def set_middle_klu(self, middle_klu): self.middle_klu = middle_klu + self.middle_area = abs(middle_klu.macdhist) def set_unittf_div(self, unittf_div): self.unittf_div = unittf_div def add_klu(self, klu): klu.set_histset(self) self.klu_list.append(klu) - self.area += klu.macdhist + self.area += abs(klu.macdhist) + if self.middle_klu: + self.middle_area += abs(klu.macdhist) if self.middle_klu and self.middle_klu.index + 1 == klu.index: self.low_klu = None self.peak_klu = None @@ -52,10 +56,6 @@ class ChanMACDHistSet(): else: self.peak_div_list.append(klu.pre) self.div_count += 1 - if klu.macd > 0 and self.histset_dir == Chan_MACDHISTSET_DIR.ABOVE: - klu.pre.continue_div = True - elif klu.macd < 0 and self.histset_dir == Chan_MACDHISTSET_DIR.UNDER: - klu.pre.continue_div = True else: self.peak_klu = klu.pre else: @@ -70,15 +70,15 @@ class ChanMACDHistSet(): else: self.peak_div_list.append(klu.pre) self.div_count += 1 - if klu.macd > 0 and self.histset_dir == Chan_MACDHISTSET_DIR.ABOVE: - klu.pre.continue_div = True - elif klu.macd < 0 and self.histset_dir == Chan_MACDHISTSET_DIR.UNDER: - klu.pre.continue_div = True else: self.peak_klu = klu.pre def set_end_klu(self, end_klu): self.end_klu = end_klu self.end_time = end_klu.time + if len(self.peak_div_list) > 0: + klu = self.peak_div_list[-1] + if klu.macd * klu.macdhist > 0: + klu.continue_div = True if self.start_klu.index == end_klu.index: self.peak_klu = self.start_klu if self.start_klu.index + 1 == end_klu.index: @@ -95,6 +95,6 @@ class ChanMACDHistSet(): peak_str = "" for peak_div in self.peak_div_list: peak_str += f"{peak_div.time}, " - if self.peak_klu and len(self.peak_div_list) > 0: - print("Continue Div: ",self.start_time, "Peak:", self.peak_klu.time, "Div: ", peak_str, self.histset_dir, self.div_count) + #if self.peak_klu and len(self.peak_div_list) > 0: + #print("Continue Div: ",self.start_time, "Peak:", self.peak_klu.time, "Div: ", peak_str, self.histset_dir, self.div_count) \ No newline at end of file diff --git a/ChanMACDUnitTF.py b/ChanMACDUnitTF.py index 5602519..3277927 100644 --- a/ChanMACDUnitTF.py +++ b/ChanMACDUnitTF.py @@ -43,11 +43,12 @@ class ChanMACDUnitTF(): self.div_type = self.histset_list[0].unittf_div self.peak_klu = self.histset_list[0].peak_klu else: + for index in range(0, len(self.histset_list)): histset = self.histset_list[index] if self.same_dir(histset): - if histset.peak_klu: - print("Unittf: ", self.start_klu.time, len(self.histset_list), histset.peak_klu.time) + #if histset.peak_klu: + #print("Unittf: ", self.start_klu.time, len(self.histset_list), histset.peak_klu.time) if self.peak_klu: if histset.peak_klu and histset.end_time: if abs(histset.peak_klu.macdhist) >= abs(self.peak_klu.macdhist): @@ -58,7 +59,7 @@ class ChanMACDUnitTF(): self.div_type = Chan_MACDUNITTF_DIV.DISCRETE self.div_count += 1 self.div_peak_list.append(histset.peak_klu) - print("Unittf: ", self.start_klu.time) + #print("Unittf: ", self.start_klu.time) if histset.peak_klu.macd > 0 and histset.histset_dir == Chan_MACDHISTSET_DIR.ABOVE: histset.peak_klu.separate_div = True elif histset.peak_klu.macd < 0 and histset.histset_dir == Chan_MACDHISTSET_DIR.UNDER: @@ -134,7 +135,7 @@ class ChanMACDUnitTF(): for histset in self.histset_list: histset_time += f"{histset.start_time}, " - if self.peak_klu and self.div_type == Chan_MACDUNITTF_DIV.DISCRETE: - print("Cross Div: ", self.start_klu.time, self.peak_klu.time, self.div_count, self.div_type, self.unittf_dir, div_time, len(self.histset_list)) + #if self.peak_klu and self.div_type == Chan_MACDUNITTF_DIV.DISCRETE: + #print("Cross Div: ", self.start_klu.time, self.peak_klu.time, self.div_count, self.div_type, self.unittf_dir, div_time, len(self.histset_list)) \ No newline at end of file