根据新的条件进行交易,添加交易规则
This commit is contained in:
@@ -27,3 +27,4 @@ feature_meta
|
||||
*.sqlite-shm
|
||||
*.sqlite-wal
|
||||
.DS_Store
|
||||
交易记录/~$交易规则.docx
|
||||
|
||||
+2
-2
@@ -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):
|
||||
|
||||
+24
-5
@@ -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
|
||||
|
||||
+2
-2
@@ -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:
|
||||
|
||||
+1
-1
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
+100
-46
@@ -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, ')');
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user