From 0b9f63409ce8b1268119277ffb30397600539f9a Mon Sep 17 00:00:00 2001 From: Porter Date: Mon, 15 Sep 2025 10:29:53 +0800 Subject: [PATCH] =?UTF-8?q?=E6=A0=B9=E6=8D=AE=E6=96=B0=E7=9A=84=E6=9D=A1?= =?UTF-8?q?=E4=BB=B6=E8=BF=9B=E8=A1=8C=E4=BA=A4=E6=98=93=EF=BC=8C=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E4=BA=A4=E6=98=93=E8=A7=84=E5=88=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + ChanKLC.py | 4 +- ChanKLU.py | 29 +++++-- ChanLun.py | 4 +- ChanMACDHistSet.py | 2 +- strategies/ChanLun_BTC_30.py | 34 ++++---- web/templates/index.html | 146 ++++++++++++++++++++++++----------- 交易记录/交易规则.docx | Bin 0 -> 12767 bytes 8 files changed, 149 insertions(+), 71 deletions(-) create mode 100644 交易记录/交易规则.docx diff --git a/.gitignore b/.gitignore index 6c9fc8d..0937f1f 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,4 @@ feature_meta *.sqlite-shm *.sqlite-wal .DS_Store +交易记录/~$交易规则.docx diff --git a/ChanKLC.py b/ChanKLC.py index 020afc1..7f78429 100644 --- a/ChanKLC.py +++ b/ChanKLC.py @@ -79,12 +79,12 @@ class ChanKLC(): if self.fx == Chan_FX_TYPE.TOP: #print(self.end_time, self.fx, self.macd, self.macdhist, len(self.klus)) if self.macdhist < 0 and self.macd > 0: - #self.klc_fx_type = Chan_KLC_FX.TOP5 + self.klc_fx_type = Chan_KLC_FX.TOP5 self.bb_out = True else: if self.fx == Chan_FX_TYPE.BOTTOM: if self.macdhist > 0 and self.macd < 0: - #self.klc_fx_type = Chan_KLC_FX.BOTTOM5 + self.klc_fx_type = Chan_KLC_FX.BOTTOM5 self.bb_out = True def cal_macd_state(self, dir): diff --git a/ChanKLU.py b/ChanKLU.py index 52296e3..fd52f6a 100644 --- a/ChanKLU.py +++ b/ChanKLU.py @@ -651,12 +651,31 @@ class ChanKLU: 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 - if self.macd > 0: - if self.macd < self.signal and self.close > self.ema52 and self.low < self.ema52: - self.near0_return = 0 + # 归零轴判断 + if self.signal > 0: + if self.macd < self.signal: + if 0 < self.low - self.ema52 < 100: + self.near0_return = 6 + elif self.close > self.ema52 and self.low < self.ema52 and self.open > self.ema52: + self.near0_return = 7 + elif self.close < self.ema52 and self.open > self.ema52 and self.high > self.ema52 and self.low < self.ema52: + self.near0_return = 8 + elif self.close < self.ema52 and self.open < self.ema52 and self.high > self.ema52 and self.low < self.ema52: + self.near0_return = 9 + elif self.close > self.ema52 and self.open > self.ema52 and self.high > self.ema52 and self.low < self.ema52: + self.near0_return = 10 else: - if self.macd > self.signal and self.close < self.ema52 and self.high > self.ema52: - self.near0_return = 0 + if self.macd > self.signal: + if 0 < self.ema52 - self.high < 100: + self.near0_return = 61 + elif self.close < self.ema52 and self.high > self.ema52 and self.open < self.ema52: + self.near0_return = 71 + elif self.close < self.ema52 and self.open > self.ema52 and self.high > self.ema52 and self.low < self.ema52: + self.near0_return = 81 + elif self.close > self.ema52 and self.open < self.ema52 and self.high > self.ema52 and self.low < self.ema52: + self.near0_return = 91 + elif self.close > self.ema52 and self.high > self.ema52 and self.open > self.ema52 and self.low < self.ema52: + self.near0_return = 101 # CROSS0 仅以 Signal 穿越零轴判定 if self.pre.signal >= 0 and self.signal < 0: self.macd_state = Chan_MACD_STATE.CROSS0_DOWN diff --git a/ChanLun.py b/ChanLun.py index abf60d7..a5d3c77 100644 --- a/ChanLun.py +++ b/ChanLun.py @@ -88,14 +88,14 @@ class ChanLun(): for klu in klu_list: if klu.macd > 0: if klu.separate_div: - state_list.append("20") + state_list.append("30") elif klu.continue_div: state_list.append("20") else: state_list.append("00") else: if klu.separate_div: - state_list.append("-20") + state_list.append("-30") elif klu.continue_div: state_list.append("-20") else: diff --git a/ChanMACDHistSet.py b/ChanMACDHistSet.py index 2570ac1..879a4e9 100644 --- a/ChanMACDHistSet.py +++ b/ChanMACDHistSet.py @@ -82,7 +82,7 @@ class ChanMACDHistSet(): klu = self.peak_div_list[-1] if klu.macd * klu.macdhist > 0: end_klu.continue_div = True - print(end_klu.time, "Continue Div") + #print(end_klu.time, "Continue Div") if self.start_klu.index == end_klu.index: self.peak_klu = self.start_klu if self.start_klu.index + 1 == end_klu.index: diff --git a/strategies/ChanLun_BTC_30.py b/strategies/ChanLun_BTC_30.py index 815fe9f..f540fba 100644 --- a/strategies/ChanLun_BTC_30.py +++ b/strategies/ChanLun_BTC_30.py @@ -50,7 +50,7 @@ class ChanLun_BTC_30(IStrategy): "240": 0 } # 15m and 30m - minimal_roi_1 = { + minimal_roi = { "0": 0.1, "240": 0.05, "480": 0.03, @@ -220,9 +220,9 @@ class ChanLun_BTC_30(IStrategy): new_entryprice = proposed_rate if trade: if trade.is_short: - new_entryprice = proposed_rate - 5 + new_entryprice = proposed_rate - 50 else: - new_entryprice = proposed_rate + 5 + new_entryprice = proposed_rate + 50 return new_entryprice def custom_exit_price(self, pair: str, trade: Trade, @@ -231,9 +231,9 @@ class ChanLun_BTC_30(IStrategy): new_exitprice = proposed_rate if trade: if trade.is_short: - new_exitprice = proposed_rate + 5 + new_exitprice = proposed_rate + 50 else: - new_exitprice = proposed_rate - 5 + new_exitprice = proposed_rate - 50 return new_exitprice def adjust_trade_position(self, trade: Trade, current_time: datetime, @@ -319,32 +319,36 @@ class ChanLun_BTC_30(IStrategy): #logger.info(f"保存开仓时ATR值: {entry_atr}") return None def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: - shift_time = self.time60 - state_str = 'resample_{}_state'.format(self.get_ticker_indicator()*shift_time) - + shift60 = self.time60 + state60 = 'resample_{}_state'.format(self.get_ticker_indicator()*shift60) + shift30 = self.time30 + state30 = 'resample_{}_state'.format(self.get_ticker_indicator()*shift30) dataframe.loc[ ( - (dataframe[state_str].shift(shift_time) == "-20") + (dataframe[state30].shift(shift30) == "-20") ), ['enter_long', 'enter_tag']] = (1, 'long_30') dataframe.loc[ ( - (dataframe[state_str].shift(shift_time) == "20") + (dataframe[state30].shift(shift30) == "20") ), ['enter_short', 'enter_tag']] = (1, 'short_30') return dataframe def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: - shift_time = self.time60 - state_str = 'resample_{}_state'.format(self.get_ticker_indicator()*shift_time) - + shift60 = self.time60 + state60 = 'resample_{}_state'.format(self.get_ticker_indicator()*shift60) + shift30 = self.time30 + state30 = 'resample_{}_state'.format(self.get_ticker_indicator()*shift30) dataframe.loc[ ( - (dataframe[state_str].shift(shift_time) == "20") + (dataframe[state30].shift(shift30) == "20") | + (dataframe[state30].shift(shift30) == "30") ), ['exit_long', 'exit_tag']] = (1, 'long_close_30') dataframe.loc[ ( - (dataframe[state_str].shift(shift_time) == "-20") + (dataframe[state30].shift(shift30) == "-20") | + (dataframe[state30].shift(shift30) == "-30") ), ['exit_short', 'exit_tag']] = (1, 'short_close_30') return dataframe diff --git a/web/templates/index.html b/web/templates/index.html index 91909db..53b4bbf 100644 --- a/web/templates/index.html +++ b/web/templates/index.html @@ -3054,62 +3054,114 @@ } ); - // 从 ChanMACD 的 klu_list 提取 continue_div / separate_div 标记,供主K线图显示 + // 同时从主/次周期的 klu_list 提取 SD/CD 标记,分别使用不同样式 try { - const kluList = Array.isArray(cm.klu_list) ? cm.klu_list : []; - const kluDivMarkers = []; - kluList.forEach((item) => { - if (!item || !item.time) return; - const ts = Math.floor(new Date(item.time).getTime() / 1000); - if (isNaN(ts)) return; - // separate_div 为 true:上方蓝色箭头 - if (item.separate_div === true) { - kluDivMarkers.push({ - time: ts, - position: 'aboveBar', - color: '#03a9f4', - shape: 'arrowUp', - text: 'SD', - size: 0.6 - }); - } - // continue_div 为 true:下方橙色箭头 - if (item.continue_div === true) { - kluDivMarkers.push({ - time: ts, - position: 'belowBar', - color: '#ff9800', - shape: 'arrowDown', - text: 'CD', - size: 0.6 - }); - } - // near0_return > 0:在主图显示具体数值 - if (item.near0_return && Number(item.near0_return) > 0) { - kluDivMarkers.push({ - time: ts, - position: 'belowBar', - color: '#8bc34a', - shape: 'circle', - text: `N${Number(item.near0_return)}`, - size: 0.6 - }); - } - }); - window.kluDivMarkers = kluDivMarkers; + const mainCm = currentData.chan_macd || {}; + const elementCm = currentData.element_chan_macd || {}; + + const mainMarkers = []; + const elementMarkers = []; + + // 主周期 U 标记(蓝/橙,与原样式一致) + if ((typeof window.showUOnMain === 'undefined' ? true : window.showUOnMain) && Array.isArray(mainCm.klu_list)) { + mainCm.klu_list.forEach((item) => { + if (!item || !item.time) return; + const ts = Math.floor(new Date(item.time).getTime() / 1000); + if (isNaN(ts)) return; + if (item.separate_div === true) { + mainMarkers.push({ time: ts, position: 'aboveBar', color: '#03a9f4', shape: 'arrowUp', text: 'SD', size: 0.6 }); + } + if (item.continue_div === true) { + mainMarkers.push({ time: ts, position: 'belowBar', color: '#ff9800', shape: 'arrowDown', text: 'CD', size: 0.6 }); + } + if (item.near0_return && Number(item.near0_return) > 0) { + mainMarkers.push({ time: ts, position: 'belowBar', color: '#8bc34a', shape: 'circle', text: `N${Number(item.near0_return)}`, size: 0.6 }); + } + }); + } + + // 次周期 U 标记(使用不同配色以区分) + if ((typeof window.showUOnElement === 'undefined' ? true : window.showUOnElement) && Array.isArray(elementCm.klu_list)) { + elementCm.klu_list.forEach((item) => { + if (!item || !item.time) return; + const ts = Math.floor(new Date(item.time).getTime() / 1000); + if (isNaN(ts)) return; + if (item.separate_div === true) { + elementMarkers.push({ time: ts, position: 'aboveBar', color: '#9c27b0', shape: 'arrowUp', text: 'SD', size: 0.6 }); + } + if (item.continue_div === true) { + elementMarkers.push({ time: ts, position: 'belowBar', color: '#4caf50', shape: 'arrowDown', text: 'CD', size: 0.6 }); + } + if (item.near0_return && Number(item.near0_return) > 0) { + elementMarkers.push({ time: ts, position: 'belowBar', color: '#009688', shape: 'circle', text: `N${Number(item.near0_return)}`, size: 0.6 }); + } + }); + } + + // 保存到全局,供主图合并标记使用 + window.kluDivMarkersMain = mainMarkers; + window.kluDivMarkersElement = elementMarkers; } catch (e) { console.warn('处理 KLU 背驰标记出错:', e); - window.kluDivMarkers = []; + window.kluDivMarkersMain = []; + window.kluDivMarkersElement = []; } } else { console.log('⚠️ 没有ChanMACD分析数据'); // 无数据时清空本次的 KLU 背驰标记 - window.kluDivMarkers = []; + window.kluDivMarkersMain = []; + window.kluDivMarkersElement = []; } } else { console.log('⚠️ ChanMACD图表创建条件不满足'); } + // 独立于当前显示周期:计算主/次周期 SD/CD 标记(用于主图合并显示) + try { + const mainCmAll = currentData.chan_macd || {}; + const elementCmAll = currentData.element_chan_macd || {}; + const mainMarkersAll = []; + const elementMarkersAll = []; + if ((typeof window.showUOnMain === 'undefined' ? true : window.showUOnMain) && Array.isArray(mainCmAll.klu_list)) { + mainCmAll.klu_list.forEach((item) => { + if (!item || !item.time) return; + const ts = Math.floor(new Date(item.time).getTime() / 1000); + if (isNaN(ts)) return; + if (item.separate_div === true) { + mainMarkersAll.push({ time: ts, position: 'aboveBar', color: '#03a9f4', shape: 'arrowUp', text: 'SD', size: 0.6 }); + } + if (item.continue_div === true) { + mainMarkersAll.push({ time: ts, position: 'belowBar', color: '#ff9800', shape: 'arrowDown', text: 'CD', size: 0.6 }); + } + if (item.near0_return && Number(item.near0_return) > 0) { + mainMarkersAll.push({ time: ts, position: 'belowBar', color: '#8bc34a', shape: 'circle', text: `N${Number(item.near0_return)}`, size: 0.6 }); + } + }); + } + if ((typeof window.showUOnElement === 'undefined' ? true : window.showUOnElement) && Array.isArray(elementCmAll.klu_list)) { + elementCmAll.klu_list.forEach((item) => { + if (!item || !item.time) return; + const ts = Math.floor(new Date(item.time).getTime() / 1000); + if (isNaN(ts)) return; + if (item.separate_div === true) { + elementMarkersAll.push({ time: ts, position: 'aboveBar', color: '#9c27b0', shape: 'arrowUp', text: 'SD', size: 0.6 }); + } + if (item.continue_div === true) { + elementMarkersAll.push({ time: ts, position: 'belowBar', color: '#4caf50', shape: 'arrowDown', text: 'CD', size: 0.6 }); + } + if (item.near0_return && Number(item.near0_return) > 0) { + elementMarkersAll.push({ time: ts, position: 'belowBar', color: '#009688', shape: 'circle', text: `N${Number(item.near0_return)}`, size: 0.6 }); + } + }); + } + window.kluDivMarkersMain = mainMarkersAll; + window.kluDivMarkersElement = elementMarkersAll; + } catch (e) { + console.warn('独立计算 KLU 背驰标记出错:', e); + window.kluDivMarkersMain = []; + window.kluDivMarkersElement = []; + } + // 实现三图联动滚动 // 同步图表的时间范围 @@ -5380,7 +5432,8 @@ const combinedMarkers = [ ...(window.mainFxMarkers || []), ...allElementFxMarkers, - ...(window.kluDivMarkers || []) + ...(window.kluDivMarkersMain || []), + ...(window.kluDivMarkersElement || []) ]; if (combinedMarkers.length > 0) { console.log('合并设置', combinedMarkers.length, '个标记(主周期分型:', (window.mainFxMarkers || []).length, '个,小周期分型:', allElementFxMarkers.length, '个,UnitTF:', (window.unittfMarkers || []).length, '个)'); @@ -5403,7 +5456,8 @@ // 只设置主周期分型 + UnitTF 标记 const onlyMainAndU = [ ...(window.mainFxMarkers || []), - ...(window.kluDivMarkers || []) + ...(window.kluDivMarkersMain || []), + ...(window.kluDivMarkersElement || []) ]; if (onlyMainAndU.length > 0) { console.log('仅设置', onlyMainAndU.length, '个主周期/UnitTF标记(主周期分型:', (window.mainFxMarkers || []).length, ',UnitTF:', (window.unittfMarkers || []).length, ')'); diff --git a/交易记录/交易规则.docx b/交易记录/交易规则.docx new file mode 100644 index 0000000000000000000000000000000000000000..ac67805b1dd8a89c696e34c732d7a8cb29884a15 GIT binary patch literal 12767 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 literal 0 HcmV?d00001