添加klu的3种形态识别
This commit is contained in:
@@ -37,7 +37,7 @@ class ChanBI():
|
|||||||
def cal_macdhist(self):
|
def cal_macdhist(self):
|
||||||
self.macd_hist = 0
|
self.macd_hist = 0
|
||||||
for klc in self.klc_list:
|
for klc in self.klc_list:
|
||||||
for klu in klc.klus:
|
for klu in klc.klu_list:
|
||||||
if self.dir == Chan_BI_DIR.UP and klu.macdhist > 0:
|
if self.dir == Chan_BI_DIR.UP and klu.macdhist > 0:
|
||||||
self.macd_hist += klu.macdhist
|
self.macd_hist += klu.macdhist
|
||||||
if self.dir == Chan_BI_DIR.DOWN and klu.macdhist < 0:
|
if self.dir == Chan_BI_DIR.DOWN and klu.macdhist < 0:
|
||||||
|
|||||||
+23
-21
@@ -14,7 +14,7 @@ class ChanKLC():
|
|||||||
self.low = klu.low
|
self.low = klu.low
|
||||||
self.dir = ddir
|
self.dir = ddir
|
||||||
self.index = index
|
self.index = index
|
||||||
self.klus = []
|
self.klu_list = []
|
||||||
self.add_klu(klu)
|
self.add_klu(klu)
|
||||||
self.fx = Chan_FX_TYPE.UNKNOWN
|
self.fx = Chan_FX_TYPE.UNKNOWN
|
||||||
self.next = None
|
self.next = None
|
||||||
@@ -49,6 +49,7 @@ class ChanKLC():
|
|||||||
self.ema24 = klu.ema24
|
self.ema24 = klu.ema24
|
||||||
self.trend = Chan_PRICE_TREND.UNKNOWN
|
self.trend = Chan_PRICE_TREND.UNKNOWN
|
||||||
self.exception = klu.exception
|
self.exception = klu.exception
|
||||||
|
self.klc_dir = Chan_KLINE_DIR.UP if klu.close > klu.open else Chan_KLINE_DIR.DOWN
|
||||||
def set_trend(self, trend):
|
def set_trend(self, trend):
|
||||||
self.trend = trend
|
self.trend = trend
|
||||||
def to_string(self):
|
def to_string(self):
|
||||||
@@ -64,22 +65,23 @@ class ChanKLC():
|
|||||||
#self.cal_fx()
|
#self.cal_fx()
|
||||||
self.cal_bb_out()
|
self.cal_bb_out()
|
||||||
def add_klu(self, klu):
|
def add_klu(self, klu):
|
||||||
self.klus.append(klu)
|
self.klu_list.append(klu)
|
||||||
def set_end_klu(self, klu):
|
def set_end_klu(self, klu):
|
||||||
self.end_klu = klu
|
self.end_klu = klu
|
||||||
self.end_time = klu.time
|
self.end_time = klu.time
|
||||||
self.close = klu.close
|
self.close = klu.close
|
||||||
for klu in self.klus:
|
for klu in self.klu_list:
|
||||||
if klu.separate_div > 0:
|
if klu.separate_div > 0:
|
||||||
self.separate_div = True
|
self.separate_div = True
|
||||||
if klu.continue_div:
|
if klu.continue_div:
|
||||||
self.continue_div = klu.continue_div
|
self.continue_div = klu.continue_div
|
||||||
if klu.macd_state != Chan_MACD_STATE.UNKNOWN:
|
if klu.macd_state != Chan_MACD_STATE.UNKNOWN:
|
||||||
self.state = klu.macd_state
|
self.state = klu.macd_state
|
||||||
|
self.klc_dir = Chan_KLINE_DIR.UP if self.close > self.open else Chan_KLINE_DIR.DOWN
|
||||||
self.cal_indicators()
|
self.cal_indicators()
|
||||||
def cal_fx(self):
|
def cal_fx(self):
|
||||||
if self.klc_fx_type == Chan_KLC_FX.TOP1 or self.klc_fx_type == Chan_KLC_FX.TOP2:
|
if self.klc_fx_type == Chan_KLC_FX.TOP1 or self.klc_fx_type == Chan_KLC_FX.TOP2:
|
||||||
#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.klu_list))
|
||||||
if self.state == Chan_MACD_STATE.HIGH_EMPTY and self.macd > 0:
|
if self.state == Chan_MACD_STATE.HIGH_EMPTY and self.macd > 0:
|
||||||
#print(self.end_time, self.state, self.macd, self.klc_fx_type)
|
#print(self.end_time, self.state, self.macd, self.klc_fx_type)
|
||||||
self.klc_fx_type = Chan_KLC_FX.TOP6
|
self.klc_fx_type = Chan_KLC_FX.TOP6
|
||||||
@@ -100,7 +102,7 @@ class ChanKLC():
|
|||||||
if self.signal < 0 and self.macd < self.signal:
|
if self.signal < 0 and self.macd < self.signal:
|
||||||
self.klc_fx_type = Chan_KLC_FX.BOTTOM8
|
self.klc_fx_type = Chan_KLC_FX.BOTTOM8
|
||||||
def cal_bb_out(self):
|
def cal_bb_out(self):
|
||||||
for klu in self.klus:
|
for klu in self.klu_list:
|
||||||
if self.klc_fx_type == Chan_KLC_FX.TOP1 or self.klc_fx_type == Chan_KLC_FX.TOP2:
|
if self.klc_fx_type == Chan_KLC_FX.TOP1 or self.klc_fx_type == Chan_KLC_FX.TOP2:
|
||||||
#print(self.start_time, self.klc_fx_type, klu.high, klu.bb52upper, self.macd, self.next.macd, klu.time)
|
#print(self.start_time, self.klc_fx_type, klu.high, klu.bb52upper, self.macd, self.next.macd, klu.time)
|
||||||
if self.high >= klu.bb52upper and klu.bb52upper > 0 and self.next and self.high > self.next.high:
|
if self.high >= klu.bb52upper and klu.bb52upper > 0 and self.next and self.high > self.next.high:
|
||||||
@@ -112,22 +114,22 @@ class ChanKLC():
|
|||||||
self.klc_fx_type = Chan_KLC_FX.BOTTOM4
|
self.klc_fx_type = Chan_KLC_FX.BOTTOM4
|
||||||
print(self.end_time, self.klc_fx_type)
|
print(self.end_time, self.klc_fx_type)
|
||||||
def cal_indicators(self):
|
def cal_indicators(self):
|
||||||
for index in range(1, len(self.klus)):
|
for index in range(1, len(self.klu_list)):
|
||||||
self.volume += self.klus[index].volume
|
self.volume += self.klu_list[index].volume
|
||||||
self.rsi += self.klus[index].rsi
|
self.rsi += self.klu_list[index].rsi
|
||||||
self.volume_ratio += self.klus[index].volume_ratio
|
self.volume_ratio += self.klu_list[index].volume_ratio
|
||||||
self.macdhist += self.klus[index].macdhist
|
self.macdhist += self.klu_list[index].macdhist
|
||||||
self.ema52 += self.klus[index].ema52
|
self.ema52 += self.klu_list[index].ema52
|
||||||
self.ema24 += self.klus[index].ema24
|
self.ema24 += self.klu_list[index].ema24
|
||||||
self.rsi = self.rsi / len(self.klus)
|
self.rsi = self.rsi / len(self.klu_list)
|
||||||
self.volume_ratio = self.volume_ratio / len(self.klus)
|
self.volume_ratio = self.volume_ratio / len(self.klu_list)
|
||||||
self.volume = self.volume / len(self.klus)
|
self.volume = self.volume / len(self.klu_list)
|
||||||
self.macdhist = self.macdhist / len(self.klus)
|
self.macdhist = self.macdhist / len(self.klu_list)
|
||||||
self.ema52 = self.ema52 / len(self.klus)
|
self.ema52 = self.ema52 / len(self.klu_list)
|
||||||
self.ema24 = self.ema24 / len(self.klus)
|
self.ema24 = self.ema24 / len(self.klu_list)
|
||||||
if len(self.klus) > 0:
|
if len(self.klu_list) > 0:
|
||||||
self.macd = self.klus[-1].macd
|
self.macd = self.klu_list[-1].macd
|
||||||
self.signal = self.klus[-1].signal
|
self.signal = self.klu_list[-1].signal
|
||||||
self.body = abs(self.close - self.open)
|
self.body = abs(self.close - self.open)
|
||||||
self.upper_shadow = self.high - max(self.close, self.open)
|
self.upper_shadow = self.high - max(self.close, self.open)
|
||||||
self.lower_shadow = min(self.close, self.open) - self.low
|
self.lower_shadow = min(self.close, self.open) - self.low
|
||||||
|
|||||||
+4
-2
@@ -35,8 +35,8 @@ class ChanKLU:
|
|||||||
self.upper_shadow = self.high - max(self.close, self.open)
|
self.upper_shadow = self.high - max(self.close, self.open)
|
||||||
self.lower_shadow = min(self.close, self.open) - self.low
|
self.lower_shadow = min(self.close, self.open) - self.low
|
||||||
self.body_ratio = self.body / self.range
|
self.body_ratio = self.body / self.range
|
||||||
self.upper_shadow_ratio = self.upper_shadow / self.body
|
self.upper_shadow_ratio = self.upper_shadow / self.range
|
||||||
self.lower_shadow_ratio = self.lower_shadow / self.body
|
self.lower_shadow_ratio = self.lower_shadow / self.range
|
||||||
self.exception = False
|
self.exception = False
|
||||||
self.cal_exception()
|
self.cal_exception()
|
||||||
self.candle_dir = Chan_K_DIR.CROSS if self.close == self.open else Chan_K_DIR.BULL if self.close > self.open else Chan_K_DIR.BEAR
|
self.candle_dir = Chan_K_DIR.CROSS if self.close == self.open else Chan_K_DIR.BULL if self.close > self.open else Chan_K_DIR.BEAR
|
||||||
@@ -70,6 +70,8 @@ class ChanKLU:
|
|||||||
self.macd_state = state
|
self.macd_state = state
|
||||||
def set_pattern(self, pattern):
|
def set_pattern(self, pattern):
|
||||||
self.pattern = pattern
|
self.pattern = pattern
|
||||||
|
def to_string(self):
|
||||||
|
return f"{self.time} {self.candle_dir} {self.pattern}"
|
||||||
def cal_exception(self):
|
def cal_exception(self):
|
||||||
if self.upper_shadow_ratio > 5 or self.lower_shadow_ratio > 5:
|
if self.upper_shadow_ratio > 5 or self.lower_shadow_ratio > 5:
|
||||||
self.exception = True
|
self.exception = True
|
||||||
|
|||||||
@@ -123,17 +123,25 @@ class TF_DF():
|
|||||||
return klu_state_list
|
return klu_state_list
|
||||||
def check_fx(self, klc):
|
def check_fx(self, klc):
|
||||||
if klc.pre and klc.next:
|
if klc.pre and klc.next:
|
||||||
if klc.high > klc.pre.high and klc.high > klc.next.high and klc.low > klc.pre.low and klc.low > klc.next.low:
|
if klc.high > klc.pre.high and klc.high > klc.next.high and klc.low > klc.pre.low and klc.low > klc.next.low and klc.next.klc_dir == Chan_KLINE_DIR.DOWN:
|
||||||
#if (klc.close > klc.ema52 or klc.next.close > klc.next.ema52) and klc.macd > 0 and klc.macd > klc.macdhist:
|
#if (klc.close > klc.ema52 or klc.next.close > klc.next.ema52) and klc.macd > 0 and klc.macd > klc.macdhist:
|
||||||
klc.set_fx(Chan_FX_TYPE.TOP)
|
klc.set_fx(Chan_FX_TYPE.TOP)
|
||||||
#print(klc.start_time, klc.end_time,klc.next.start_time, klc.next.end_time, klc.macd, klc.state, klc.fx, "TOP")
|
#print(klc.start_time, klc.end_time,klc.next.start_time, klc.next.end_time, klc.macd, klc.state, klc.fx, "TOP")
|
||||||
return Chan_FX_TYPE.TOP
|
return Chan_FX_TYPE.TOP
|
||||||
elif klc.low < klc.pre.low and klc.low < klc.next.low and klc.high < klc.pre.high and klc.high < klc.next.high:
|
elif klc.low < klc.pre.low and klc.low < klc.next.low and klc.high < klc.pre.high and klc.high < klc.next.high and klc.next.klc_dir == Chan_KLINE_DIR.UP:
|
||||||
#if (klc.close < klc.ema52 or klc.next.close < klc.next.ema52) and klc.macd < 0 and klc.macd < klc.macdhist:
|
#if (klc.close < klc.ema52 or klc.next.close < klc.next.ema52) and klc.macd < 0 and klc.macd < klc.macdhist:
|
||||||
klc.set_fx(Chan_FX_TYPE.BOTTOM)
|
klc.set_fx(Chan_FX_TYPE.BOTTOM)
|
||||||
#print(klc.start_time, klc.end_time,klc.next.start_time, klc.next.end_time, klc.macd, klc.state, klc.fx, "BOTTOM")
|
#print(klc.start_time, klc.end_time,klc.next.start_time, klc.next.end_time, klc.macd, klc.state, klc.fx, "BOTTOM")
|
||||||
return Chan_FX_TYPE.BOTTOM
|
return Chan_FX_TYPE.BOTTOM
|
||||||
return Chan_FX_TYPE.UNKNOWN
|
return Chan_FX_TYPE.UNKNOWN
|
||||||
|
def check_fx_pattern(self, klc):
|
||||||
|
klu_list = klc.pre.klu_list + klc.klu_list + klc.next.klu_list
|
||||||
|
|
||||||
|
self.cal_klu_pattern(klu_list)
|
||||||
|
p = ""
|
||||||
|
for klu in klu_list:
|
||||||
|
p += klu.to_string()
|
||||||
|
#print(p)
|
||||||
def cal_volume_ratio(self, dataframe, window=10):
|
def cal_volume_ratio(self, dataframe, window=10):
|
||||||
df = dataframe.copy()
|
df = dataframe.copy()
|
||||||
# 计算过去N根K线的平均成交量
|
# 计算过去N根K线的平均成交量
|
||||||
@@ -914,6 +922,7 @@ class TF_DF():
|
|||||||
last_top = klc
|
last_top = klc
|
||||||
#print(klc.start_time, klc.fx, bi_list[-1].dir, "Last Top Change 1")
|
#print(klc.start_time, klc.fx, bi_list[-1].dir, "Last Top Change 1")
|
||||||
klc.set_klc_fx_type(Chan_KLC_FX.TOP1)
|
klc.set_klc_fx_type(Chan_KLC_FX.TOP1)
|
||||||
|
self.check_fx_pattern(klc)
|
||||||
#print(klc.end_time, klc.fx, "一类卖点Sell 1")
|
#print(klc.end_time, klc.fx, "一类卖点Sell 1")
|
||||||
#klc.set_fx(fx)
|
#klc.set_fx(fx)
|
||||||
#klc.set_state("10")
|
#klc.set_state("10")
|
||||||
@@ -968,6 +977,7 @@ class TF_DF():
|
|||||||
last_top = klc
|
last_top = klc
|
||||||
#print(klc.end_time, klc.fx, bi_list[-1].dir, "Last Top Change 2")
|
#print(klc.end_time, klc.fx, bi_list[-1].dir, "Last Top Change 2")
|
||||||
klc.set_klc_fx_type(Chan_KLC_FX.TOP2)
|
klc.set_klc_fx_type(Chan_KLC_FX.TOP2)
|
||||||
|
self.check_fx_pattern(klc)
|
||||||
#klc.set_state('30')
|
#klc.set_state('30')
|
||||||
bi_list[-1].add_klc(klc)
|
bi_list[-1].add_klc(klc)
|
||||||
klc.set_bi(bi_list[-1])
|
klc.set_bi(bi_list[-1])
|
||||||
@@ -1040,6 +1050,7 @@ class TF_DF():
|
|||||||
last_bottom = klc
|
last_bottom = klc
|
||||||
#print(klc.start_time, klc.fx, bi_list[-1].dir, "Last Bottom Change 1")
|
#print(klc.start_time, klc.fx, bi_list[-1].dir, "Last Bottom Change 1")
|
||||||
klc.set_klc_fx_type(Chan_KLC_FX.BOTTOM1)
|
klc.set_klc_fx_type(Chan_KLC_FX.BOTTOM1)
|
||||||
|
self.check_fx_pattern(klc)
|
||||||
#print(klc.start_time, klc.fx, "一类买点Buy 1")
|
#print(klc.start_time, klc.fx, "一类买点Buy 1")
|
||||||
#klc.set_state("-10")
|
#klc.set_state("-10")
|
||||||
bi_list[-1].add_klc(klc)
|
bi_list[-1].add_klc(klc)
|
||||||
@@ -1095,6 +1106,7 @@ class TF_DF():
|
|||||||
last_bottom = klc
|
last_bottom = klc
|
||||||
#print(klc.start_time, klc.fx, bi_list[-1].dir, "Last Bottom Change 2")
|
#print(klc.start_time, klc.fx, bi_list[-1].dir, "Last Bottom Change 2")
|
||||||
klc.set_klc_fx_type(Chan_KLC_FX.BOTTOM2)
|
klc.set_klc_fx_type(Chan_KLC_FX.BOTTOM2)
|
||||||
|
self.check_fx_pattern(klc)
|
||||||
#klc.set_state('-30')
|
#klc.set_state('-30')
|
||||||
bi_list[-1].add_klc(klc)
|
bi_list[-1].add_klc(klc)
|
||||||
klc.set_bi(bi_list[-1])
|
klc.set_bi(bi_list[-1])
|
||||||
@@ -1354,10 +1366,11 @@ class TF_DF():
|
|||||||
|
|
||||||
def get_klu_list(self, dataframe):
|
def get_klu_list(self, dataframe):
|
||||||
klu_list = self.get_kl_data(dataframe)
|
klu_list = self.get_kl_data(dataframe)
|
||||||
return self.cal_klu_pattern(klu_list)
|
#klu_list = self.cal_klu_pattern(klu_list)
|
||||||
|
return klu_list
|
||||||
def cal_klu_pattern(self, klu_list):
|
def cal_klu_pattern(self, klu_list):
|
||||||
"""
|
"""
|
||||||
计算裸K的pattern - 只识别反转形态
|
计算裸K的pattern - 识别反转形态
|
||||||
"""
|
"""
|
||||||
if not klu_list or len(klu_list) < 3:
|
if not klu_list or len(klu_list) < 3:
|
||||||
return klu_list
|
return klu_list
|
||||||
@@ -1365,8 +1378,17 @@ class TF_DF():
|
|||||||
for i, klu in enumerate(klu_list):
|
for i, klu in enumerate(klu_list):
|
||||||
# 单根K线反转模式识别
|
# 单根K线反转模式识别
|
||||||
self._detect_single_reversal_pattern(klu)
|
self._detect_single_reversal_pattern(klu)
|
||||||
|
|
||||||
|
# 双根K线形态识别
|
||||||
|
if i >= 1:
|
||||||
|
self._detect_double_pattern(klu_list[i-1], klu)
|
||||||
|
|
||||||
|
# 三根K线形态识别
|
||||||
|
if i >= 2:
|
||||||
|
self._detect_triple_pattern(klu_list[i-2], klu_list[i-1], klu)
|
||||||
|
|
||||||
if klu.pattern != Chan_KLU_PATTERN.UNKNOWN:
|
if klu.pattern != Chan_KLU_PATTERN.UNKNOWN:
|
||||||
print(klu.time, klu.pattern)
|
print(klu.time, klu.pattern, klu.lower_shadow_ratio, klu.upper_shadow_ratio, klu.body_ratio, klu.lower_shadow_ratio/klu.body_ratio, klu.upper_shadow_ratio/klu.body_ratio)
|
||||||
return klu_list
|
return klu_list
|
||||||
|
|
||||||
def _detect_single_reversal_pattern(self, klu):
|
def _detect_single_reversal_pattern(self, klu):
|
||||||
@@ -1383,23 +1405,23 @@ class TF_DF():
|
|||||||
body_ratio = body / total_range
|
body_ratio = body / total_range
|
||||||
upper_ratio = upper_shadow / total_range
|
upper_ratio = upper_shadow / total_range
|
||||||
lower_ratio = lower_shadow / total_range
|
lower_ratio = lower_shadow / total_range
|
||||||
|
#print(klu.time, upper_ratio, lower_ratio, body_ratio, upper_ratio/body_ratio, lower_ratio/body_ratio)
|
||||||
# 锤子线/上吊线 - 反转信号
|
# 锤子线/上吊线 - 反转信号
|
||||||
if lower_ratio / body_ratio >= 2:
|
if lower_ratio / body_ratio >= 2:
|
||||||
# 锤子线:底部反转,需要前面一段
|
# 锤子线:底部反转,需要前面一段
|
||||||
if klu.close > klu.open and klu.pre and klu.pre.close < klu.pre.open:
|
if klu.close > klu.open and klu.pre:
|
||||||
klu.set_pattern(Chan_KLU_PATTERN.HAMMER) # 底部反转
|
klu.set_pattern(Chan_KLU_PATTERN.HAMMER) # 底部反转
|
||||||
# 上吊线:顶部反转,需要前一根是上涨趋势
|
# 上吊线:顶部反转,需要前一根是上涨趋势
|
||||||
elif klu.close < klu.open and klu.pre and klu.pre.close > klu.pre.open:
|
elif klu.close < klu.open and klu.pre:
|
||||||
klu.set_pattern(Chan_KLU_PATTERN.HANGING_MAN) # 顶部反转
|
klu.set_pattern(Chan_KLU_PATTERN.HANGING_MAN) # 顶部反转
|
||||||
|
|
||||||
# 倒锤子线/射击之星 - 反转信号
|
# 倒锤子线/射击之星 - 反转信号
|
||||||
elif upper_ratio / body_ratio >= 2:
|
elif upper_ratio / body_ratio >= 2:
|
||||||
# 倒锤子线:底部反转,需要前一根是下跌趋势
|
# 倒锤子线:底部反转,需要前一根是下跌趋势
|
||||||
if klu.close > klu.open and klu.pre and klu.pre.close < klu.pre.open:
|
if klu.close > klu.open and klu.pre:
|
||||||
klu.set_pattern(Chan_KLU_PATTERN.INVERTED_HAMMER) # 底部反转
|
klu.set_pattern(Chan_KLU_PATTERN.INVERTED_HAMMER) # 底部反转
|
||||||
# 射击之星:顶部反转,需要前一根是上涨趋势
|
# 射击之星:顶部反转,需要前一根是上涨趋势
|
||||||
elif klu.close < klu.open and klu.pre and klu.pre.close > klu.pre.open:
|
elif klu.close < klu.open and klu.pre:
|
||||||
klu.set_pattern(Chan_KLU_PATTERN.SHOOTING_STAR) # 顶部反转
|
klu.set_pattern(Chan_KLU_PATTERN.SHOOTING_STAR) # 顶部反转
|
||||||
|
|
||||||
# 十字星 - 反转信号
|
# 十字星 - 反转信号
|
||||||
@@ -1417,5 +1439,231 @@ class TF_DF():
|
|||||||
else:
|
else:
|
||||||
klu.set_pattern(Chan_KLU_PATTERN.DOJI) # 一般反转信号
|
klu.set_pattern(Chan_KLU_PATTERN.DOJI) # 一般反转信号
|
||||||
|
|
||||||
|
def _detect_double_pattern(self, prev_klu, curr_klu):
|
||||||
|
"""检测两根K线形成的形态
|
||||||
|
包括:吞没形态(看涨/看跌)、乌云盖顶、曙光初现
|
||||||
|
"""
|
||||||
|
# 如果前一根K线已经有形态,不再识别双K线形态
|
||||||
|
if prev_klu.pattern != Chan_KLU_PATTERN.UNKNOWN:
|
||||||
|
return
|
||||||
|
|
||||||
|
# 计算K线实体
|
||||||
|
prev_body = abs(prev_klu.close - prev_klu.open)
|
||||||
|
curr_body = abs(curr_klu.close - curr_klu.open)
|
||||||
|
|
||||||
|
# 判断K线颜色(阴阳)
|
||||||
|
prev_bullish = prev_klu.close > prev_klu.open
|
||||||
|
curr_bullish = curr_klu.close > curr_klu.open
|
||||||
|
|
||||||
|
# 检查是否存在长期趋势(至少需要5根K线的趋势)
|
||||||
|
def check_long_trend(klu, bullish_trend=True, min_bars=5):
|
||||||
|
"""检查是否存在长期趋势
|
||||||
|
bullish_trend=True: 检查上涨趋势
|
||||||
|
bullish_trend=False: 检查下跌趋势
|
||||||
|
min_bars: 最少需要多少根K线形成趋势
|
||||||
|
"""
|
||||||
|
if not klu or not klu.pre:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
# 使用EMA指标判断长期趋势
|
||||||
|
if klu.ema52 > 0:
|
||||||
|
if bullish_trend and klu.close < klu.ema52:
|
||||||
|
return False
|
||||||
|
if not bullish_trend and klu.close > klu.ema52:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 检查连续的K线方向
|
||||||
|
count = 0
|
||||||
|
current = klu.pre
|
||||||
|
|
||||||
|
while current and count < min_bars:
|
||||||
|
if not current.pre:
|
||||||
|
break
|
||||||
|
|
||||||
|
if bullish_trend:
|
||||||
|
# 上涨趋势:当前收盘价高于前一根收盘价
|
||||||
|
if current.close <= current.pre.close:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
# 下跌趋势:当前收盘价低于前一根收盘价
|
||||||
|
if current.close >= current.pre.close:
|
||||||
|
break
|
||||||
|
|
||||||
|
count += 1
|
||||||
|
current = current.pre
|
||||||
|
|
||||||
|
return count >= min_bars
|
||||||
|
|
||||||
|
# 1. 看涨吞没形态:前阴后阳,后者完全吞没前者
|
||||||
|
# 要求前面有明显的下跌趋势
|
||||||
|
if not prev_bullish and curr_bullish and \
|
||||||
|
abs(curr_klu.open - prev_klu.close) < 10 and \
|
||||||
|
curr_klu.close > prev_klu.open and \
|
||||||
|
check_long_trend(prev_klu, bullish_trend=False, min_bars=5):
|
||||||
|
curr_klu.set_pattern(Chan_KLU_PATTERN.BULLISH_ENGULFING)
|
||||||
|
return
|
||||||
|
|
||||||
|
# 2. 看跌吞没形态:前阳后阴,后者完全吞没前者
|
||||||
|
# 要求前面有明显的上涨趋势
|
||||||
|
if prev_bullish and not curr_bullish and \
|
||||||
|
abs(curr_klu.open - prev_klu.close) < 10 and \
|
||||||
|
curr_klu.close < prev_klu.open and \
|
||||||
|
check_long_trend(prev_klu, bullish_trend=True, min_bars=5):
|
||||||
|
curr_klu.set_pattern(Chan_KLU_PATTERN.BEARISH_ENGULFING)
|
||||||
|
return
|
||||||
|
|
||||||
|
# 3. 乌云盖顶:前阳后阴,后者开盘价高于前者最高价,收盘价在前者实体中部以下
|
||||||
|
# 要求前面有明显的上涨趋势
|
||||||
|
if prev_bullish and not curr_bullish and \
|
||||||
|
curr_klu.open > prev_klu.high and \
|
||||||
|
curr_klu.close < (prev_klu.open + prev_klu.close) / 2 and \
|
||||||
|
curr_klu.close > prev_klu.open and \
|
||||||
|
check_long_trend(prev_klu, bullish_trend=True, min_bars=5):
|
||||||
|
curr_klu.set_pattern(Chan_KLU_PATTERN.DARK_CLOUD_COVER)
|
||||||
|
return
|
||||||
|
|
||||||
|
# 4. 曙光初现:前阴后阳,后者开盘价低于前者最低价,收盘价在前者实体中部以上
|
||||||
|
# 要求前面有明显的下跌趋势
|
||||||
|
if not prev_bullish and curr_bullish and \
|
||||||
|
curr_klu.open < prev_klu.low and \
|
||||||
|
curr_klu.close > (prev_klu.open + prev_klu.close) / 2 and \
|
||||||
|
curr_klu.close < prev_klu.open and \
|
||||||
|
check_long_trend(prev_klu, bullish_trend=False, min_bars=5):
|
||||||
|
curr_klu.set_pattern(Chan_KLU_PATTERN.PIERCING_LINE)
|
||||||
|
return
|
||||||
|
|
||||||
|
# 平顶和平底移至三根K线形态中判断
|
||||||
|
|
||||||
|
def _detect_triple_pattern(self, first_klu, second_klu, third_klu):
|
||||||
|
"""检测三根K线形成的形态
|
||||||
|
包括:早晨之星、黄昏之星、平顶、平底
|
||||||
|
"""
|
||||||
|
# 如果前两根K线已经有形态,不再识别三K线形态
|
||||||
|
if first_klu.pattern != Chan_KLU_PATTERN.UNKNOWN or \
|
||||||
|
second_klu.pattern != Chan_KLU_PATTERN.UNKNOWN:
|
||||||
|
return
|
||||||
|
|
||||||
|
# 判断K线颜色(阴阳)
|
||||||
|
first_bullish = first_klu.close > first_klu.open
|
||||||
|
second_bullish = second_klu.close > second_klu.open
|
||||||
|
third_bullish = third_klu.close > third_klu.open
|
||||||
|
|
||||||
|
# 计算实体大小
|
||||||
|
first_body = abs(first_klu.close - first_klu.open)
|
||||||
|
second_body = abs(second_klu.close - second_klu.open)
|
||||||
|
third_body = abs(third_klu.close - third_klu.open)
|
||||||
|
|
||||||
|
# 检查是否存在长期趋势(至少需要5根K线的趋势)
|
||||||
|
def check_long_trend(klu, bullish_trend=True, min_bars=5):
|
||||||
|
"""检查是否存在长期趋势
|
||||||
|
bullish_trend=True: 检查上涨趋势
|
||||||
|
bullish_trend=False: 检查下跌趋势
|
||||||
|
min_bars: 最少需要多少根K线形成趋势
|
||||||
|
"""
|
||||||
|
if not klu or not klu.pre:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 使用EMA指标判断长期趋势
|
||||||
|
if klu.ema52 > 0:
|
||||||
|
if bullish_trend and klu.close < klu.ema52:
|
||||||
|
return False
|
||||||
|
if not bullish_trend and klu.close > klu.ema52:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 检查连续的K线方向
|
||||||
|
count = 0
|
||||||
|
current = klu.pre
|
||||||
|
|
||||||
|
while current and count < min_bars:
|
||||||
|
if not current.pre:
|
||||||
|
break
|
||||||
|
|
||||||
|
if bullish_trend:
|
||||||
|
# 上涨趋势:当前收盘价高于前一根收盘价
|
||||||
|
if current.close <= current.pre.close:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
# 下跌趋势:当前收盘价低于前一根收盘价
|
||||||
|
if current.close >= current.pre.close:
|
||||||
|
break
|
||||||
|
|
||||||
|
count += 1
|
||||||
|
current = current.pre
|
||||||
|
|
||||||
|
return count >= min_bars
|
||||||
|
|
||||||
|
# 1. 早晨之星:第一根阴线,第二根十字星或小实体,第三根阳线
|
||||||
|
# 要求前面有明显的下跌趋势
|
||||||
|
if not first_bullish and third_bullish and \
|
||||||
|
second_body < first_body * 0.3 and \
|
||||||
|
third_body > first_body * 0.5 and \
|
||||||
|
max(second_klu.open, second_klu.close) < first_klu.close and \
|
||||||
|
min(second_klu.open, second_klu.close) < third_klu.open and \
|
||||||
|
third_klu.close > (first_klu.open + first_klu.close) / 2 and \
|
||||||
|
check_long_trend(first_klu, bullish_trend=False, min_bars=7):
|
||||||
|
third_klu.set_pattern(Chan_KLU_PATTERN.MORNING_STAR)
|
||||||
|
return
|
||||||
|
|
||||||
|
# 2. 黄昏之星:第一根阳线,第二根十字星或小实体,第三根阴线
|
||||||
|
# 要求前面有明显的上涨趋势
|
||||||
|
if first_bullish and not third_bullish and \
|
||||||
|
second_body < first_body * 0.3 and \
|
||||||
|
third_body > first_body * 0.5 and \
|
||||||
|
min(second_klu.open, second_klu.close) > first_klu.close and \
|
||||||
|
max(second_klu.open, second_klu.close) > third_klu.open and \
|
||||||
|
third_klu.close < (first_klu.open + first_klu.close) / 2 and \
|
||||||
|
check_long_trend(first_klu, bullish_trend=True, min_bars=7):
|
||||||
|
third_klu.set_pattern(Chan_KLU_PATTERN.EVENING_STAR)
|
||||||
|
return
|
||||||
|
|
||||||
|
# 3. 平顶:三根K线的最高点几乎相同(上升趋势中更有意义)
|
||||||
|
# 要求前面有明显的上涨趋势
|
||||||
|
if (abs(first_klu.high - second_klu.high) / first_klu.high < 0.0002 and
|
||||||
|
abs(second_klu.high - third_klu.high) / second_klu.high < 0.0002 and
|
||||||
|
check_long_trend(first_klu, bullish_trend=True, min_bars=7)):
|
||||||
|
# 额外确认:价格接近阻力位或关键技术指标
|
||||||
|
is_near_resistance = False
|
||||||
|
|
||||||
|
# 检查是否接近EMA52阻力位
|
||||||
|
if first_klu.ema52 > 0:
|
||||||
|
resistance_level = first_klu.ema52
|
||||||
|
if abs(first_klu.high - resistance_level) / resistance_level < 0.01:
|
||||||
|
is_near_resistance = True
|
||||||
|
|
||||||
|
# 检查是否有成交量确认(成交量减少表示上涨动能减弱)
|
||||||
|
volume_confirmation = False
|
||||||
|
if (first_klu.volume > 0 and second_klu.volume > 0 and third_klu.volume > 0 and
|
||||||
|
third_klu.volume < second_klu.volume and second_klu.volume < first_klu.volume):
|
||||||
|
volume_confirmation = True
|
||||||
|
|
||||||
|
if is_near_resistance or volume_confirmation:
|
||||||
|
third_klu.set_pattern(Chan_KLU_PATTERN.TWEEZER_TOP)
|
||||||
|
return
|
||||||
|
|
||||||
|
# 4. 平底:三根K线的最低点几乎相同(下降趋势中更有意义)
|
||||||
|
# 要求前面有明显的下跌趋势
|
||||||
|
if (abs(first_klu.low - second_klu.low) / first_klu.low < 0.0002 and
|
||||||
|
abs(second_klu.low - third_klu.low) / second_klu.low < 0.0002 and
|
||||||
|
check_long_trend(first_klu, bullish_trend=False, min_bars=7)):
|
||||||
|
# 额外确认:价格接近支撑位或关键技术指标
|
||||||
|
is_near_support = False
|
||||||
|
|
||||||
|
# 检查是否接近EMA52支撑位
|
||||||
|
if first_klu.ema52 > 0:
|
||||||
|
support_level = first_klu.ema52
|
||||||
|
if abs(first_klu.low - support_level) / support_level < 0.01:
|
||||||
|
is_near_support = True
|
||||||
|
|
||||||
|
# 检查是否有成交量确认(成交量减少表示下跌动能减弱)
|
||||||
|
volume_confirmation = False
|
||||||
|
if (first_klu.volume > 0 and second_klu.volume > 0 and third_klu.volume > 0 and
|
||||||
|
third_klu.volume < second_klu.volume and second_klu.volume < first_klu.volume):
|
||||||
|
volume_confirmation = True
|
||||||
|
|
||||||
|
if is_near_support or volume_confirmation:
|
||||||
|
third_klu.set_pattern(Chan_KLU_PATTERN.TWEEZER_BOTTOM)
|
||||||
|
return
|
||||||
|
|
||||||
def get_decimal(self, value):
|
def get_decimal(self, value):
|
||||||
return Decimal("{:.2f}".format(value))
|
return Decimal("{:.2f}".format(value))
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
{
|
|
||||||
"strategy_name": "PatternTrader",
|
|
||||||
"params": {
|
|
||||||
"trailing": {
|
|
||||||
"trailing_stop": false,
|
|
||||||
"trailing_stop_positive": null,
|
|
||||||
"trailing_stop_positive_offset": 0.0,
|
|
||||||
"trailing_only_offset_is_reached": false
|
|
||||||
},
|
|
||||||
"max_open_trades": {
|
|
||||||
"max_open_trades": 1
|
|
||||||
},
|
|
||||||
"buy": {
|
|
||||||
"fast_ma": 9,
|
|
||||||
"lev": 2.9,
|
|
||||||
"slow_ma": 31,
|
|
||||||
"time": 29
|
|
||||||
},
|
|
||||||
"sell": {
|
|
||||||
"exit_delay": 6
|
|
||||||
},
|
|
||||||
"protection": {},
|
|
||||||
"roi": {
|
|
||||||
"0": 0.062,
|
|
||||||
"6": 0.019,
|
|
||||||
"16": 0.014,
|
|
||||||
"40": 0
|
|
||||||
},
|
|
||||||
"stoploss": {
|
|
||||||
"stoploss": -0.316
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ft_stratparam_v": 1,
|
|
||||||
"export_time": "2025-10-28 07:39:54.768566+00:00"
|
|
||||||
}
|
|
||||||
Vendored
BIN
Binary file not shown.
+1
-48
@@ -352,39 +352,8 @@ def analyze_chan(df, symbol=None, timeframe=None):
|
|||||||
|
|
||||||
# 初始化多时间周期数据以获取EMA52
|
# 初始化多时间周期数据以获取EMA52
|
||||||
ema52_dict = None
|
ema52_dict = None
|
||||||
# 先暂时不用这个功能,太慢了
|
|
||||||
if symbol and timeframe and False:
|
|
||||||
try:
|
|
||||||
# 获取不同时间周期的数据用于初始化
|
|
||||||
df_1h = get_kl_data(symbol, '1h', limit=1500) if timeframe != '1h' else df
|
|
||||||
df_1d = get_kl_data(symbol, '1d', limit=2000) if timeframe != '1d' else df
|
|
||||||
df_1M = get_kl_data(symbol, '1M', limit=1500) if timeframe != '1M' else df
|
|
||||||
|
|
||||||
# 添加指标
|
|
||||||
if df_1h is not None and len(df_1h) > 0:
|
|
||||||
df_1h = add_indicators(df_1h)
|
|
||||||
if df_1d is not None and len(df_1d) > 0:
|
|
||||||
df_1d = add_indicators(df_1d)
|
|
||||||
if df_1M is not None and len(df_1M) > 0:
|
|
||||||
df_1M = add_indicators(df_1M)
|
|
||||||
|
|
||||||
# 初始化多时间周期数据
|
|
||||||
chan.init_dataframes(df, df_1h, df_1d, df_1M)
|
|
||||||
|
|
||||||
# 获取EMA52数据
|
|
||||||
ema52_dict = chan.get_ema52_dict()
|
|
||||||
# 处理NaN值
|
|
||||||
if ema52_dict:
|
|
||||||
for key, value in ema52_dict.items():
|
|
||||||
if pd.isna(value) or value is None:
|
|
||||||
ema52_dict[key] = None
|
|
||||||
else:
|
|
||||||
ema52_dict[key] = float(value)
|
|
||||||
except Exception as e:
|
|
||||||
print(f"获取多时间周期EMA52数据失败: {e}")
|
|
||||||
ema52_dict = None
|
|
||||||
|
|
||||||
# 获取分析结果
|
# 获取分析结果
|
||||||
|
klu_list = chan.get_kl_data(df)
|
||||||
klc_list = chan.get_klc_list(df)
|
klc_list = chan.get_klc_list(df)
|
||||||
bi_list = chan.cal_bi_list(klc_list)
|
bi_list = chan.cal_bi_list(klc_list)
|
||||||
#for index in range(0, 10):
|
#for index in range(0, 10):
|
||||||
@@ -408,8 +377,6 @@ def analyze_chan(df, symbol=None, timeframe=None):
|
|||||||
chan_macd = None
|
chan_macd = None
|
||||||
chan_macd_data = {}
|
chan_macd_data = {}
|
||||||
try:
|
try:
|
||||||
# 直接使用ChanLun的get_klu_list方法获取KLU列表
|
|
||||||
klu_list = chan.get_klu_list(df)
|
|
||||||
|
|
||||||
if klu_list and len(klu_list) > 0:
|
if klu_list and len(klu_list) > 0:
|
||||||
print(f"获取到KLU列表,长度: {len(klu_list)}")
|
print(f"获取到KLU列表,长度: {len(klu_list)}")
|
||||||
@@ -457,20 +424,6 @@ def analyze_chan(df, symbol=None, timeframe=None):
|
|||||||
'cross0_down_list': []
|
'cross0_down_list': []
|
||||||
}
|
}
|
||||||
|
|
||||||
# 获取原始K线数据用于KLU分型分析
|
|
||||||
klu_list = []
|
|
||||||
try:
|
|
||||||
# 尝试获取KLU数据
|
|
||||||
if hasattr(chan, 'get_klu_list'):
|
|
||||||
klu_list = chan.get_klu_list(df)
|
|
||||||
elif hasattr(chan, 'klu_list'):
|
|
||||||
klu_list = chan.klu_list
|
|
||||||
else:
|
|
||||||
# 如果没有专门的KLU方法,尝试从KLC获取原始K线数据
|
|
||||||
pass
|
|
||||||
except Exception as e:
|
|
||||||
klu_list = []
|
|
||||||
|
|
||||||
# 提取K线分型信息
|
# 提取K线分型信息
|
||||||
klc_fx_info = []
|
klc_fx_info = []
|
||||||
for klc in klc_list:
|
for klc in klc_list:
|
||||||
|
|||||||
Reference in New Issue
Block a user