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 0000000..ac67805 Binary files /dev/null and b/交易记录/交易规则.docx differ