add changes

This commit is contained in:
jackyu66git
2025-04-24 17:49:36 +08:00
parent 48d5647ebf
commit b849ce8ec8
14 changed files with 242 additions and 408 deletions
+2 -1
View File
@@ -53,6 +53,7 @@ class ChanBI():
self.end_klc = klc
self.set_is_sure(True, sure_klc.end_time)
self.end_time = klc.end_time
#print(self.start_time, klc.start_time, klc.fx, "This bi is ended")
def set_is_sure(self, is_sure, time):
self.is_sure = is_sure
self.sure_time = time
@@ -88,4 +89,4 @@ class ChanBI():
self.low = klc.low
self.is_sure = False
self.sure_time = None
#print(self.start_klc.start_time, klc.start_time, klc.fx, "This bi is extended")
#print(self.start_time, klc.start_time, klc.fx, "This bi is extended")
+11
View File
@@ -40,6 +40,17 @@ class Chan_FX_TYPE(Enum):
DOWN = auto()
TT = auto()
BB = auto()
PTOP = auto()
PBOTTOM = auto()
class Chan_KLC_FX(Enum):
TOP1 = auto()
TOP2 = auto()
TOP3 = auto()
BOTTOM1 = auto()
BOTTOM2 = auto()
BOTTOM3 = auto()
UNKNOWN = auto()
class Chan_BI_DIR(Enum):
+12 -1
View File
@@ -1,7 +1,7 @@
import copy
from typing import Dict, Optional
from ChanEnum import Chan_FX_TYPE, Chan_KLINE_DIR, Chan_BI_DIR
from ChanEnum import Chan_FX_TYPE, Chan_KLINE_DIR, Chan_BI_DIR, Chan_KLC_FX
import ChanKLU
import ChanCTime
@@ -29,6 +29,10 @@ class ChanKLC():
self.bi_macdhist = 0
self.bi_macd_div = 0.0
self.bi = None
self.distance = 0
self.klc_fx_type = Chan_KLC_FX.UNKNOWN
def set_klc_fx_type(self, klc_fx_type):
self.klc_fx_type = klc_fx_type
def add_klu(self, klu):
self.klus.append(klu)
def set_end_klu(self, klu):
@@ -123,6 +127,12 @@ class ChanKLC():
self.bi_macdhist += klc.get_macdhist()
elif bi.dir == Chan_BI_DIR.DOWN and klc.get_macdhist() < 0:
self.bi_macdhist -= klc.get_macdhist()
self.distance = self.index - bi.start_klc.index
if False:
if bi.is_sure:
print(self.start_time, self.distance, bi.index, bi.start_time, bi.dir, bi.end_time)
else:
print(self.start_time, self.distance, bi.index, bi.start_time, bi.dir)
def get_macdhist(self):
self.macdhist = 0
for klu in self.klus:
@@ -164,6 +174,7 @@ class ChanKLC():
features['klc_pre_fx'] = (0 if self.pre.fx == Chan_FX_TYPE.UNKNOWN else 1 if self.pre.fx == Chan_FX_TYPE.TOP else 2) if self.pre else 0 #10
features['klc_pre_pre_fx'] = (0 if self.pre.pre.fx == Chan_FX_TYPE.UNKNOWN else 1 if self.pre.pre.fx == Chan_FX_TYPE.TOP else 2) if self.pre and self.pre.pre else 0 #11
features['klc_pre_pre_pre_fx'] = (0 if self.pre.pre.pre.fx == Chan_FX_TYPE.UNKNOWN else 1 if self.pre.pre.pre.fx == Chan_FX_TYPE.TOP else 2) if self.pre and self.pre.pre and self.pre.pre.pre else 0 #12
features['klc_distance'] = self.distance #13
# New Add 20250422
#features['klc_macdhist'] = self.get_macdhist() #11
#features['klc_bi_macdhist'] = self.bi_macdhist #12
+9
View File
@@ -21,6 +21,15 @@ class ChanKLU:
def set_idx(self, idx):
self.idx = idx
self.index = idx
def set_indicators(self, dict):
self.macd = dict['macd']
self.signal = dict['macdsignal']
self.macdhist = dict['macdhist']
self.ma5 = dict['ma5']
self.ma10 = dict['ma10']
self.ma30 = dict['ma30']
self.ma250 = dict['ma250']
self.rsi = dict['rsi']
def get_feature_data(self):
features = dict()
features['klu_close'] = self.close
+80 -33
View File
@@ -1,6 +1,6 @@
from datetime import timedelta
from pandas import DataFrame
from ChanEnum import Chan_FX_TYPE, Chan_KLINE_DIR, Chan_BI_DIR, Chan_SEG_DIR, Chan_ZS_DIR, Chan_BSP_DIR, Chan_BSP_TYPE
from ChanEnum import Chan_FX_TYPE, Chan_KLINE_DIR, Chan_BI_DIR, Chan_SEG_DIR, Chan_ZS_DIR, Chan_BSP_DIR, Chan_BSP_TYPE, Chan_KLC_FX
from ChanKLU import ChanKLU
from ChanKLC import ChanKLC
from ChanBI import ChanBI
@@ -88,6 +88,7 @@ class ChanLun():
df['macdhist'] = macd['macdhist']
return df
def plot_dataframe(self, dataframe):
return self.get_klu_list(dataframe)
klc_list = self.get_klc_list(dataframe)
bi_list= self.cal_bi_list(klc_list)
seg_list = self.get_seg_list(bi_list)
@@ -529,7 +530,7 @@ class ChanLun():
fx = self.check_fx(klc)
# Do nothing
if fx == Chan_FX_TYPE.UNKNOWN:
klc.set_fx(Chan_FX_TYPE.UNKNOWN)
klc.set_fx(fx)
if len(bi_list) > 0:
bi_list[-1].add_klc(klc)
klc.set_bi(bi_list[-1])
@@ -541,16 +542,19 @@ class ChanLun():
if last_bottom.index < last_top.index:
# Second top lower to be second sell point
if last_top.high > klc.high:
klc.set_fx(Chan_FX_TYPE.TT)
#klc.set_fx(Chan_FX_TYPE.TT)
klc.set_state("20")
bi_list[-1].add_klc(klc)
klc.set_bi(bi_list[-1])
#print(klc.start_time, klc.fx, "二类卖点Sell 1")
#print(klc.start_time, klc.fx, "二类卖点Sell 1")
else:
# A new top found
#last_top.set_fx(Chan_FX_TYPE.UNKNOWN)
last_top = klc
#print(klc.start_time, klc.fx, "一类买卖点Sell 1")
print(klc.start_time, klc.fx, bi_list[-1].dir, "Last Top Change 1")
klc.set_klc_fx_type(Chan_KLC_FX.TOP1)
#print(klc.start_time, klc.fx, "一类卖点Sell 1")
#klc.set_fx(fx)
klc.set_state("10")
bi_list[-1].add_klc(klc)
klc.set_bi(bi_list[-1])
@@ -558,8 +562,8 @@ class ChanLun():
# 不满足结合律的分型
if last_bottom.index + 4 > klc.index:
if last_top.high > klc.high:
#print(klc.start_time, last_bottom.start_time, klc.fx, "中枢买卖点Sell 1")
klc.set_fx(Chan_FX_TYPE.UNKNOWN)
#print(klc.start_time, klc.fx, "二类卖点Sell 1")
#klc.set_fx(Chan_FX_TYPE.PTOP)
bi_list[-1].add_klc(klc)
klc.set_bi(bi_list[-1])
# New TOP Found replace last top
@@ -571,18 +575,21 @@ class ChanLun():
pre_last_bi.update_bi(klc)
bi_list.remove(last_bi)
pre_last_bi.set_next(None)
last_top.set_fx(Chan_FX_TYPE.UNKNOWN)
#last_top.set_fx(Chan_FX_TYPE.PTOP)
last_top = klc
last_bottom = pre_last_bi.start_klc
print(klc.start_time, klc.fx, bi_list[-1].dir, "Last Top Bottom Change 1")
klc.set_klc_fx_type(Chan_KLC_FX.TOP2)
#print(klc.start_time, last_bi.start_klc.start_time, "New TOP Found reset last bi")
klc.set_state("10")
#print(klc.start_time, klc.fx, "笔卖点Sell 1")
bi_list[-1].add_klc(klc)
klc.set_bi(bi_list[-1])
else:
klc.set_fx(Chan_FX_TYPE.UNKNOWN)
klc.set_fx(Chan_FX_TYPE.PTOP)
bi_list[-1].add_klc(klc)
klc.set_bi(bi_list[-1])
#print(klc.start_time, last_bottom.start_time, klc.fx, "中枢买卖点Sell 2")
#print(klc.start_time, klc.fx, "无效分型")
# 满足结合律
else:
# New Temp TOP and last bottom confirmed ***** confirm last down bi(last bottom and last top)
@@ -595,10 +602,13 @@ class ChanLun():
bi.add_klc(klc)
bi_list.append(bi)
last_top = klc
print(klc.start_time, klc.fx, bi_list[-1].dir, "Last Top Change 2")
klc.set_klc_fx_type(Chan_KLC_FX.TOP2)
klc.set_state('30')
bi_list[-1].add_klc(klc)
klc.set_bi(bi_list[-1])
#print(klc.start_time, last_bottom.start_time, "Normal TOP Found, Confirm down bi 4")
#print(klc.start_time, klc.fx, "笔卖点Sell 2")
# last bottom = None
else:
if last_top.high < klc.high:
@@ -606,29 +616,42 @@ class ChanLun():
last_bi.set_start_klc(klc, Chan_BI_DIR.DOWN)
#last_top.set_fx(Chan_FX_TYPE.UNKNOWN)
last_top = klc
print(klc.start_time, klc.fx, bi_list[-1].dir, "Last Top Change 3")
bi_list[-1].add_klc(klc)
klc.set_bi(bi_list[-1])
#print(klc.start_time, klc.fx, "笔卖点Sell 3")
else:
klc.set_fx(Chan_FX_TYPE.TT)
klc.set_state('20')
#print(klc.start_time, klc.fx, "二类卖点Sell 2")
#print(klc.start_time, klc.fx, "二类卖点Sell 2")
bi_list[-1].add_klc(klc)
klc.set_bi(bi_list[-1])
else:
if last_bottom:
# 不满足结合律的分型
if last_bottom.index + 4 > klc.index:
klc.set_fx(Chan_FX_TYPE.UNKNOWN)
#klc.set_fx(Chan_FX_TYPE.PTOP)
bi_list[-1].add_klc(klc)
klc.set_bi(bi_list[-1])
#print(klc.start_time, klc.fx, "中枢卖点Sell 1")
else:
# First temp top and last bottom confirmed
last_top = klc
print(klc.start_time, klc.fx, bi_list[-1].dir, "Last Top Change 4")
bi_list[-1].add_klc(klc)
klc.set_bi(bi_list[-1])
#print(klc.start_time, klc.fx, "一类卖点Sell 1")
# Last top = None, last bottom = None, create first down bi
else:
# First temp top
last_top = klc
bi = ChanBI(klc, len(bi_list), Chan_BI_DIR.DOWN)
bi_list.append(bi)
bi_list[-1].add_klc(klc)
klc.set_bi(bi_list[-1])
print(klc.start_time, klc.fx, bi_list[-1].dir, "Last Top Change 5")
#print(klc.start_time, 'Create first top')
#print(klc.start_time, klc.fx, "笔卖点Sell 1")
#klc.fx = Bottom ========================
else:
if last_bottom:
@@ -637,16 +660,18 @@ class ChanLun():
if last_top.index < last_bottom.index:
# Second bottom uppper to be second buy point and confirm last bi
if last_bottom.low < klc.low:
klc.set_fx(Chan_FX_TYPE.BB)
#klc.set_fx(Chan_FX_TYPE.BB)
klc.set_state("-20")
bi_list[-1].add_klc(klc)
klc.set_bi(bi_list[-1])
#print(klc.start_time, klc.fx, "二类买点Buy 1")
#print(klc.start_time, klc.fx, "二类买点Buy 1")
else:
# A new bottom found
#last_bottom.set_fx(Chan_FX_TYPE.UNKNOWN)
last_bottom = klc
#print(klc.start_time, klc.fx, "一类买卖点Buy 1")
print(klc.start_time, klc.fx, bi_list[-1].dir, "Last Bottom Change 1")
klc.set_klc_fx_type(Chan_KLC_FX.BOTTOM1)
#print(klc.start_time, klc.fx, "一类买点Buy 1")
klc.set_state("-10")
bi_list[-1].add_klc(klc)
klc.set_bi(bi_list[-1])
@@ -654,10 +679,10 @@ class ChanLun():
# 不满足结合律的分型
if last_top.index + 4 > klc.index:
if last_bottom.low < klc.low:
klc.set_fx(Chan_FX_TYPE.UNKNOWN)
#klc.set_fx(Chan_FX_TYPE.PBOTTOM)
#klc.set_fx(Chan_FX_TYPE.BB)
#klc.set_state("-100")
#print(klc.start_time, klc.fx, "中枢买点Buy 1")
#print(klc.start_time, klc.fx, "中枢买点Buy 1")
bi_list[-1].add_klc(klc)
klc.set_bi(bi_list[-1])
# Found new bottom
@@ -669,18 +694,21 @@ class ChanLun():
pre_last_bi.update_bi(klc)
bi_list.remove(last_bi)
pre_last_bi.set_next(None)
last_bottom.set_fx(Chan_FX_TYPE.UNKNOWN)
#last_bottom.set_fx(Chan_FX_TYPE.PBOTTOM)
last_bottom = klc
last_top = pre_last_bi.start_klc
print(klc.start_time, klc.fx, bi_list[-1].dir, "Last Top Bottom Change 2")
klc.set_klc_fx_type(Chan_KLC_FX.BOTTOM2)
#print(klc.start_time, last_bi.start_klc.start_time, "New BOTTOM Found reset last bi")
klc.set_state("-10")
#print(klc.start_time, klc.fx, "笔买点Buy 1")
bi_list[-1].add_klc(klc)
klc.set_bi(bi_list[-1])
else:
klc.set_fx(Chan_FX_TYPE.UNKNOWN)
#klc.set_fx(Chan_FX_TYPE.UNKNOWN)
bi_list[-1].add_klc(klc)
klc.set_bi(bi_list[-1])
#print(klc.start_time, klc.fx, "中枢买卖点Buy 2")
#print(klc.start_time, klc.fx, "无效分型")
# 满足结合律的分型
else:
# New Temp Bottom and last top confirmed ***** confirm last up bi(last bottom and last top)
@@ -693,9 +721,12 @@ class ChanLun():
bi.add_klc(klc)
bi_list.append(bi)
last_bottom = klc
print(klc.start_time, klc.fx, bi_list[-1].dir, "Last Bottom Change 2")
klc.set_klc_fx_type(Chan_KLC_FX.BOTTOM2)
klc.set_state('-30')
bi_list[-1].add_klc(klc)
klc.set_bi(bi_list[-1])
#print(klc.start_time, klc.fx, "笔买点Buy 2")
#print(klc.start_time, last_top.start_time, "Normal Bottom Found, Confirm up bi 6")
# last_top = None
else:
@@ -704,12 +735,14 @@ class ChanLun():
last_bi.set_start_klc(klc, Chan_BI_DIR.UP)
#last_bottom.set_fx(Chan_FX_TYPE.UNKNOWN)
last_bottom = klc
print(klc.start_time, klc.fx, bi_list[-1].dir, "Last Bottom Change 3")
bi_list[-1].add_klc(klc)
klc.set_bi(bi_list[-1])
#print(klc.start_time, klc.fx, "笔买点Buy 3")
else:
klc.set_fx(Chan_FX_TYPE.BB)
klc.set_state('-20')
#print(klc.start_time, klc.fx, "二类买点Buy 2")
#print(klc.start_time, klc.fx, "二类买点Buy 2")
bi_list[-1].add_klc(klc)
klc.set_bi(bi_list[-1])
# last_bottom = None
@@ -717,14 +750,17 @@ class ChanLun():
if last_top:
# 不满足结合律的分型
if last_top.index + 4 > klc.index:
klc.set_fx(Chan_FX_TYPE.UNKNOWN)
#klc.set_fx(Chan_FX_TYPE.PBOTTOM)
bi_list[-1].add_klc(klc)
klc.set_bi(bi_list[-1])
#print(klc.start_time, klc.fx, "中枢买点Buy 1")
else:
# First temp bottom and last top confirmed
last_bottom = klc
print(klc.start_time, klc.fx, bi_list[-1].dir, "Last Bottom Change 4")
bi_list[-1].add_klc(klc)
klc.set_bi(bi_list[-1])
#print(klc.start_time, klc.fx, "一类买点Buy 1")
# Last top = None, last bottom = None, create first up bi
else:
# First temp bottom and no top yet
@@ -733,8 +769,11 @@ class ChanLun():
bi_list.append(bi)
bi_list[-1].add_klc(klc)
klc.set_bi(bi_list[-1])
#print(klc.start_time, 'Create first bottom')
#print(klc.time, klc.fx, klc.state)
print(klc.start_time, klc.fx, bi_list[-1].dir, "Last Bottom Change 5")
#print(klc.start_time, klc.fx, "笔买点Buy 4")
#if klc.fx != Chan_FX_TYPE.UNKNOWN:
#print(klc.start_time, klc.fx, klc.index)
"""
for klc in klc_list:
if klc.fx == Chan_FX_TYPE.TOP:
klc.state = "10"
@@ -742,6 +781,7 @@ class ChanLun():
if klc.fx == Chan_FX_TYPE.BOTTOM:
klc.state = "-10"
#print(klc.time, klc.state)
"""
return bi_list
def get_zs_list(self, bi_list, seg_list):
@@ -997,7 +1037,20 @@ class ChanLun():
def get_decimal(self, value):
return Decimal("{:.2f}".format(value))
def add_indicators(self, df):
fast = 8
slow = 16
period = 6
macd = ta.MACD(df, fastperiod=fast, slowperiod=slow, signalperiod=period)
df['macd'] = macd['macd']
df['macdsignal'] = macd['macdsignal']
df['macdhist'] = macd['macdhist']
df['ma5'] = ta.MA(df, timeperiod=5)
df['ma10'] = ta.MA(df, timeperiod=10)
df['ma30'] = ta.EMA(df, timeperiod=30)
df['ma250'] = ta.MA(df, timeperiod=250)
df['rsi'] = ta.RSI(df, timeperiod=14)
return df
def get_klc_list(self, dataframe):
klu_list = self.get_klu_list(dataframe)
klc_list = []
@@ -1075,14 +1128,8 @@ class ChanLun():
klu = ChanKLU(time_str, o, h, l, c, v)
klu.set_idx(i)
klu_list.append(klu)
klu.macd = item['macd']
klu.signal = item['macdsignal']
klu.macdhist = item['macdhist']
klu.ma5 = item['ma5']
klu.ma10 = item['ma10']
klu.ma30 = item['ma30']
klu.ma250 = item['ma250']
klu.rsi = item['rsi']
if True:
klu.set_indicators(item)
return klu_list
def get_bsp_list1(self, big_df):
+18 -4
View File
@@ -289,6 +289,13 @@ class ChanLunClassifier:
bi_index = 0
sample_list = []
for klc in klc_list:
if klc.pre and klc.next:
if klc.high > klc.pre.high and klc.high > klc.next.high:
klc.set_fx(Chan_FX_TYPE.TOP)
elif klc.low < klc.pre.low and klc.low < klc.next.low:
klc.set_fx(Chan_FX_TYPE.BOTTOM)
else:
klc.set_fx(Chan_FX_TYPE.UNKNOWN)
if klc.fx != Chan_FX_TYPE.UNKNOWN:
sample_list.append(klc)
for klc in sample_list:
@@ -311,8 +318,8 @@ class ChanLunClassifier:
# 判断这个bi是否赚钱(这里简单定义为:如果bi的结束价格高于起始价格,则标记为1,否则为0)
# 这个标签定义可以根据实际需求修改
seg = seg_list[bi_index]
if bi.start_klc.index == klc.index and bi.dir == Chan_BI_DIR.UP:
bi = bi_list[bi_index]
if bi.start_klc.index == klc.index:
#if klc.index == seg.start_bi.start_klc.index and seg.dir == Chan_SEG_DIR.UP:
label = 1
bi_index += 2
@@ -339,6 +346,13 @@ class ChanLunClassifier:
bi_index = 0
sample_list = []
for klc in klc_list:
if klc.pre and klc.next:
if klc.high > klc.pre.high and klc.high > klc.next.high:
klc.set_fx(Chan_FX_TYPE.TOP)
elif klc.low < klc.pre.low and klc.low < klc.next.low:
klc.set_fx(Chan_FX_TYPE.BOTTOM)
else:
klc.set_fx(Chan_FX_TYPE.UNKNOWN)
if klc.fx != Chan_FX_TYPE.UNKNOWN:
sample_list.append(klc)
for klc in sample_list:
@@ -357,7 +371,7 @@ class ChanLunClassifier:
else:
feature_vec.append(0)
seg = seg_list[bi_index]
if bi.start_klc.index == klc.index and bi.dir == Chan_BI_DIR.UP:
if bi.start_klc.index == klc.index:
#if klc.index == seg.start_bi.start_klc.index and seg.dir == Chan_SEG_DIR.UP:
label = 1
bi_index += 2
@@ -449,5 +463,5 @@ class ChanLunClassifier:
return self.get_decimal(self.model.predict(dtest)[0])
def get_decimal(self, value):
return Decimal("{:.2f}".format(value))
return Decimal("{:.4f}".format(value))
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+19 -54
View File
@@ -128,14 +128,25 @@ def get_kl_data(symbol, timeframe, limit=1000, start_time=None, end_time=None):
# 按时间排序
df = df.sort_values('timestamp')
chan = ChanLun()
klc_list = chan.get_klc_list(df)
klc_index = 0
df_copy = df.copy()
ret_df = pd.DataFrame(columns=['timestamp', 'open', 'high', 'low', 'close', 'volume', 'date'])
for index in range(0, len(df)):
if klc_index >= len(klc_list):
klc_index = len(klc_list) - 1
klc = klc_list[klc_index]
if index == klc.start_klu.index:
ret_df.loc[klc_index] = df_copy.loc[index]
klc_index += 1
# 如果过滤后没有数据,返回None
if len(df) == 0:
if len(ret_df) == 0:
print("过滤后无数据")
return None
print(f"获取到总共 {len(df)} 条数据")
return df
print(f"获取到总共 {len(ret_df)} 条数据")
return ret_df
except Exception as e:
print(f"获取数据错误: {e}")
@@ -166,9 +177,6 @@ def analyze_chan(df):
seg_list = chan.get_seg_list(bi_list)
zs_list = chan.calculate_zs(bi_list, seg_list)
# 获取笔中枢列表
bi_zs_list = chan.get_bi_zs_list(bi_list)
# 添加买卖点识别
buy_sell_points = identify_trade_points(bi_list, seg_list, zs_list)
@@ -177,7 +185,6 @@ def analyze_chan(df):
'bi_list': bi_list,
'seg_list': seg_list,
'zs_list': zs_list,
'bi_zs_list': bi_zs_list, # 添加笔中枢数据
'trade_points': buy_sell_points
}
@@ -403,36 +410,16 @@ def analyze():
'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,
'is_sure': zs.is_sure, # 添加中枢是否完成的标志
'type': getattr(zs, 'type', 'SEG_ZS') # 中枢类型,默认为线段中枢
'is_sure': zs.is_sure # 添加中枢是否完成的标志
} for zs in analysis_result['zs_list'] if zs.end_klc],
# 添加笔中枢列表
'bi_zs_list': [{
'start_time': zs.start_klc.start_time if isinstance(zs.start_klc.start_time, str) else zs.start_klc.start_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,
'is_sure': zs.is_sure, # 添加中枢是否完成的标志
'type': 'BI_ZS' # 标记为笔中枢
} for zs in analysis_result['bi_zs_list'] if zs.end_klc],
# 添加未完成中枢列表
'uncompleted_zs_list': [{
'start_time': zs.start_klc.start_time if isinstance(zs.start_klc.start_time, str) else zs.start_klc.start_time.astimezone(client_tz).isoformat(),
'end_time': None, # 未完成中枢没有结束时间
'zg': zs.zg,
'zd': zs.zd,
'is_sure': zs.is_sure, # 未完成中枢的is_sure为False
'type': getattr(zs, 'type', 'SEG_ZS') # 中枢类型,默认为线段中枢
'is_sure': zs.is_sure # 未完成中枢的is_sure为False
} for zs in analysis_result['zs_list'] if not zs.is_sure],
# 添加未完成笔中枢列表
'uncompleted_bi_zs_list': [{
'start_time': zs.start_klc.start_time if isinstance(zs.start_klc.start_time, str) else zs.start_klc.start_time.astimezone(client_tz).isoformat(),
'end_time': None, # 未完成中枢没有结束时间
'zg': zs.zg,
'zd': zs.zd,
'is_sure': zs.is_sure, # 未完成中枢的is_sure为False
'type': 'BI_ZS' # 标记为笔中枢
} for zs in analysis_result['bi_zs_list'] if not zs.is_sure],
'trade_points': [{
'type': point['type'],
'time': format_time_safely(point['time'], client_tz),
@@ -477,40 +464,18 @@ def analyze():
'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,
'is_sure': zs.is_sure, # 添加中枢是否完成的标志
'type': getattr(zs, 'type', 'SEG_ZS') # 中枢类型,默认为线段中枢
'is_sure': zs.is_sure # 添加中枢是否完成的标志
} for zs in element_analysis['zs_list'] if zs.end_klc]
# 添加小周期笔中枢
result['element_bi_zs_list'] = [{
'start_time': zs.start_klc.start_time if isinstance(zs.start_klc.start_time, str) else zs.start_klc.start_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,
'is_sure': zs.is_sure, # 添加中枢是否完成的标志
'type': 'BI_ZS' # 标记为笔中枢
} for zs in element_analysis['bi_zs_list'] if zs.end_klc]
# 添加小周期未完成中枢列表
result['element_uncompleted_zs_list'] = [{
'start_time': zs.start_klc.start_time if isinstance(zs.start_klc.start_time, str) else zs.start_klc.start_time.astimezone(client_tz).isoformat(),
'end_time': None, # 未完成中枢没有结束时间
'zg': zs.zg,
'zd': zs.zd,
'is_sure': zs.is_sure, # 未完成中枢的is_sure为False
'type': getattr(zs, 'type', 'SEG_ZS') # 中枢类型,默认为线段中枢
'is_sure': zs.is_sure # 未完成中枢的is_sure为False
} for zs in element_analysis['zs_list'] if not zs.is_sure]
# 添加小周期未完成笔中枢列表
result['element_uncompleted_bi_zs_list'] = [{
'start_time': zs.start_klc.start_time if isinstance(zs.start_klc.start_time, str) else zs.start_klc.start_time.astimezone(client_tz).isoformat(),
'end_time': None, # 未完成中枢没有结束时间
'zg': zs.zg,
'zd': zs.zd,
'is_sure': zs.is_sure, # 未完成中枢的is_sure为False
'type': 'BI_ZS' # 标记为笔中枢
} for zs in element_analysis['bi_zs_list'] if not zs.is_sure]
result['element_trade_points'] = [{
'type': point['type'],
'time': format_time_safely(point['time'], client_tz),
+78 -302
View File
@@ -196,16 +196,6 @@
font-weight: bold;
}
.main-bi-zs {
color: #FF9800;
font-weight: bold;
}
.element-bi-zs {
color: #9C27B0;
font-weight: bold;
}
/* 加载指示器旋转动画 */
@keyframes spin {
0% { transform: rotate(0deg); }
@@ -308,14 +298,6 @@
<input class="form-check-input" type="checkbox" id="showMainUncompletedZs" checked>
<label class="form-check-label" for="showMainUncompletedZs">未完成中枢</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="showMainBiZs" checked>
<label class="form-check-label" for="showMainBiZs">笔中枢</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="showMainUncompletedBiZs" checked>
<label class="form-check-label" for="showMainUncompletedBiZs">未完成笔中枢</label>
</div>
</div>
<div class="d-flex align-items-center mt-2">
<label class="form-label me-3 mb-0">次周期:</label>
@@ -335,14 +317,6 @@
<input class="form-check-input" type="checkbox" id="showElementUncompletedZs" checked>
<label class="form-check-label" for="showElementUncompletedZs">未完成中枢</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="showElementBiZs" checked>
<label class="form-check-label" for="showElementBiZs">笔中枢</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="showElementUncompletedBiZs" checked>
<label class="form-check-label" for="showElementUncompletedBiZs">未完成笔中枢</label>
</div>
</div>
</div>
<div class="col-md-6">
@@ -542,14 +516,10 @@
mainSegSeries: [],
mainZsSeries: [],
mainUncompletedZsSeries: [],
mainBiZsSeries: [], // 添加主周期笔中枢系列
mainUncompletedBiZsSeries: [], // 添加未完成主周期笔中枢系列
elementBiSeries: [],
elementSegSeries: [],
elementZsSeries: [],
elementUncompletedZsSeries: [],
elementBiZsSeries: [], // 添加元素周期笔中枢系列
elementUncompletedBiZsSeries: [], // 添加未完成元素周期笔中枢系列
tradePointSeries: []
},
state: {
@@ -735,16 +705,6 @@
updateChartDisplay();
});
// 添加笔中枢复选框变更事件
$('#showMainBiZs').change(function() {
updateChartDisplay();
});
// 添加未完成笔中枢复选框变更事件
$('#showMainUncompletedBiZs').change(function() {
updateChartDisplay();
});
// 添加买卖点复选框变更事件
$('#showElementBi').change(function() {
updateChartDisplay();
@@ -765,16 +725,6 @@
updateChartDisplay();
});
// 添加笔中枢复选框变更事件
$('#showElementBiZs').change(function() {
updateChartDisplay();
});
// 添加未完成笔中枢复选框变更事件
$('#showElementUncompletedBiZs').change(function() {
updateChartDisplay();
});
// 添加买卖点复选框变更事件
$('#showTradePoints').change(function() {
updateChartDisplay();
@@ -946,39 +896,51 @@
});
function updateChartDisplay() {
// 获取复选框状态
var showMainBi = $('#showMainBi').prop('checked');
var showMainSeg = $('#showMainSeg').prop('checked');
var showMainZs = $('#showMainZs').prop('checked');
var showMainUncompletedZs = $('#showMainUncompletedZs').prop('checked');
var showMainBiZs = $('#showMainBiZs').prop('checked');
var showMainUncompletedBiZs = $('#showMainUncompletedBiZs').prop('checked');
var showElementBi = $('#showElementBi').prop('checked');
var showElementSeg = $('#showElementSeg').prop('checked');
var showElementZs = $('#showElementZs').prop('checked');
var showElementUncompletedZs = $('#showElementUncompletedZs').prop('checked');
var showElementBiZs = $('#showElementBiZs').prop('checked');
var showElementUncompletedBiZs = $('#showElementUncompletedBiZs').prop('checked');
var showTradePoints = $('#showTradePoints').prop('checked');
if (currentData) {
// 保存当前的可见逻辑范围
let logicalRange = null;
let visibleRange = null;
// 调用显示/隐藏函数
if (showMainBi) { drawBi(main_bi_list); } else { removeBi(); }
if (showMainSeg) { drawSeg(main_seg_list); } else { removeSeg(); }
if (showMainZs) { drawZones(main_zs_list); } else { removeZones(); }
if (showMainUncompletedZs) { drawUncompletedZones(main_uncompleted_zs_list); } else { removeUncompletedZones(); }
if (showMainBiZs) { drawBiZones(main_bi_zs_list); } else { removeBiZones(); }
if (showMainUncompletedBiZs) { drawUncompletedBiZones(main_uncompleted_bi_zs_list); } else { removeUncompletedBiZones(); }
if (tvWidget && tvWidget.mainChart) {
// 优先使用逻辑范围,这样在缩放级别上更准确
logicalRange = tvWidget.mainChart.timeScale().getVisibleLogicalRange();
// 备用方案,获取实际时间戳范围
visibleRange = tvWidget.mainChart.timeScale().getVisibleRange();
}
// 元素显示
if (showElementBi) { drawBiOnElement(element_bi_list); } else { removeBiOnElement(); }
if (showElementSeg) { drawSegOnElement(element_seg_list); } else { removeSegOnElement(); }
if (showElementZs) { drawZonesOnElement(element_zs_list); } else { removeZonesOnElement(); }
if (showElementUncompletedZs) { drawUncompletedZonesOnElement(element_uncompleted_zs_list); } else { removeUncompletedZonesOnElement(); }
if (showElementBiZs) { drawBiZonesOnElement(element_bi_zs_list); } else { removeBiZonesOnElement(); }
if (showElementUncompletedBiZs) { drawUncompletedBiZonesOnElement(element_uncompleted_bi_zs_list); } else { removeUncompletedBiZonesOnElement(); }
console.log('更新图表显示');
console.log('- 显示K线:', $('#showOriginalKline').is(':checked'));
console.log('- 显示笔:', $('#showMainBi').is(':checked'));
console.log('- 显示线段:', $('#showMainSeg').is(':checked'));
console.log('- 显示中枢:', $('#showMainZs').is(':checked'));
console.log('- 显示未完成中枢:', $('#showMainUncompletedZs').is(':checked'));
console.log('- 显示MACD:', $('#showMacd').is(':checked'));
console.log('- 显示买卖点:', $('#showTradePoints').is(':checked'));
// 买卖点
if (showTradePoints) { drawTradePoints(trade_points_list); } else { removeTradePoints(); }
// 重新初始化图表,这将清除旧图形并重新绘制
initTradingView($('#symbol').val(), $('#timeframe').val());
// 如果有保存的范围,恢复它
setTimeout(() => {
if (tvWidget) {
if (logicalRange) {
// 应用保存的逻辑范围到所有图表
tvWidget.mainChart.timeScale().setVisibleLogicalRange(logicalRange);
tvWidget.volumeChart.timeScale().setVisibleLogicalRange(logicalRange);
if (tvWidget.macdChart) {
tvWidget.macdChart.timeScale().setVisibleLogicalRange(logicalRange);
}
} else if (visibleRange) {
// 如果没有逻辑范围,使用时间戳范围
tvWidget.mainChart.timeScale().setVisibleRange(visibleRange);
tvWidget.volumeChart.timeScale().setVisibleRange(visibleRange);
if (tvWidget.macdChart) {
tvWidget.macdChart.timeScale().setVisibleRange(visibleRange);
}
}
}
}, 100);
}
}
// 确保所有时间处理都使用UTC时间,包括表格数据显示
@@ -1116,14 +1078,10 @@
mainSegSeries: [],
mainZsSeries: [],
mainUncompletedZsSeries: [],
mainBiZsSeries: [], // 添加主周期笔中枢系列
mainUncompletedBiZsSeries: [], // 添加未完成主周期笔中枢系列
elementBiSeries: [],
elementSegSeries: [],
elementZsSeries: [],
elementUncompletedZsSeries: [],
elementBiZsSeries: [], // 添加元素周期笔中枢系列
elementUncompletedBiZsSeries: [], // 添加未完成元素周期笔中枢系列
tradePointSeries: []
},
state: {
@@ -3366,14 +3324,48 @@
}
console.log('重绘分形元素 - 开始');
console.log('- 显示笔:', $('#showMainBi').is(':checked'));
console.log('- 显示线段:', $('#showMainSeg').is(':checked'));
console.log('- 显示中枢:', $('#showMainZs').is(':checked'));
// 保存当前的图表可见范围
const mainChart = tvWidget.mainChart;
const visibleRange = mainChart.timeScale().getVisibleRange();
const logicalRange = mainChart.timeScale().getVisibleLogicalRange();
// 调用显示所有元素函数
displayAllElements();
// 获取当前图表设置
const showMacd = $('#showMacd').is(':checked');
const showOriginalKline = $('#showOriginalKline').is(':checked');
const showBi = $('#showMainBi').is(':checked');
const showSeg = $('#showMainSeg').is(':checked');
const showZs = $('#showMainZs').is(':checked');
// 保存原始K线和MACD数据,确保即使使用小级别,我们依然使用主周期的K线和MACD数据
const originalKlineData = currentData.kline_data;
const originalMacdData = currentData.macd;
// 重新初始化图表 - 这将清除所有系列并重新创建
console.log('重新初始化图表...');
// 临时存储currentData中可能被修改的字段
const tempCurrentData = {
kline_data: originalKlineData,
macd: originalMacdData
};
// 在重绘前确保K线和MACD数据不变
if (currentData.original_kline_data && currentData.original_macd) {
// 如果已经保存了原始数据,恢复它们
currentData.kline_data = currentData.original_kline_data;
currentData.macd = currentData.original_macd;
} else {
// 首次运行 - 保存原始数据
currentData.original_kline_data = originalKlineData;
currentData.original_macd = originalMacdData;
}
// 调用初始化函数
initTradingView($('#symbol').val(), $('#timeframe').val());
// 恢复原始可见范围
setTimeout(() => {
@@ -3486,14 +3478,12 @@
console.log('API返回数据:', data);
console.log('K线数据点数:', data.kline_data ? data.kline_data.length : 0);
console.log('笔中枢数据点数:', data.bi_zs_list ? data.bi_zs_list.length : 0);
// 检查是否包含小周期数据
if (data.element_timeframe) {
console.log('小周期笔数据点数:', data.element_bi_list ? data.element_bi_list.length : 0);
console.log('小周期线段数据点数:', data.element_seg_list ? data.element_seg_list.length : 0);
console.log('小周期中枢数据点数:', data.element_zs_list ? data.element_zs_list.length : 0);
console.log('小周期笔中枢数据点数:', data.element_bi_zs_list ? data.element_bi_zs_list.length : 0);
// 更新小周期选择器
$('#elementTimeframe').val(data.element_timeframe);
@@ -3517,220 +3507,6 @@
// 更新表格数据
updateTables(data);
}
// 显示中枢
function displayZones() {
// 主周期或元素周期中枢
if ($('#showMainZs').is(':checked') || $('#showElementZs').is(':checked') ||
$('#showMainUncompletedZs').is(':checked') || $('#showElementUncompletedZs').is(':checked') ||
$('#showMainBiZs').is(':checked') || $('#showElementBiZs').is(':checked') ||
$('#showMainUncompletedBiZs').is(':checked') || $('#showElementUncompletedBiZs').is(':checked')) {
console.log('绘制中枢 - 已启用');
// 绘制主周期线段中枢
if ($('#showMainZs').is(':checked') && currentData.zs_list && currentData.zs_list.length > 0) {
console.log(`绘制主周期线段中枢数据,共${currentData.zs_list.length}条`);
drawZoneList(currentData.zs_list, '#F1C40F', 'SEG_ZS');
}
// 绘制主周期笔中枢
if ($('#showMainBiZs').is(':checked') && currentData.bi_zs_list && currentData.bi_zs_list.length > 0) {
console.log(`绘制主周期笔中枢数据,共${currentData.bi_zs_list.length}条`);
// 添加检查笔中枢数据的详细日志
if (currentData.bi_zs_list.length > 0) {
console.log('笔中枢数据示例:', currentData.bi_zs_list[0]);
}
drawZoneList(currentData.bi_zs_list, '#FF9800', 'BI_ZS');
} else {
console.log('笔中枢数据不存在或勾选框未选中:',
'勾选状态=', $('#showMainBiZs').is(':checked'),
'数据存在=', !!(currentData.bi_zs_list),
'数据长度=', currentData.bi_zs_list ? currentData.bi_zs_list.length : 0);
}
// 元素周期线段中枢
if ($('#showElementZs').is(':checked') && currentData.element_zs_list && currentData.element_zs_list.length > 0) {
console.log(`绘制元素周期线段中枢数据,共${currentData.element_zs_list.length}条`);
drawZoneList(currentData.element_zs_list, '#3f51b5', 'SEG_ZS');
}
// 元素周期笔中枢
if ($('#showElementBiZs').is(':checked') && currentData.element_bi_zs_list && currentData.element_bi_zs_list.length > 0) {
console.log(`绘制元素周期笔中枢数据,共${currentData.element_bi_zs_list.length}条`);
if (currentData.element_bi_zs_list.length > 0) {
console.log('元素笔中枢数据示例:', currentData.element_bi_zs_list[0]);
}
drawZoneList(currentData.element_bi_zs_list, '#9C27B0', 'BI_ZS');
} else {
console.log('元素笔中枢数据不存在或勾选框未选中:',
'勾选状态=', $('#showElementBiZs').is(':checked'),
'数据存在=', !!(currentData.element_bi_zs_list),
'数据长度=', currentData.element_bi_zs_list ? currentData.element_bi_zs_list.length : 0);
}
// 主周期未完成线段中枢
if ($('#showMainUncompletedZs').is(':checked') && currentData.uncompleted_zs_list && currentData.uncompleted_zs_list.length > 0) {
console.log(`绘制主周期未完成线段中枢数据,共${currentData.uncompleted_zs_list.length}条`);
drawZoneList(currentData.uncompleted_zs_list, '#F1C40F', 'SEG_ZS', true);
}
// 主周期未完成笔中枢
if ($('#showMainUncompletedBiZs').is(':checked') && currentData.uncompleted_bi_zs_list && currentData.uncompleted_bi_zs_list.length > 0) {
console.log(`绘制主周期未完成笔中枢数据,共${currentData.uncompleted_bi_zs_list.length}条`);
drawZoneList(currentData.uncompleted_bi_zs_list, '#FF9800', 'BI_ZS', true);
}
// 元素周期未完成线段中枢
if ($('#showElementUncompletedZs').is(':checked') && currentData.element_uncompleted_zs_list && currentData.element_uncompleted_zs_list.length > 0) {
console.log(`绘制元素周期未完成线段中枢数据,共${currentData.element_uncompleted_zs_list.length}条`);
drawZoneList(currentData.element_uncompleted_zs_list, '#3f51b5', 'SEG_ZS', true);
}
// 元素周期未完成笔中枢
if ($('#showElementUncompletedBiZs').is(':checked') && currentData.element_uncompleted_bi_zs_list && currentData.element_uncompleted_bi_zs_list.length > 0) {
console.log(`绘制元素周期未完成笔中枢数据,共${currentData.element_uncompleted_bi_zs_list.length}条`);
drawZoneList(currentData.element_uncompleted_bi_zs_list, '#9C27B0', 'BI_ZS', true);
}
} else {
console.log('绘制中枢 - 已禁用');
}
}
// 绘制中枢列表函数
function drawZoneList(zoneList, color, type, isUncompleted = false) {
zoneList.forEach(function(zs) {
try {
// 直接使用UTC时间戳(秒)
const startTime = Math.floor(new Date(zs.start_time).getTime() / 1000);
// 未完成中枢的结束时间设为当前K线的最后时间
let endTime;
if (isUncompleted) {
endTime = Math.floor(new Date(currentData.kline_data[currentData.kline_data.length-1].date).getTime() / 1000);
} else {
endTime = Math.floor(new Date(zs.end_time).getTime() / 1000);
}
if (isNaN(startTime) || isNaN(endTime)) {
console.error(`${type}中枢时间转换错误:`, zs.start_time, zs.end_time);
return;
}
const zg = parseFloat(zs.zg); // 中枢上沿
const zd = parseFloat(zs.zd); // 中枢下沿
if (isNaN(zg) || isNaN(zd)) {
console.error(`${type}中枢价格转换错误:`, zs.zg, zs.zd);
return;
}
// 创建中枢上边界
const topSeries = mainChart.addLineSeries({
color: color,
lineWidth: 1,
lineStyle: isUncompleted ? 2 : 0, // 未完成中枢用虚线
lastValueVisible: false,
priceLineVisible: false,
});
topSeries.setData([
{ time: startTime, value: zg },
{ time: endTime, value: zg }
]);
// 为下边界创建另一条线
const bottomSeries = mainChart.addLineSeries({
color: color,
lineWidth: 1,
lineStyle: isUncompleted ? 2 : 0, // 未完成中枢用虚线
lastValueVisible: false,
priceLineVisible: false,
});
bottomSeries.setData([
{ time: startTime, value: zd },
{ time: endTime, value: zd }
]);
// 添加左边界
const leftSeries = mainChart.addLineSeries({
color: color,
lineWidth: 1,
lineStyle: isUncompleted ? 2 : 0, // 未完成中枢用虚线
lastValueVisible: false,
priceLineVisible: false,
});
leftSeries.setData([
{ time: startTime, value: zd },
{ time: startTime, value: zg }
]);
// 添加右边界
const rightSeries = mainChart.addLineSeries({
color: color,
lineWidth: 1,
lineStyle: isUncompleted ? 2 : 0, // 未完成中枢用虚线
lastValueVisible: false,
priceLineVisible: false,
});
rightSeries.setData([
{ time: endTime, value: zd },
{ time: endTime, value: zg }
]);
// 储存到相应的系列集合中
if (type === 'BI_ZS') {
if (color === '#FF9800') { // 主周期笔中枢
tvWidget.series.mainBiZsSeries.push({
time: startTime,
value: zg,
color: color,
lineWidth: 1,
lineStyle: isUncompleted ? 2 : 0
});
} else { // 元素周期笔中枢
tvWidget.series.elementBiZsSeries.push({
time: startTime,
value: zg,
color: color,
lineWidth: 1,
lineStyle: isUncompleted ? 2 : 0
});
}
}
} catch (e) {
console.error(`${type}中枢处理出错:`, e);
}
});
}
// 辅助函数,在updateTradingViewData或redrawFractalElements中调用
function displayAllElements() {
console.log('调用displayAllElements,开始绘制所有图形元素...');
// 显示笔
if ($('#showMainBi').is(':checked') || $('#showElementBi').is(':checked')) {
displayBi();
}
// 显示线段
if ($('#showMainSeg').is(':checked') || $('#showElementSeg').is(':checked')) {
displaySeg();
}
// 显示中枢(包含线段中枢和笔中枢)
displayZones();
// 显示买卖点
if ($('#showTradePoints').is(':checked')) {
displayTradePoints();
}
console.log('displayAllElements执行完成');
}
</script>
</body>
</html>