diff --git a/ChanKLC.py b/ChanKLC.py index 2a5b8f0..d728911 100644 --- a/ChanKLC.py +++ b/ChanKLC.py @@ -50,6 +50,13 @@ class ChanKLC(): self.trend = Chan_PRICE_TREND.UNKNOWN def set_trend(self, trend): self.trend = trend + def to_string(self): + out = "" + start = self.start_time if self.start_time is not None else "" + end = self.end_time if self.end_time is not None else "" + price_diff = getattr(self, 'price_diff', None) + out += str(start) + " " + str(end) + " " + str(self.close) + " " + str(self.ema24) + " " + str(self.ema52) + " " + str(self.trend) + " " + str(self.close - self.ema52) + return out def set_klc_fx_type(self, klc_fx_type): #print(self.start_time, klc_fx_type, self.get_feature_data()['klu_macd'], self.get_feature_data()['klu_macdhist'], self.get_feature_data()['klu_rsi']) self.klc_fx_type = klc_fx_type diff --git a/ChanLun.py b/ChanLun.py index a7e16f0..7606a15 100644 --- a/ChanLun.py +++ b/ChanLun.py @@ -74,18 +74,24 @@ class ChanLun(): if len(self.tf_df_dict) > 0: return {key: self.tf_df_dict[key].get_ema24() for key in self.ema_symbols} return None + def get_current_klc_dict(self): + if len(self.tf_df_dict) > 0: + return {key: self.tf_df_dict[key].get_current_klc() for key in self.ema_symbols} + return None def cal_bsp(self): return def check_fx(self, klc): if klc.pre and klc.next: if klc.high > klc.pre.high and klc.high > klc.next.high: - klc.set_fx(Chan_FX_TYPE.TOP) - #print(klc.start_time, klc.end_time,klc.next.start_time, klc.next.end_time,klc.fx, "TOP") + if klc.close > klc.ema52 or klc.next.close > klc.next.ema52: + klc.set_fx(Chan_FX_TYPE.TOP) + #print(klc.start_time, klc.end_time,klc.next.start_time, klc.next.end_time,klc.fx, "TOP") return Chan_FX_TYPE.TOP elif klc.low < klc.pre.low and klc.low < klc.next.low: - klc.set_fx(Chan_FX_TYPE.BOTTOM) - #print(klc.start_time, klc.end_time,klc.next.start_time, klc.next.end_time,klc.fx, "BOTTOM") - return Chan_FX_TYPE.BOTTOM + if klc.close < klc.ema52 or klc.next.close < klc.next.ema52: + klc.set_fx(Chan_FX_TYPE.BOTTOM) + #print(klc.start_time, klc.end_time,klc.next.start_time, klc.next.end_time,klc.fx, "BOTTOM") + return Chan_FX_TYPE.BOTTOM return Chan_FX_TYPE.UNKNOWN def add_indicators(self, df): fast = 12 diff --git a/ChanMACDHistSet.py b/ChanMACDHistSet.py index d1d59d5..f585ba6 100644 --- a/ChanMACDHistSet.py +++ b/ChanMACDHistSet.py @@ -26,7 +26,7 @@ class ChanMACDHistSet(): def set_middle_klu(self, middle_klu): self.middle_klu = middle_klu #self.middle_area = abs(middle_klu.macdhist) - #self.middle_klu = None + self.middle_klu = None def set_unittf_div(self, unittf_div): self.unittf_div = unittf_div def add_klu(self, klu): diff --git a/TF_DF.py b/TF_DF.py index a195ade..f335c63 100644 --- a/TF_DF.py +++ b/TF_DF.py @@ -56,6 +56,10 @@ class TF_DF(): return None return float(ema24_value) return None + def get_current_klc(self): + if len(self.klc_list) > 0: + return self.klc_list[-2] + return None def add_indicators(self, df): fast = 12 slow = 26 @@ -93,14 +97,33 @@ class TF_DF(): df['macd'] = macd['macd'] df['macdsignal'] = macd['macdsignal'] df['macdhist'] = macd['macdhist'] - df['ema5'] = ta.EMA(df, timeperiod=5) - df['ema10'] = ta.EMA(df, timeperiod=10) - df['ema24'] = ta.EMA(df, timeperiod=24) - df['ema26'] = ta.EMA(df, timeperiod=26) - df['ema52'] = ta.EMA(df, timeperiod=52) + df['ema5'] = self.cal_ema(df, 5) + df['ema10'] = self.cal_ema(df, 10) + df['ema24'] = self.cal_ema(df, 24) + df['ema26'] = self.cal_ema(df, 26) + df['ema52'] = self.cal_ema(df, 52) df['rsi'] = ta.RSI(df, timeperiod=14) df['volume_ratio'] = self.cal_volume_ratio(df) return df + @staticmethod + def cal_ema(df, timeperiod): + """ + 计算 EMA,优先使用 pandas ewm(adjust=False) 以贴近前端/TradingView 显示; + 必要时回退到 TA-Lib(abstract)。 + """ + try: + series = df['close'].astype(float) if isinstance(df, pd.DataFrame) else pd.Series(df).astype(float) + return series.ewm(span=int(timeperiod), adjust=False).mean() + except Exception: + try: + if isinstance(df, pd.DataFrame): + return ta.EMA(df, timeperiod=int(timeperiod)) + except Exception: + pass + # 最后回退:返回同索引的 NaN 序列 + if isinstance(df, pd.DataFrame) and 'close' in df: + return pd.Series(np.nan, index=df.index) + return pd.Series(dtype=float) def check_fx(self, klc): if klc.pre and klc.next: if klc.high > klc.pre.high and klc.high > klc.next.high: diff --git a/strategies/ChanLun_BTC.py b/strategies/ChanLun_BTC.py index b2e78f5..dba6a92 100644 --- a/strategies/ChanLun_BTC.py +++ b/strategies/ChanLun_BTC.py @@ -22,7 +22,7 @@ logger = logging.getLogger(__name__) # freqtrade trade -c ./user_data/Chan/config/ChanLun_BTC_30.json --strategy ChanLun_BTC --strategy-path ./user_data/Chan/strategies # freqtrade backtesting -c ./user_data/Chan/config/ChanLun_BTC_30.json --strategy ChanLun_BTC --strategy-path ./user_data/Chan/strategies --timerange=20250901- -# freqtrade download-data -c ./user_data/Chan/config/ChanLun_BTC_30.json -t 1m --pairs BTC/USDT:USDT --timerange=20250405- +# freqtrade download-data -c ./user_data/Chan/config/ChanLun_BTC_30.json -t 1m 1m 1h 1d 1M --pairs BTC/USDT:USDT --timerange=20250405- # freqtrade download-data -c ./user_data/Chan/config/ChanLun_BTC_30.json -t 1m 1h 1d 1M --pairs BTC/USDT --timerange=20170101- # freqtrade hyperopt --hyperopt-loss SharpeHyperOptLossDaily --spaces roi --strategy ChanLun_BTC --strategy-path ./user_data/Chan/strategies -c ./user_data/Chan/config/ChanLun_BTC_30.json -e 200 --timerange=20250201-20250901 # freqtrade edge -c ./user_data/Chan/config/ChanLun_BTC_30.json --strategy ChanLun_BTC --strategy-path ./user_data/Chan/strategies --timerange 20250721-20250901 @@ -120,13 +120,16 @@ class ChanLun_BTC(IStrategy): dataframe_1d = self.dp.get_pair_dataframe(pair=self.pair, timeframe='1d') dataframe_1M = self.dp.get_pair_dataframe(pair=self.pair, timeframe='1M') self.chan.init_dataframes(dataframe_m, dataframe_1h, dataframe_1d, dataframe_1M) - self.print_all_ema52() + self.print_all_current_klc() def print_all_ema52(self): for key, value in self.chan.get_ema52_dict().items(): print(key, value) def print_all_ema24(self): for key, value in self.chan.get_ema24_dict().items(): print(key, value) + def print_all_current_klc(self): + for key, value in self.chan.get_current_klc_dict().items(): + print(key, value.to_string()) def add_indicators(self, df): fast = 12 slow = 26 diff --git a/web/app.py b/web/app.py index 92ac231..38d4384 100644 --- a/web/app.py +++ b/web/app.py @@ -352,12 +352,13 @@ def analyze_chan(df, symbol=None, timeframe=None): # 初始化多时间周期数据以获取EMA52 ema52_dict = None - if symbol and timeframe: + # 先暂时不用这个功能,太慢了 + if symbol and timeframe and False: try: # 获取不同时间周期的数据用于初始化 - df_1h = get_kl_data(symbol, '1h', limit=800) if timeframe != '1h' else df - df_1d = get_kl_data(symbol, '1d', limit=800) if timeframe != '1d' else df - df_1M = get_kl_data(symbol, '1M', limit=800) if timeframe != '1M' else df + 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: @@ -1659,6 +1660,24 @@ def analyze(): else: replay_data = None + # 基于已有 KLC 列表生成趋势标记(不做额外计算) + klc_trend = [] + try: + for klc in analysis_result.get('klc_list', []): + trend_val = getattr(klc, 'trend', None) + t_obj = getattr(klc, 'end_time', None) or getattr(klc, 'start_time', None) + if trend_val is None or t_obj is None: + continue + # 统一成字符串:UP/DOWN/FLAT/UNKNOWN + trend_name = str(trend_val) + if '.' in trend_name: + trend_name = trend_name.split('.')[-1] + time_str = format_time_safely(t_obj, client_tz) + if time_str: + klc_trend.append({'time': time_str, 'trend': trend_name}) + except Exception: + klc_trend = [] + # 添加主周期分析结果到返回数据 result.update({ 'kline_data': clean_dataframe_for_json(df).to_dict('records'), @@ -1753,7 +1772,9 @@ def analyze(): # 添加ChanMACD分析数据 'chan_macd': serialize_chan_macd_data(analysis_result.get('chan_macd', {}), client_tz), # 添加多时间周期EMA52数据 - 'ema52_dict': analysis_result.get('ema52_dict', {}) + 'ema52_dict': analysis_result.get('ema52_dict', {}), + # 直接输出KLC趋势标记(使用已有trend字段) + 'klc_trend': klc_trend }) # 如果生成了回放数据,添加到返回结果中 @@ -1775,6 +1796,23 @@ def analyze(): # 计算小周期MACD数据 element_macd_data = calculate_macd(element_df) + # 组装小周期 KLC 趋势(仅提取已有 trend,不做重算) + try: + element_klc_trend = [] + for klc in element_analysis.get('klc_list', []): + trend_val = getattr(klc, 'trend', None) + t_obj = getattr(klc, 'end_time', None) or getattr(klc, 'start_time', None) + if trend_val is None or t_obj is None: + continue + trend_name = str(trend_val) + if '.' in trend_name: + trend_name = trend_name.split('.')[-1] + time_str = format_time_safely(t_obj, client_tz) + if time_str: + element_klc_trend.append({'time': time_str, 'trend': trend_name}) + except Exception: + element_klc_trend = [] + # 添加小周期分析结果到返回数据 result['element_timeframe'] = element_timeframe result['element_macd'] = element_macd_data # 添加小周期MACD数据 @@ -1880,6 +1918,9 @@ def analyze(): # 添加次周期ChanMACD分析数据 result['element_chan_macd'] = serialize_chan_macd_data(element_analysis.get('chan_macd', {}), client_tz) + # 添加小周期 KLC 趋势标记 + result['element_klc_trend'] = element_klc_trend + pass return jsonify(result) diff --git a/web/templates/index.html b/web/templates/index.html index 7a01d72..528aade 100644 --- a/web/templates/index.html +++ b/web/templates/index.html @@ -801,8 +801,8 @@