diff --git a/ChanLun.py b/ChanLun.py
index d745a20..bca059a 100644
--- a/ChanLun.py
+++ b/ChanLun.py
@@ -73,8 +73,7 @@ class ChanLun():
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
- if klc.pre and klc.next:
- if 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)
#print(klc.start_time, klc.end_time,klc.next.start_time, klc.next.end_time,klc.fx, "BOTTOM")
return Chan_FX_TYPE.BOTTOM
@@ -778,7 +777,7 @@ class ChanLun():
# Do nothing
if fx == Chan_FX_TYPE.UNKNOWN:
- if len(bi_list) > 0:
+ if len(bi_list) > 0 and klc.end_klu:
last_bi = bi_list[-1]
#print(klc.start_time, last_bi.start_time, last_bi.end_time, last_bi.dir, last_bi.high, last_bi.low, last_bottom.end_time, "last bi")
if last_top and last_bi.dir == Chan_BI_DIR.DOWN:
@@ -1082,6 +1081,8 @@ class ChanLun():
if seg.next.next.is_sure:
zg = min(seg.high, seg.next.high, seg.next.next.high)
zd = max(seg.low, seg.next.low, seg.next.next.low)
+ gg = max(seg.high, seg.next.high, seg.next.next.high)
+ dd = min(seg.low, seg.next.low, seg.next.next.low)
ddir = Chan_ZS_DIR.UP
ddir = None
if last_zs:
@@ -1104,6 +1105,8 @@ class ChanLun():
zs = ChanZS(seg, len(zs_list), ddir)
zs.set_zg(zg)
zs.set_zd(zd)
+ zs.set_gg(gg)
+ zs.set_dd(dd)
if last_zs:
last_zs.set_next(zs)
zs.set_pre(last_zs)
@@ -1130,6 +1133,8 @@ class ChanLun():
if seg.next.next.is_sure:
zg = min(seg.high, seg.next.high, seg.next.next.high)
zd = max(seg.low, seg.next.low, seg.next.next.low)
+ gg = max(seg.high, seg.next.high, seg.next.next.high)
+ dd = min(seg.low, seg.next.low, seg.next.next.low)
ddir = None
if last_zs:
if zg < last_zs.zd:
@@ -1151,6 +1156,8 @@ class ChanLun():
zs = ChanZS(seg, len(zs_list), ddir)
zs.set_zg(zg)
zs.set_zd(zd)
+ zs.set_gg(gg)
+ zs.set_dd(dd)
last_zs.set_next(zs)
zs.set_pre(last_zs)
zs_list.append(zs)
diff --git a/ChanZS.py b/ChanZS.py
index ca577f8..367368a 100644
--- a/ChanZS.py
+++ b/ChanZS.py
@@ -21,6 +21,8 @@ class ChanZS():
self.is_sure = False
self.zg = 0
self.zd = 0
+ self.gg = 0
+ self.dd = 0
self.dir = ddir
self.sure_time = None
self.end_klc = None
@@ -63,4 +65,8 @@ class ChanZS():
def set_zg(self, zg):
self.zg = zg
def set_zd(self, zd):
- self.zd = zd
\ No newline at end of file
+ self.zd = zd
+ def set_gg(self, gg):
+ self.gg = gg
+ def set_dd(self, dd):
+ self.dd = dd
\ No newline at end of file
diff --git a/web/app.py b/web/app.py
index af9889b..bbf1c66 100644
--- a/web/app.py
+++ b/web/app.py
@@ -538,6 +538,15 @@ def generate_replay_data(df, client_tz, symbol=None, element_timeframe=None, sta
'direction': convert_direction(bi.dir),
'macd_div': float(bi.macd_div) if hasattr(bi, 'macd_div') else 0
} for bi in filtered_bi_list if bi.end_klc],
+ 'element_uncompleted_bi_list': [{
+ 'start_time': bi.start_klc.end_time if isinstance(bi.start_klc.end_time, str) else bi.start_klc.end_time.astimezone(client_tz).isoformat(),
+ 'end_time': None, # 未完成笔没有结束时间
+ 'sure_time': format_time_safely(bi.sure_time, client_tz) if bi.sure_time else None,
+ 'start_price': bi.start_klc.low if convert_direction(bi.dir) == 1 else bi.start_klc.high,
+ 'end_price': None, # 未完成笔没有结束价格
+ 'direction': convert_direction(bi.dir),
+ 'macd_div': float(bi.macd_div) if hasattr(bi, 'macd_div') else 0
+ } for bi in filtered_bi_list if not bi.end_klc],
'element_seg_list': [{
'start_time': seg.start_bi.start_klc.end_time if isinstance(seg.start_bi.start_klc.end_time, str) else seg.start_bi.start_klc.end_time.astimezone(client_tz).isoformat(),
'end_time': (seg.end_bi.end_klc.end_time if isinstance(seg.end_bi.end_klc.end_time, str) else seg.end_bi.end_klc.end_time.astimezone(client_tz).isoformat()) if seg.end_bi else None,
@@ -545,12 +554,15 @@ def generate_replay_data(df, client_tz, symbol=None, element_timeframe=None, sta
'start_price': seg.start_bi.start_klc.low if convert_direction(seg.dir) == 1 else seg.start_bi.start_klc.high,
'end_price': seg.end_bi.end_klc.high if convert_direction(seg.dir) == 1 else seg.end_bi.end_klc.low if seg.end_bi else None,
'direction': convert_direction(seg.dir)
- } for seg in filtered_seg_list if seg.end_bi],
+ } for seg in filtered_seg_list if seg.is_sure],
+ 'element_uncompleted_seg_list': get_uncompleted_seg_list(filtered_seg_list, client_tz),
'element_zs_list': [{
'start_time': zs.start_klc.end_time if isinstance(zs.start_klc.end_time, str) else zs.start_klc.end_time.astimezone(client_tz).isoformat(),
'end_time': (zs.end_klc.end_time if isinstance(zs.end_klc.end_time, str) else zs.end_klc.end_time.astimezone(client_tz).isoformat()) if zs.end_klc else None,
'zg': zs.zg,
'zd': zs.zd,
+ 'gg': zs.gg,
+ 'dd': zs.dd,
'is_sure': zs.is_sure
} for zs in filtered_zs_list if zs.end_klc],
'element_uncompleted_zs_list': [{
@@ -558,6 +570,8 @@ def generate_replay_data(df, client_tz, symbol=None, element_timeframe=None, sta
'end_time': None,
'zg': zs.zg,
'zd': zs.zd,
+ 'gg': zs.gg,
+ 'dd': zs.dd,
'is_sure': zs.is_sure
} for zs in filtered_zs_list if not zs.is_sure],
'element_trade_points': [{
@@ -616,6 +630,15 @@ def generate_replay_data(df, client_tz, symbol=None, element_timeframe=None, sta
'direction': convert_direction(bi.dir),
'macd_div': float(bi.macd_div) if hasattr(bi, 'macd_div') else 0
} for bi in analysis_result['bi_list'] if bi.end_klc],
+ 'uncompleted_bi_list': [{
+ 'start_time': bi.start_klc.end_time if isinstance(bi.start_klc.end_time, str) else bi.start_klc.end_time.astimezone(client_tz).isoformat(),
+ 'end_time': None, # 未完成笔没有结束时间
+ 'sure_time': format_time_safely(bi.sure_time, client_tz) if bi.sure_time else None,
+ 'start_price': bi.start_klc.low if convert_direction(bi.dir) == 1 else bi.start_klc.high,
+ 'end_price': None, # 未完成笔没有结束价格
+ 'direction': convert_direction(bi.dir),
+ 'macd_div': float(bi.macd_div) if hasattr(bi, 'macd_div') else 0
+ } for bi in analysis_result['bi_list'] if not bi.end_klc],
'seg_list': [{
'start_time': seg.start_bi.start_klc.end_time if isinstance(seg.start_bi.start_klc.end_time, str) else seg.start_bi.start_klc.end_time.astimezone(client_tz).isoformat(),
'end_time': (seg.end_bi.end_klc.end_time if isinstance(seg.end_bi.end_klc.end_time, str) else seg.end_bi.end_klc.end_time.astimezone(client_tz).isoformat()) if seg.end_bi else None,
@@ -623,12 +646,15 @@ def generate_replay_data(df, client_tz, symbol=None, element_timeframe=None, sta
'start_price': seg.start_bi.start_klc.low if convert_direction(seg.dir) == 1 else seg.start_bi.start_klc.high,
'end_price': seg.end_bi.end_klc.high if convert_direction(seg.dir) == 1 else seg.end_bi.end_klc.low if seg.end_bi else None,
'direction': convert_direction(seg.dir)
- } for seg in analysis_result['seg_list'] if seg.end_bi],
+ } for seg in analysis_result['seg_list'] if seg.is_sure],
+ 'uncompleted_seg_list': get_uncompleted_seg_list(analysis_result['seg_list'], client_tz),
'zs_list': [{
'start_time': zs.start_klc.end_time if isinstance(zs.start_klc.end_time, str) else zs.start_klc.end_time.astimezone(client_tz).isoformat(),
'end_time': (zs.end_klc.end_time if isinstance(zs.end_klc.end_time, str) else zs.end_klc.end_time.astimezone(client_tz).isoformat()) if zs.end_klc else None,
'zg': zs.zg,
'zd': zs.zd,
+ 'gg': zs.gg,
+ 'dd': zs.dd,
'is_sure': zs.is_sure
} for zs in analysis_result['zs_list'] if zs.end_klc],
'uncompleted_zs_list': [{
@@ -636,6 +662,8 @@ def generate_replay_data(df, client_tz, symbol=None, element_timeframe=None, sta
'end_time': None,
'zg': zs.zg,
'zd': zs.zd,
+ 'gg': zs.gg,
+ 'dd': zs.dd,
'is_sure': zs.is_sure
} for zs in analysis_result['zs_list'] if not zs.is_sure],
'trade_points': [{
@@ -686,7 +714,9 @@ def generate_replay_data(df, client_tz, symbol=None, element_timeframe=None, sta
step_data.update({
'element_kline_data': [],
'element_bi_list': [],
+ 'element_uncompleted_bi_list': [],
'element_seg_list': [],
+ 'element_uncompleted_seg_list': [],
'element_zs_list': [],
'element_uncompleted_zs_list': [],
'element_trade_points': [],
@@ -1089,6 +1119,16 @@ def analyze():
'direction': convert_direction(bi.dir),
'macd_div': float(bi.macd_div) if hasattr(bi, 'macd_div') else 0
} for bi in analysis_result['bi_list'] if bi.end_klc],
+ # 添加未完成笔列表
+ 'uncompleted_bi_list': [{
+ 'start_time': bi.start_klc.end_time if isinstance(bi.start_klc.end_time, str) else bi.start_klc.end_time.astimezone(client_tz).isoformat(),
+ 'end_time': None, # 未完成笔没有结束时间
+ 'sure_time': format_time_safely(bi.sure_time, client_tz) if bi.sure_time else None,
+ 'start_price': bi.start_klc.low if convert_direction(bi.dir) == 1 else bi.start_klc.high,
+ 'end_price': None, # 未完成笔没有结束价格
+ 'direction': convert_direction(bi.dir),
+ 'macd_div': float(bi.macd_div) if hasattr(bi, 'macd_div') else 0
+ } for bi in analysis_result['bi_list'] if not bi.end_klc],
'seg_list': [{
'start_time': seg.start_bi.start_klc.end_time if isinstance(seg.start_bi.start_klc.end_time, str) else seg.start_bi.start_klc.end_time.astimezone(client_tz).isoformat(),
'end_time': (seg.end_bi.end_klc.end_time if isinstance(seg.end_bi.end_klc.end_time, str) else seg.end_bi.end_klc.end_time.astimezone(client_tz).isoformat()) if seg.end_bi else None,
@@ -1096,12 +1136,16 @@ def analyze():
'start_price': seg.start_bi.start_klc.low if convert_direction(seg.dir) == 1 else seg.start_bi.start_klc.high,
'end_price': seg.end_bi.end_klc.high if convert_direction(seg.dir) == 1 else seg.end_bi.end_klc.low if seg.end_bi else None,
'direction': convert_direction(seg.dir)
- } for seg in analysis_result['seg_list'] if seg.end_bi],
+ } for seg in analysis_result['seg_list'] if seg.is_sure],
+ # 添加未完成线段列表
+ 'uncompleted_seg_list': get_uncompleted_seg_list(analysis_result['seg_list'], client_tz),
'zs_list': [{
'start_time': zs.start_klc.end_time if isinstance(zs.start_klc.end_time, str) else zs.start_klc.end_time.astimezone(client_tz).isoformat(),
'end_time': (zs.end_klc.end_time if isinstance(zs.end_klc.end_time, str) else zs.end_klc.end_time.astimezone(client_tz).isoformat()) if zs.end_klc else None,
'zg': zs.zg,
'zd': zs.zd,
+ 'gg': zs.gg,
+ 'dd': zs.dd,
'is_sure': zs.is_sure # 添加中枢是否完成的标志
} for zs in analysis_result['zs_list'] if zs.end_klc],
# 添加未完成中枢列表
@@ -1110,6 +1154,8 @@ def analyze():
'end_time': None, # 未完成中枢没有结束时间
'zg': zs.zg,
'zd': zs.zd,
+ 'gg': zs.gg,
+ 'dd': zs.dd,
'is_sure': zs.is_sure # 未完成中枢的is_sure为False
} for zs in analysis_result['zs_list'] if not zs.is_sure],
'trade_points': [{
@@ -1201,6 +1247,16 @@ def analyze():
'direction': convert_direction(bi.dir),
'macd_div': float(bi.macd_div) if hasattr(bi, 'macd_div') else 0
} for bi in element_analysis['bi_list'] if bi.end_klc]
+ # 添加次周期未完成笔列表
+ result['element_uncompleted_bi_list'] = [{
+ 'start_time': bi.start_klc.end_time if isinstance(bi.start_klc.end_time, str) else bi.start_klc.end_time.astimezone(client_tz).isoformat(),
+ 'end_time': None, # 未完成笔没有结束时间
+ 'sure_time': format_time_safely(bi.sure_time, client_tz) if bi.sure_time else None,
+ 'start_price': bi.start_klc.low if convert_direction(bi.dir) == 1 else bi.start_klc.high,
+ 'end_price': None, # 未完成笔没有结束价格
+ 'direction': convert_direction(bi.dir),
+ 'macd_div': float(bi.macd_div) if hasattr(bi, 'macd_div') else 0
+ } for bi in element_analysis['bi_list'] if not bi.end_klc]
# 添加小周期K线数据
result['element_kline_data'] = clean_dataframe_for_json(element_df).to_dict('records')
@@ -1212,13 +1268,17 @@ def analyze():
'start_price': seg.start_bi.start_klc.low if convert_direction(seg.dir) == 1 else seg.start_bi.start_klc.high,
'end_price': seg.end_bi.end_klc.high if convert_direction(seg.dir) == 1 else seg.end_bi.end_klc.low if seg.end_bi else None,
'direction': convert_direction(seg.dir)
- } for seg in element_analysis['seg_list'] if seg.end_bi]
+ } for seg in element_analysis['seg_list'] if seg.is_sure]
+ # 添加次周期未完成线段列表
+ result['element_uncompleted_seg_list'] = get_uncompleted_seg_list(element_analysis['seg_list'], client_tz)
result['element_zs_list'] = [{
'start_time': zs.start_klc.end_time if isinstance(zs.start_klc.end_time, str) else zs.start_klc.end_time.astimezone(client_tz).isoformat(),
'end_time': (zs.end_klc.end_time if isinstance(zs.end_klc.end_time, str) else zs.end_klc.end_time.astimezone(client_tz).isoformat()) if zs.end_klc else None,
'zg': zs.zg,
'zd': zs.zd,
+ 'gg': zs.gg,
+ 'dd': zs.dd,
'is_sure': zs.is_sure # 添加中枢是否完成的标志
} for zs in element_analysis['zs_list'] if zs.end_klc]
@@ -1227,6 +1287,8 @@ def analyze():
'end_time': None, # 未完成中枢没有结束时间
'zg': zs.zg,
'zd': zs.zd,
+ 'gg': zs.gg,
+ 'dd': zs.dd,
'is_sure': zs.is_sure # 未完成中枢的is_sure为False
} for zs in element_analysis['zs_list'] if not zs.is_sure]
@@ -1605,6 +1667,43 @@ def filter_stocks():
else:
return jsonify({'error': str(e)})
+def get_uncompleted_seg_list(seg_list, client_tz):
+ """获取未完成线段列表,正确处理倒数第二个和最后一个未完成线段"""
+ uncompleted_segs = [seg for seg in seg_list if not seg.is_sure]
+
+ if len(uncompleted_segs) == 0:
+ return []
+
+ result = []
+
+ for i, seg in enumerate(uncompleted_segs):
+ is_last = (i == len(uncompleted_segs) - 1) # 是否为最后一个未完成线段
+
+ seg_data = {
+ 'start_time': seg.start_bi.start_klc.end_time if isinstance(seg.start_bi.start_klc.end_time, str) else seg.start_bi.start_klc.end_time.astimezone(client_tz).isoformat(),
+ 'sure_time': format_time_safely(seg.sure_time, client_tz) if seg.sure_time else None,
+ 'start_price': seg.start_bi.start_klc.low if convert_direction(seg.dir) == 1 else seg.start_bi.start_klc.high,
+ 'direction': convert_direction(seg.dir)
+ }
+
+ if is_last:
+ # 最后一个未完成线段:没有结束时间和价格
+ seg_data['end_time'] = None
+ seg_data['end_price'] = None
+ else:
+ # 倒数第二个及之前的未完成线段:使用实际的结束时间和价格
+ if seg.end_bi and seg.end_bi.end_klc:
+ seg_data['end_time'] = seg.end_bi.end_klc.end_time if isinstance(seg.end_bi.end_klc.end_time, str) else seg.end_bi.end_klc.end_time.astimezone(client_tz).isoformat()
+ seg_data['end_price'] = seg.end_bi.end_klc.high if convert_direction(seg.dir) == 1 else seg.end_bi.end_klc.low
+ else:
+ # 如果没有结束笔,设为None
+ seg_data['end_time'] = None
+ seg_data['end_price'] = None
+
+ result.append(seg_data)
+
+ return result
+
def format_fx_type(fx_type):
"""格式化分型类型显示"""
fx_type_map = {
diff --git a/web/templates/index.html b/web/templates/index.html
index e21f65e..1359660 100644
--- a/web/templates/index.html
+++ b/web/templates/index.html
@@ -1111,6 +1111,7 @@
起始价格 |
结束价格 |
方向 |
+ 状态 |
@@ -2578,6 +2579,7 @@
// 清空已有的主周期笔系列
tvWidget.series.mainBiSeries = [];
+ tvWidget.series.mainUncompletedBiSeries = [];
// 遍历处理每个笔
currentData.bi_list.forEach(function(bi) {
@@ -2658,6 +2660,10 @@
if ($('#showElementBi').is(':checked') && currentData.element_bi_list && currentData.element_bi_list.length > 0) {
console.log(`绘制次周期笔数据,共${currentData.element_bi_list.length}条`);
+ // 清空已有的次周期笔系列
+ tvWidget.series.elementBiSeries = [];
+ tvWidget.series.elementUncompletedBiSeries = [];
+
currentData.element_bi_list.forEach(function(bi) {
try {
// 直接使用UTC时间戳(秒)
@@ -2732,11 +2738,130 @@
});
}
+ // 绘制未完成笔 - 主周期
+ if ($('#showMainBi').is(':checked') && currentData.uncompleted_bi_list && currentData.uncompleted_bi_list.length > 0) {
+ console.log(`绘制主周期未完成笔数据,共${currentData.uncompleted_bi_list.length}条`);
+
+ currentData.uncompleted_bi_list.forEach(function(bi) {
+ try {
+ // 直接使用UTC时间戳(秒)
+ const startTime = Math.floor(new Date(bi.start_time).getTime() / 1000);
+ // 未完成笔的结束时间设为当前K线的最后时间
+ const endTime = Math.floor(new Date(currentData.kline_data[currentData.kline_data.length-1].date).getTime() / 1000);
+
+ if (isNaN(startTime) || isNaN(endTime)) {
+ console.error('主周期未完成笔时间转换错误:', bi.start_time);
+ return;
+ }
+
+ const startPrice = parseFloat(bi.start_price);
+
+ if (isNaN(startPrice)) {
+ console.error('主周期未完成笔价格转换错误:', bi.start_price);
+ return;
+ }
+
+ // 根据笔的方向确定终点价格
+ let endPrice;
+ const latestKline = currentData.kline_data[currentData.kline_data.length-1];
+ if (bi.direction === 1) {
+ // 向上笔,终点为最新K线的最高点
+ endPrice = parseFloat(latestKline.high);
+ } else {
+ // 向下笔,终点为最新K线的最低点
+ endPrice = parseFloat(latestKline.low);
+ }
+
+ // 添加未完成笔(红色虚线)
+ biLines.push({
+ startTime: startTime,
+ endTime: endTime,
+ startPrice: startPrice,
+ endPrice: endPrice,
+ color: '#FF0000', // 红色
+ lineWidth: 1,
+ lineStyle: 2 // 虚线
+ });
+
+ // 添加到图表对象
+ tvWidget.series.mainUncompletedBiSeries.push({
+ time: startTime,
+ value: startPrice,
+ color: '#FF0000',
+ lineWidth: 1,
+ lineStyle: 2
+ });
+ } catch (e) {
+ console.error('主周期未完成笔处理出错:', e);
+ }
+ });
+ }
+
+ // 绘制未完成笔 - 次周期
+ if ($('#showElementBi').is(':checked') && currentData.element_uncompleted_bi_list && currentData.element_uncompleted_bi_list.length > 0) {
+ console.log(`绘制次周期未完成笔数据,共${currentData.element_uncompleted_bi_list.length}条`);
+
+ currentData.element_uncompleted_bi_list.forEach(function(bi) {
+ try {
+ // 直接使用UTC时间戳(秒)
+ const startTime = Math.floor(new Date(bi.start_time).getTime() / 1000);
+ // 未完成笔的结束时间设为当前K线的最后时间
+ const endTime = Math.floor(new Date(currentData.kline_data[currentData.kline_data.length-1].date).getTime() / 1000);
+
+ if (isNaN(startTime) || isNaN(endTime)) {
+ console.error('次周期未完成笔时间转换错误:', bi.start_time);
+ return;
+ }
+
+ const startPrice = parseFloat(bi.start_price);
+
+ if (isNaN(startPrice)) {
+ console.error('次周期未完成笔价格转换错误:', bi.start_price);
+ return;
+ }
+
+ // 根据笔的方向确定终点价格
+ let endPrice;
+ const latestKline = currentData.kline_data[currentData.kline_data.length-1];
+ if (bi.direction === 1) {
+ // 向上笔,终点为最新K线的最高点
+ endPrice = parseFloat(latestKline.high);
+ } else {
+ // 向下笔,终点为最新K线的最低点
+ endPrice = parseFloat(latestKline.low);
+ }
+
+ // 添加未完成笔(红色虚线)
+ biLines.push({
+ startTime: startTime,
+ endTime: endTime,
+ startPrice: startPrice,
+ endPrice: endPrice,
+ color: '#FF0000', // 红色
+ lineWidth: 1,
+ lineStyle: 2 // 虚线
+ });
+
+ // 添加到图表对象
+ tvWidget.series.elementUncompletedBiSeries.push({
+ time: startTime,
+ value: startPrice,
+ color: '#FF0000',
+ lineWidth: 1,
+ lineStyle: 2
+ });
+ } catch (e) {
+ console.error('次周期未完成笔处理出错:', e);
+ }
+ });
+ }
+
// 添加所有笔到图表
biLines.forEach(line => {
const lineSeries = mainChart.addLineSeries({
color: line.color,
lineWidth: line.lineWidth,
+ lineStyle: line.lineStyle || 0, // 支持虚线样式
lastValueVisible: false,
priceLineVisible: false,
});
@@ -2759,6 +2884,10 @@
if ($('#showMainSeg').is(':checked') && currentData.seg_list && currentData.seg_list.length > 0) {
console.log(`绘制主周期线段数据,共${currentData.seg_list.length}条`);
+ // 清空已有的主周期线段系列
+ tvWidget.series.mainSegSeries = [];
+ tvWidget.series.mainUncompletedSegSeries = [];
+
currentData.seg_list.forEach(function(seg) {
try {
// 直接使用UTC时间戳(秒)
@@ -2805,6 +2934,10 @@
if ($('#showElementSeg').is(':checked') && currentData.element_seg_list && currentData.element_seg_list.length > 0) {
console.log(`绘制次周期线段数据,共${currentData.element_seg_list.length}条`);
+ // 清空已有的次周期线段系列
+ tvWidget.series.elementSegSeries = [];
+ tvWidget.series.elementUncompletedSegSeries = [];
+
currentData.element_seg_list.forEach(function(seg) {
try {
// 直接使用UTC时间戳(秒)
@@ -2847,11 +2980,166 @@
});
}
+ // 绘制未完成线段 - 主周期
+ if ($('#showMainSeg').is(':checked') && currentData.uncompleted_seg_list && currentData.uncompleted_seg_list.length > 0) {
+ console.log(`绘制主周期未完成线段数据,共${currentData.uncompleted_seg_list.length}条`);
+
+ currentData.uncompleted_seg_list.forEach(function(seg) {
+ try {
+ // 直接使用UTC时间戳(秒)
+ const startTime = Math.floor(new Date(seg.start_time).getTime() / 1000);
+
+ if (isNaN(startTime)) {
+ console.error('主周期未完成线段时间转换错误:', seg.start_time);
+ return;
+ }
+
+ const startPrice = parseFloat(seg.start_price);
+
+ if (isNaN(startPrice)) {
+ console.error('主周期未完成线段价格转换错误:', seg.start_price);
+ return;
+ }
+
+ let endTime, endPrice;
+
+ if (seg.end_time && seg.end_price) {
+ // 有结束时间和价格的未完成线段(倒数第二个等)
+ endTime = Math.floor(new Date(seg.end_time).getTime() / 1000);
+ endPrice = parseFloat(seg.end_price);
+
+ if (isNaN(endTime) || isNaN(endPrice)) {
+ console.error('主周期未完成线段结束时间或价格转换错误:', seg.end_time, seg.end_price);
+ return;
+ }
+ } else {
+ // 没有结束时间和价格的未完成线段(最后一个)
+ endTime = Math.floor(new Date(currentData.kline_data[currentData.kline_data.length-1].date).getTime() / 1000);
+
+ if (isNaN(endTime)) {
+ console.error('主周期未完成线段结束时间转换错误');
+ return;
+ }
+
+ // 根据线段的方向确定终点价格
+ const latestKline = currentData.kline_data[currentData.kline_data.length-1];
+ if (seg.direction === 1) {
+ // 向上线段,终点为最新K线的最高点
+ endPrice = parseFloat(latestKline.high);
+ } else {
+ // 向下线段,终点为最新K线的最低点
+ endPrice = parseFloat(latestKline.low);
+ }
+ }
+
+ // 添加未完成线段(红色虚线)
+ segLines.push({
+ startTime: startTime,
+ endTime: endTime,
+ startPrice: startPrice,
+ endPrice: endPrice,
+ color: '#FF0000', // 红色
+ lineWidth: 2,
+ lineStyle: 2 // 虚线
+ });
+
+ // 添加到图表对象
+ tvWidget.series.mainUncompletedSegSeries.push({
+ time: startTime,
+ value: startPrice,
+ color: '#FF0000',
+ lineWidth: 2,
+ lineStyle: 2
+ });
+ } catch (e) {
+ console.error('主周期未完成线段处理出错:', e);
+ }
+ });
+ }
+
+ // 绘制未完成线段 - 次周期
+ if ($('#showElementSeg').is(':checked') && currentData.element_uncompleted_seg_list && currentData.element_uncompleted_seg_list.length > 0) {
+ console.log(`绘制次周期未完成线段数据,共${currentData.element_uncompleted_seg_list.length}条`);
+
+ currentData.element_uncompleted_seg_list.forEach(function(seg) {
+ try {
+ // 直接使用UTC时间戳(秒)
+ const startTime = Math.floor(new Date(seg.start_time).getTime() / 1000);
+
+ if (isNaN(startTime)) {
+ console.error('次周期未完成线段时间转换错误:', seg.start_time);
+ return;
+ }
+
+ const startPrice = parseFloat(seg.start_price);
+
+ if (isNaN(startPrice)) {
+ console.error('次周期未完成线段价格转换错误:', seg.start_price);
+ return;
+ }
+
+ let endTime, endPrice;
+
+ if (seg.end_time && seg.end_price) {
+ // 有结束时间和价格的未完成线段(倒数第二个等)
+ endTime = Math.floor(new Date(seg.end_time).getTime() / 1000);
+ endPrice = parseFloat(seg.end_price);
+
+ if (isNaN(endTime) || isNaN(endPrice)) {
+ console.error('次周期未完成线段结束时间或价格转换错误:', seg.end_time, seg.end_price);
+ return;
+ }
+ } else {
+ // 没有结束时间和价格的未完成线段(最后一个)
+ endTime = Math.floor(new Date(currentData.kline_data[currentData.kline_data.length-1].date).getTime() / 1000);
+
+ if (isNaN(endTime)) {
+ console.error('次周期未完成线段结束时间转换错误');
+ return;
+ }
+
+ // 根据线段的方向确定终点价格
+ const latestKline = currentData.kline_data[currentData.kline_data.length-1];
+ if (seg.direction === 1) {
+ // 向上线段,终点为最新K线的最高点
+ endPrice = parseFloat(latestKline.high);
+ } else {
+ // 向下线段,终点为最新K线的最低点
+ endPrice = parseFloat(latestKline.low);
+ }
+ }
+
+ // 添加未完成线段(红色虚线)
+ segLines.push({
+ startTime: startTime,
+ endTime: endTime,
+ startPrice: startPrice,
+ endPrice: endPrice,
+ color: '#FF0000', // 红色
+ lineWidth: 2,
+ lineStyle: 2 // 虚线
+ });
+
+ // 添加到图表对象
+ tvWidget.series.elementUncompletedSegSeries.push({
+ time: startTime,
+ value: startPrice,
+ color: '#FF0000',
+ lineWidth: 2,
+ lineStyle: 2
+ });
+ } catch (e) {
+ console.error('次周期未完成线段处理出错:', e);
+ }
+ });
+ }
+
// 添加所有线段到图表
segLines.forEach(line => {
const lineSeries = mainChart.addLineSeries({
color: line.color,
lineWidth: line.lineWidth,
+ lineStyle: line.lineStyle || 0, // 支持虚线样式
lastValueVisible: false,
priceLineVisible: false,
});
@@ -2886,6 +3174,8 @@
const zg = parseFloat(zs.zg); // 中枢上沿
const zd = parseFloat(zs.zd); // 中枢下沿
+ const gg = parseFloat(zs.gg); // 中枢高高
+ const dd = parseFloat(zs.dd); // 中枢低低
if (isNaN(zg) || isNaN(zd)) {
console.error('主周期中枢价格转换错误:', zs.zg, zs.zd);
@@ -2944,6 +3234,36 @@
{ time: endTime, value: zg }
]);
+ // 绘制gg线(中枢高高)
+ if (!isNaN(gg) && gg > 0) {
+ const ggSeries = mainChart.addLineSeries({
+ color: '#F1C40F', // 使用中枢自己的颜色
+ lineWidth: 1,
+ lastValueVisible: false,
+ priceLineVisible: false,
+ });
+
+ ggSeries.setData([
+ { time: startTime, value: gg },
+ { time: endTime, value: gg }
+ ]);
+ }
+
+ // 绘制dd线(中枢低低)
+ if (!isNaN(dd) && dd > 0) {
+ const ddSeries = mainChart.addLineSeries({
+ color: '#F1C40F', // 使用中枢自己的颜色
+ lineWidth: 1,
+ lastValueVisible: false,
+ priceLineVisible: false,
+ });
+
+ ddSeries.setData([
+ { time: startTime, value: dd },
+ { time: endTime, value: dd }
+ ]);
+ }
+
// 添加到图表对象
tvWidget.series.mainZsSeries.push({
time: startTime,
@@ -2992,6 +3312,8 @@
const zg = parseFloat(zs.zg); // 中枢上沿
const zd = parseFloat(zs.zd); // 中枢下沿
+ const gg = parseFloat(zs.gg); // 中枢高高
+ const dd = parseFloat(zs.dd); // 中枢低低
if (isNaN(zg) || isNaN(zd)) {
console.error('次周期中枢价格转换错误:', zs.zg, zs.zd);
@@ -3050,6 +3372,36 @@
{ time: endTime, value: zg }
]);
+ // 绘制gg线(中枢高高)
+ if (!isNaN(gg) && gg > 0) {
+ const ggSeries = mainChart.addLineSeries({
+ color: '#3f51b5', // 使用次周期中枢自己的颜色
+ lineWidth: 1,
+ lastValueVisible: false,
+ priceLineVisible: false,
+ });
+
+ ggSeries.setData([
+ { time: startTime, value: gg },
+ { time: endTime, value: gg }
+ ]);
+ }
+
+ // 绘制dd线(中枢低低)
+ if (!isNaN(dd) && dd > 0) {
+ const ddSeries = mainChart.addLineSeries({
+ color: '#3f51b5', // 使用次周期中枢自己的颜色
+ lineWidth: 1,
+ lastValueVisible: false,
+ priceLineVisible: false,
+ });
+
+ ddSeries.setData([
+ { time: startTime, value: dd },
+ { time: endTime, value: dd }
+ ]);
+ }
+
// 添加到图表对象
tvWidget.series.elementZsSeries.push({
time: startTime,
@@ -3106,6 +3458,8 @@
const zg = parseFloat(zs.zg); // 中枢上沿
const zd = parseFloat(zs.zd); // 中枢下沿
+ const gg = parseFloat(zs.gg); // 中枢高高
+ const dd = parseFloat(zs.dd); // 中枢低低
if (isNaN(zg) || isNaN(zd)) {
console.error('主周期未完成中枢价格转换错误:', zs.zg, zs.zd);
@@ -3116,7 +3470,6 @@
const topSeries = mainChart.addLineSeries({
color: '#F1C40F', // 主周期中枢颜色
lineWidth: 1,
- lineStyle: 2, // 虚线
lastValueVisible: false,
priceLineVisible: false,
});
@@ -3130,7 +3483,6 @@
const bottomSeries = mainChart.addLineSeries({
color: '#F1C40F', // 主周期中枢颜色
lineWidth: 1,
- lineStyle: 2, // 虚线
lastValueVisible: false,
priceLineVisible: false,
});
@@ -3144,7 +3496,6 @@
const leftSeries = mainChart.addLineSeries({
color: '#F1C40F', // 主周期中枢颜色
lineWidth: 1,
- lineStyle: 2, // 虚线
lastValueVisible: false,
priceLineVisible: false,
});
@@ -3171,34 +3522,60 @@
}
]);
+ // 绘制gg线(中枢高高)
+ if (!isNaN(gg) && gg > 0) {
+ const ggSeries = mainChart.addLineSeries({
+ color: '#F1C40F', // 使用中枢自己的颜色
+ lineWidth: 1,
+ lastValueVisible: false,
+ priceLineVisible: false,
+ });
+
+ ggSeries.setData([
+ { time: startTime, value: gg },
+ { time: endTime, value: gg }
+ ]);
+ }
+
+ // 绘制dd线(中枢低低)
+ if (!isNaN(dd) && dd > 0) {
+ const ddSeries = mainChart.addLineSeries({
+ color: '#F1C40F', // 使用中枢自己的颜色
+ lineWidth: 1,
+ lastValueVisible: false,
+ priceLineVisible: false,
+ });
+
+ ddSeries.setData([
+ { time: startTime, value: dd },
+ { time: endTime, value: dd }
+ ]);
+ }
+
// 添加到图表对象
tvWidget.series.mainUncompletedZsSeries.push({
time: startTime,
value: zg,
color: '#F1C40F',
- lineWidth: 1,
- lineStyle: 2
+ lineWidth: 1
});
tvWidget.series.mainUncompletedZsSeries.push({
time: endTime,
value: zg,
color: '#F1C40F',
- lineWidth: 1,
- lineStyle: 2
+ lineWidth: 1
});
tvWidget.series.mainUncompletedZsSeries.push({
time: startTime,
value: zd,
color: '#F1C40F',
- lineWidth: 1,
- lineStyle: 2
+ lineWidth: 1
});
tvWidget.series.mainUncompletedZsSeries.push({
time: endTime,
value: zd,
color: '#F1C40F',
- lineWidth: 1,
- lineStyle: 2
+ lineWidth: 1
});
} catch (e) {
console.error('主周期未完成中枢处理出错:', e);
@@ -3224,6 +3601,8 @@
const zg = parseFloat(zs.zg); // 中枢上沿
const zd = parseFloat(zs.zd); // 中枢下沿
+ const gg = parseFloat(zs.gg); // 中枢高高
+ const dd = parseFloat(zs.dd); // 中枢低低
if (isNaN(zg) || isNaN(zd)) {
console.error('次周期未完成中枢价格转换错误:', zs.zg, zs.zd);
@@ -3234,7 +3613,6 @@
const topSeries = mainChart.addLineSeries({
color: '#3f51b5', // 次周期中枢颜色
lineWidth: 1,
- lineStyle: 2, // 虚线
lastValueVisible: false,
priceLineVisible: false,
});
@@ -3248,7 +3626,6 @@
const bottomSeries = mainChart.addLineSeries({
color: '#3f51b5', // 次周期中枢颜色
lineWidth: 1,
- lineStyle: 2, // 虚线
lastValueVisible: false,
priceLineVisible: false,
});
@@ -3262,7 +3639,6 @@
const leftSeries = mainChart.addLineSeries({
color: '#3f51b5', // 次周期中枢颜色
lineWidth: 1,
- lineStyle: 2, // 虚线
lastValueVisible: false,
priceLineVisible: false,
});
@@ -3289,34 +3665,60 @@
}
]);
+ // 绘制gg线(中枢高高)
+ if (!isNaN(gg) && gg > 0) {
+ const ggSeries = mainChart.addLineSeries({
+ color: '#3f51b5', // 使用次周期中枢自己的颜色
+ lineWidth: 1,
+ lastValueVisible: false,
+ priceLineVisible: false,
+ });
+
+ ggSeries.setData([
+ { time: startTime, value: gg },
+ { time: endTime, value: gg }
+ ]);
+ }
+
+ // 绘制dd线(中枢低低)
+ if (!isNaN(dd) && dd > 0) {
+ const ddSeries = mainChart.addLineSeries({
+ color: '#3f51b5', // 使用次周期中枢自己的颜色
+ lineWidth: 1,
+ lastValueVisible: false,
+ priceLineVisible: false,
+ });
+
+ ddSeries.setData([
+ { time: startTime, value: dd },
+ { time: endTime, value: dd }
+ ]);
+ }
+
// 添加到图表对象
tvWidget.series.elementUncompletedZsSeries.push({
time: startTime,
value: zg,
color: '#3f51b5',
- lineWidth: 1,
- lineStyle: 2
+ lineWidth: 1
});
tvWidget.series.elementUncompletedZsSeries.push({
time: endTime,
value: zg,
color: '#3f51b5',
- lineWidth: 1,
- lineStyle: 2
+ lineWidth: 1
});
tvWidget.series.elementUncompletedZsSeries.push({
time: startTime,
value: zd,
color: '#3f51b5',
- lineWidth: 1,
- lineStyle: 2
+ lineWidth: 1
});
tvWidget.series.elementUncompletedZsSeries.push({
time: endTime,
value: zd,
color: '#3f51b5',
- lineWidth: 1,
- lineStyle: 2
+ lineWidth: 1
});
} catch (e) {
console.error('次周期未完成中枢处理出错:', e);
@@ -5233,27 +5635,56 @@
}
// 根据用户选择决定使用哪个周期的数据
- let segData, segSource;
+ let segData, segSource, uncompletedSegData;
if (useElementPeriod && data.element_seg_list && data.element_seg_list.length > 0) {
segData = data.element_seg_list;
+ uncompletedSegData = data.element_uncompleted_seg_list || [];
segSource = '小周期';
} else {
segData = data.seg_list;
+ uncompletedSegData = data.uncompleted_seg_list || [];
segSource = '主周期';
}
- console.log(`表格显示${segSource}线段数据,共${segData ? segData.length : 0}条`);
+
+ // 合并已完成和未完成的线段数据
+ let allSegData = [];
+ if (segData && segData.length > 0) {
+ allSegData = allSegData.concat(segData.map(seg => ({...seg, status: '已完成'})));
+ }
+ if (uncompletedSegData && uncompletedSegData.length > 0) {
+ allSegData = allSegData.concat(uncompletedSegData.map(seg => ({...seg, status: '未完成'})));
+ }
+
+ console.log(`表格显示${segSource}线段数据,已完成${segData ? segData.length : 0}条,未完成${uncompletedSegData ? uncompletedSegData.length : 0}条,总计${allSegData.length}条`);
tables.seg = $('#segTable').DataTable({
- data: segData || [],
+ data: allSegData,
order: [[0, 'desc']],
pageLength: 25,
columns: [
{ data: 'start_time', render: formatTime },
- { data: 'end_time', render: formatTime },
+ { data: 'end_time', render: function(data, type, row) {
+ if (data === null || data === undefined) {
+ return type === 'display' ? '未完成' : '';
+ }
+ return formatTime(data, type, row);
+ }},
{ data: 'sure_time', render: formatConfirmTime },
{ data: 'start_price', render: formatPrice },
- { data: 'end_price', render: formatPrice },
- { data: 'direction', render: formatDirection }
+ { data: 'end_price', render: function(data, type, row) {
+ if (data === null || data === undefined) {
+ return type === 'display' ? '未完成' : '';
+ }
+ return formatPrice(data, type, row);
+ }},
+ { data: 'direction', render: formatDirection },
+ { data: 'status', render: function(data, type, row) {
+ if (type === 'display') {
+ const color = data === '已完成' ? 'green' : 'red';
+ return `${data}`;
+ }
+ return data;
+ }}
]
});