修改排版,加入up,down,flat,unknown显示
This commit is contained in:
@@ -50,6 +50,13 @@ class ChanKLC():
|
|||||||
self.trend = Chan_PRICE_TREND.UNKNOWN
|
self.trend = Chan_PRICE_TREND.UNKNOWN
|
||||||
def set_trend(self, trend):
|
def set_trend(self, trend):
|
||||||
self.trend = 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):
|
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'])
|
#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
|
self.klc_fx_type = klc_fx_type
|
||||||
|
|||||||
+11
-5
@@ -74,18 +74,24 @@ class ChanLun():
|
|||||||
if len(self.tf_df_dict) > 0:
|
if len(self.tf_df_dict) > 0:
|
||||||
return {key: self.tf_df_dict[key].get_ema24() for key in self.ema_symbols}
|
return {key: self.tf_df_dict[key].get_ema24() for key in self.ema_symbols}
|
||||||
return None
|
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):
|
def cal_bsp(self):
|
||||||
return
|
return
|
||||||
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:
|
if klc.high > klc.pre.high and klc.high > klc.next.high:
|
||||||
klc.set_fx(Chan_FX_TYPE.TOP)
|
if klc.close > klc.ema52 or klc.next.close > klc.next.ema52:
|
||||||
#print(klc.start_time, klc.end_time,klc.next.start_time, klc.next.end_time,klc.fx, "TOP")
|
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
|
return Chan_FX_TYPE.TOP
|
||||||
elif klc.low < klc.pre.low and klc.low < klc.next.low:
|
elif klc.low < klc.pre.low and klc.low < klc.next.low:
|
||||||
klc.set_fx(Chan_FX_TYPE.BOTTOM)
|
if klc.close < klc.ema52 or klc.next.close < klc.next.ema52:
|
||||||
#print(klc.start_time, klc.end_time,klc.next.start_time, klc.next.end_time,klc.fx, "BOTTOM")
|
klc.set_fx(Chan_FX_TYPE.BOTTOM)
|
||||||
return 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
|
return Chan_FX_TYPE.UNKNOWN
|
||||||
def add_indicators(self, df):
|
def add_indicators(self, df):
|
||||||
fast = 12
|
fast = 12
|
||||||
|
|||||||
+1
-1
@@ -26,7 +26,7 @@ class ChanMACDHistSet():
|
|||||||
def set_middle_klu(self, middle_klu):
|
def set_middle_klu(self, middle_klu):
|
||||||
self.middle_klu = middle_klu
|
self.middle_klu = middle_klu
|
||||||
#self.middle_area = abs(middle_klu.macdhist)
|
#self.middle_area = abs(middle_klu.macdhist)
|
||||||
#self.middle_klu = None
|
self.middle_klu = None
|
||||||
def set_unittf_div(self, unittf_div):
|
def set_unittf_div(self, unittf_div):
|
||||||
self.unittf_div = unittf_div
|
self.unittf_div = unittf_div
|
||||||
def add_klu(self, klu):
|
def add_klu(self, klu):
|
||||||
|
|||||||
@@ -56,6 +56,10 @@ class TF_DF():
|
|||||||
return None
|
return None
|
||||||
return float(ema24_value)
|
return float(ema24_value)
|
||||||
return None
|
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):
|
def add_indicators(self, df):
|
||||||
fast = 12
|
fast = 12
|
||||||
slow = 26
|
slow = 26
|
||||||
@@ -93,14 +97,33 @@ class TF_DF():
|
|||||||
df['macd'] = macd['macd']
|
df['macd'] = macd['macd']
|
||||||
df['macdsignal'] = macd['macdsignal']
|
df['macdsignal'] = macd['macdsignal']
|
||||||
df['macdhist'] = macd['macdhist']
|
df['macdhist'] = macd['macdhist']
|
||||||
df['ema5'] = ta.EMA(df, timeperiod=5)
|
df['ema5'] = self.cal_ema(df, 5)
|
||||||
df['ema10'] = ta.EMA(df, timeperiod=10)
|
df['ema10'] = self.cal_ema(df, 10)
|
||||||
df['ema24'] = ta.EMA(df, timeperiod=24)
|
df['ema24'] = self.cal_ema(df, 24)
|
||||||
df['ema26'] = ta.EMA(df, timeperiod=26)
|
df['ema26'] = self.cal_ema(df, 26)
|
||||||
df['ema52'] = ta.EMA(df, timeperiod=52)
|
df['ema52'] = self.cal_ema(df, 52)
|
||||||
df['rsi'] = ta.RSI(df, timeperiod=14)
|
df['rsi'] = ta.RSI(df, timeperiod=14)
|
||||||
df['volume_ratio'] = self.cal_volume_ratio(df)
|
df['volume_ratio'] = self.cal_volume_ratio(df)
|
||||||
return 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):
|
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:
|
if klc.high > klc.pre.high and klc.high > klc.next.high:
|
||||||
|
|||||||
@@ -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 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 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 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 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 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_1d = self.dp.get_pair_dataframe(pair=self.pair, timeframe='1d')
|
||||||
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.init_dataframes(dataframe_m, dataframe_1h, dataframe_1d, dataframe_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):
|
def print_all_ema52(self):
|
||||||
for key, value in self.chan.get_ema52_dict().items():
|
for key, value in self.chan.get_ema52_dict().items():
|
||||||
print(key, value)
|
print(key, value)
|
||||||
def print_all_ema24(self):
|
def print_all_ema24(self):
|
||||||
for key, value in self.chan.get_ema24_dict().items():
|
for key, value in self.chan.get_ema24_dict().items():
|
||||||
print(key, value)
|
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):
|
def add_indicators(self, df):
|
||||||
fast = 12
|
fast = 12
|
||||||
slow = 26
|
slow = 26
|
||||||
|
|||||||
+46
-5
@@ -352,12 +352,13 @@ def analyze_chan(df, symbol=None, timeframe=None):
|
|||||||
|
|
||||||
# 初始化多时间周期数据以获取EMA52
|
# 初始化多时间周期数据以获取EMA52
|
||||||
ema52_dict = None
|
ema52_dict = None
|
||||||
if symbol and timeframe:
|
# 先暂时不用这个功能,太慢了
|
||||||
|
if symbol and timeframe and False:
|
||||||
try:
|
try:
|
||||||
# 获取不同时间周期的数据用于初始化
|
# 获取不同时间周期的数据用于初始化
|
||||||
df_1h = get_kl_data(symbol, '1h', limit=800) if timeframe != '1h' else df
|
df_1h = get_kl_data(symbol, '1h', limit=1500) if timeframe != '1h' else df
|
||||||
df_1d = get_kl_data(symbol, '1d', limit=800) if timeframe != '1d' else df
|
df_1d = get_kl_data(symbol, '1d', limit=2000) if timeframe != '1d' else df
|
||||||
df_1M = get_kl_data(symbol, '1M', limit=800) if timeframe != '1M' 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:
|
if df_1h is not None and len(df_1h) > 0:
|
||||||
@@ -1659,6 +1660,24 @@ def analyze():
|
|||||||
else:
|
else:
|
||||||
replay_data = None
|
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({
|
result.update({
|
||||||
'kline_data': clean_dataframe_for_json(df).to_dict('records'),
|
'kline_data': clean_dataframe_for_json(df).to_dict('records'),
|
||||||
@@ -1753,7 +1772,9 @@ def analyze():
|
|||||||
# 添加ChanMACD分析数据
|
# 添加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数据
|
||||||
'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数据
|
# 计算小周期MACD数据
|
||||||
element_macd_data = calculate_macd(element_df)
|
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_timeframe'] = element_timeframe
|
||||||
result['element_macd'] = element_macd_data # 添加小周期MACD数据
|
result['element_macd'] = element_macd_data # 添加小周期MACD数据
|
||||||
@@ -1880,6 +1918,9 @@ def analyze():
|
|||||||
# 添加次周期ChanMACD分析数据
|
# 添加次周期ChanMACD分析数据
|
||||||
result['element_chan_macd'] = serialize_chan_macd_data(element_analysis.get('chan_macd', {}), client_tz)
|
result['element_chan_macd'] = serialize_chan_macd_data(element_analysis.get('chan_macd', {}), client_tz)
|
||||||
|
|
||||||
|
# 添加小周期 KLC 趋势标记
|
||||||
|
result['element_klc_trend'] = element_klc_trend
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
return jsonify(result)
|
return jsonify(result)
|
||||||
|
|||||||
+207
-122
@@ -801,8 +801,8 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<div class="row g-3 align-items-end">
|
<div class="row g-1 d-flex align-items-end">
|
||||||
<div class="col-md-1">
|
<div class="col-md-2">
|
||||||
<label for="dataSource" class="form-label">数据源:</label>
|
<label for="dataSource" class="form-label">数据源:</label>
|
||||||
<select id="dataSource" class="form-select">
|
<select id="dataSource" class="form-select">
|
||||||
<option value="crypto" selected>加密货币</option>
|
<option value="crypto" selected>加密货币</option>
|
||||||
@@ -825,14 +825,6 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-1">
|
|
||||||
<label for="timeframe" class="form-label">时间周期:</label>
|
|
||||||
<select id="timeframe" class="form-select">
|
|
||||||
{% for value, label in timeframes.items() %}
|
|
||||||
<option value="{{ value }}" {% if value == '5m' %}selected{% endif %}>{{ label }}</option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-1">
|
<div class="col-md-1">
|
||||||
<label for="timezone" class="form-label">时区:</label>
|
<label for="timezone" class="form-label">时区:</label>
|
||||||
<select id="timezone" class="form-select">
|
<select id="timezone" class="form-select">
|
||||||
@@ -844,11 +836,11 @@
|
|||||||
<option value="Asia/Tokyo">Asia/Tokyo (UTC+9)</option>
|
<option value="Asia/Tokyo">Asia/Tokyo (UTC+9)</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3">
|
<div class="col-md-2">
|
||||||
<label for="start_time" class="form-label">开始时间:</label>
|
<label for="start_time" class="form-label">开始时间:</label>
|
||||||
<input type="datetime-local" id="start_time" class="form-control">
|
<input type="datetime-local" id="start_time" class="form-control">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3">
|
<div class="col-md-2">
|
||||||
<label for="end_time" class="form-label">结束时间:</label>
|
<label for="end_time" class="form-label">结束时间:</label>
|
||||||
<input type="datetime-local" id="end_time" class="form-control">
|
<input type="datetime-local" id="end_time" class="form-control">
|
||||||
</div>
|
</div>
|
||||||
@@ -860,7 +852,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row mt-3">
|
<div class="row mt-3">
|
||||||
<div class="col-md-8">
|
<div class="col-md-16">
|
||||||
<div class="d-flex align-items-center">
|
<div class="d-flex align-items-center">
|
||||||
<label class="form-label me-3 mb-0">基础显示:</label>
|
<label class="form-label me-3 mb-0">基础显示:</label>
|
||||||
<div class="form-check form-check-inline">
|
<div class="form-check form-check-inline">
|
||||||
@@ -876,11 +868,41 @@
|
|||||||
<div class="form-check form-check-inline">
|
<div class="form-check form-check-inline">
|
||||||
<input class="form-check-input" type="radio" name="klinePeriod" id="elementPeriodKline">
|
<input class="form-check-input" type="radio" name="klinePeriod" id="elementPeriodKline">
|
||||||
<label class="form-check-label" for="elementPeriodKline">小周期</label>
|
<label class="form-check-label" for="elementPeriodKline">小周期</label>
|
||||||
<i class="bi bi-info-circle" data-bs-toggle="tooltip" title="显示小周期K线,同时可以叠加大周期分型和笔段"></i>
|
</div>
|
||||||
|
<div class="form-check form-check-inline">
|
||||||
|
<input class="form-check-input" type="checkbox" id="showMacd" checked>
|
||||||
|
<label class="form-check-label" for="showMacd">ChanMACD</label>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex align-items-center mb-2">
|
||||||
|
<label for="refreshInterval" class="form-label me-2 mb-0">自动刷新:</label>
|
||||||
|
<select id="refreshInterval" class="form-select form-select-sm me-2" style="width: 80px;">
|
||||||
|
<option value="0.0833">5秒</option>
|
||||||
|
<option value="0.1667">10秒</option>
|
||||||
|
<option value="0.25">15秒</option>
|
||||||
|
<option value="0.5">30秒</option>
|
||||||
|
<option value="1">1分钟</option>
|
||||||
|
<option value="2">2分钟</option>
|
||||||
|
<option value="3">3分钟</option>
|
||||||
|
<option value="5" selected>5分钟</option>
|
||||||
|
<option value="10">10分钟</option>
|
||||||
|
</select>
|
||||||
|
<div class="form-check form-check-inline me-2">
|
||||||
|
<input class="form-check-input" type="checkbox" id="autoRefresh">
|
||||||
|
<label class="form-check-label" for="autoRefresh">启用</label>
|
||||||
|
</div>
|
||||||
|
<span id="nextRefreshTime" class="text-muted" style="display:none;font-size:0.85rem;"></span>
|
||||||
|
<div id="refreshLoadingSpinner" class="loading-spinner ms-2" style="display:none;"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="d-flex align-items-center mt-2">
|
<div class="d-flex align-items-center mt-2">
|
||||||
<label class="form-label me-3 mb-0">主周期:</label>
|
<label class="form-label me-0 mb-0">主周期:</label>
|
||||||
|
<div class="form-check form-check-inline">
|
||||||
|
<select id="timeframe" class="form-select form-select-sm me-2" style="width: 100px;">
|
||||||
|
{% for value, label in timeframes.items() %}
|
||||||
|
<option value="{{ value }}" {% if value == '5m' %}selected{% endif %}>{{ label }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
<div class="form-check form-check-inline">
|
<div class="form-check form-check-inline">
|
||||||
<input class="form-check-input" type="checkbox" id="showMainBi" checked>
|
<input class="form-check-input" type="checkbox" id="showMainBi" checked>
|
||||||
<label class="form-check-label" for="showMainBi">笔</label>
|
<label class="form-check-label" for="showMainBi">笔</label>
|
||||||
@@ -897,33 +919,28 @@
|
|||||||
<input class="form-check-input" type="checkbox" id="showMainUncompletedZs">
|
<input class="form-check-input" type="checkbox" id="showMainUncompletedZs">
|
||||||
<label class="form-check-label" for="showMainUncompletedZs">未完成中枢</label>
|
<label class="form-check-label" for="showMainUncompletedZs">未完成中枢</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-check form-check-inline">
|
|
||||||
<input class="form-check-input" type="checkbox" id="showMainMacdDiv">
|
|
||||||
<label class="form-check-label" for="showMainMacdDiv">MACD背离</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-check form-check-inline">
|
<div class="form-check form-check-inline">
|
||||||
<input class="form-check-input" type="checkbox" id="showKlcFxType" checked>
|
<input class="form-check-input" type="checkbox" id="showKlcFxType" checked>
|
||||||
<label class="form-check-label" for="showKlcFxType">KLC分型</label>
|
<label class="form-check-label" for="showKlcFxType">KLC分型</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-check form-check-inline">
|
<div class="form-check form-check-inline">
|
||||||
<input class="form-check-input" type="checkbox" id="showKluFxType">
|
<input class="form-check-input" type="checkbox" id="showMainTrend" checked>
|
||||||
<label class="form-check-label" for="showKluFxType">KLU分型</label>
|
<label class="form-check-label" for="showMainTrend">Trend</label>
|
||||||
</div>
|
|
||||||
<div class="form-check form-check-inline">
|
|
||||||
<input class="form-check-input" type="checkbox" id="showMainBollinger">
|
|
||||||
<label class="form-check-label" for="showMainBollinger">布林带</label>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="form-check form-check-inline">
|
<div class="form-check form-check-inline">
|
||||||
<input class="form-check-input" type="checkbox" id="toggleUOnMain" checked>
|
<input class="form-check-input" type="checkbox" id="toggleUOnMain" checked>
|
||||||
<label class="form-check-label" for="toggleUOnMain">显示U</label>
|
<label class="form-check-label" for="toggleUOnMain">显示U</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-check form-check-inline">
|
|
||||||
<input class="form-check-input" type="checkbox" id="showMacd" checked>
|
|
||||||
<label class="form-check-label" for="showMacd">ChanMACD</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="d-flex align-items-center mt-2">
|
<div class="d-flex align-items-center mt-2">
|
||||||
<label class="form-label me-3 mb-0">次周期:</label>
|
<label class="form-label me-0 mb-0">次周期:</label>
|
||||||
|
<div class="form-check form-check-inline">
|
||||||
|
<select id="elementTimeframe" class="form-select form-select-sm me-2" style="width: 100px;">
|
||||||
|
{% for value, label in timeframes.items() %}
|
||||||
|
<option value="{{ value }}" {% if value == '1m' %}selected{% endif %}>{{ label }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
<div class="form-check form-check-inline">
|
<div class="form-check form-check-inline">
|
||||||
<input class="form-check-input" type="checkbox" id="showElementBi">
|
<input class="form-check-input" type="checkbox" id="showElementBi">
|
||||||
<label class="form-check-label" for="showElementBi">笔</label>
|
<label class="form-check-label" for="showElementBi">笔</label>
|
||||||
@@ -940,21 +957,13 @@
|
|||||||
<input class="form-check-input" type="checkbox" id="showElementUncompletedZs">
|
<input class="form-check-input" type="checkbox" id="showElementUncompletedZs">
|
||||||
<label class="form-check-label" for="showElementUncompletedZs">未完成中枢</label>
|
<label class="form-check-label" for="showElementUncompletedZs">未完成中枢</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-check form-check-inline">
|
|
||||||
<input class="form-check-input" type="checkbox" id="showElementMacdDiv">
|
|
||||||
<label class="form-check-label" for="showElementMacdDiv">MACD背离</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-check form-check-inline">
|
<div class="form-check form-check-inline">
|
||||||
<input class="form-check-input" type="checkbox" id="showElementKlcFxType">
|
<input class="form-check-input" type="checkbox" id="showElementKlcFxType">
|
||||||
<label class="form-check-label" for="showElementKlcFxType">KLC分型</label>
|
<label class="form-check-label" for="showElementKlcFxType">KLC分型</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-check form-check-inline">
|
<div class="form-check form-check-inline">
|
||||||
<input class="form-check-input" type="checkbox" id="showElementKluFxType">
|
<input class="form-check-input" type="checkbox" id="showElementTrend">
|
||||||
<label class="form-check-label" for="showElementKluFxType">KLU分型</label>
|
<label class="form-check-label" for="showElementTrend">Trend</label>
|
||||||
</div>
|
|
||||||
<div class="form-check form-check-inline">
|
|
||||||
<input class="form-check-input" type="checkbox" id="showElementBollinger">
|
|
||||||
<label class="form-check-label" for="showElementBollinger">布林带</label>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="form-check form-check-inline">
|
<div class="form-check form-check-inline">
|
||||||
<input class="form-check-input" type="checkbox" id="toggleUOnElement">
|
<input class="form-check-input" type="checkbox" id="toggleUOnElement">
|
||||||
@@ -962,64 +971,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-4">
|
|
||||||
<div class="d-flex align-items-center mb-2">
|
|
||||||
<label for="elementTimeframe" class="form-label me-2 mb-0">次级别时间周期:</label>
|
|
||||||
<select id="elementTimeframe" class="form-select form-select-sm me-2" style="width: 120px;">
|
|
||||||
{% for value, label in timeframes.items() %}
|
|
||||||
<option value="{{ value }}" {% if value == '1m' %}selected{% endif %}>{{ label }}</option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="d-flex align-items-center mb-2">
|
|
||||||
<label for="refreshInterval" class="form-label me-2 mb-0">自动刷新:</label>
|
|
||||||
<select id="refreshInterval" class="form-select form-select-sm me-2" style="width: 80px;">
|
|
||||||
<option value="0.0833">5秒</option>
|
|
||||||
<option value="0.1667">10秒</option>
|
|
||||||
<option value="0.25">15秒</option>
|
|
||||||
<option value="0.5">30秒</option>
|
|
||||||
<option value="1">1分钟</option>
|
|
||||||
<option value="2">2分钟</option>
|
|
||||||
<option value="3">3分钟</option>
|
|
||||||
<option value="5" selected>5分钟</option>
|
|
||||||
<option value="10">10分钟</option>
|
|
||||||
</select>
|
|
||||||
<div class="form-check form-check-inline me-2">
|
|
||||||
<input class="form-check-input" type="checkbox" id="autoRefresh">
|
|
||||||
<label class="form-check-label" for="autoRefresh">启用</label>
|
|
||||||
</div>
|
|
||||||
<span id="nextRefreshTime" class="text-muted" style="display:none;font-size:0.85rem;"></span>
|
|
||||||
<div id="refreshLoadingSpinner" class="loading-spinner ms-2" style="display:none;"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 添加数据回放控制面板 -->
|
|
||||||
<div class="d-flex align-items-center">
|
|
||||||
<label for="replayInterval" class="form-label me-2 mb-0">数据回放:</label>
|
|
||||||
<select id="replayInterval" class="form-select form-select-sm me-2" style="width: 80px;">
|
|
||||||
<option value="0.5">0.5秒</option>
|
|
||||||
<option value="1" selected>1秒</option>
|
|
||||||
<option value="2">2秒</option>
|
|
||||||
<option value="3">3秒</option>
|
|
||||||
<option value="5">5秒</option>
|
|
||||||
</select>
|
|
||||||
<div class="btn-group me-2">
|
|
||||||
<button id="startReplay" class="btn btn-sm btn-success">
|
|
||||||
<i class="bi bi-play-fill"></i> 开始回放
|
|
||||||
</button>
|
|
||||||
<button id="pauseReplay" class="btn btn-sm btn-warning" style="display:none;">
|
|
||||||
<i class="bi bi-pause-fill"></i> 暂停
|
|
||||||
</button>
|
|
||||||
<button id="stopReplay" class="btn btn-sm btn-danger" style="display:none;">
|
|
||||||
<i class="bi bi-stop-fill"></i> 停止
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<span id="replayStatus" class="text-muted" style="font-size:0.85rem;"></span>
|
|
||||||
<div id="replayProgress" class="progress ms-2" style="width: 80px; height: 8px; display: none;">
|
|
||||||
<div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" style="width: 0%"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -2025,19 +1976,9 @@
|
|||||||
refreshChart(currentData);
|
refreshChart(currentData);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 添加分型类型复选框变更事件
|
// Trend 显示开关
|
||||||
$('#showKlcFxType').change(function() {
|
$('#showMainTrend').change(function() { refreshChartOnly(); });
|
||||||
refreshChartOnly();
|
$('#showElementTrend').change(function() { refreshChartOnly(); });
|
||||||
});
|
|
||||||
|
|
||||||
// 添加布林带显示变更事件
|
|
||||||
$('#showMainBollinger').change(function() {
|
|
||||||
updateChartDisplay();
|
|
||||||
});
|
|
||||||
|
|
||||||
$('#showElementBollinger').change(function() {
|
|
||||||
updateChartDisplay();
|
|
||||||
});
|
|
||||||
|
|
||||||
// 添加K线周期切换事件监听器
|
// 添加K线周期切换事件监听器
|
||||||
$('input[name="klinePeriod"]').change(function() {
|
$('input[name="klinePeriod"]').change(function() {
|
||||||
@@ -3029,8 +2970,8 @@
|
|||||||
// 添加ChanMACD分析标注
|
// 添加ChanMACD分析标注
|
||||||
// 根据主/次周期开关与各自的“显示U”独立控制
|
// 根据主/次周期开关与各自的“显示U”独立控制
|
||||||
const cm = useElementPeriod ? (currentData.element_chan_macd || currentData.chan_macd) : currentData.chan_macd;
|
const cm = useElementPeriod ? (currentData.element_chan_macd || currentData.chan_macd) : currentData.chan_macd;
|
||||||
const allowU = useElementPeriod ? (typeof window.showUOnElement === 'undefined' ? true : window.showUOnElement)
|
// 默认不显示,必须用户勾选对应复选框
|
||||||
: (typeof window.showUOnMain === 'undefined' ? true : window.showUOnMain);
|
const allowU = useElementPeriod ? !!window.showUOnElement : !!window.showUOnMain;
|
||||||
if (cm && allowU) {
|
if (cm && allowU) {
|
||||||
console.log('添加ChanMACD分析标注:', {
|
console.log('添加ChanMACD分析标注:', {
|
||||||
segListLength: cm.seg_list ? cm.seg_list.length : 0,
|
segListLength: cm.seg_list ? cm.seg_list.length : 0,
|
||||||
@@ -3088,7 +3029,7 @@
|
|||||||
);
|
);
|
||||||
|
|
||||||
// 主周期 U 标记(蓝/橙,与原样式一致)
|
// 主周期 U 标记(蓝/橙,与原样式一致)
|
||||||
if ((typeof window.showUOnMain === 'undefined' ? true : window.showUOnMain) && Array.isArray(mainCm.klu_list)) {
|
if (window.showUOnMain && Array.isArray(mainCm.klu_list)) {
|
||||||
mainCm.klu_list.forEach((item) => {
|
mainCm.klu_list.forEach((item) => {
|
||||||
if (!item || !item.time) return;
|
if (!item || !item.time) return;
|
||||||
const ts = Math.floor(new Date(item.time).getTime() / 1000);
|
const ts = Math.floor(new Date(item.time).getTime() / 1000);
|
||||||
@@ -3110,7 +3051,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 次周期 U 标记(使用不同配色以区分)
|
// 次周期 U 标记(使用不同配色以区分)
|
||||||
if ((typeof window.showUOnElement === 'undefined' ? true : window.showUOnElement) && Array.isArray(elementCm.klu_list)) {
|
if (window.showUOnElement && Array.isArray(elementCm.klu_list)) {
|
||||||
elementCm.klu_list.forEach((item) => {
|
elementCm.klu_list.forEach((item) => {
|
||||||
if (!item || !item.time) return;
|
if (!item || !item.time) return;
|
||||||
const ts = Math.floor(new Date(item.time).getTime() / 1000);
|
const ts = Math.floor(new Date(item.time).getTime() / 1000);
|
||||||
@@ -5488,12 +5429,86 @@
|
|||||||
window.fxMarkers = elementFxMarkers;
|
window.fxMarkers = elementFxMarkers;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 合并主周期、小周期分型与 UnitTF 标记,统一设置到K线数据系列
|
// 基于后端提供的 KLC 趋势生成标记(不进行任何计算)
|
||||||
|
let klcTrendMarkers = [];
|
||||||
|
try {
|
||||||
|
if (currentData.klc_trend && currentData.klc_trend.length > 0) {
|
||||||
|
console.log('KLC趋势点数量:', currentData.klc_trend.length, currentData.klc_trend.slice(0, 3));
|
||||||
|
// 当前图表的bar时间集合(秒)用于对齐标记到最近的K线
|
||||||
|
const seriesTimes = (typeof candles !== 'undefined' && Array.isArray(candles)) ? new Set(candles.map(c => c.time)) : new Set();
|
||||||
|
const nearestTime = (target) => {
|
||||||
|
if (!Array.isArray(candles) || candles.length === 0) return target;
|
||||||
|
// 简单线性查找(数据量通常可接受),必要时可替换为二分
|
||||||
|
let best = candles[0].time;
|
||||||
|
let bestDiff = Math.abs(best - target);
|
||||||
|
for (let i = 1; i < candles.length; i++) {
|
||||||
|
const t = candles[i].time;
|
||||||
|
const d = Math.abs(t - target);
|
||||||
|
if (d < bestDiff) { best = t; bestDiff = d; }
|
||||||
|
}
|
||||||
|
return best;
|
||||||
|
};
|
||||||
|
|
||||||
|
klcTrendMarkers = currentData.klc_trend.map(t => {
|
||||||
|
const ts = Math.floor(new Date(t.time).getTime() / 1000);
|
||||||
|
const trendRaw = (t.trend || '').toString().toUpperCase();
|
||||||
|
let timeAligned = seriesTimes.has(ts) ? ts : nearestTime(ts);
|
||||||
|
let marker = { time: timeAligned, position: 'inBar', color: '#9E9E9E', shape: 'square', size: 0.8 };
|
||||||
|
if (trendRaw === 'UP') {
|
||||||
|
marker = { time: timeAligned, position: 'aboveBar', color: '#00C853', shape: 'arrowUp', size: 0.5 };
|
||||||
|
} else if (trendRaw === 'DOWN') {
|
||||||
|
marker = { time: timeAligned, position: 'belowBar', color: '#D32F2F', shape: 'arrowDown', size: 0.5 };
|
||||||
|
} else if (trendRaw === 'FLAT') {
|
||||||
|
marker = { time: timeAligned, position: 'inBar', color: '#9E9E9E', shape: 'circle', size: 0.8 };
|
||||||
|
} else {
|
||||||
|
// UNKNOWN 或其他
|
||||||
|
marker = { time: timeAligned, position: 'inBar', color: '#2196F3', shape: 'square', size: 0.8 };
|
||||||
|
}
|
||||||
|
return marker;
|
||||||
|
});
|
||||||
|
console.log('KLC趋势标记(对齐后)示例:', klcTrendMarkers.slice(0, 5));
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
klcTrendMarkers = [];
|
||||||
|
}
|
||||||
|
// 暴露到全局以便调试或后续合并
|
||||||
|
window.klcTrendMarkers = klcTrendMarkers;
|
||||||
|
|
||||||
|
// 无论当前显示主/小周期,只要勾选对应Trend,就叠加出来
|
||||||
|
let trendMarkersToUse = [];
|
||||||
|
if ($('#showMainTrend').is(':checked')) {
|
||||||
|
trendMarkersToUse = trendMarkersToUse.concat(window.klcTrendMarkers || []);
|
||||||
|
}
|
||||||
|
if ($('#showElementTrend').is(':checked') && currentData.element_klc_trend) {
|
||||||
|
const candlesTimes = (typeof candles !== 'undefined' && Array.isArray(candles)) ? new Set(candles.map(c => c.time)) : new Set();
|
||||||
|
const nearestTime = (target) => {
|
||||||
|
if (!Array.isArray(candles) || candles.length === 0) return target;
|
||||||
|
let best = candles[0].time, bestDiff = Math.abs(best - target);
|
||||||
|
for (let i = 1; i < candles.length; i++) {
|
||||||
|
const t = candles[i].time, d = Math.abs(t - target);
|
||||||
|
if (d < bestDiff) { best = t; bestDiff = d; }
|
||||||
|
}
|
||||||
|
return best;
|
||||||
|
};
|
||||||
|
const elementMarkers = currentData.element_klc_trend.map(t => {
|
||||||
|
const ts = Math.floor(new Date(t.time).getTime() / 1000);
|
||||||
|
const trendRaw = (t.trend || '').toString().toUpperCase();
|
||||||
|
const timeAligned = candlesTimes.has(ts) ? ts : nearestTime(ts);
|
||||||
|
if (trendRaw === 'UP') return { time: timeAligned, position: 'aboveBar', color: '#00C853', shape: 'arrowUp', size: 0.5 };
|
||||||
|
if (trendRaw === 'DOWN') return { time: timeAligned, position: 'belowBar', color: '#D32F2F', shape: 'arrowDown', size: 0.5 };
|
||||||
|
if (trendRaw === 'FLAT') return { time: timeAligned, position: 'inBar', color: '#9E9E9E', shape: 'circle', size: 0.8 };
|
||||||
|
return { time: timeAligned, position: 'inBar', color: '#2196F3', shape: 'square', size: 0.8 };
|
||||||
|
});
|
||||||
|
trendMarkersToUse = trendMarkersToUse.concat(elementMarkers);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 合并标记并设置
|
||||||
const combinedMarkers = [
|
const combinedMarkers = [
|
||||||
...(window.mainFxMarkers || []),
|
...(window.mainFxMarkers || []),
|
||||||
...allElementFxMarkers,
|
...allElementFxMarkers,
|
||||||
...(window.kluDivMarkersMain || []),
|
...(window.kluDivMarkersMain || []),
|
||||||
...(window.kluDivMarkersElement || [])
|
...(window.kluDivMarkersElement || []),
|
||||||
|
...trendMarkersToUse
|
||||||
];
|
];
|
||||||
if (combinedMarkers.length > 0) {
|
if (combinedMarkers.length > 0) {
|
||||||
console.log('合并设置', combinedMarkers.length, '个标记(主周期分型:', (window.mainFxMarkers || []).length, '个,小周期分型:', allElementFxMarkers.length, '个,UnitTF:', (window.unittfMarkers || []).length, '个)');
|
console.log('合并设置', combinedMarkers.length, '个标记(主周期分型:', (window.mainFxMarkers || []).length, '个,小周期分型:', allElementFxMarkers.length, '个,UnitTF:', (window.unittfMarkers || []).length, '个)');
|
||||||
@@ -5513,11 +5528,76 @@
|
|||||||
} else {
|
} else {
|
||||||
console.log('绘制小周期分型标记 - 已禁用或无数据');
|
console.log('绘制小周期分型标记 - 已禁用或无数据');
|
||||||
|
|
||||||
// 只设置主周期分型 + UnitTF 标记
|
// 计算并缓存KLC趋势标记(即使未启用小周期分型,也应显示趋势)
|
||||||
|
try {
|
||||||
|
let klcTrendMarkers = [];
|
||||||
|
if (currentData.klc_trend && currentData.klc_trend.length > 0) {
|
||||||
|
console.log('KLC趋势点数量:', currentData.klc_trend.length, currentData.klc_trend.slice(0, 3));
|
||||||
|
const seriesTimes = (typeof candles !== 'undefined' && Array.isArray(candles)) ? new Set(candles.map(c => c.time)) : new Set();
|
||||||
|
const nearestTime = (target) => {
|
||||||
|
if (!Array.isArray(candles) || candles.length === 0) return target;
|
||||||
|
let best = candles[0].time;
|
||||||
|
let bestDiff = Math.abs(best - target);
|
||||||
|
for (let i = 1; i < candles.length; i++) {
|
||||||
|
const t = candles[i].time;
|
||||||
|
const d = Math.abs(t - target);
|
||||||
|
if (d < bestDiff) { best = t; bestDiff = d; }
|
||||||
|
}
|
||||||
|
return best;
|
||||||
|
};
|
||||||
|
klcTrendMarkers = currentData.klc_trend.map(t => {
|
||||||
|
const ts = Math.floor(new Date(t.time).getTime() / 1000);
|
||||||
|
const trendRaw = (t.trend || '').toString().toUpperCase();
|
||||||
|
const timeAligned = seriesTimes.has(ts) ? ts : nearestTime(ts);
|
||||||
|
if (trendRaw === 'UP') {
|
||||||
|
return { time: timeAligned, position: 'aboveBar', color: '#00C853', shape: 'arrowUp', size: 0.5 };
|
||||||
|
} else if (trendRaw === 'DOWN') {
|
||||||
|
return { time: timeAligned, position: 'belowBar', color: '#D32F2F', shape: 'arrowDown', size: 0.5 };
|
||||||
|
} else if (trendRaw === 'FLAT') {
|
||||||
|
return { time: timeAligned, position: 'inBar', color: '#9E9E9E', shape: 'circle', size: 0.8 };
|
||||||
|
} else {
|
||||||
|
return { time: timeAligned, position: 'inBar', color: '#2196F3', shape: 'square', size: 0.8 };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
console.log('KLC趋势标记(对齐后)示例:', klcTrendMarkers.slice(0, 5));
|
||||||
|
}
|
||||||
|
window.klcTrendMarkers = klcTrendMarkers;
|
||||||
|
} catch (e) {
|
||||||
|
window.klcTrendMarkers = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 与上方一致:勾选哪个Trend就显示哪个
|
||||||
|
let trendMarkersToUse = [];
|
||||||
|
if ($('#showMainTrend').is(':checked')) {
|
||||||
|
trendMarkersToUse = trendMarkersToUse.concat(window.klcTrendMarkers || []);
|
||||||
|
}
|
||||||
|
if ($('#showElementTrend').is(':checked') && currentData.element_klc_trend) {
|
||||||
|
const candlesTimes = (typeof candles !== 'undefined' && Array.isArray(candles)) ? new Set(candles.map(c => c.time)) : new Set();
|
||||||
|
const nearestTime = (target) => {
|
||||||
|
if (!Array.isArray(candles) || candles.length === 0) return target;
|
||||||
|
let best = candles[0].time, bestDiff = Math.abs(best - target);
|
||||||
|
for (let i = 1; i < candles.length; i++) {
|
||||||
|
const t = candles[i].time, d = Math.abs(t - target);
|
||||||
|
if (d < bestDiff) { best = t; bestDiff = d; }
|
||||||
|
}
|
||||||
|
return best;
|
||||||
|
};
|
||||||
|
const elementMarkers = currentData.element_klc_trend.map(t => {
|
||||||
|
const ts = Math.floor(new Date(t.time).getTime() / 1000);
|
||||||
|
const trendRaw = (t.trend || '').toString().toUpperCase();
|
||||||
|
const timeAligned = candlesTimes.has(ts) ? ts : nearestTime(ts);
|
||||||
|
if (trendRaw === 'UP') return { time: timeAligned, position: 'aboveBar', color: '#00C853', shape: 'arrowUp', size: 0.5 };
|
||||||
|
if (trendRaw === 'DOWN') return { time: timeAligned, position: 'belowBar', color: '#D32F2F', shape: 'arrowDown', size: 0.5 };
|
||||||
|
if (trendRaw === 'FLAT') return { time: timeAligned, position: 'inBar', color: '#9E9E9E', shape: 'circle', size: 0.8 };
|
||||||
|
return { time: timeAligned, position: 'inBar', color: '#2196F3', shape: 'square', size: 0.8 };
|
||||||
|
});
|
||||||
|
trendMarkersToUse = trendMarkersToUse.concat(elementMarkers);
|
||||||
|
}
|
||||||
const onlyMainAndU = [
|
const onlyMainAndU = [
|
||||||
...(window.mainFxMarkers || []),
|
...(window.mainFxMarkers || []),
|
||||||
...(window.kluDivMarkersMain || []),
|
...(window.kluDivMarkersMain || []),
|
||||||
...(window.kluDivMarkersElement || [])
|
...(window.kluDivMarkersElement || []),
|
||||||
|
...trendMarkersToUse
|
||||||
];
|
];
|
||||||
if (onlyMainAndU.length > 0) {
|
if (onlyMainAndU.length > 0) {
|
||||||
console.log('仅设置', onlyMainAndU.length, '个主周期/UnitTF标记(主周期分型:', (window.mainFxMarkers || []).length, ',UnitTF:', (window.unittfMarkers || []).length, ')');
|
console.log('仅设置', onlyMainAndU.length, '个主周期/UnitTF标记(主周期分型:', (window.mainFxMarkers || []).length, ',UnitTF:', (window.unittfMarkers || []).length, ')');
|
||||||
@@ -5675,6 +5755,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 添加买卖点提示
|
// 添加买卖点提示
|
||||||
|
// 初始化 tooltip 与 U 显示状态
|
||||||
|
window.showUOnMain = $('#toggleUOnMain').is(':checked');
|
||||||
|
window.showUOnElement = $('#toggleUOnElement').is(':checked');
|
||||||
setupTooltip(mainChart, [], [], mainChartContainer, volumeChartContainer, atrChartContainer, macdChartContainer, chanMacdChartContainer, volumeChart, atrChart, macdChart, chanMacdChart, showMacd);
|
setupTooltip(mainChart, [], [], mainChartContainer, volumeChartContainer, atrChartContainer, macdChartContainer, chanMacdChartContainer, volumeChart, atrChart, macdChart, chanMacdChart, showMacd);
|
||||||
|
|
||||||
// 显示买卖点
|
// 显示买卖点
|
||||||
@@ -5895,7 +5978,8 @@
|
|||||||
try {
|
try {
|
||||||
if (typeof clearChanMacdMarkers === 'function') clearChanMacdMarkers();
|
if (typeof clearChanMacdMarkers === 'function') clearChanMacdMarkers();
|
||||||
const cm = useElementPeriod ? (currentData.element_chan_macd || currentData.chan_macd) : currentData.chan_macd;
|
const cm = useElementPeriod ? (currentData.element_chan_macd || currentData.chan_macd) : currentData.chan_macd;
|
||||||
if (cm) {
|
const allowU = useElementPeriod ? !!window.showUOnElement : !!window.showUOnMain;
|
||||||
|
if (cm && allowU) {
|
||||||
addAllChanMacdMarkers(
|
addAllChanMacdMarkers(
|
||||||
cm.seg_list || [],
|
cm.seg_list || [],
|
||||||
cm.unittf_list || [],
|
cm.unittf_list || [],
|
||||||
@@ -10279,9 +10363,10 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 保存到全局,供主图与分型一起统一合并绘制
|
// 保存到全局,供主图与分型一起统一合并绘制(仅在开关开启时)
|
||||||
console.log('DEBUG: U 标记数量:', signalMarkers.length);
|
console.log('DEBUG: U 标记数量:', signalMarkers.length);
|
||||||
window.unittfMarkers = [...signalMarkers, ...boundaryMarkers];
|
const allowUMerge = (window.showUOnMain && window.showUOnElement);
|
||||||
|
window.unittfMarkers = allowUMerge ? [...signalMarkers, ...boundaryMarkers] : [];
|
||||||
if (uTooltipMarkers.length > 0) {
|
if (uTooltipMarkers.length > 0) {
|
||||||
if (window.fxMarkers) {
|
if (window.fxMarkers) {
|
||||||
window.fxMarkers = [ ...window.fxMarkers, ...uTooltipMarkers ];
|
window.fxMarkers = [ ...window.fxMarkers, ...uTooltipMarkers ];
|
||||||
|
|||||||
Reference in New Issue
Block a user