根据新的条件进行交易,添加交易规则

This commit is contained in:
Porter
2025-09-15 10:29:53 +08:00
parent 3c360ea35b
commit 0b9f63409c
8 changed files with 149 additions and 71 deletions
+1
View File
@@ -27,3 +27,4 @@ feature_meta
*.sqlite-shm *.sqlite-shm
*.sqlite-wal *.sqlite-wal
.DS_Store .DS_Store
交易记录/~$交易规则.docx
+2 -2
View File
@@ -79,12 +79,12 @@ class ChanKLC():
if self.fx == Chan_FX_TYPE.TOP: if self.fx == Chan_FX_TYPE.TOP:
#print(self.end_time, self.fx, self.macd, self.macdhist, len(self.klus)) #print(self.end_time, self.fx, self.macd, self.macdhist, len(self.klus))
if self.macdhist < 0 and self.macd > 0: 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 self.bb_out = True
else: else:
if self.fx == Chan_FX_TYPE.BOTTOM: if self.fx == Chan_FX_TYPE.BOTTOM:
if self.macdhist > 0 and self.macd < 0: 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 self.bb_out = True
def cal_macd_state(self, dir): def cal_macd_state(self, dir):
+24 -5
View File
@@ -651,12 +651,31 @@ class ChanKLU:
if (self.macd == 0 and self.signal == 0 and self.macdhist == 0) or self.ema52 == 0: if (self.macd == 0 and self.signal == 0 and self.macdhist == 0) or self.ema52 == 0:
self.macd_state = Chan_MACD_STATE.UNKNOWN self.macd_state = Chan_MACD_STATE.UNKNOWN
return self.macd_state return self.macd_state
if self.macd > 0: # 归零轴判断
if self.macd < self.signal and self.close > self.ema52 and self.low < self.ema52: if self.signal > 0:
self.near0_return = 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: else:
if self.macd > self.signal and self.close < self.ema52 and self.high > self.ema52: if self.macd > self.signal:
self.near0_return = 0 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 穿越零轴判定 # CROSS0 仅以 Signal 穿越零轴判定
if self.pre.signal >= 0 and self.signal < 0: if self.pre.signal >= 0 and self.signal < 0:
self.macd_state = Chan_MACD_STATE.CROSS0_DOWN self.macd_state = Chan_MACD_STATE.CROSS0_DOWN
+2 -2
View File
@@ -88,14 +88,14 @@ class ChanLun():
for klu in klu_list: for klu in klu_list:
if klu.macd > 0: if klu.macd > 0:
if klu.separate_div: if klu.separate_div:
state_list.append("20") state_list.append("30")
elif klu.continue_div: elif klu.continue_div:
state_list.append("20") state_list.append("20")
else: else:
state_list.append("00") state_list.append("00")
else: else:
if klu.separate_div: if klu.separate_div:
state_list.append("-20") state_list.append("-30")
elif klu.continue_div: elif klu.continue_div:
state_list.append("-20") state_list.append("-20")
else: else:
+1 -1
View File
@@ -82,7 +82,7 @@ class ChanMACDHistSet():
klu = self.peak_div_list[-1] klu = self.peak_div_list[-1]
if klu.macd * klu.macdhist > 0: if klu.macd * klu.macdhist > 0:
end_klu.continue_div = True 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: if self.start_klu.index == end_klu.index:
self.peak_klu = self.start_klu self.peak_klu = self.start_klu
if self.start_klu.index + 1 == end_klu.index: if self.start_klu.index + 1 == end_klu.index:
+19 -15
View File
@@ -50,7 +50,7 @@ class ChanLun_BTC_30(IStrategy):
"240": 0 "240": 0
} }
# 15m and 30m # 15m and 30m
minimal_roi_1 = { minimal_roi = {
"0": 0.1, "0": 0.1,
"240": 0.05, "240": 0.05,
"480": 0.03, "480": 0.03,
@@ -220,9 +220,9 @@ class ChanLun_BTC_30(IStrategy):
new_entryprice = proposed_rate new_entryprice = proposed_rate
if trade: if trade:
if trade.is_short: if trade.is_short:
new_entryprice = proposed_rate - 5 new_entryprice = proposed_rate - 50
else: else:
new_entryprice = proposed_rate + 5 new_entryprice = proposed_rate + 50
return new_entryprice return new_entryprice
def custom_exit_price(self, pair: str, trade: Trade, def custom_exit_price(self, pair: str, trade: Trade,
@@ -231,9 +231,9 @@ class ChanLun_BTC_30(IStrategy):
new_exitprice = proposed_rate new_exitprice = proposed_rate
if trade: if trade:
if trade.is_short: if trade.is_short:
new_exitprice = proposed_rate + 5 new_exitprice = proposed_rate + 50
else: else:
new_exitprice = proposed_rate - 5 new_exitprice = proposed_rate - 50
return new_exitprice return new_exitprice
def adjust_trade_position(self, trade: Trade, current_time: datetime, 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}") #logger.info(f"保存开仓时ATR值: {entry_atr}")
return None return None
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
shift_time = self.time60 shift60 = self.time60
state_str = 'resample_{}_state'.format(self.get_ticker_indicator()*shift_time) state60 = 'resample_{}_state'.format(self.get_ticker_indicator()*shift60)
shift30 = self.time30
state30 = 'resample_{}_state'.format(self.get_ticker_indicator()*shift30)
dataframe.loc[ dataframe.loc[
( (
(dataframe[state_str].shift(shift_time) == "-20") (dataframe[state30].shift(shift30) == "-20")
), ),
['enter_long', 'enter_tag']] = (1, 'long_30') ['enter_long', 'enter_tag']] = (1, 'long_30')
dataframe.loc[ dataframe.loc[
( (
(dataframe[state_str].shift(shift_time) == "20") (dataframe[state30].shift(shift30) == "20")
), ),
['enter_short', 'enter_tag']] = (1, 'short_30') ['enter_short', 'enter_tag']] = (1, 'short_30')
return dataframe return dataframe
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
shift_time = self.time60 shift60 = self.time60
state_str = 'resample_{}_state'.format(self.get_ticker_indicator()*shift_time) state60 = 'resample_{}_state'.format(self.get_ticker_indicator()*shift60)
shift30 = self.time30
state30 = 'resample_{}_state'.format(self.get_ticker_indicator()*shift30)
dataframe.loc[ 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') ['exit_long', 'exit_tag']] = (1, 'long_close_30')
dataframe.loc[ 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') ['exit_short', 'exit_tag']] = (1, 'short_close_30')
return dataframe return dataframe
+100 -46
View File
@@ -3054,62 +3054,114 @@
} }
); );
// 从 ChanMACD 的 klu_list 提取 continue_div / separate_div 标记,供主K线图显示 // 同时从主/次周期的 klu_list 提取 SD/CD 标记,分别使用不同样式
try { try {
const kluList = Array.isArray(cm.klu_list) ? cm.klu_list : []; const mainCm = currentData.chan_macd || {};
const kluDivMarkers = []; const elementCm = currentData.element_chan_macd || {};
kluList.forEach((item) => {
if (!item || !item.time) return; const mainMarkers = [];
const ts = Math.floor(new Date(item.time).getTime() / 1000); const elementMarkers = [];
if (isNaN(ts)) return;
// separate_div 为 true:上方蓝色箭头 // 主周期 U 标记(蓝/橙,与原样式一致)
if (item.separate_div === true) { if ((typeof window.showUOnMain === 'undefined' ? true : window.showUOnMain) && Array.isArray(mainCm.klu_list)) {
kluDivMarkers.push({ mainCm.klu_list.forEach((item) => {
time: ts, if (!item || !item.time) return;
position: 'aboveBar', const ts = Math.floor(new Date(item.time).getTime() / 1000);
color: '#03a9f4', if (isNaN(ts)) return;
shape: 'arrowUp', if (item.separate_div === true) {
text: 'SD', mainMarkers.push({ time: ts, position: 'aboveBar', color: '#03a9f4', shape: 'arrowUp', text: 'SD', size: 0.6 });
size: 0.6 }
}); if (item.continue_div === true) {
} mainMarkers.push({ time: ts, position: 'belowBar', color: '#ff9800', shape: 'arrowDown', text: 'CD', size: 0.6 });
// continue_div 为 true:下方橙色箭头 }
if (item.continue_div === true) { if (item.near0_return && Number(item.near0_return) > 0) {
kluDivMarkers.push({ mainMarkers.push({ time: ts, position: 'belowBar', color: '#8bc34a', shape: 'circle', text: `N${Number(item.near0_return)}`, size: 0.6 });
time: ts, }
position: 'belowBar', });
color: '#ff9800', }
shape: 'arrowDown',
text: 'CD', // 次周期 U 标记(使用不同配色以区分)
size: 0.6 if ((typeof window.showUOnElement === 'undefined' ? true : window.showUOnElement) && Array.isArray(elementCm.klu_list)) {
}); elementCm.klu_list.forEach((item) => {
} if (!item || !item.time) return;
// near0_return > 0:在主图显示具体数值 const ts = Math.floor(new Date(item.time).getTime() / 1000);
if (item.near0_return && Number(item.near0_return) > 0) { if (isNaN(ts)) return;
kluDivMarkers.push({ if (item.separate_div === true) {
time: ts, elementMarkers.push({ time: ts, position: 'aboveBar', color: '#9c27b0', shape: 'arrowUp', text: 'SD', size: 0.6 });
position: 'belowBar', }
color: '#8bc34a', if (item.continue_div === true) {
shape: 'circle', elementMarkers.push({ time: ts, position: 'belowBar', color: '#4caf50', shape: 'arrowDown', text: 'CD', size: 0.6 });
text: `N${Number(item.near0_return)}`, }
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.kluDivMarkers = kluDivMarkers; }
// 保存到全局,供主图合并标记使用
window.kluDivMarkersMain = mainMarkers;
window.kluDivMarkersElement = elementMarkers;
} catch (e) { } catch (e) {
console.warn('处理 KLU 背驰标记出错:', e); console.warn('处理 KLU 背驰标记出错:', e);
window.kluDivMarkers = []; window.kluDivMarkersMain = [];
window.kluDivMarkersElement = [];
} }
} else { } else {
console.log('⚠️ 没有ChanMACD分析数据'); console.log('⚠️ 没有ChanMACD分析数据');
// 无数据时清空本次的 KLU 背驰标记 // 无数据时清空本次的 KLU 背驰标记
window.kluDivMarkers = []; window.kluDivMarkersMain = [];
window.kluDivMarkersElement = [];
} }
} else { } else {
console.log('⚠️ ChanMACD图表创建条件不满足'); 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 = [ const combinedMarkers = [
...(window.mainFxMarkers || []), ...(window.mainFxMarkers || []),
...allElementFxMarkers, ...allElementFxMarkers,
...(window.kluDivMarkers || []) ...(window.kluDivMarkersMain || []),
...(window.kluDivMarkersElement || [])
]; ];
if (combinedMarkers.length > 0) { if (combinedMarkers.length > 0) {
console.log('合并设置', combinedMarkers.length, '个标记(主周期分型:', (window.mainFxMarkers || []).length, '个,小周期分型:', allElementFxMarkers.length, '个,UnitTF:', (window.unittfMarkers || []).length, '个)'); console.log('合并设置', combinedMarkers.length, '个标记(主周期分型:', (window.mainFxMarkers || []).length, '个,小周期分型:', allElementFxMarkers.length, '个,UnitTF:', (window.unittfMarkers || []).length, '个)');
@@ -5403,7 +5456,8 @@
// 只设置主周期分型 + UnitTF 标记 // 只设置主周期分型 + UnitTF 标记
const onlyMainAndU = [ const onlyMainAndU = [
...(window.mainFxMarkers || []), ...(window.mainFxMarkers || []),
...(window.kluDivMarkers || []) ...(window.kluDivMarkersMain || []),
...(window.kluDivMarkersElement || [])
]; ];
if (onlyMainAndU.length > 0) { if (onlyMainAndU.length > 0) {
console.log('仅设置', onlyMainAndU.length, '个主周期/UnitTF标记(主周期分型:', (window.mainFxMarkers || []).length, 'UnitTF:', (window.unittfMarkers || []).length, ''); console.log('仅设置', onlyMainAndU.length, '个主周期/UnitTF标记(主周期分型:', (window.mainFxMarkers || []).length, 'UnitTF:', (window.unittfMarkers || []).length, '');
Binary file not shown.