添加klc识别ema线接触

This commit is contained in:
jackyu66git
2026-02-08 22:01:45 +08:00
parent b9c11b6163
commit 10b8ba398b
6 changed files with 305 additions and 32 deletions
+24
View File
@@ -22,6 +22,30 @@ class Chan_K_DIR(Enum):
BULL = auto() BULL = auto()
BEAR = auto() BEAR = auto()
CROSS = auto() CROSS = auto()
class Chan_EMA_POS(Enum):
"""K线与任意EMA的位置关系(与趋势方向无关的客观分类,支持threshold容差)"""
ABOVE = auto() # 完全在EMA上方(远离):low > ema + threshold
NEAR_ABOVE = auto() # 在EMA上方但接近:ema < low <= ema + threshold
CROSS_CLOSE_ABOVE = auto() # 跨越EMA,收盘在上方:close > ema, low <= ema(含threshold范围内触碰)
ON_EMA = auto() # 收盘价在EMA附近:abs(close - ema) <= threshold
CROSS_CLOSE_BELOW = auto() # 跨越EMA,收盘在下方:close < ema, high >= ema(含threshold范围内触碰)
NEAR_BELOW = auto() # 在EMA下方但接近:ema - threshold <= high < ema
BELOW = auto() # 完全在EMA下方(远离):high < ema - threshold
UNKNOWN = auto() # 未知(EMA值无效)
class Chan_EMA_SEMANTIC(Enum):
"""K线与EMA结合趋势方向的语义状态(用于交易判断)"""
STRONG_TREND = auto() # 7: 顺势K线完全在EMA趋势侧(强势,远未及EMA)
TREND_SIDE = auto() # 6: 完全在EMA趋势侧(正常趋势运行)
RECOVER = auto() # 5: 逆势后穿越EMA回到趋势侧(收复EMA,趋势恢复)
TOUCH_FAIL = auto() # 4: 逆势触碰EMA但未穿越(反弹/反抽力度不足)
DEEP_COUNTER = auto() # 3: 完全在EMA逆势侧(深度回调/反抽)
BREAK = auto() # 2: 穿越EMA,收盘在逆势侧(支撑/压力失败)
TOUCH_HOLD = auto() # 1: 触碰EMA,收盘守住趋势侧(支撑/压力有效)
WEAK_COUNTER = auto() # 8: 逆势K线完全在EMA逆势侧(弱势,远未到EMA)
APPROACHING = auto() # 9: K线接近EMA但未触碰(即将测试支撑/压力)
NEUTRAL = auto() # 0: 盘整/无法判断
class Chan_KL_TYPE(Enum): class Chan_KL_TYPE(Enum):
K_1S = auto() K_1S = auto()
K_1M = auto() K_1M = auto()
+254 -9
View File
@@ -1,7 +1,7 @@
import copy import copy
from typing import Dict, Optional from typing import Dict, Optional
from ChanEnum import Chan_FX_TYPE, Chan_KLINE_DIR, Chan_BI_DIR, Chan_KLC_FX, Chan_K_DIR, Chan_MACD_STATE, Chan_PRICE_TREND from ChanEnum import Chan_FX_TYPE, Chan_KLINE_DIR, Chan_BI_DIR, Chan_KLC_FX, Chan_K_DIR, Chan_MACD_STATE, Chan_PRICE_TREND, Chan_EMA_POS, Chan_EMA_SEMANTIC
import ChanKLU import ChanKLU
import ChanCTime import ChanCTime
@@ -45,11 +45,247 @@ class ChanKLC():
self.state = Chan_MACD_STATE.UNKNOWN self.state = Chan_MACD_STATE.UNKNOWN
self.continue_div = False self.continue_div = False
self.separate_div = False self.separate_div = False
self.ema52 = klu.ema52
self.ema24 = klu.ema24 self.ema24 = klu.ema24
self.ema52 = klu.ema52
self.ema104 = klu.ema104
self.ema156 = klu.ema156
self.ema208 = klu.ema208
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 self.klc_dir = Chan_KLINE_DIR.UP if klu.close > klu.open else Chan_KLINE_DIR.DOWN
self.ema_dir = klu.ema_dir
self.bsp = False
# EMA状态字典:key为EMA名称,value为 {'pos': Chan_EMA_POS, 'semantic': Chan_EMA_SEMANTIC}
self.ema_status = {}
# 向后兼容:保留 ema52_status 和 ema52_pos
self.ema52_status = 0
self.ema52_pos = Chan_EMA_POS.UNKNOWN
self.cal_all_ema_status()
# ==================== EMA 通用计算方法 ====================
@staticmethod
def cal_ema_pos(high, low, close, ema_value, threshold=0):
"""
计算K线与任意EMA的客观位置关系(与趋势方向无关,支持threshold容差)
参数:
high, low, close: K线的高低收盘价
ema_value: EMA的值
threshold: 容差值(绝对值),在此范围内视为"接近/触碰"
例如 BTC 价格 $100,000 时 threshold=100 表示差100点视为触碰
返回:
Chan_EMA_POS 枚举值
判断逻辑(以threshold=100, ema=97000为例):
ema_zone = [96900, 97100] EMA上下各扩展threshold
ABOVE: low > 97100 K线完全在zone上方(远离EMA)
NEAR_ABOVE: 97000 < low <= 97100 K线在上方但下影线进入zone(接近EMA)
CROSS_CLOSE_ABOVE: close > 97000, low <= 97000 K线穿越EMA,收盘在上方
ON_EMA: abs(close - 97000) <= 100 收盘价在zone内
CROSS_CLOSE_BELOW: close < 97000, high >= 97000 K线穿越EMA,收盘在下方
NEAR_BELOW: 96900 <= high < 97000 K线在下方但上影线进入zone(接近EMA)
BELOW: high < 96900 K线完全在zone下方(远离EMA)
"""
if ema_value is None or ema_value == 0:
return Chan_EMA_POS.UNKNOWN
ema_upper = ema_value + threshold # EMA zone 上界
ema_lower = ema_value - threshold # EMA zone 下界
# 1. 收盘价在EMA附近(zone内)
if threshold > 0 and abs(close - ema_value) <= threshold:
# 收盘价在zone内,但还需要看是否有实际穿越
if low <= ema_value and close >= ema_value:
return Chan_EMA_POS.CROSS_CLOSE_ABOVE # 实际穿越了精确EMA线
elif high >= ema_value and close <= ema_value:
return Chan_EMA_POS.CROSS_CLOSE_BELOW
return Chan_EMA_POS.ON_EMA
# 2. K线实际穿越了精确的EMA线
if close > ema_value and low <= ema_value:
return Chan_EMA_POS.CROSS_CLOSE_ABOVE
if close < ema_value and high >= ema_value:
return Chan_EMA_POS.CROSS_CLOSE_BELOW
if close == ema_value:
return Chan_EMA_POS.ON_EMA
# 3. 没有实际穿越,检查是否"接近"(在threshold zone内)
if close > ema_value:
# K线在EMA上方
if threshold > 0 and low <= ema_upper:
return Chan_EMA_POS.NEAR_ABOVE # 下影线进入zone,接近但未触碰
return Chan_EMA_POS.ABOVE # 远离EMA
else:
# K线在EMA下方
if threshold > 0 and high >= ema_lower:
return Chan_EMA_POS.NEAR_BELOW # 上影线进入zone,接近但未触碰
return Chan_EMA_POS.BELOW # 远离EMA
@staticmethod
def cal_ema_semantic(ema_pos, kline_dir, ema_dir):
"""
根据客观位置 + K线方向 + 趋势方向,计算语义状态
参数:
ema_pos: Chan_EMA_POS 客观位置
kline_dir: Chan_KLINE_DIR K线方向 (UP/DOWN/COMBINE/INCLUDED)
ema_dir: int 趋势方向 (1=多头, -1=空头, 0=盘整)
返回:
Chan_EMA_SEMANTIC 枚举值
语义含义(以多头为例,空头完全对称):
TOUCH_HOLD: 触碰EMA,收盘守住趋势侧(支撑/压力有效)
BREAK: 穿越EMA,收盘在逆势侧(支撑/压力失败)
DEEP_COUNTER: 完全在EMA逆势侧(深度回调/反抽)
TOUCH_FAIL: 逆势触碰EMA但未穿越(反弹/反抽力度不足)
RECOVER: 逆势后穿越EMA回到趋势侧(收复EMA)
TREND_SIDE: 完全在EMA趋势侧(正常运行)
STRONG_TREND: 顺势K线完全在EMA趋势侧(强势,远未及EMA)
WEAK_COUNTER: 逆势K线完全在EMA逆势侧(弱势,远未到EMA)
"""
if ema_pos == Chan_EMA_POS.UNKNOWN:
return Chan_EMA_SEMANTIC.NEUTRAL
# 统一处理:将多头/盘整和空头映射到同一套逻辑
# is_bull=True 时,"趋势侧"=上方,"逆势侧"=下方
# is_bull=False时,"趋势侧"=下方,"逆势侧"=上方
is_bull = ema_dir >= 0 # 多头和盘整都按多头逻辑处理
# K线是否是顺势方向(多头下UP为顺势,空头下DOWN为顺势)
is_trend_kline = (kline_dir == Chan_KLINE_DIR.UP) if is_bull else (kline_dir == Chan_KLINE_DIR.DOWN)
is_counter_kline = (kline_dir == Chan_KLINE_DIR.DOWN) if is_bull else (kline_dir == Chan_KLINE_DIR.UP)
# 位置映射:多头下 ABOVE=趋势侧, BELOW=逆势侧; 空头反过来
trend_side = Chan_EMA_POS.ABOVE if is_bull else Chan_EMA_POS.BELOW
counter_side = Chan_EMA_POS.BELOW if is_bull else Chan_EMA_POS.ABOVE
near_trend = Chan_EMA_POS.NEAR_ABOVE if is_bull else Chan_EMA_POS.NEAR_BELOW
near_counter = Chan_EMA_POS.NEAR_BELOW if is_bull else Chan_EMA_POS.NEAR_ABOVE
cross_to_trend = Chan_EMA_POS.CROSS_CLOSE_ABOVE if is_bull else Chan_EMA_POS.CROSS_CLOSE_BELOW
cross_to_counter = Chan_EMA_POS.CROSS_CLOSE_BELOW if is_bull else Chan_EMA_POS.CROSS_CLOSE_ABOVE
# COMBINE / INCLUDED 方向:只看位置,不区分强弱
if not is_trend_kline and not is_counter_kline:
if ema_pos == trend_side:
return Chan_EMA_SEMANTIC.TREND_SIDE
elif ema_pos in (near_trend, cross_to_trend, Chan_EMA_POS.ON_EMA):
return Chan_EMA_SEMANTIC.APPROACHING
elif ema_pos in (near_counter, cross_to_counter):
return Chan_EMA_SEMANTIC.APPROACHING
elif ema_pos == counter_side:
return Chan_EMA_SEMANTIC.DEEP_COUNTER
return Chan_EMA_SEMANTIC.NEUTRAL
# 逆势K线(多头下的下跌K线 / 空头下的上涨K线)
if is_counter_kline:
if ema_pos == trend_side:
return Chan_EMA_SEMANTIC.STRONG_TREND # 逆势K线仍在趋势侧(回调很浅)
elif ema_pos == near_trend:
return Chan_EMA_SEMANTIC.APPROACHING # 接近EMA,即将测试支撑/压力
elif ema_pos == cross_to_trend:
return Chan_EMA_SEMANTIC.TOUCH_HOLD # 触碰EMA后守住趋势侧
elif ema_pos == Chan_EMA_POS.ON_EMA:
return Chan_EMA_SEMANTIC.TOUCH_HOLD # 收盘在EMA附近,视为守住
elif ema_pos == cross_to_counter:
return Chan_EMA_SEMANTIC.BREAK # 穿越EMA到逆势侧
elif ema_pos == near_counter:
return Chan_EMA_SEMANTIC.BREAK # 接近EMA但收盘在逆势侧,也视为击穿
elif ema_pos == counter_side:
return Chan_EMA_SEMANTIC.DEEP_COUNTER # 完全在逆势侧
# 顺势K线(多头下的上涨K线 / 空头下的下跌K线)
if is_trend_kline:
if ema_pos == counter_side:
return Chan_EMA_SEMANTIC.WEAK_COUNTER # 顺势K线却在逆势侧(弱势)
elif ema_pos == near_counter:
return Chan_EMA_SEMANTIC.APPROACHING # 从逆势侧接近EMA
elif ema_pos == cross_to_counter:
return Chan_EMA_SEMANTIC.TOUCH_FAIL # 触碰EMA但未穿越回趋势侧
elif ema_pos == Chan_EMA_POS.ON_EMA:
return Chan_EMA_SEMANTIC.TOUCH_FAIL # 收盘在EMA附近,未确认突破
elif ema_pos == cross_to_trend:
return Chan_EMA_SEMANTIC.RECOVER # 从逆势侧穿越回趋势侧
elif ema_pos == near_trend:
return Chan_EMA_SEMANTIC.RECOVER # 接近趋势侧(刚收复EMA附近)
elif ema_pos == trend_side:
return Chan_EMA_SEMANTIC.TREND_SIDE # 完全在趋势侧(正常)
return Chan_EMA_SEMANTIC.NEUTRAL
@staticmethod
def semantic_to_int(semantic):
"""将 Chan_EMA_SEMANTIC 枚举转换为整数,兼容旧的 ema52_status 数值"""
mapping = {
Chan_EMA_SEMANTIC.TOUCH_HOLD: 1,
Chan_EMA_SEMANTIC.BREAK: 2,
Chan_EMA_SEMANTIC.DEEP_COUNTER: 3,
Chan_EMA_SEMANTIC.TOUCH_FAIL: 4,
Chan_EMA_SEMANTIC.RECOVER: 5,
Chan_EMA_SEMANTIC.TREND_SIDE: 6,
Chan_EMA_SEMANTIC.STRONG_TREND: 7,
Chan_EMA_SEMANTIC.WEAK_COUNTER: 8,
Chan_EMA_SEMANTIC.APPROACHING: 9,
Chan_EMA_SEMANTIC.NEUTRAL: 0,
}
return mapping.get(semantic, 0)
# threshold_pct: 阈值百分比,用于自动计算绝对阈值
# 例如 0.001 表示 EMA 值的 0.1%BTC $100,000 时 threshold = $100
threshold_pct = 0.001
def cal_all_ema_status(self):
"""
统一计算所有EMA与K线的位置关系和语义状态
threshold 自动按 EMA 值的百分比计算(cls.threshold_pct,默认0.1%
- BTC $100,000 时:threshold ≈ $100
- ETH $3,000 时:threshold ≈ $3
- SOL $200 时:threshold ≈ $0.2
结果存储在 self.ema_status 字典中,格式:
{
'ema24': {'pos': Chan_EMA_POS, 'semantic': Chan_EMA_SEMANTIC, 'value': float, 'threshold': float},
'ema52': {...},
...
}
同时保持向后兼容:self.ema52_pos 和 self.ema52_status
"""
ema_configs = {
'ema24': self.ema24,
'ema52': self.ema52,
'ema104': self.ema104,
'ema156': self.ema156,
'ema208': self.ema208,
}
self.ema_status = {}
for name, value in ema_configs.items():
# 按 EMA 值的百分比自动计算阈值
threshold = abs(value) * self.threshold_pct if value and self.threshold_pct > 0 else 0
pos = ChanKLC.cal_ema_pos(self.high, self.low, self.close, value, threshold)
semantic = ChanKLC.cal_ema_semantic(pos, self.dir, self.ema_dir)
self.ema_status[name] = {
'pos': pos,
'semantic': semantic,
'value': value,
'threshold': threshold,
}
# 向后兼容
self.ema52_pos = self.ema_status['ema52']['pos']
self.ema52_status = ChanKLC.semantic_to_int(self.ema_status['ema52']['semantic'])
def get_ema_pos(self, ema_name):
"""获取指定EMA的客观位置,如 klc.get_ema_pos('ema24')"""
if ema_name in self.ema_status:
return self.ema_status[ema_name]['pos']
return Chan_EMA_POS.UNKNOWN
def get_ema_semantic(self, ema_name):
"""获取指定EMA的语义状态,如 klc.get_ema_semantic('ema52')"""
if ema_name in self.ema_status:
return self.ema_status[ema_name]['semantic']
return Chan_EMA_SEMANTIC.NEUTRAL
def set_trend(self, trend): def set_trend(self, trend):
self.trend = trend self.trend = trend
def to_string(self): def to_string(self):
@@ -123,14 +359,23 @@ class ChanKLC():
self.rsi += self.klu_list[index].rsi self.rsi += self.klu_list[index].rsi
self.volume_ratio += self.klu_list[index].volume_ratio self.volume_ratio += self.klu_list[index].volume_ratio
self.macdhist += self.klu_list[index].macdhist self.macdhist += self.klu_list[index].macdhist
self.ema52 += self.klu_list[index].ema52
self.ema24 += self.klu_list[index].ema24 self.ema24 += self.klu_list[index].ema24
self.rsi = self.rsi / len(self.klu_list) self.ema52 += self.klu_list[index].ema52
self.volume_ratio = self.volume_ratio / len(self.klu_list) self.ema104 += self.klu_list[index].ema104
self.volume = self.volume / len(self.klu_list) self.ema156 += self.klu_list[index].ema156
self.macdhist = self.macdhist / len(self.klu_list) self.ema208 += self.klu_list[index].ema208
self.ema52 = self.ema52 / len(self.klu_list) if self.ema_dir != self.klu_list[index].ema_dir:
self.ema24 = self.ema24 / len(self.klu_list) self.ema_dir = 0
n = len(self.klu_list)
self.rsi = self.rsi / n
self.volume_ratio = self.volume_ratio / n
self.volume = self.volume / n
self.macdhist = self.macdhist / n
self.ema24 = self.ema24 / n
self.ema52 = self.ema52 / n
self.ema104 = self.ema104 / n
self.ema156 = self.ema156 / n
self.ema208 = self.ema208 / n
if len(self.klu_list) > 0: if len(self.klu_list) > 0:
self.macd = self.klu_list[-1].macd self.macd = self.klu_list[-1].macd
self.signal = self.klu_list[-1].signal self.signal = self.klu_list[-1].signal
+6 -4
View File
@@ -69,6 +69,8 @@ class ChanKLU:
self.mode4_touch52_no_zero = False # 先触碰EMA52但黄白线未归零 self.mode4_touch52_no_zero = False # 先触碰EMA52但黄白线未归零
self.div_type = "none" # {bearish, bullish, hidden_bearish, hidden_bullish, none} self.div_type = "none" # {bearish, bullish, hidden_bearish, hidden_bullish, none}
self.div_score = 0.0 # 背离强度(0-100) self.div_score = 0.0 # 背离强度(0-100)
self.ema_dir = 0
self.get_ema_dir()
#print(self.open, self.close, self.high, self.low, self.candle_dir, self.strength) #print(self.open, self.close, self.high, self.low, self.candle_dir, self.strength)
def set_macd_state(self, state): def set_macd_state(self, state):
self.macd_state = state self.macd_state = state
@@ -123,14 +125,14 @@ class ChanKLU:
return 0 return 0
else: else:
return 0 return 0
def ema_pattern(self): def get_ema_dir(self):
if self.check_indicators(): if self.check_indicators():
if self.ema24 > self.ema52 and self.ema52 > self.ema104 and self.ema104 > self.ema156: if self.ema24 > self.ema52 and self.ema52 > self.ema104 and self.ema104 > self.ema156:
return 1 self.ema_dir = 1
elif self.ema24 < self.ema52 and self.ema52 < self.ema104 and self.ema104 < self.ema156: elif self.ema24 < self.ema52 and self.ema52 < self.ema104 and self.ema104 < self.ema156:
return -1 self.ema_dir = -1
else: else:
return 0 self.ema_dir = 0
def check_indicators(self): def check_indicators(self):
if self.ema156 == 0: if self.ema156 == 0:
return False return False
+10 -5
View File
@@ -51,9 +51,9 @@ class ChanLun():
self.time1y = 12*30*24*60 self.time1y = 12*30*24*60
self.time_M_intervals = [2*30*24*60, 3*30*24*60, 6*30*24*60, 12*30*24*60] self.time_M_intervals = [2*30*24*60, 3*30*24*60, 6*30*24*60, 12*30*24*60]
self.time_M_symbols = ['2M', '3M', '6M', '1y'] self.time_M_symbols = ['2M', '3M', '6M', '1y']
self.time_symbols = ['1m', '2m', '3m', '5m', '10m', '15m', '20m', '30m', '45m','1h', '2h', '4h', '6h', '8h', '12h', '16h', '1d', '2d', '3d', '1w', '2w', '1M', '3M', '6M', '1y'] self.time_symbols = ['1m', '2m', '3m', '5m', '10m', '15m', '20m', '30m', '45m','1h', '2h', '4h', '6h', '8h', '12h', '16h', '1d', '2d', '3d']
self.tf_df_dict = {} self.tf_df_dict = {}
self.ema_symbols = ['5m', '10m', '15m', '20m', '30m', '45m', '1h', '2h', '4h', '6h', '8h', '12h', '16h', '1d', '2d', '3d'] self.ema_symbols = ['5m', '15m', '30m', '45m', '1h', '2h', '4h', '8h', '12h', '1d', '2d', '3d']
self.tf_df = TF_DF() self.tf_df = TF_DF()
def init_data(self, dataframe, intervals, timeframes): def init_data(self, dataframe, intervals, timeframes):
for index in range(0, len(intervals)): for index in range(0, len(intervals)):
@@ -74,10 +74,10 @@ class ChanLun():
if dataframe_d is not None: if dataframe_d is not None:
self.tf_df_dict['1d'] = TF_DF(dataframe_d, 1, '1d') self.tf_df_dict['1d'] = TF_DF(dataframe_d, 1, '1d')
self.init_data(dataframe_d, self.time_d_intervals, self.time_d_symbols) self.init_data(dataframe_d, self.time_d_intervals, self.time_d_symbols)
if dataframe_w is not None: if dataframe_w is not None and False:
self.tf_df_dict['1w'] = TF_DF(dataframe_w, 1, '1w') self.tf_df_dict['1w'] = TF_DF(dataframe_w, 1, '1w')
self.init_data(dataframe_w, self.time_w_intervals, self.time_w_symbols) self.init_data(dataframe_w, self.time_w_intervals, self.time_w_symbols)
if dataframe_M is not None: if dataframe_M is not None and False:
self.tf_df_dict['1M'] = TF_DF(dataframe_M, 1, '1M') self.tf_df_dict['1M'] = TF_DF(dataframe_M, 1, '1M')
self.init_data(dataframe_M, self.time_M_intervals, self.time_M_symbols) self.init_data(dataframe_M, self.time_M_intervals, self.time_M_symbols)
def get_ema52_dict(self): def get_ema52_dict(self):
@@ -105,7 +105,12 @@ class ChanLun():
if abs(price - ema52_dict[key]) < 100: if abs(price - ema52_dict[key]) < 100:
key_list.append(key) key_list.append(key)
return key_list return key_list
def get_ema_bsp(self, long_tf='1h', short_tf='15m'):
if long_tf in self.tf_df_dict and short_tf in self.tf_df_dict:
long_df = self.tf_df_dict[long_tf]
short_df = self.tf_df_dict[short_tf]
return long_df.get_ema_bsp(short_df)
return None
+3
View File
@@ -40,6 +40,7 @@ class TF_DF():
self.zs_list = [] self.zs_list = []
self.bsp_list = [] self.bsp_list = []
self.seg_list = [] self.seg_list = []
self.klc_fx_list = []
self.klu_list = self.cal_kl_data(self.dataframe) self.klu_list = self.cal_kl_data(self.dataframe)
self.klc_list = self.get_klc_list(self.klu_list) self.klc_list = self.get_klc_list(self.klu_list)
self.bi_list = self.cal_bi_list(self.klc_list) self.bi_list = self.cal_bi_list(self.klc_list)
@@ -47,6 +48,8 @@ class TF_DF():
self.zs_list = self.get_zs_list(self.bi_list, self.seg_list) self.zs_list = self.get_zs_list(self.bi_list, self.seg_list)
self.chanmacd = ChanMACD(self.klu_list) self.chanmacd = ChanMACD(self.klu_list)
self.klu_list = self.chanmacd.cal_macd_state() self.klu_list = self.chanmacd.cal_macd_state()
def get_ema52(self, index=-1): def get_ema52(self, index=-1):
if self.klu_list: if self.klu_list:
ema52_value = self.klu_list[index].ema52 ema52_value = self.klu_list[index].ema52
+8 -14
View File
@@ -112,27 +112,25 @@ class ChanLun_EMA52(IStrategy):
def informative_pairs(self): def informative_pairs(self):
return [(self.pair, "1h"), return [(self.pair, "1h"),
(self.pair, "1d"), (self.pair, "1d"),
(self.pair, "1M"), #(self.pair, "1M"),
(self.pair, "15m"), (self.pair, "15m"),
(self.pair, "1w"), #(self.pair, "1w"),
] ]
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
dataframe = self.add_indicators(dataframe) dataframe = self.add_indicators(dataframe)
long_df = self.dp.get_pair_dataframe(pair=self.pair, timeframe='1h') long_df = self.dp.get_pair_dataframe(pair=self.pair, timeframe='1h')
long_df = self.add_indicators(long_df) long_df = self.add_indicators(long_df)
long_df['entry_long'] = self.long_entry_condition(long_df)
dataframe['rsi'] = ta.RSI(long_df, timeperiod=14) dataframe['rsi'] = ta.RSI(long_df, timeperiod=14)
if self.last_time is None or self.last_time + timedelta(minutes=1) < datetime.now(): if self.last_time is None or self.last_time + timedelta(seconds=10) < datetime.now():
self.last_time = datetime.now() self.last_time = datetime.now()
logger.info("init_dataframes----------------------------") logger.info("init_dataframes----------------------------")
last_price = dataframe.iloc[-1]['close'] last_price = dataframe.iloc[-1]['close']
macdstr = str(long_df.iloc[-1]['macd']) + " " + str(long_df.iloc[-1]['macdsignal']) + " " + str(long_df.iloc[-1]['macdhist'])
date = dataframe.iloc[-1]['date'] date = dataframe.iloc[-1]['date']
tf_ema52_list = self.chan.check_price_ema52(last_price) tf_ema52_list = self.chan.check_price_ema52(last_price)
self.init_dataframes(dataframe) self.init_dataframes(dataframe)
logger.info("Date: " + date.strftime('%Y-%m-%d %H:%M:%S') + " Price: " + str(last_price) + " EMA52_list: " + str(tf_ema52_list) + " MACD: " + macdstr) logger.info("Date: " + date.strftime('%Y-%m-%d %H:%M:%S') + " Price: " + str(last_price) + " EMA52_list: " + str(tf_ema52_list))
#print(long_df.iloc[-1])
dataframe = resampled_merge(dataframe, long_df) dataframe = resampled_merge(dataframe, long_df)
#print(dataframe.iloc[-1])
return dataframe return dataframe
def ema_dir(self, dataframe): def ema_dir(self, dataframe):
""" """
@@ -200,9 +198,6 @@ class ChanLun_EMA52(IStrategy):
dataframe['ema_slope'] = (ema52 - ema52.shift(3)) / ema52.shift(3) * 100 dataframe['ema_slope'] = (ema52 - ema52.shift(3)) / ema52.shift(3) * 100
return dataframe return dataframe
def long_entry_condition(self, long_df):
long_entry_condition = (long_df['dir52'] > 0) & (long_df['dir156'] > 0) & (long_df['macdhist'] > 0)
return long_entry_condition
def add_indicators(self, dataframe): def add_indicators(self, dataframe):
dataframe['ema24'] = ta.EMA(dataframe, timeperiod=24) dataframe['ema24'] = ta.EMA(dataframe, timeperiod=24)
dataframe['dir24'] = dataframe['close'] - dataframe['ema24'] dataframe['dir24'] = dataframe['close'] - dataframe['ema24']
@@ -222,10 +217,9 @@ class ChanLun_EMA52(IStrategy):
dataframe_15m = self.dp.get_pair_dataframe(pair=self.pair, timeframe='15m') dataframe_15m = self.dp.get_pair_dataframe(pair=self.pair, timeframe='15m')
dataframe_1h = self.dp.get_pair_dataframe(pair=self.pair, timeframe='1h') dataframe_1h = self.dp.get_pair_dataframe(pair=self.pair, timeframe='1h')
dataframe_1d = self.dp.get_pair_dataframe(pair=self.pair, timeframe='1d') dataframe_1d = self.dp.get_pair_dataframe(pair=self.pair, timeframe='1d')
dataframe_1w = self.dp.get_pair_dataframe(pair=self.pair, timeframe='1w') #dataframe_1w = self.dp.get_pair_dataframe(pair=self.pair, timeframe='1w')
dataframe_1M = self.dp.get_pair_dataframe(pair=self.pair, timeframe='1M') #dataframe_1M = self.dp.get_pair_dataframe(pair=self.pair, timeframe='1M')
self.chan = ChanLun() self.chan.init_dataframes(dataframe_1m, dataframe_15m,dataframe_1h, dataframe_1d)
self.chan.init_dataframes(dataframe_1m, dataframe_15m,dataframe_1h, dataframe_1d, dataframe_1w, dataframe_1M)
def custom_entry_price(self, pair: str, trade: Trade | None, current_time: datetime, proposed_rate: float, def custom_entry_price(self, pair: str, trade: Trade | None, current_time: datetime, proposed_rate: float,
entry_tag: str | None, side: str, **kwargs) -> float: entry_tag: str | None, side: str, **kwargs) -> float:
new_entryprice = proposed_rate new_entryprice = proposed_rate