添加ema52显示
This commit is contained in:
+60
-27
@@ -21,29 +21,61 @@ from ChanMACD import ChanMACD
|
||||
from TF_DF import TF_DF
|
||||
|
||||
class ChanLun():
|
||||
time1 = 1
|
||||
time3 = 3
|
||||
time5 = 5
|
||||
time10 = 10
|
||||
time15 = 15
|
||||
time30 = 30
|
||||
time60 = 60
|
||||
time2h = 120
|
||||
time4h = 240
|
||||
time6h = 360
|
||||
time8h = 480
|
||||
time12h = 720
|
||||
time1d = 1440
|
||||
timeframes = [time1, time3, time5, time10, time15, time30, time60]
|
||||
tf_df_dict = {}
|
||||
def init_data(self, dataframe, ticker_indicator):
|
||||
for timeframe in self.timeframes:
|
||||
self.tf_df_dict[timeframe] = TF_DF(timeframe, dataframe, ticker_indicator)
|
||||
def cal_bsp(self, dataframe, ticker_indicator):
|
||||
# 初始化多周期数据
|
||||
self.init_data(dataframe, ticker_indicator)
|
||||
|
||||
|
||||
def __init__(self):
|
||||
self.time3m = 3
|
||||
self.time5m = 5
|
||||
self.time10m = 10
|
||||
self.time15m = 15
|
||||
self.time30m = 30
|
||||
self.time_m_intervals = [3, 5, 10, 15, 30]
|
||||
self.time_m_symbols = ['3m', '5m', '10m', '15m', '30m']
|
||||
self.time2h = 2*60
|
||||
self.time4h = 4*60
|
||||
self.time6h = 6*60
|
||||
self.time8h = 8*60
|
||||
self.time12h = 12*60
|
||||
self.time16h = 16*60
|
||||
self.time_h_intervals = [2*60, 4*60, 6*60, 8*60, 12*60, 16*60]
|
||||
self.time_h_symbols = ['2h', '4h', '6h', '8h', '12h', '16h']
|
||||
self.time2d = 2*24*60
|
||||
self.time3d = 3*24*60
|
||||
self.time1w = 7*24*60
|
||||
self.time2w = 14*24*60
|
||||
self.time_d_intervals = [2*24*60, 3*24*60, 7*24*60, 14*24*60]
|
||||
self.time_d_symbols = ['2d', '3d', '1w', '2w']
|
||||
self.time2M = 2*30*24*60
|
||||
self.time3M = 3*30*24*60
|
||||
self.time6M = 6*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_symbols = ['2M', '3M', '6M', '1y']
|
||||
self.time_symbols = ['1m', '3m', '5m', '15m', '30m', '1h', '2h', '4h', '6h', '8h', '12h', '16h', '1d', '2d', '3d', '1w', '2w', '1M', '3M', '6M', '1y']
|
||||
self.tf_df_dict = {}
|
||||
self.ema_symbols = ['1m', '3m', '5m', '15m', '30m', '1h', '2h', '4h', '6h', '8h', '12h', '16h', '1d', '2d', '3d', '1w', '2w']
|
||||
def init_data(self, dataframe, intervals, timeframes):
|
||||
for index in range(0, len(intervals)):
|
||||
timeframe = timeframes[index]
|
||||
interval = intervals[index]
|
||||
self.tf_df_dict[timeframe] = TF_DF(dataframe, interval, timeframe)
|
||||
def init_dataframes(self, dataframe_m, dataframe_h, dataframe_d, dataframe_M):
|
||||
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.tf_df_dict['1h'] = TF_DF(dataframe_h, 1, '1h')
|
||||
self.init_data(dataframe_h, self.time_h_intervals, self.time_h_symbols)
|
||||
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.tf_df_dict['1M'] = TF_DF(dataframe_M, 1, '1M')
|
||||
self.init_data(dataframe_M, self.time_M_intervals, self.time_M_symbols)
|
||||
def get_ema52_dict(self):
|
||||
if len(self.tf_df_dict) > 0:
|
||||
return {key: self.tf_df_dict[key].get_ema52() for key in self.ema_symbols}
|
||||
return None
|
||||
def get_ema24_dict(self):
|
||||
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 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:
|
||||
@@ -444,13 +476,14 @@ class ChanLun():
|
||||
hist = getattr(klc, 'macdhist', 0) if getattr(klc, 'macdhist', None) is not None else 0
|
||||
rsi = getattr(klc, 'rsi', None)
|
||||
trend = Chan_PRICE_TREND.UNKNOWN
|
||||
score = 0
|
||||
try:
|
||||
# 有效性
|
||||
price_valid = price is not None and price != 0
|
||||
ema24_valid = ema24 is not None and ema24 != 0
|
||||
ema52_valid = ema52 is not None and ema52 != 0
|
||||
# 多因子投票
|
||||
score = 0
|
||||
|
||||
# 1) 均线结构 + 价位
|
||||
if ema24_valid and ema52_valid:
|
||||
score += 1 if ema24 > ema52 else -1
|
||||
@@ -542,9 +575,9 @@ class ChanLun():
|
||||
setattr(klc, 'trend', trend)
|
||||
last_trend = trend
|
||||
price_diff = klc.close - klc.pre.close if klc.pre else 0
|
||||
if klc.index > len(klc_list) - 10:
|
||||
print(klc.start_time, klc.end_time, klc.close, klc.ema24, klc.ema52, klc.macd, klc.signal, klc.macdhist, klc.trend, price_diff)
|
||||
#print(klc.start_time, klc.end_time, klc.trend, price_diff)
|
||||
#if klc.index > len(klc_list) - 10:
|
||||
#print(klc.start_time, klc.end_time, klc.close, klc.ema24, klc.ema52, klc.macd, klc.signal, klc.macdhist, klc.trend, price_diff, score)
|
||||
#print(klc.start_time, klc.end_time, klc.trend, price_diff, score)
|
||||
return klc_list
|
||||
def cal_bi_list(self, klc_list):
|
||||
bi_list = []
|
||||
|
||||
@@ -20,11 +20,11 @@ import numpy as np
|
||||
from ChanMACD import ChanMACD
|
||||
|
||||
class TF_DF():
|
||||
def __init__(self, timeframe, df, ticker_indicator):
|
||||
def __init__(self, df, interval, timeframe):
|
||||
self.timeframe = timeframe
|
||||
self.dataframe = resample_to_interval(df, ticker_indicator*timeframe)
|
||||
self.interval = interval
|
||||
self.dataframe = resample_to_interval(df, interval)
|
||||
self.dataframe = self.add_indicators(self.dataframe)
|
||||
self.ticker_indicator = ticker_indicator
|
||||
self.klu_list = []
|
||||
self.klc_list = []
|
||||
self.bi_list = []
|
||||
@@ -40,6 +40,22 @@ class TF_DF():
|
||||
self.zs_list = self.cal_zs_list(self.bi_list, self.seg_list)
|
||||
self.chanmacd = ChanMACD(self.klu_list)
|
||||
self.klu_list = self.chanmacd.cal_macd_state()
|
||||
def get_ema52(self):
|
||||
if self.klu_list:
|
||||
ema52_value = self.klu_list[-1].ema52
|
||||
# 处理NaN值
|
||||
if pd.isna(ema52_value) or ema52_value is None:
|
||||
return None
|
||||
return float(ema52_value)
|
||||
return None
|
||||
def get_ema24(self):
|
||||
if self.klu_list:
|
||||
ema24_value = self.klu_list[-1].ema24
|
||||
# 处理NaN值
|
||||
if pd.isna(ema24_value) or ema24_value is None:
|
||||
return None
|
||||
return float(ema24_value)
|
||||
return None
|
||||
def add_indicators(self, df):
|
||||
fast = 12
|
||||
slow = 26
|
||||
@@ -223,7 +239,7 @@ class TF_DF():
|
||||
setattr(klc, 'trend', trend)
|
||||
last_trend = trend
|
||||
price_diff = klc.close - klc.pre.close if klc.pre else 0
|
||||
print(klc.start_time, klc.end_time, klc.close, klc.ema24, klc.ema52, klc.macd, klc.signal, klc.macdhist, klc.trend, price_diff)
|
||||
#print(klc.start_time, klc.end_time, klc.close, klc.ema24, klc.ema52, klc.macd, klc.signal, klc.macdhist, klc.trend, price_diff)
|
||||
#print(klc.start_time, klc.end_time, klc.trend, price_diff)
|
||||
return klc_list
|
||||
def cal_kl_data(self, dataframe:DataFrame):
|
||||
@@ -627,7 +643,6 @@ class TF_DF():
|
||||
bi_list[-1].add_klc(klc)
|
||||
klc.set_bi(bi_list[-1])
|
||||
#klc.set_klc_fx_type(Chan_KLC_FX.TOP3)
|
||||
klc.set_last_top_klu(last_top)
|
||||
#print(klc.start_time, klc.fx, "二类卖点Sell 1")
|
||||
else:
|
||||
# A new top found
|
||||
@@ -752,7 +767,6 @@ class TF_DF():
|
||||
bi_list[-1].add_klc(klc)
|
||||
klc.set_bi(bi_list[-1])
|
||||
#klc.set_klc_fx_type(Chan_KLC_FX.BOTTOM3)
|
||||
klc.set_last_bottom_klc(last_bottom)
|
||||
#print(last_bottom.start_time, last_bottom.end_time, "--------------------------------1")
|
||||
#print(klc.start_time, klc.fx, "二类买点Buy 1")
|
||||
else:
|
||||
|
||||
+63
-44
@@ -23,6 +23,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 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
|
||||
# freqtrade plot-dataframe -c ./user_data/Chan/config/ChanLun_BTC_30.json --strategy ChanLun_BTC --strategy-path ./user_data/Chan/strategies --timerange 20250721-20250901
|
||||
@@ -76,36 +77,56 @@ class ChanLun_BTC(IStrategy):
|
||||
|
||||
# 关闭分批止盈/仓位调整
|
||||
position_adjustment_enable = False
|
||||
startup_candle_count = 2880
|
||||
time3 = 3
|
||||
time5 = 5
|
||||
time15 = 15
|
||||
time30 = 30
|
||||
time60 = 60
|
||||
time2h = 120
|
||||
time4h = 240
|
||||
time1d = 1440
|
||||
startup_candle_count = 1600
|
||||
time3m = 3
|
||||
time5m = 5
|
||||
time10m = 10
|
||||
time15m = 15
|
||||
time30m = 30
|
||||
time_m = [3, 5, 10, 15, 30]
|
||||
|
||||
time1h = 60
|
||||
time2h = 2
|
||||
time4h = 4
|
||||
time6h = 6
|
||||
time8h = 8
|
||||
time12h = 12
|
||||
time16h = 16
|
||||
time_h = [2, 4, 6, 8, 12, 16]
|
||||
|
||||
time2d = 2
|
||||
time3d = 3
|
||||
time1w = 7
|
||||
time_d = [2, 3, 7]
|
||||
|
||||
time2M = 2
|
||||
time3M = 3
|
||||
time_M = [2, 3]
|
||||
|
||||
last_time = datetime.now()
|
||||
chan = ChanLun()
|
||||
last_order = None
|
||||
last_trade = None
|
||||
|
||||
pair = 'BTC/USDT:USDT'
|
||||
def informative_pairs(self):
|
||||
timeframes = ['1h', '1d', '1M']
|
||||
informative_pairs = [(self.pair, tf) for tf in timeframes]
|
||||
return informative_pairs
|
||||
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||
|
||||
dataframe = self.add_indicators(dataframe)
|
||||
# 仅保留15m(用于BSP)与60m(用于ATR过滤/止损)两个重采样
|
||||
dataframe_15 = resample_to_interval(dataframe, self.get_ticker_indicator() * 15)
|
||||
dataframe_60 = resample_to_interval(dataframe, self.get_ticker_indicator() * 60)
|
||||
# 计算多周期BSP(以15m为基准),并合并到15m数据上
|
||||
# 先给重采样帧补指标
|
||||
dataframe_15 = self.add_indicators(dataframe_15)
|
||||
dataframe_60 = self.add_indicators(dataframe_60)
|
||||
# 计算15m BSP
|
||||
bsp_15 = self.chan.cal_bsp(dataframe, self.get_ticker_indicator())
|
||||
# 合并15m与60m到主DF,生成 resample_*_* 列
|
||||
dataframe = resampled_merge(dataframe, dataframe_15)
|
||||
dataframe = resampled_merge(dataframe, dataframe_60)
|
||||
self.init_dataframes(dataframe)
|
||||
return dataframe
|
||||
def init_dataframes(self, dataframe_m):
|
||||
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_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()
|
||||
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 add_indicators(self, df):
|
||||
fast = 12
|
||||
slow = 26
|
||||
@@ -212,9 +233,9 @@ class ChanLun_BTC(IStrategy):
|
||||
return -0.05
|
||||
dataframe, _ = self.dp.get_analyzed_dataframe(trade.pair, self.timeframe)
|
||||
last_candle = dataframe.iloc[-1].squeeze()
|
||||
ema52_str = 'resample_{}_ema52'.format(self.get_ticker_indicator()*self.time15)
|
||||
ema52_str = 'resample_{}_ema52'.format(self.time15m)
|
||||
ema52_val = float(last_candle.get(ema52_str, 0) or 0)
|
||||
close_str = 'resample_{}_close'.format(self.get_ticker_indicator()*self.time15)
|
||||
close_str = 'resample_{}_close'.format(self.time15m)
|
||||
close_val = float(last_candle.get(close_str, 0) or 0)
|
||||
if close_val < ema52_val:
|
||||
return -0.01
|
||||
@@ -240,7 +261,7 @@ class ChanLun_BTC(IStrategy):
|
||||
if dataframe is None or len(dataframe) == 0:
|
||||
return False
|
||||
last = dataframe.iloc[-1]
|
||||
atr_str = 'resample_{}_atr'.format(self.get_ticker_indicator()*self.time60)
|
||||
atr_str = 'resample_{}_atr'.format(self.time1h)
|
||||
atr_val = float(last.get(atr_str, 0) or 0)
|
||||
if atr_val < 0.001:
|
||||
#logger.info(f"ATR过滤:atr={atr_val:.2f} < 100, 拒绝进场 {pair}")
|
||||
@@ -263,7 +284,7 @@ class ChanLun_BTC(IStrategy):
|
||||
# Obtain pair dataframe (just to show how to access it)
|
||||
dataframe, _ = self.dp.get_analyzed_dataframe(trade.pair, self.timeframe)
|
||||
last_candle = dataframe.iloc[-1].squeeze()
|
||||
atr_str = 'resample_{}_atr'.format(self.get_ticker_indicator()*self.time15)
|
||||
atr_str = 'resample_{}_atr'.format(elf.time15)
|
||||
# 保存开仓时的ATR值用于止损计算
|
||||
if (trade.nr_of_successful_entries == 1) and (order.ft_order_side == trade.entry_side):
|
||||
entry_atr = last_candle[atr_str] * 4
|
||||
@@ -271,13 +292,13 @@ class ChanLun_BTC(IStrategy):
|
||||
#logger.info(f"保存开仓时ATR值: {entry_atr}")
|
||||
return None
|
||||
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||
shift15 = self.time15
|
||||
shift60 = self.time60
|
||||
bsp_col = 'resample_{}_bsp_mtf'.format(self.get_ticker_indicator()*shift15)
|
||||
score_col = 'resample_{}_mtf_score'.format(self.get_ticker_indicator()*shift15)
|
||||
macdh_col = 'resample_{}_macdhist'.format(self.get_ticker_indicator()*shift15)
|
||||
c60_col = 'resample_{}_close'.format(self.get_ticker_indicator()*shift60)
|
||||
e60_col = 'resample_{}_ema52'.format(self.get_ticker_indicator()*shift60)
|
||||
shift15 = self.time15m
|
||||
shift60 = self.time1h
|
||||
bsp_col = 'resample_{}_bsp_mtf'.format(shift15)
|
||||
score_col = 'resample_{}_mtf_score'.format(shift15)
|
||||
macdh_col = 'resample_{}_macdhist'.format(shift15)
|
||||
c60_col = 'resample_{}_close'.format(shift60)
|
||||
e60_col = 'resample_{}_ema52'.format(shift60)
|
||||
# 强化过滤:15m BSP + 分数阈值 + 60m 趋势同向 + 15m MACD柱同向
|
||||
if all(col in dataframe.columns for col in [bsp_col, score_col, macdh_col, c60_col, e60_col]):
|
||||
dataframe.loc[
|
||||
@@ -298,12 +319,12 @@ class ChanLun_BTC(IStrategy):
|
||||
['enter_short', 'enter_tag']] = (1, 'short_bsp15_v2')
|
||||
return dataframe
|
||||
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||
shift15 = self.time15
|
||||
shift60 = self.time60
|
||||
bsp_col = 'resample_{}_bsp_mtf'.format(self.get_ticker_indicator()*shift15)
|
||||
score_col = 'resample_{}_mtf_score'.format(self.get_ticker_indicator()*shift15)
|
||||
c60_col = 'resample_{}_close'.format(self.get_ticker_indicator()*shift60)
|
||||
e60_col = 'resample_{}_ema52'.format(self.get_ticker_indicator()*shift60)
|
||||
shift15 = self.time15m
|
||||
shift60 = self.time1h
|
||||
bsp_col = 'resample_{}_bsp_mtf'.format(shift15)
|
||||
score_col = 'resample_{}_mtf_score'.format(shift15)
|
||||
c60_col = 'resample_{}_close'.format(shift60)
|
||||
e60_col = 'resample_{}_ema52'.format(shift60)
|
||||
# 反向强信号或60m趋势反向时平仓
|
||||
if all(col in dataframe.columns for col in [bsp_col, score_col, c60_col, e60_col]):
|
||||
dataframe.loc[
|
||||
@@ -323,6 +344,4 @@ class ChanLun_BTC(IStrategy):
|
||||
proposed_leverage: float, max_leverage: float, entry_tag: Optional[str], side: str,
|
||||
**kwargs) -> float:
|
||||
return self.lev
|
||||
|
||||
def get_ticker_indicator(self):
|
||||
return int(self.timeframe[:-1])
|
||||
|
||||
+45
-9
@@ -346,10 +346,43 @@ def calculate_macd(df):
|
||||
'histogram': histogram.tolist()
|
||||
}
|
||||
|
||||
def analyze_chan(df):
|
||||
def analyze_chan(df, symbol=None, timeframe=None):
|
||||
"""进行缠论分析"""
|
||||
chan = ChanLun()
|
||||
|
||||
# 初始化多时间周期数据以获取EMA52
|
||||
ema52_dict = None
|
||||
if symbol and timeframe:
|
||||
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
|
||||
|
||||
# 添加指标
|
||||
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
|
||||
|
||||
# 获取分析结果
|
||||
klc_list = chan.get_klc_list(df)
|
||||
bi_list = chan.cal_bi_list(klc_list)
|
||||
@@ -546,7 +579,8 @@ def analyze_chan(df):
|
||||
'trade_points': buy_sell_points,
|
||||
'klc_fx_info': klc_fx_info, # KLC分型信息
|
||||
'klu_fx_info': klu_fx_info, # 添加KLU分型信息
|
||||
'chan_macd': chan_macd_data # 添加ChanMACD分析数据
|
||||
'chan_macd': chan_macd_data, # 添加ChanMACD分析数据
|
||||
'ema52_dict': ema52_dict # 添加多时间周期EMA52数据
|
||||
}
|
||||
|
||||
def generate_replay_data(df, client_tz, symbol=None, element_timeframe=None, start_time=None, end_time=None):
|
||||
@@ -572,7 +606,7 @@ def generate_replay_data(df, client_tz, symbol=None, element_timeframe=None, sta
|
||||
current_df = add_indicators(current_df)
|
||||
|
||||
# 进行缠论分析
|
||||
analysis_result = analyze_chan(current_df)
|
||||
analysis_result = analyze_chan(current_df, symbol, timeframe)
|
||||
|
||||
# 计算MACD
|
||||
macd_data = calculate_macd(current_df)
|
||||
@@ -590,7 +624,7 @@ def generate_replay_data(df, client_tz, symbol=None, element_timeframe=None, sta
|
||||
if len(element_current_df) > 0:
|
||||
# 重新对当前时间范围的次周期数据进行缠论分析
|
||||
# 这样可以确保数据的准确性,避免时间筛选的复杂性
|
||||
element_current_analysis = analyze_chan(element_current_df)
|
||||
element_current_analysis = analyze_chan(element_current_df, symbol, element_timeframe)
|
||||
|
||||
# 直接使用分析结果,无需复杂的时间筛选
|
||||
filtered_bi_list = element_current_analysis['bi_list']
|
||||
@@ -1614,7 +1648,7 @@ def analyze():
|
||||
df = add_indicators(df)
|
||||
|
||||
# 进行缠论分析
|
||||
analysis_result = analyze_chan(df)
|
||||
analysis_result = analyze_chan(df, symbol, timeframe)
|
||||
|
||||
# 计算MACD
|
||||
macd_data = calculate_macd(df)
|
||||
@@ -1717,7 +1751,9 @@ def analyze():
|
||||
'fx_confirmed': bool(point['fx_confirmed']) # 分型是否确认
|
||||
} for point in analysis_result['klu_fx_info']],
|
||||
# 添加ChanMACD分析数据
|
||||
'chan_macd': serialize_chan_macd_data(analysis_result.get('chan_macd', {}), client_tz)
|
||||
'chan_macd': serialize_chan_macd_data(analysis_result.get('chan_macd', {}), client_tz),
|
||||
# 添加多时间周期EMA52数据
|
||||
'ema52_dict': analysis_result.get('ema52_dict', {})
|
||||
})
|
||||
|
||||
# 如果生成了回放数据,添加到返回结果中
|
||||
@@ -1734,7 +1770,7 @@ def analyze():
|
||||
element_df = add_indicators(element_df)
|
||||
|
||||
# 对小周期数据进行缠论分析
|
||||
element_analysis = analyze_chan(element_df)
|
||||
element_analysis = analyze_chan(element_df, symbol, element_timeframe)
|
||||
|
||||
# 计算小周期MACD数据
|
||||
element_macd_data = calculate_macd(element_df)
|
||||
@@ -1936,7 +1972,7 @@ def test_element_data():
|
||||
|
||||
# 分析次周期数据
|
||||
element_df = add_indicators(element_df)
|
||||
element_analysis = analyze_chan(element_df)
|
||||
element_analysis = analyze_chan(element_df, symbol, element_timeframe)
|
||||
|
||||
return jsonify({
|
||||
'main_data_count': len(main_df),
|
||||
@@ -2123,7 +2159,7 @@ def filter_stocks():
|
||||
continue
|
||||
|
||||
# 进行缠论分析
|
||||
analysis_result = analyze_chan(df)
|
||||
analysis_result = analyze_chan(df, symbol, timeframe)
|
||||
|
||||
if not analysis_result or 'klc_fx_info' not in analysis_result:
|
||||
continue
|
||||
|
||||
+201
-2
@@ -1788,6 +1788,7 @@
|
||||
elementBollingerSeries: [],
|
||||
maSeries: [], // 添加均线系列
|
||||
bbSeries: [], // 添加布林带系列
|
||||
ema52Series: [], // 添加EMA52系列数组
|
||||
chanMacdLineSeries: null, // ChanMACD线
|
||||
chanMacdSignalSeries: null, // ChanMACD信号线
|
||||
chanMacdHistSeries: null, // ChanMACD柱状图
|
||||
@@ -2426,7 +2427,8 @@
|
||||
mainBollingerSeries: [],
|
||||
elementBollingerSeries: [],
|
||||
maSeries: [], // 添加均线系列数组
|
||||
bbSeries: [] // 添加布林带系列数组
|
||||
bbSeries: [], // 添加布林带系列数组
|
||||
ema52Series: [] // 添加EMA52系列数组
|
||||
},
|
||||
state: {
|
||||
isInitialized: false,
|
||||
@@ -5680,6 +5682,11 @@
|
||||
displayTradePoints();
|
||||
}
|
||||
|
||||
// 更新EMA52显示
|
||||
if (currentData) {
|
||||
updateEMA52Display(currentData);
|
||||
}
|
||||
|
||||
console.log('图表初始化完成');
|
||||
} catch (e) {
|
||||
console.error('图表初始化错误:', e);
|
||||
@@ -5912,6 +5919,9 @@
|
||||
// 重新显示笔、线段和中枢等图形
|
||||
redrawFractalElements();
|
||||
|
||||
// 更新EMA52显示
|
||||
updateEMA52Display(currentData);
|
||||
|
||||
// 恢复之前的可视范围 - 优先使用visibleRange以确保时间轴对齐
|
||||
if (tvWidget.mainChart) {
|
||||
if (tvWidget.state.visibleRange) {
|
||||
@@ -7121,6 +7131,8 @@
|
||||
// 先销毁现有图表实例
|
||||
if (tvWidget.mainChart) {
|
||||
try {
|
||||
// 清理EMA52系列
|
||||
clearEMA52Series();
|
||||
// 销毁主图表及其关联的线系列
|
||||
tvWidget.mainChart = null;
|
||||
tvWidget.volumeChart = null;
|
||||
@@ -7147,7 +7159,8 @@
|
||||
mainBollingerSeries: [],
|
||||
elementBollingerSeries: [],
|
||||
maSeries: [], // 添加均线系列
|
||||
bbSeries: [] // 添加布林带系列
|
||||
bbSeries: [], // 添加布林带系列
|
||||
ema52Series: [] // 添加EMA52系列数组
|
||||
};
|
||||
} catch (e) {
|
||||
console.error('销毁图表错误:', e);
|
||||
@@ -7576,6 +7589,12 @@
|
||||
// 更新表格数据
|
||||
updateTables(data);
|
||||
|
||||
// 只有在图表已初始化且调用了updateTradingViewData时才不需要重复调用EMA52显示
|
||||
// 如果图表未初始化,调用了initTradingView,那么EMA52显示已经在initTradingView中处理了
|
||||
// 但为了确保在所有情况下都能正确显示,这里统一调用一次
|
||||
if (currentData && currentData.ema52_dict) {
|
||||
updateEMA52Display(currentData);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8653,6 +8672,186 @@
|
||||
let bollingerBands = []; // 存储所有布林带配置
|
||||
let bbIdCounter = 0; // 布林带ID计数器
|
||||
|
||||
// 清理EMA52系列
|
||||
function clearEMA52Series() {
|
||||
// 清理所有EMA52相关系列(包括线系列和标记系列)
|
||||
if (tvWidget && tvWidget.series && tvWidget.series.ema52Series) {
|
||||
tvWidget.series.ema52Series.forEach(series => {
|
||||
try {
|
||||
if (tvWidget.mainChart) {
|
||||
tvWidget.mainChart.removeSeries(series);
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('移除EMA52系列失败:', e);
|
||||
}
|
||||
});
|
||||
tvWidget.series.ema52Series = [];
|
||||
}
|
||||
|
||||
console.log('✅ EMA52系列已清理');
|
||||
}
|
||||
|
||||
// 存储上次的EMA52数据,用于比较
|
||||
let lastEMA52Data = null;
|
||||
|
||||
// 更新EMA52显示
|
||||
function updateEMA52Display(data) {
|
||||
console.log('更新EMA52显示', data.ema52_dict);
|
||||
|
||||
// 检查是否有EMA52数据
|
||||
if (!data.ema52_dict || Object.keys(data.ema52_dict).length === 0) {
|
||||
// 即使没有数据也要清理之前的系列
|
||||
clearEMA52Series();
|
||||
lastEMA52Data = null;
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查数据是否与上次相同,如果相同则跳过更新
|
||||
const currentDataStr = JSON.stringify(data.ema52_dict);
|
||||
if (lastEMA52Data === currentDataStr) {
|
||||
console.log('EMA52数据未变化,跳过更新');
|
||||
return;
|
||||
}
|
||||
|
||||
// 清除之前的EMA52线系列
|
||||
clearEMA52Series();
|
||||
|
||||
// 保存当前数据
|
||||
lastEMA52Data = currentDataStr;
|
||||
|
||||
// 定义时间周期的显示顺序和颜色
|
||||
const timeframeColors = {
|
||||
'1m': '#FF0000', // 红色
|
||||
'3m': '#FF6600', // 橙红色
|
||||
'5m': '#FF9900', // 橙色
|
||||
'15m': '#FFCC00', // 黄色
|
||||
'30m': '#99FF00', // 黄绿色
|
||||
'1h': '#00FF00', // 绿色
|
||||
'2h': '#00FF99', // 青绿色
|
||||
'4h': '#00FFFF', // 青色
|
||||
'6h': '#0099FF', // 蓝青色
|
||||
'8h': '#0066FF', // 蓝色
|
||||
'12h': '#3300FF', // 蓝紫色
|
||||
'16h': '#6600FF', // 紫色
|
||||
'1d': '#9900FF', // 紫红色
|
||||
'2d': '#CC00FF', // 品红色
|
||||
'3d': '#FF00CC', // 粉红色
|
||||
'1w': '#FF0099', // 玫瑰色
|
||||
'2w': '#FF3366' // 深粉色
|
||||
};
|
||||
|
||||
// 按照预定义顺序排列时间周期
|
||||
const orderedTimeframes = ['1m', '3m', '5m', '15m', '30m', '1h', '2h', '4h', '6h', '8h', '12h', '16h', '1d', '2d', '3d', '1w', '2w'];
|
||||
|
||||
// 获取主图表容器
|
||||
const chartContainer = document.getElementById('tradingview_chart');
|
||||
if (!chartContainer || !tvWidget.mainChart) {
|
||||
return;
|
||||
}
|
||||
|
||||
orderedTimeframes.forEach(timeframe => {
|
||||
if (data.ema52_dict[timeframe] !== undefined && data.ema52_dict[timeframe] !== null) {
|
||||
const value = data.ema52_dict[timeframe];
|
||||
const color = timeframeColors[timeframe] || '#800080';
|
||||
|
||||
// 添加虚线到主图表
|
||||
if (tvWidget.mainChart) {
|
||||
try {
|
||||
// 使用LightweightCharts的addLineSeries创建虚线
|
||||
const lineSeries = tvWidget.mainChart.addLineSeries({
|
||||
color: color,
|
||||
lineWidth: 1,
|
||||
lineStyle: 2, // 虚线样式
|
||||
title: `EMA52-${timeframe}`,
|
||||
lastValueVisible: false, // 不在价格标尺显示数值
|
||||
priceLineVisible: false, // 不显示默认价格线
|
||||
crosshairMarkerVisible: false,
|
||||
priceFormat: {
|
||||
type: 'price',
|
||||
precision: 2,
|
||||
minMove: 0.01,
|
||||
},
|
||||
priceScaleId: 'right',
|
||||
});
|
||||
|
||||
// 创建横线数据(使用K线数据的时间范围)
|
||||
if (data.kline_data && data.kline_data.length > 0) {
|
||||
const firstKline = data.kline_data[0];
|
||||
const lastKline = data.kline_data[data.kline_data.length - 1];
|
||||
|
||||
const startTime = Math.floor(new Date(firstKline.date).getTime() / 1000);
|
||||
const endTime = Math.floor(new Date(lastKline.date).getTime() / 1000);
|
||||
|
||||
const lineData = [
|
||||
{ time: startTime, value: value },
|
||||
{ time: endTime, value: value }
|
||||
];
|
||||
lineSeries.setData(lineData);
|
||||
|
||||
// 使用标记在K线右侧显示文字
|
||||
if (data.kline_data && data.kline_data.length > 0) {
|
||||
const lastKline = data.kline_data[data.kline_data.length - 1];
|
||||
const lastTime = Math.floor(new Date(lastKline.date).getTime() / 1000);
|
||||
|
||||
// 计算时间间隔(用于右偏移)
|
||||
let timeInterval = 60; // 默认1分钟
|
||||
if (data.kline_data.length > 1) {
|
||||
const secondLastKline = data.kline_data[data.kline_data.length - 2];
|
||||
const secondLastTime = Math.floor(new Date(secondLastKline.date).getTime() / 1000);
|
||||
timeInterval = lastTime - secondLastTime;
|
||||
}
|
||||
|
||||
// 创建右偏移的时间点(在最后一根K线右边)
|
||||
const rightOffsetTime = lastTime + timeInterval * 0.3;
|
||||
|
||||
// 创建一个新的线系列用于显示文字标记
|
||||
const markerSeries = tvWidget.mainChart.addLineSeries({
|
||||
color: 'transparent',
|
||||
lineWidth: 0,
|
||||
lastValueVisible: false,
|
||||
priceLineVisible: false,
|
||||
crosshairMarkerVisible: false,
|
||||
priceScaleId: 'right',
|
||||
});
|
||||
|
||||
// 添加一个透明的数据点在右偏移位置
|
||||
markerSeries.setData([{
|
||||
time: rightOffsetTime,
|
||||
value: value
|
||||
}]);
|
||||
|
||||
// 在右偏移位置添加文字标记
|
||||
markerSeries.setMarkers([{
|
||||
time: rightOffsetTime,
|
||||
position: 'inBar',
|
||||
color: color,
|
||||
shape: 'square',
|
||||
text: `${timeframe} ${value.toFixed(2)}`,
|
||||
size: 1,
|
||||
}]);
|
||||
|
||||
// 保存标记系列引用
|
||||
if (!tvWidget.series.ema52Series) {
|
||||
tvWidget.series.ema52Series = [];
|
||||
}
|
||||
tvWidget.series.ema52Series.push(markerSeries);
|
||||
}
|
||||
}
|
||||
|
||||
// 保存系列引用以便后续清理
|
||||
if (!tvWidget.series.ema52Series) {
|
||||
tvWidget.series.ema52Series = [];
|
||||
}
|
||||
tvWidget.series.ema52Series.push(lineSeries);
|
||||
|
||||
} catch (e) {
|
||||
console.warn('添加EMA52虚线失败:', timeframe, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 获取随机颜色
|
||||
function getRandomColor() {
|
||||
const colors = ['#2962FF', '#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4',
|
||||
|
||||
Reference in New Issue
Block a user