添加动能理论chanmacd,顶底分型5隐形形态

This commit is contained in:
jackyu66git
2025-08-19 16:35:56 +08:00
parent b15ba06a00
commit 29e98d6eed
8 changed files with 128 additions and 24 deletions
+6 -8
View File
@@ -1,7 +1,7 @@
import copy
from typing import Dict, Optional
from ChanEnum import Chan_FX_TYPE, Chan_KLINE_DIR, Chan_BI_DIR, Chan_KLC_FX, Chan_K_DIR
from ChanEnum import Chan_FX_TYPE, Chan_KLINE_DIR, Chan_BI_DIR, Chan_KLC_FX, Chan_K_DIR, Chan_MACD_STATE
import ChanKLU
import ChanCTime
@@ -78,17 +78,15 @@ class ChanKLC():
self.klc_fx_type = Chan_KLC_FX.BOTTOM4
if self.fx == Chan_FX_TYPE.TOP:
#print(self.end_time, self.fx, self.macd, self.macdhist, len(self.klus))
if self.macd > 0:
self.bb_out = True
if self.macdhist < 0:
if self.macdhist < 0 and self.macd > 0 and self.macd > self.pre.macd and self.macd > self.next.macd:
self.klc_fx_type = Chan_KLC_FX.TOP5
self.bb_out = True
else:
if self.fx == Chan_FX_TYPE.BOTTOM:
if self.macd < 0:
self.bb_out = True
if self.macdhist > 0:
if self.macdhist > 0 and self.macd < 0 and self.macd < self.pre.macd and self.macd < self.next.macd:
self.klc_fx_type = Chan_KLC_FX.BOTTOM5
#self.bb_out = True
self.bb_out = True
def cal_macd_state(self, dir):
macd_state = 0
return macd_state
+3
View File
@@ -17,6 +17,7 @@ from technical.util import resample_to_interval
from decimal import Decimal
import xgboost as xgb
import numpy as np
from ChanMACD import ChanMACD
class ChanLun():
timeframes = ["5m", "15m", "30m", "60m", "4h"]
@@ -1155,6 +1156,8 @@ class ChanLun():
klu_list = self.get_klu_list(dataframe)
klc_list = []
last_klu = None
macd = ChanMACD(klu_list)
klu_list = macd.cal_macd()
for klu in klu_list:
if len(klc_list) > 0:
last_klc = klc_list[-1]
+3 -1
View File
@@ -16,6 +16,7 @@ class ChanMACD():
self.return_zero_list = [] # 归零轴列表
self.cross0_up_list = [] # 向上穿越零轴列表
self.cross0_down_list = [] # 向下穿越零轴列表
# 计算段 / UnitTF / HistSet 及状态标记
self.cal_macd()
def cal_macd(self):
last_seg = None
@@ -139,4 +140,5 @@ class ChanMACD():
last_seg.add_histset(histset)
if last_unittf:
last_unittf.add_histset(histset)
last_klu = klu
last_klu = klu
return self.klu_list
+16 -1
View File
@@ -1,4 +1,4 @@
from ChanEnum import Chan_MACDHISTSET_DIR
class ChanMACDHistSet():
def __init__(self, start_time, start_klu, pre_histset, dir):
@@ -10,11 +10,26 @@ class ChanMACDHistSet():
self.histset_dir = dir
self.next = None
self.pre = pre_histset
self.high_klu = start_klu
self.low_klu = start_klu
def set_next(self, next_histset):
self.next = next_histset
def add_klu(self, klu):
self.klu_list.append(klu)
klu.set_histset(self)
if self.histset_dir == Chan_MACDHISTSET_DIR.ABOVE:
if klu.macdhist > self.high_klu.macdhist:
self.high_klu = klu
else:
if klu.macdhist < self.low_klu.macdhist:
self.low_klu = klu
else:
if klu.macdhist < self.high_klu.macdhist:
self.high_klu = klu
else:
if klu.macdhist > self.low_klu.macdhist:
self.low_klu = klu
def set_end_klu(self, end_klu):
self.add_klu(end_klu)
self.end_klu = end_klu
self.end_time = end_klu.time
+15 -5
View File
@@ -14,7 +14,9 @@ class ChanMACDSeg():
self.seg_dir = seg_dir
self.pre = pre_seg
self.next = None
self.peak_klu = start_klu
self.high_klu = start_klu
self.low_klu = start_klu
self.ref_klu = None
def set_next(self, next_seg):
self.next = next_seg
def add_klu(self, klu):
@@ -22,11 +24,19 @@ class ChanMACDSeg():
self.klu_list.append(klu)
klu.set_seg(self)
if self.seg_dir == Chan_MACDSEG_DIR.ABOVE:
if klu.high > self.peak_klu.high:
self.peak_klu = klu
if klu.macdhist > self.high_klu.macdhist:
self.high_klu = klu
else:
if klu.macdhist < self.low_klu.macdhist:
self.low_klu = klu
else:
if klu.low < self.peak_klu.low:
self.peak_klu = klu
if klu.macdhist < self.high_klu.macdhist:
self.high_klu = klu
else:
if klu.macdhist > self.low_klu.macdhist:
self.low_klu = klu
if self.high_klu.index != self.start_klu.index:
self.ref_klu = self.high_klu
def add_unittf(self, unittf):
self.unittf_list.append(unittf)
unittf.set_next(self)
+15 -4
View File
@@ -1,4 +1,4 @@
from ChanEnum import Chan_MACD_STATE
from ChanEnum import Chan_MACD_STATE, Chan_MACDUNITTF_DIR
class ChanMACDUnitTF():
@@ -15,8 +15,9 @@ class ChanMACDUnitTF():
self.unittf_dir = dir
self.start_type = start_type
self.end_type = None
self.peak_hist = start_klu.macdhist
self.near0_count = 1
self.high_klu = start_klu
self.low_klu = start_klu
def set_next(self, next_unittf):
self.next = next_unittf
def add_histset(self, histset):
@@ -26,8 +27,18 @@ class ChanMACDUnitTF():
if klu:
self.klu_list.append(klu)
klu.set_unittf(self)
if klu.macdhist > self.peak_hist:
self.peak_hist = klu.macdhist
if self.unittf_dir == Chan_MACDUNITTF_DIR.ABOVE:
if klu.macdhist > self.high_klu.macdhist:
self.high_klu = klu
else:
if klu.macdhist < self.low_klu.macdhist:
self.low_klu = klu
else:
if klu.macdhist < self.high_klu.macdhist:
self.high_klu = klu
else:
if klu.macdhist > self.low_klu.macdhist:
self.low_klu = klu
if klu.macd_state == Chan_MACD_STATE.NEAR0 and len(self.histset_list) > 1:
#print(self.start_time, klu.time, self.near0_count)
self.near0_count += 1
+1 -1
View File
@@ -138,7 +138,7 @@ MACD黄白线在零轴之下第一个单位调整周期 + 分立跳空背离(
如果在单位调整周期内发生了跳空的形态,当跳空能量堆的高度高于前一个能量堆的高度,此时的跳空则为非背离跳空。非背离跳空可以理解为单位周期的单边行情,前一个能量堆失效,以新的最高的能量堆作为后续行情的1号参考点。不管是连续跳空还是分立跳空都遵守这个法则。
单位调整周期之间的背离
单位调整周期之间的背离,是指两个或者两个以上的单位调整周期相比较而建立的关系,相比较的周期必须在同一个线段周期内,不可跨线段比较。当K线在某个时间级别的线段中,MACD经过了一个单位调整周期的运行,黄白线在此回零轴后,由于零轴的支撑反弹或压力反抽的作用,因此出现第二个单位调整周期,当第二个单位调整周期的黄白线这里特指白线离开零轴的距离小于第一个单位调整周期黄白线离开零轴的距离时,单位调整周期之间就产生了相悖的关系,即价格穿新高或新低,而白线能量出现了减弱或增强,这种背离称为单位调整周期之间的背离,单位调整周期之间的背离也叫区间背离。
单位调整周期之间的背离,是指两个或者两个以上的单位调整周期相比较而建立的关系,相比较的周期必须在同一个线段周期内,不可跨线段比较。当K线在某个时间级别的线段中,MACD经过了一个单位调整周期的运行,黄白线在此回零轴后,由于零轴的支撑反弹或压力反抽的作用,因此出现第二个单位调整周期,当第二个单位调整周期的黄白线这里特指白线离开零轴的距离小于第一个单位调整周期黄白线离开零轴的距离时,单位调整周期之间就产生了相悖的关系,即价格穿新高或新低,而白线能量出现了减弱或增强,这种背离称为单位调整周期之间的背离,单位调整周期之间的背离也叫区间背离。
上涨线段单位调整周期之间的顶背离为卖点(前提:长级别MACD在高位)
下跌线段单位调整周期之间的底背离为买点(前提:长级别MACD在高位)
+69 -4
View File
@@ -919,7 +919,7 @@
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="showMacd" checked>
<label class="form-check-label" for="showMacd">MACD</label>
<label class="form-check-label" for="showMacd">ChanMACD</label>
</div>
</div>
<div class="d-flex align-items-center mt-2">
@@ -2160,6 +2160,9 @@
if (tvWidget.macdChart) {
tvWidget.macdChart.timeScale().setVisibleLogicalRange(logicalRange);
}
if (tvWidget.chanMacdChart) {
tvWidget.chanMacdChart.timeScale().setVisibleLogicalRange(logicalRange);
}
} else if (visibleRange) {
// 如果没有逻辑范围,使用时间戳范围
tvWidget.mainChart.timeScale().setVisibleRange(visibleRange);
@@ -2477,6 +2480,16 @@
chanMacdChartContainer.style.right = '0';
chanMacdChartContainer.style.borderTop = '1px solid #e0e0e0';
chanMacdChartContainer.style.zIndex = '10';
// 水印:便于区分是新的 ChanMACD 子图
const chanMacdWatermark = document.createElement('div');
chanMacdWatermark.textContent = 'ChanMACD';
chanMacdWatermark.style.position = 'absolute';
chanMacdWatermark.style.top = '4px';
chanMacdWatermark.style.left = '8px';
chanMacdWatermark.style.fontSize = '11px';
chanMacdWatermark.style.color = '#888';
chanMacdWatermark.style.pointerEvents = 'none';
chanMacdChartContainer.appendChild(chanMacdWatermark);
// 成交量位于 ChanMACD 之下
volumeChartContainer.style.top = '70%';
@@ -2887,11 +2900,12 @@
isArray: Array.isArray(currentData.kline_data)
});
if (showMacd && chanMacdChart && currentData.macd && currentData.kline_data && Array.isArray(currentData.kline_data)) {
console.log('✅ 开始创建 ChanMACD 系列');
// 创建ChanMACD线系列
const chanMacdLineSeries = chanMacdChart.addLineSeries({
color: '#2962FF',
lineWidth: 1,
title: 'MACD',
title: 'ChanMACD',
lastValueVisible: false,
priceLineVisible: false,
});
@@ -2900,7 +2914,7 @@
const chanMacdSignalSeries = chanMacdChart.addLineSeries({
color: '#FF6B6B',
lineWidth: 1,
title: 'Signal',
title: 'ChanSignal',
lastValueVisible: false,
priceLineVisible: false,
});
@@ -2908,7 +2922,7 @@
// 创建ChanMACD柱状图系列
const chanMacdHistSeries = chanMacdChart.addHistogramSeries({
color: '#26a69a',
title: 'Histogram',
title: 'ChanHistogram',
priceFormat: {
type: 'price',
precision: 4,
@@ -5477,6 +5491,9 @@
if (showMacd && macdChart) {
macdChart.timeScale().setVisibleRange(visibleRange);
}
if (showMacd && chanMacdChart) {
chanMacdChart.timeScale().setVisibleRange(visibleRange);
}
console.log('🔧 最终时间轴对齐完成');
}
@@ -5676,6 +5693,53 @@
tvWidget.series.histogramSeries.setData(histogramData);
}
// 更新 ChanMACD 数据与自定义标注
if (tvWidget.series.chanMacdLineSeries && currentData.macd && currentData.kline_data && Array.isArray(currentData.kline_data)) {
const klineDataSource = useElementPeriod ? currentData.element_kline_data : currentData.kline_data;
const macdDataSource = useElementPeriod ? (currentData.element_macd || currentData.macd) : currentData.macd;
if (macdDataSource && macdDataSource.macd && macdDataSource.signal && macdDataSource.histogram) {
const chanMacdData = [];
const chanSignalData = [];
const chanHistData = [];
for (let i = 0; i < klineDataSource.length; i++) {
const kline = klineDataSource[i];
if (kline && kline.date && i < macdDataSource.macd.length && macdDataSource.macd[i] !== null && macdDataSource.macd[i] !== undefined) {
const timestamp = Math.floor(new Date(kline.date).getTime() / 1000);
chanMacdData.push({ time: timestamp, value: macdDataSource.macd[i] });
chanSignalData.push({ time: timestamp, value: macdDataSource.signal[i] });
chanHistData.push({ time: timestamp, value: macdDataSource.histogram[i], color: macdDataSource.histogram[i] >= 0 ? 'rgba(220, 53, 69, 0.5)' : 'rgba(40, 167, 69, 0.5)' });
}
}
if (chanMacdData.length > 0) {
tvWidget.series.chanMacdLineSeries.setData(chanMacdData);
tvWidget.series.chanMacdSignalSeries.setData(chanSignalData);
tvWidget.series.chanMacdHistSeries.setData(chanHistData);
}
}
// 重新应用自定义标注(段/UnitTF/HistSet/状态点)
try {
if (typeof clearChanMacdMarkers === 'function') clearChanMacdMarkers();
if (currentData.chan_macd) {
addAllChanMacdMarkers(
currentData.chan_macd.seg_list || [],
currentData.chan_macd.unittf_list || [],
currentData.chan_macd.histset_list || [],
{
high_position_list: currentData.chan_macd.high_position_list || [],
high_empty_list: currentData.chan_macd.high_empty_list || [],
low_position_list: currentData.chan_macd.low_position_list || [],
low_empty_list: currentData.chan_macd.low_empty_list || [],
return_zero_list: currentData.chan_macd.return_zero_list || [],
cross0_up_list: currentData.chan_macd.cross0_up_list || [],
cross0_down_list: currentData.chan_macd.cross0_down_list || []
}
);
}
} catch (e) {
console.warn('更新ChanMACD标注失败:', e);
}
}
// 重新显示笔、线段和中枢等图形
redrawFractalElements();
@@ -7191,6 +7255,7 @@
if (tvWidget.volumeChart) tvWidget.volumeChart.timeScale().setVisibleLogicalRange(logicalRange);
if (tvWidget.atrChart) tvWidget.atrChart.timeScale().setVisibleLogicalRange(logicalRange);
if (tvWidget.macdChart) tvWidget.macdChart.timeScale().setVisibleLogicalRange(logicalRange);
if (tvWidget.chanMacdChart) tvWidget.chanMacdChart.timeScale().setVisibleLogicalRange(logicalRange);
} else if (visibleRange) {
tvWidget.mainChart.timeScale().setVisibleRange(visibleRange);
if (tvWidget.volumeChart) tvWidget.volumeChart.timeScale().setVisibleRange(visibleRange);