Add klc and klu fx strength check
This commit is contained in:
+8
-2
@@ -49,7 +49,13 @@ class ChanKLC():
|
|||||||
self.volume_ratio = self.volume_ratio / len(self.klus)
|
self.volume_ratio = self.volume_ratio / len(self.klus)
|
||||||
self.volume = self.volume / len(self.klus)
|
self.volume = self.volume / len(self.klus)
|
||||||
self.macdhist = self.macdhist / len(self.klus)
|
self.macdhist = self.macdhist / len(self.klus)
|
||||||
|
def contain_klu_fx(self):
|
||||||
|
if len(self.klus) > 0:
|
||||||
|
for klu in self.klus:
|
||||||
|
klu.update_realtime_analysis()
|
||||||
|
if klu.fx_type == self.fx and klu.fx_strength > 1.8:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
def set_next(self, klc):
|
def set_next(self, klc):
|
||||||
self.next = klc
|
self.next = klc
|
||||||
def set_pre(self, klc):
|
def set_pre(self, klc):
|
||||||
@@ -1221,7 +1227,7 @@ class ChanKLC():
|
|||||||
# 获取分型后的几根K线数据
|
# 获取分型后的几根K线数据
|
||||||
subsequent_klcs = []
|
subsequent_klcs = []
|
||||||
temp = self.next
|
temp = self.next
|
||||||
for i in range(5): # 检查后续5根K线
|
for i in range(2): # 检查后续5根K线
|
||||||
if temp:
|
if temp:
|
||||||
subsequent_klcs.append(temp)
|
subsequent_klcs.append(temp)
|
||||||
temp = temp.next if hasattr(temp, 'next') else None
|
temp = temp.next if hasattr(temp, 'next') else None
|
||||||
|
|||||||
+385
@@ -1,3 +1,4 @@
|
|||||||
|
from ChanEnum import Chan_FX_TYPE
|
||||||
class ChanKLU:
|
class ChanKLU:
|
||||||
def __init__(self, time, open, high, low, close, volume):
|
def __init__(self, time, open, high, low, close, volume):
|
||||||
# _time, _close, _open, _high, _low, _extra_info={}
|
# _time, _close, _open, _high, _low, _extra_info={}
|
||||||
@@ -21,9 +22,375 @@ class ChanKLU:
|
|||||||
self.ma250 = 0
|
self.ma250 = 0
|
||||||
self.rsi = 0
|
self.rsi = 0
|
||||||
self.volume_ratio = 0
|
self.volume_ratio = 0
|
||||||
|
|
||||||
|
# === 新增:实时分型相关属性 ===
|
||||||
|
self.pre = None # 前一根K线
|
||||||
|
self.next = None # 后一根K线
|
||||||
|
self.fx_type = Chan_FX_TYPE.UNKNOWN # 分型类型:0=无分型,1=顶分型,-1=底分型
|
||||||
|
self.fx_strength = 0 # 分型强度:0-100
|
||||||
|
self.fx_confirmed = False # 分型是否确认
|
||||||
|
|
||||||
|
def set_next(self, next):
|
||||||
|
self.next = next
|
||||||
|
self.update_realtime_analysis()
|
||||||
|
#if self.fx_type != Chan_FX_TYPE.UNKNOWN and self.fx_strength > 1:
|
||||||
|
#print(self.index, self.time, self.fx_type, self.fx_confirmed, self.fx_strength)
|
||||||
|
def set_pre(self, pre):
|
||||||
|
self.pre = pre
|
||||||
|
def detect_realtime_fx(self):
|
||||||
|
"""
|
||||||
|
实时检测K线分型(不等待KLC确认)
|
||||||
|
基于原始K线的即时分型识别
|
||||||
|
"""
|
||||||
|
if not self.pre or not self.next:
|
||||||
|
self.fx_type = Chan_FX_TYPE.UNKNOWN
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 顶分型检测
|
||||||
|
if (self.high > self.pre.high and
|
||||||
|
self.high > self.next.high):
|
||||||
|
self.fx_type = Chan_FX_TYPE.TOP
|
||||||
|
self.fx_confirmed = True
|
||||||
|
return True
|
||||||
|
|
||||||
|
# 底分型检测
|
||||||
|
elif (self.low < self.pre.low and
|
||||||
|
self.low < self.next.low):
|
||||||
|
self.fx_type = Chan_FX_TYPE.BOTTOM
|
||||||
|
self.fx_confirmed = True
|
||||||
|
return True
|
||||||
|
|
||||||
|
self.fx_type = Chan_FX_TYPE.UNKNOWN
|
||||||
|
self.fx_confirmed = False
|
||||||
|
return False
|
||||||
|
|
||||||
|
def calculate_realtime_fx_strength(self):
|
||||||
|
"""
|
||||||
|
用self.pre和self.next实现分型强弱判断(与KLC中cal_fx_strength一致)
|
||||||
|
|
||||||
|
核心缠论原理:
|
||||||
|
- 强分型:出现在笔的末端,能够终结当前笔,标志着趋势转折
|
||||||
|
- 弱分型:出现在笔的中间,是中继性质,笔还会继续延伸
|
||||||
|
|
||||||
|
返回值:
|
||||||
|
3: 极强分型(笔终结+强确认)
|
||||||
|
2: 强分型(笔终结)
|
||||||
|
1: 偏强分型(可能终结笔)
|
||||||
|
0: 中性分型
|
||||||
|
-1: 偏弱分型(中继特征明显)
|
||||||
|
-2: 弱分型(明显中继)
|
||||||
|
-3: 极弱分型(无效分型)
|
||||||
|
"""
|
||||||
|
# 检查是否为分型,且有前后K线数据
|
||||||
|
if self.fx_type == Chan_FX_TYPE.UNKNOWN:
|
||||||
|
return 0
|
||||||
|
if not self.pre or not self.next:
|
||||||
|
return 100
|
||||||
|
# === 核心判断:分型在笔中的位置 ===
|
||||||
|
|
||||||
|
# 1. 检查这个分型是否能够终结当前笔
|
||||||
|
is_bi_end = self._check_if_bi_ending_fx()
|
||||||
|
|
||||||
|
# 2. 检查分型的后续走势确认
|
||||||
|
post_fx_confirmation = self._check_post_fx_confirmation()
|
||||||
|
|
||||||
|
# 3. 检查分型的标准性和强度
|
||||||
|
fx_quality = self._check_fx_quality()
|
||||||
|
|
||||||
|
# === 综合评分 ===
|
||||||
|
base_score = 0
|
||||||
|
|
||||||
|
# 笔位置是最重要的判断标准
|
||||||
|
if is_bi_end == 2: # 强烈确认笔终结
|
||||||
|
base_score = 2
|
||||||
|
elif is_bi_end == 1: # 可能笔终结
|
||||||
|
base_score = 1
|
||||||
|
elif is_bi_end == -1: # 明显中继
|
||||||
|
base_score = -2
|
||||||
|
elif is_bi_end == -2: # 强烈中继特征
|
||||||
|
base_score = -3
|
||||||
|
else: # 不确定
|
||||||
|
base_score = 0
|
||||||
|
|
||||||
|
# 后续确认调整
|
||||||
|
base_score += post_fx_confirmation
|
||||||
|
|
||||||
|
# 分型质量调整
|
||||||
|
base_score += fx_quality
|
||||||
|
|
||||||
|
# 限制在-3到3范围内
|
||||||
|
final_score = max(-3, min(3, base_score))
|
||||||
|
self.fx_strength = final_score
|
||||||
|
# 转换为0-100分制以保持接口一致性
|
||||||
|
#self.fx_strength = int((final_score + 3) * 100 / 6) # -3到3映射到0-100
|
||||||
|
if final_score > 1.8:
|
||||||
|
print(self.time, final_score, is_bi_end, post_fx_confirmation, fx_quality)
|
||||||
|
#print(self.time, final_score, is_bi_end, post_fx_confirmation, fx_quality)
|
||||||
|
return self.fx_strength
|
||||||
|
|
||||||
|
def _check_if_bi_ending_fx(self):
|
||||||
|
"""
|
||||||
|
检查分型是否为笔终结分型
|
||||||
|
返回值:
|
||||||
|
2: 强烈确认笔终结
|
||||||
|
1: 可能笔终结
|
||||||
|
0: 不确定
|
||||||
|
-1: 明显中继
|
||||||
|
-2: 强烈中继特征
|
||||||
|
"""
|
||||||
|
# 检查是否有足够的后续数据来判断
|
||||||
|
if not self.next or not hasattr(self.next, 'next'):
|
||||||
|
return 0
|
||||||
|
|
||||||
|
# 获取分型后的几根K线数据
|
||||||
|
subsequent_klus = []
|
||||||
|
temp = self.next
|
||||||
|
for i in range(2): # 检查后续2根K线
|
||||||
|
if temp:
|
||||||
|
subsequent_klus.append(temp)
|
||||||
|
temp = temp.next if hasattr(temp, 'next') else None
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
|
if len(subsequent_klus) < 2:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
if self.fx_type == Chan_FX_TYPE.TOP:
|
||||||
|
return self._check_top_bi_ending(subsequent_klus)
|
||||||
|
else: # BOTTOM
|
||||||
|
return self._check_bottom_bi_ending(subsequent_klus)
|
||||||
|
|
||||||
|
def _check_top_bi_ending(self, subsequent_klus):
|
||||||
|
"""检查顶分型是否为笔终结"""
|
||||||
|
# 强烈笔终结特征:
|
||||||
|
# 1. 后续K线持续下跌,且跌破关键位置
|
||||||
|
# 2. 没有新的更高的高点出现
|
||||||
|
|
||||||
|
broken_key_levels = 0
|
||||||
|
new_highs = 0
|
||||||
|
downward_trend = 0
|
||||||
|
|
||||||
|
# 检查关键价位突破
|
||||||
|
first_low = self.pre.low
|
||||||
|
middle_low = self.low
|
||||||
|
key_support = min(first_low, middle_low)
|
||||||
|
|
||||||
|
for i, klu in enumerate(subsequent_klus):
|
||||||
|
# 检查是否跌破关键支撑
|
||||||
|
if klu.low < key_support:
|
||||||
|
broken_key_levels += 1
|
||||||
|
|
||||||
|
# 检查是否出现新高
|
||||||
|
if klu.high > self.high:
|
||||||
|
new_highs += 1
|
||||||
|
|
||||||
|
# 检查下跌趋势
|
||||||
|
if i > 0 and klu.close < subsequent_klus[i-1].close:
|
||||||
|
downward_trend += 1
|
||||||
|
|
||||||
|
# 强烈笔终结:跌破关键位且无新高
|
||||||
|
if broken_key_levels >= 1 and new_highs == 0 and downward_trend >= 2:
|
||||||
|
return 2
|
||||||
|
|
||||||
|
# 可能笔终结:部分条件满足
|
||||||
|
if (broken_key_levels >= 1 and new_highs <= 1) or (new_highs == 0 and downward_trend >= 3):
|
||||||
|
return 1
|
||||||
|
|
||||||
|
# 明显中继:出现新高且未跌破关键位
|
||||||
|
if new_highs >= 2 and broken_key_levels == 0:
|
||||||
|
return -2
|
||||||
|
|
||||||
|
# 中继倾向:出现新高
|
||||||
|
if new_highs >= 1:
|
||||||
|
return -1
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def _check_bottom_bi_ending(self, subsequent_klus):
|
||||||
|
"""检查底分型是否为笔终结"""
|
||||||
|
# 强烈笔终结特征:
|
||||||
|
# 1. 后续K线持续上涨,且突破关键位置
|
||||||
|
# 2. 没有新的更低的低点出现
|
||||||
|
|
||||||
|
broken_key_levels = 0
|
||||||
|
new_lows = 0
|
||||||
|
upward_trend = 0
|
||||||
|
|
||||||
|
# 检查关键价位突破
|
||||||
|
first_high = self.pre.high
|
||||||
|
middle_high = self.high
|
||||||
|
key_resistance = max(first_high, middle_high)
|
||||||
|
|
||||||
|
for i, klu in enumerate(subsequent_klus):
|
||||||
|
# 检查是否突破关键阻力
|
||||||
|
if klu.high > key_resistance:
|
||||||
|
broken_key_levels += 1
|
||||||
|
|
||||||
|
# 检查是否出现新低
|
||||||
|
if klu.low < self.low:
|
||||||
|
new_lows += 1
|
||||||
|
|
||||||
|
# 检查上涨趋势
|
||||||
|
if i > 0 and klu.close > subsequent_klus[i-1].close:
|
||||||
|
upward_trend += 1
|
||||||
|
|
||||||
|
# 强烈笔终结:突破关键位且无新低
|
||||||
|
if broken_key_levels >= 1 and new_lows == 0 and upward_trend >= 2:
|
||||||
|
return 2
|
||||||
|
|
||||||
|
# 可能笔终结:部分条件满足
|
||||||
|
if (broken_key_levels >= 1 and new_lows <= 1) or (new_lows == 0 and upward_trend >= 3):
|
||||||
|
return 1
|
||||||
|
|
||||||
|
# 明显中继:出现新低且未突破关键位
|
||||||
|
if new_lows >= 2 and broken_key_levels == 0:
|
||||||
|
return -2
|
||||||
|
|
||||||
|
# 中继倾向:出现新低
|
||||||
|
if new_lows >= 1:
|
||||||
|
return -1
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def _check_post_fx_confirmation(self):
|
||||||
|
"""
|
||||||
|
检查分型后的走势确认
|
||||||
|
返回值:-1到1的调整分数
|
||||||
|
"""
|
||||||
|
if not self.next:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
score = 0
|
||||||
|
|
||||||
|
# 检查第三根K线的确认
|
||||||
|
third_klu = self.next
|
||||||
|
|
||||||
|
if self.fx_type == Chan_FX_TYPE.TOP:
|
||||||
|
# 顶分型:第三根K线应该走弱
|
||||||
|
middle_price = (self.high + self.low) / 2
|
||||||
|
|
||||||
|
if third_klu.close < middle_price:
|
||||||
|
score += 0.5
|
||||||
|
if third_klu.low < self.pre.low: # 跌破第一根K线低点
|
||||||
|
score += 0.5
|
||||||
|
if third_klu.close < third_klu.open and abs(third_klu.close - third_klu.open) > abs(self.close - self.open) * 0.5:
|
||||||
|
score += 0.3 # 明显阴线
|
||||||
|
|
||||||
|
else: # BOTTOM
|
||||||
|
# 底分型:第三根K线应该走强
|
||||||
|
middle_price = (self.high + self.low) / 2
|
||||||
|
|
||||||
|
if third_klu.close > middle_price:
|
||||||
|
score += 0.5
|
||||||
|
if third_klu.high > self.pre.high: # 突破第一根K线高点
|
||||||
|
score += 0.5
|
||||||
|
if third_klu.close > third_klu.open and abs(third_klu.close - third_klu.open) > abs(self.close - self.open) * 0.5:
|
||||||
|
score += 0.3 # 明显阳线
|
||||||
|
|
||||||
|
return min(1, max(-1, score))
|
||||||
|
|
||||||
|
def _check_fx_quality(self):
|
||||||
|
"""
|
||||||
|
检查分型本身的质量
|
||||||
|
返回值:-1到1的调整分数
|
||||||
|
"""
|
||||||
|
score = 0
|
||||||
|
|
||||||
|
# 检查分型的标准性
|
||||||
|
if self.fx_type == Chan_FX_TYPE.TOP:
|
||||||
|
# 高点突出程度
|
||||||
|
high_diff1 = (self.high - self.pre.high) / self.high if self.high > 0 else 0
|
||||||
|
high_diff2 = (self.high - self.next.high) / self.high if self.high > 0 else 0
|
||||||
|
min_diff = min(high_diff1, high_diff2)
|
||||||
|
|
||||||
|
if min_diff > 0.03: # 非常突出
|
||||||
|
score += 0.5
|
||||||
|
elif min_diff > 0.01: # 比较突出
|
||||||
|
score += 0.2
|
||||||
|
elif min_diff < 0.003: # 不够突出
|
||||||
|
score -= 0.5
|
||||||
|
|
||||||
|
else: # BOTTOM
|
||||||
|
# 低点突出程度
|
||||||
|
low_diff1 = (self.pre.low - self.low) / self.pre.low if self.pre.low > 0 else 0
|
||||||
|
low_diff2 = (self.next.low - self.low) / self.next.low if self.next.low > 0 else 0
|
||||||
|
min_diff = min(low_diff1, low_diff2)
|
||||||
|
|
||||||
|
if min_diff > 0.03: # 非常突出
|
||||||
|
score += 0.5
|
||||||
|
elif min_diff > 0.01: # 比较突出
|
||||||
|
score += 0.2
|
||||||
|
elif min_diff < 0.003: # 不够突出
|
||||||
|
score -= 0.5
|
||||||
|
|
||||||
|
# 检查量价配合
|
||||||
|
avg_volume = self._get_avg_volume(lookback=5)
|
||||||
|
if avg_volume > 0:
|
||||||
|
volume_ratio = self.volume / avg_volume
|
||||||
|
if volume_ratio > 1.5:
|
||||||
|
score += 0.3
|
||||||
|
elif volume_ratio < 0.7:
|
||||||
|
score -= 0.2
|
||||||
|
|
||||||
|
return min(1, max(-1, score))
|
||||||
|
|
||||||
|
def _get_avg_volume(self, lookback=5):
|
||||||
|
"""获取前N根K线平均成交量"""
|
||||||
|
volumes = []
|
||||||
|
temp = self.pre
|
||||||
|
|
||||||
|
for i in range(lookback):
|
||||||
|
if temp:
|
||||||
|
volumes.append(temp.volume)
|
||||||
|
temp = temp.pre if hasattr(temp, 'pre') else None
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
|
return sum(volumes) / len(volumes) if volumes else self.volume
|
||||||
|
|
||||||
|
def get_fx_signal(self):
|
||||||
|
"""
|
||||||
|
获取分型交易信号
|
||||||
|
返回: (信号类型, 强度, 建议)
|
||||||
|
"""
|
||||||
|
if not self.fx_confirmed:
|
||||||
|
return ("无信号", 0, "等待分型确认")
|
||||||
|
|
||||||
|
strength_level = "弱"
|
||||||
|
if self.fx_strength >= 80:
|
||||||
|
strength_level = "极强"
|
||||||
|
elif self.fx_strength >= 65:
|
||||||
|
strength_level = "强"
|
||||||
|
elif self.fx_strength >= 50:
|
||||||
|
strength_level = "中等"
|
||||||
|
|
||||||
|
if self.fx_type == Chan_FX_TYPE.TOP:
|
||||||
|
signal_type = f"{strength_level}顶分型"
|
||||||
|
if self.fx_strength >= 65:
|
||||||
|
suggestion = "考虑减仓或止盈"
|
||||||
|
else:
|
||||||
|
suggestion = "谨慎观望"
|
||||||
|
else:
|
||||||
|
signal_type = f"{strength_level}底分型"
|
||||||
|
if self.fx_strength >= 65:
|
||||||
|
suggestion = "考虑建仓或加仓"
|
||||||
|
else:
|
||||||
|
suggestion = "谨慎观望"
|
||||||
|
|
||||||
|
return (signal_type, self.fx_strength, suggestion)
|
||||||
|
|
||||||
|
def update_realtime_analysis(self):
|
||||||
|
"""
|
||||||
|
更新实时分析(在每根K线完成时调用)
|
||||||
|
"""
|
||||||
|
self.detect_realtime_fx()
|
||||||
|
if self.fx_confirmed:
|
||||||
|
self.calculate_realtime_fx_strength()
|
||||||
|
|
||||||
def set_idx(self, idx):
|
def set_idx(self, idx):
|
||||||
self.idx = idx
|
self.idx = idx
|
||||||
self.index = idx
|
self.index = idx
|
||||||
|
|
||||||
def set_indicators(self, item):
|
def set_indicators(self, item):
|
||||||
self.macd = float(item['macd']) if 'macd' in item and item['macd'] else 0
|
self.macd = float(item['macd']) if 'macd' in item and item['macd'] else 0
|
||||||
self.signal = float(item['macdsignal']) if 'macdsignal' in item and item['macdsignal'] else 0
|
self.signal = float(item['macdsignal']) if 'macdsignal' in item and item['macdsignal'] else 0
|
||||||
@@ -39,6 +406,10 @@ class ChanKLU:
|
|||||||
|
|
||||||
self.rsi = float(item['rsi']) if 'rsi' in item and item['rsi'] else 0
|
self.rsi = float(item['rsi']) if 'rsi' in item and item['rsi'] else 0
|
||||||
self.volume_ratio = float(item['volume_ratio']) if 'volume_ratio' in item and item['volume_ratio'] else 0
|
self.volume_ratio = float(item['volume_ratio']) if 'volume_ratio' in item and item['volume_ratio'] else 0
|
||||||
|
|
||||||
|
# 设置指标后更新实时分析
|
||||||
|
self.update_realtime_analysis()
|
||||||
|
|
||||||
def get_feature_data(self):
|
def get_feature_data(self):
|
||||||
features = dict()
|
features = dict()
|
||||||
features['klu_close'] = self.close
|
features['klu_close'] = self.close
|
||||||
@@ -58,4 +429,18 @@ class ChanKLU:
|
|||||||
features['klu_ma250'] = self.ma250
|
features['klu_ma250'] = self.ma250
|
||||||
features['klu_rsi'] = self.rsi
|
features['klu_rsi'] = self.rsi
|
||||||
features['klu_volume_ratio'] = self.volume_ratio
|
features['klu_volume_ratio'] = self.volume_ratio
|
||||||
|
|
||||||
|
# === 新增:实时分型特征 ===
|
||||||
|
# 将枚举转换为数值:UNKNOWN=0, TOP=1, BOTTOM=-1
|
||||||
|
if self.fx_type == Chan_FX_TYPE.TOP:
|
||||||
|
fx_type_value = 1
|
||||||
|
elif self.fx_type == Chan_FX_TYPE.BOTTOM:
|
||||||
|
fx_type_value = -1
|
||||||
|
else:
|
||||||
|
fx_type_value = 0
|
||||||
|
|
||||||
|
features['klu_fx_type'] = fx_type_value
|
||||||
|
features['klu_fx_strength'] = self.fx_strength
|
||||||
|
features['klu_fx_confirmed'] = 1 if self.fx_confirmed else 0
|
||||||
|
|
||||||
return features
|
return features
|
||||||
+10
-4
@@ -67,12 +67,12 @@ class ChanLun():
|
|||||||
else:
|
else:
|
||||||
print(bi.start_klc.end_time, bi.dir, bi.is_sure)
|
print(bi.start_klc.end_time, bi.dir, bi.is_sure)
|
||||||
def check_fx(self, klc):
|
def check_fx(self, klc):
|
||||||
if klc.pre and klc.next:
|
if klc.pre and klc.next and klc.next.end_klu:
|
||||||
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)
|
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")
|
#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
|
||||||
if klc.pre and klc.next:
|
if klc.pre and klc.next and klc.next.end_klu:
|
||||||
if klc.low < klc.pre.low and klc.low < klc.next.low:
|
if klc.low < klc.pre.low and klc.low < klc.next.low:
|
||||||
klc.set_fx(Chan_FX_TYPE.BOTTOM)
|
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")
|
#print(klc.start_time, klc.end_time,klc.next.start_time, klc.next.end_time,klc.fx, "BOTTOM")
|
||||||
@@ -149,9 +149,9 @@ class ChanLun():
|
|||||||
klc = klc_list[klc_index]
|
klc = klc_list[klc_index]
|
||||||
if klc.end_klu and klc.end_klu.idx == index:
|
if klc.end_klu and klc.end_klu.idx == index:
|
||||||
klc_index += 1
|
klc_index += 1
|
||||||
if klc.klc_fx_type == Chan_KLC_FX.TOP1 or klc.klc_fx_type == Chan_KLC_FX.TOP2:
|
if (klc.klc_fx_type == Chan_KLC_FX.TOP1 or klc.klc_fx_type == Chan_KLC_FX.TOP2) and klc.contain_klu_fx():
|
||||||
fx_list.append(1)
|
fx_list.append(1)
|
||||||
elif klc.klc_fx_type == Chan_KLC_FX.BOTTOM1 or klc.klc_fx_type == Chan_KLC_FX.BOTTOM2:
|
elif (klc.klc_fx_type == Chan_KLC_FX.BOTTOM1 or klc.klc_fx_type == Chan_KLC_FX.BOTTOM2) and klc.contain_klu_fx():
|
||||||
fx_list.append(-1)
|
fx_list.append(-1)
|
||||||
else:
|
else:
|
||||||
fx_list.append(0)
|
fx_list.append(0)
|
||||||
@@ -235,6 +235,7 @@ class ChanLun():
|
|||||||
def get_kl_data(self, dataframe:DataFrame):
|
def get_kl_data(self, dataframe:DataFrame):
|
||||||
fields = "time,open,high,low,close,volume"
|
fields = "time,open,high,low,close,volume"
|
||||||
klu_list = []
|
klu_list = []
|
||||||
|
last_klu = None
|
||||||
for i in range(0, len(dataframe)):
|
for i in range(0, len(dataframe)):
|
||||||
item = dataframe.iloc[i]
|
item = dataframe.iloc[i]
|
||||||
date = item['date']
|
date = item['date']
|
||||||
@@ -258,8 +259,13 @@ class ChanLun():
|
|||||||
klu = ChanKLU(time_str, o, h, l, c, v)
|
klu = ChanKLU(time_str, o, h, l, c, v)
|
||||||
klu.set_idx(i)
|
klu.set_idx(i)
|
||||||
klu_list.append(klu)
|
klu_list.append(klu)
|
||||||
|
if last_klu:
|
||||||
|
klu.set_pre(last_klu)
|
||||||
|
last_klu.set_next(klu)
|
||||||
|
last_klu.detect_realtime_fx()
|
||||||
if 'macd' in item:
|
if 'macd' in item:
|
||||||
klu.set_indicators(item)
|
klu.set_indicators(item)
|
||||||
|
last_klu = klu
|
||||||
return klu_list
|
return klu_list
|
||||||
def cal_volume_ratio(self, dataframe, window=10):
|
def cal_volume_ratio(self, dataframe, window=10):
|
||||||
df = dataframe.copy()
|
df = dataframe.copy()
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,223 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
KLU与KLC分型强度算法一致性测试
|
||||||
|
验证两种算法在相同数据下是否产生一致的结果
|
||||||
|
"""
|
||||||
|
|
||||||
|
from ChanKLU import ChanKLU
|
||||||
|
from ChanKLC import ChanKLC
|
||||||
|
from ChanEnum import Chan_FX_TYPE, Chan_KLINE_DIR
|
||||||
|
import pandas as pd
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
|
def create_test_data():
|
||||||
|
"""创建测试用的K线数据"""
|
||||||
|
test_cases = [
|
||||||
|
# 测试用例1:标准顶分型
|
||||||
|
{
|
||||||
|
'name': '标准顶分型',
|
||||||
|
'data': [
|
||||||
|
{'open': 100, 'high': 102, 'low': 99, 'close': 101, 'volume': 1000}, # K1
|
||||||
|
{'open': 101, 'high': 105, 'low': 100, 'close': 103, 'volume': 1500}, # K2 (顶分型中心)
|
||||||
|
{'open': 103, 'high': 104, 'low': 98, 'close': 99, 'volume': 1200}, # K3
|
||||||
|
]
|
||||||
|
},
|
||||||
|
# 测试用例2:标准底分型
|
||||||
|
{
|
||||||
|
'name': '标准底分型',
|
||||||
|
'data': [
|
||||||
|
{'open': 100, 'high': 102, 'low': 99, 'close': 101, 'volume': 1000}, # K1
|
||||||
|
{'open': 101, 'high': 103, 'low': 95, 'close': 97, 'volume': 1500}, # K2 (底分型中心)
|
||||||
|
{'open': 97, 'high': 104, 'low': 96, 'close': 102, 'volume': 1200}, # K3
|
||||||
|
]
|
||||||
|
},
|
||||||
|
# 测试用例3:强势顶分型(放量+下影线)
|
||||||
|
{
|
||||||
|
'name': '强势顶分型',
|
||||||
|
'data': [
|
||||||
|
{'open': 100, 'high': 102, 'low': 99, 'close': 101, 'volume': 1000}, # K1
|
||||||
|
{'open': 101, 'high': 108, 'low': 100, 'close': 102, 'volume': 2500}, # K2 (强顶分型)
|
||||||
|
{'open': 102, 'high': 103, 'low': 95, 'close': 96, 'volume': 1800}, # K3 (大阴线确认)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
return test_cases
|
||||||
|
|
||||||
|
def setup_klu_chain(data_list):
|
||||||
|
"""设置KLU链"""
|
||||||
|
klus = []
|
||||||
|
base_time = datetime.now()
|
||||||
|
|
||||||
|
for i, data in enumerate(data_list):
|
||||||
|
time_str = (base_time + timedelta(minutes=i)).strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
klu = ChanKLU(time_str, data['open'], data['high'], data['low'], data['close'], data['volume'])
|
||||||
|
klu.set_idx(i)
|
||||||
|
|
||||||
|
# 设置基础技术指标
|
||||||
|
indicators = {
|
||||||
|
'ma5': data['close'] + (i-1) * 0.1,
|
||||||
|
'ma10': data['close'] + (i-1) * 0.05,
|
||||||
|
'rsi': 50 + (i % 3 - 1) * 15,
|
||||||
|
'macd': (i % 3 - 1) * 0.01,
|
||||||
|
'macdhist': (i % 2) * 0.005,
|
||||||
|
'volume_ratio': 1.0 + (i % 2) * 0.3
|
||||||
|
}
|
||||||
|
klu.set_indicators(indicators)
|
||||||
|
klus.append(klu)
|
||||||
|
|
||||||
|
# 建立前后关系
|
||||||
|
for i in range(len(klus)):
|
||||||
|
if i > 0:
|
||||||
|
klus[i].set_pre(klus[i-1])
|
||||||
|
if i < len(klus) - 1:
|
||||||
|
klus[i].set_next(klus[i+1])
|
||||||
|
|
||||||
|
return klus
|
||||||
|
|
||||||
|
def setup_klc_chain(data_list):
|
||||||
|
"""设置KLC链(基于KLU)"""
|
||||||
|
klus = setup_klu_chain(data_list)
|
||||||
|
klcs = []
|
||||||
|
|
||||||
|
# 为简化测试,假设每个KLU对应一个KLC(无包含关系处理)
|
||||||
|
for i, klu in enumerate(klus):
|
||||||
|
klc = ChanKLC(klu, i, Chan_KLINE_DIR.UP)
|
||||||
|
klc.set_end_klu(klu)
|
||||||
|
klcs.append(klc)
|
||||||
|
|
||||||
|
# 建立前后关系
|
||||||
|
for i in range(len(klcs)):
|
||||||
|
if i > 0:
|
||||||
|
klcs[i].set_pre(klcs[i-1])
|
||||||
|
if i < len(klcs) - 1:
|
||||||
|
klcs[i].set_next(klcs[i+1])
|
||||||
|
|
||||||
|
# 设置分型类型
|
||||||
|
if len(klcs) >= 3:
|
||||||
|
middle_klc = klcs[1]
|
||||||
|
if (middle_klc.high > klcs[0].high and middle_klc.high > klcs[2].high):
|
||||||
|
middle_klc.set_fx(Chan_FX_TYPE.TOP)
|
||||||
|
elif (middle_klc.low < klcs[0].low and middle_klc.low < klcs[2].low):
|
||||||
|
middle_klc.set_fx(Chan_FX_TYPE.BOTTOM)
|
||||||
|
|
||||||
|
return klcs
|
||||||
|
|
||||||
|
def compare_algorithms(test_cases):
|
||||||
|
"""对比KLU和KLC算法"""
|
||||||
|
|
||||||
|
print("=" * 80)
|
||||||
|
print("KLU与KLC分型强度算法一致性测试")
|
||||||
|
print("=" * 80)
|
||||||
|
|
||||||
|
for case in test_cases:
|
||||||
|
print(f"\n🔍 测试用例: {case['name']}")
|
||||||
|
print("-" * 50)
|
||||||
|
|
||||||
|
# 准备数据
|
||||||
|
klus = setup_klu_chain(case['data'])
|
||||||
|
klcs = setup_klc_chain(case['data'])
|
||||||
|
|
||||||
|
if len(klus) >= 3 and len(klcs) >= 3:
|
||||||
|
middle_klu = klus[1]
|
||||||
|
middle_klc = klcs[1]
|
||||||
|
|
||||||
|
# KLU分析
|
||||||
|
middle_klu.update_realtime_analysis()
|
||||||
|
klu_fx_type = middle_klu.fx_type
|
||||||
|
klu_strength = middle_klu.fx_strength
|
||||||
|
klu_confirmed = middle_klu.fx_confirmed
|
||||||
|
|
||||||
|
# KLC分析
|
||||||
|
klc_fx_type = middle_klc.fx
|
||||||
|
klc_strength_raw = middle_klc.cal_fx_strength() # -3到3
|
||||||
|
klc_strength_converted = int((klc_strength_raw + 3) * 100 / 6) # 转换为0-100
|
||||||
|
|
||||||
|
# 输出对比结果
|
||||||
|
print(f"K线数据: {case['data'][1]}")
|
||||||
|
print(f"\nKLU算法结果:")
|
||||||
|
print(f" 分型类型: {klu_fx_type}")
|
||||||
|
print(f" 分型强度: {klu_strength}")
|
||||||
|
print(f" 是否确认: {klu_confirmed}")
|
||||||
|
|
||||||
|
print(f"\nKLC算法结果:")
|
||||||
|
print(f" 分型类型: {klc_fx_type}")
|
||||||
|
print(f" 分型强度(原始): {klc_strength_raw}")
|
||||||
|
print(f" 分型强度(转换): {klc_strength_converted}")
|
||||||
|
|
||||||
|
# 一致性检查
|
||||||
|
type_consistent = (klu_fx_type == klc_fx_type)
|
||||||
|
strength_diff = abs(klu_strength - klc_strength_converted)
|
||||||
|
strength_consistent = strength_diff <= 10 # 允许10分以内的差异
|
||||||
|
|
||||||
|
print(f"\n一致性检查:")
|
||||||
|
print(f" 分型类型一致: {'✅' if type_consistent else '❌'}")
|
||||||
|
print(f" 强度差异: {strength_diff}分 {'✅' if strength_consistent else '❌'}")
|
||||||
|
|
||||||
|
if not type_consistent or not strength_consistent:
|
||||||
|
print(f" ⚠️ 算法结果不一致!")
|
||||||
|
else:
|
||||||
|
print(f" ✅ 算法结果一致")
|
||||||
|
else:
|
||||||
|
print("❌ 数据不足,无法进行对比")
|
||||||
|
|
||||||
|
def detailed_strength_analysis():
|
||||||
|
"""详细的强度分析对比"""
|
||||||
|
print("\n" + "=" * 80)
|
||||||
|
print("详细强度分析对比")
|
||||||
|
print("=" * 80)
|
||||||
|
|
||||||
|
# 创建一个明确的强分型案例
|
||||||
|
strong_top_data = [
|
||||||
|
{'open': 100, 'high': 101, 'low': 99, 'close': 100, 'volume': 1000},
|
||||||
|
{'open': 100, 'high': 110, 'low': 99, 'close': 102, 'volume': 3000}, # 强顶分型
|
||||||
|
{'open': 102, 'high': 103, 'low': 92, 'close': 93, 'volume': 2000}, # 强确认
|
||||||
|
{'open': 93, 'high': 94, 'low': 90, 'close': 91, 'volume': 1500}, # 继续下跌
|
||||||
|
{'open': 91, 'high': 92, 'low': 88, 'close': 89, 'volume': 1200}, # 进一步确认
|
||||||
|
]
|
||||||
|
|
||||||
|
klus = setup_klu_chain(strong_top_data)
|
||||||
|
|
||||||
|
if len(klus) >= 5:
|
||||||
|
target_klu = klus[1] # 目标分型K线
|
||||||
|
|
||||||
|
print(f"分析目标: 第2根K线 (索引1)")
|
||||||
|
print(f"K线数据: {strong_top_data[1]}")
|
||||||
|
|
||||||
|
# 更新分析
|
||||||
|
target_klu.update_realtime_analysis()
|
||||||
|
|
||||||
|
print(f"\n分型检测结果:")
|
||||||
|
print(f" 分型类型: {target_klu.fx_type}")
|
||||||
|
print(f" 分型确认: {target_klu.fx_confirmed}")
|
||||||
|
print(f" 最终强度: {target_klu.fx_strength}")
|
||||||
|
|
||||||
|
# 显示中间计算过程(需要重新调用以获取详细信息)
|
||||||
|
if target_klu.fx_confirmed:
|
||||||
|
print(f"\n强度计算过程:")
|
||||||
|
is_bi_end = target_klu._check_if_bi_ending_fx()
|
||||||
|
post_confirmation = target_klu._check_post_fx_confirmation()
|
||||||
|
fx_quality = target_klu._check_fx_quality()
|
||||||
|
|
||||||
|
print(f" 笔终结判断: {is_bi_end}")
|
||||||
|
print(f" 后续确认: {post_confirmation}")
|
||||||
|
print(f" 分型质量: {fx_quality}")
|
||||||
|
|
||||||
|
raw_score = is_bi_end + post_confirmation + fx_quality
|
||||||
|
final_raw = max(-3, min(3, raw_score))
|
||||||
|
converted_score = int((final_raw + 3) * 100 / 6)
|
||||||
|
|
||||||
|
print(f" 原始总分: {raw_score} -> {final_raw}")
|
||||||
|
print(f" 转换分数: {converted_score}")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# 运行测试
|
||||||
|
test_cases = create_test_data()
|
||||||
|
compare_algorithms(test_cases)
|
||||||
|
|
||||||
|
# 详细分析
|
||||||
|
detailed_strength_analysis()
|
||||||
|
|
||||||
|
print("\n" + "=" * 80)
|
||||||
|
print("测试完成!")
|
||||||
|
print("=" * 80)
|
||||||
@@ -0,0 +1,220 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
实时K线分型强弱判断示例
|
||||||
|
解决KLC滞后问题,提供即时的分型信号
|
||||||
|
"""
|
||||||
|
|
||||||
|
from ChanKLU import ChanKLU
|
||||||
|
from ChanEnum import Chan_FX_TYPE
|
||||||
|
import pandas as pd
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
|
class RealtimeFxAnalyzer:
|
||||||
|
"""实时分型分析器"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.klu_list = []
|
||||||
|
self.latest_signals = []
|
||||||
|
|
||||||
|
def add_kline(self, time, open_price, high, low, close, volume, indicators=None):
|
||||||
|
"""
|
||||||
|
添加新的K线数据并进行实时分析
|
||||||
|
|
||||||
|
Args:
|
||||||
|
time: 时间
|
||||||
|
open_price, high, low, close, volume: K线数据
|
||||||
|
indicators: 技术指标字典 {'macd': xx, 'rsi': xx, 'ma5': xx, ...}
|
||||||
|
"""
|
||||||
|
# 创建新的KLU对象
|
||||||
|
new_klu = ChanKLU(time, open_price, high, low, close, volume)
|
||||||
|
|
||||||
|
# 设置技术指标
|
||||||
|
if indicators:
|
||||||
|
new_klu.set_indicators(indicators)
|
||||||
|
|
||||||
|
# 设置索引
|
||||||
|
new_klu.set_idx(len(self.klu_list))
|
||||||
|
|
||||||
|
# 建立前后关系链
|
||||||
|
if len(self.klu_list) >= 1:
|
||||||
|
prev_klu = self.klu_list[-1]
|
||||||
|
new_klu.set_pre(prev_klu)
|
||||||
|
prev_klu.set_next(new_klu)
|
||||||
|
|
||||||
|
# 如果有足够的数据,设置前一根K线的next关系
|
||||||
|
if len(self.klu_list) >= 2:
|
||||||
|
prev_prev_klu = self.klu_list[-2]
|
||||||
|
prev_prev_klu.set_next(self.klu_list[-1])
|
||||||
|
|
||||||
|
self.klu_list.append(new_klu)
|
||||||
|
|
||||||
|
# 实时分析最近的K线分型
|
||||||
|
self._analyze_recent_fractals()
|
||||||
|
|
||||||
|
return new_klu
|
||||||
|
|
||||||
|
def _analyze_recent_fractals(self):
|
||||||
|
"""分析最近的分型情况"""
|
||||||
|
if len(self.klu_list) < 3:
|
||||||
|
return
|
||||||
|
|
||||||
|
# 检查倒数第二根K线的分型(因为需要左右两根K线确认)
|
||||||
|
target_idx = len(self.klu_list) - 2
|
||||||
|
if target_idx >= 1:
|
||||||
|
target_klu = self.klu_list[target_idx]
|
||||||
|
|
||||||
|
# 进行实时分型分析
|
||||||
|
target_klu.update_realtime_analysis()
|
||||||
|
|
||||||
|
# 如果发现分型,记录信号
|
||||||
|
if target_klu.fx_confirmed:
|
||||||
|
signal = target_klu.get_fx_signal()
|
||||||
|
signal_info = {
|
||||||
|
'time': target_klu.time,
|
||||||
|
'price': target_klu.close,
|
||||||
|
'signal_type': signal[0],
|
||||||
|
'strength': signal[1],
|
||||||
|
'suggestion': signal[2],
|
||||||
|
'fx_type': target_klu.fx_type
|
||||||
|
}
|
||||||
|
|
||||||
|
self.latest_signals.append(signal_info)
|
||||||
|
|
||||||
|
# 保持最近20个信号
|
||||||
|
if len(self.latest_signals) > 20:
|
||||||
|
self.latest_signals.pop(0)
|
||||||
|
|
||||||
|
print(f"🔔 分型信号: {signal_info['time']} - {signal_info['signal_type']} "
|
||||||
|
f"(强度: {signal_info['strength']}) - {signal_info['suggestion']}")
|
||||||
|
|
||||||
|
def get_latest_signal(self):
|
||||||
|
"""获取最新的分型信号"""
|
||||||
|
return self.latest_signals[-1] if self.latest_signals else None
|
||||||
|
|
||||||
|
def get_current_fx_status(self):
|
||||||
|
"""获取当前分型状态统计"""
|
||||||
|
if len(self.klu_list) < 10:
|
||||||
|
return {"status": "数据不足"}
|
||||||
|
|
||||||
|
recent_10 = self.klu_list[-10:]
|
||||||
|
|
||||||
|
top_fx_count = sum(1 for klu in recent_10 if klu.fx_type == Chan_FX_TYPE.TOP)
|
||||||
|
bottom_fx_count = sum(1 for klu in recent_10 if klu.fx_type == Chan_FX_TYPE.BOTTOM)
|
||||||
|
|
||||||
|
strong_fx_count = sum(1 for klu in recent_10 if klu.fx_strength >= 65)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"最近10根K线": len(recent_10),
|
||||||
|
"顶分型数量": top_fx_count,
|
||||||
|
"底分型数量": bottom_fx_count,
|
||||||
|
"强分型数量": strong_fx_count,
|
||||||
|
"最新K线时间": recent_10[-1].time,
|
||||||
|
"最新信号": self.get_latest_signal()
|
||||||
|
}
|
||||||
|
|
||||||
|
def simulate_realtime_trading():
|
||||||
|
"""模拟实时交易场景"""
|
||||||
|
|
||||||
|
print("=== 实时K线分型分析示例 ===\n")
|
||||||
|
|
||||||
|
# 创建分析器
|
||||||
|
analyzer = RealtimeFxAnalyzer()
|
||||||
|
|
||||||
|
# 模拟实时K线数据流
|
||||||
|
base_time = datetime.now()
|
||||||
|
base_price = 100.0
|
||||||
|
|
||||||
|
print("开始接收K线数据...\n")
|
||||||
|
|
||||||
|
for i in range(20):
|
||||||
|
# 模拟价格波动
|
||||||
|
if i < 5: # 上涨阶段
|
||||||
|
price_change = 0.5
|
||||||
|
elif i < 10: # 下跌阶段
|
||||||
|
price_change = -0.8
|
||||||
|
elif i < 15: # 震荡阶段
|
||||||
|
price_change = 0.3 * ((-1) ** i)
|
||||||
|
else: # 再次上涨
|
||||||
|
price_change = 0.6
|
||||||
|
|
||||||
|
current_price = base_price + price_change
|
||||||
|
|
||||||
|
# 构造K线数据
|
||||||
|
open_price = base_price
|
||||||
|
high = max(open_price, current_price) + abs(price_change) * 0.2
|
||||||
|
low = min(open_price, current_price) - abs(price_change) * 0.2
|
||||||
|
close = current_price
|
||||||
|
volume = 1000 + i * 50
|
||||||
|
|
||||||
|
# 模拟技术指标
|
||||||
|
indicators = {
|
||||||
|
'ma5': base_price + (i - 10) * 0.1,
|
||||||
|
'ma10': base_price + (i - 10) * 0.05,
|
||||||
|
'rsi': 50 + (i % 7 - 3) * 10,
|
||||||
|
'macd': (i % 6 - 3) * 0.01,
|
||||||
|
'macdhist': (i % 4 - 2) * 0.005,
|
||||||
|
'volume_ratio': 1.0 + (i % 3 - 1) * 0.2
|
||||||
|
}
|
||||||
|
|
||||||
|
# 添加K线数据
|
||||||
|
kline_time = base_time + timedelta(minutes=i)
|
||||||
|
analyzer.add_kline(
|
||||||
|
time=kline_time.strftime("%Y-%m-%d %H:%M:%S"),
|
||||||
|
open_price=open_price,
|
||||||
|
high=high,
|
||||||
|
low=low,
|
||||||
|
close=close,
|
||||||
|
volume=volume,
|
||||||
|
indicators=indicators
|
||||||
|
)
|
||||||
|
|
||||||
|
base_price = current_price
|
||||||
|
|
||||||
|
# 每5根K线显示一次状态
|
||||||
|
if (i + 1) % 5 == 0:
|
||||||
|
status = analyzer.get_current_fx_status()
|
||||||
|
print(f"\n--- 第{i+1}根K线后的状态 ---")
|
||||||
|
for key, value in status.items():
|
||||||
|
if key != "最新信号":
|
||||||
|
print(f"{key}: {value}")
|
||||||
|
|
||||||
|
if "最新信号" in status and status["最新信号"]:
|
||||||
|
signal = status["最新信号"]
|
||||||
|
print(f"最新信号: {signal['signal_type']} (强度: {signal['strength']})")
|
||||||
|
print()
|
||||||
|
|
||||||
|
print("\n=== 所有分型信号汇总 ===")
|
||||||
|
for signal in analyzer.latest_signals:
|
||||||
|
print(f"{signal['time']} | {signal['signal_type']} | 强度: {signal['strength']} | {signal['suggestion']}")
|
||||||
|
|
||||||
|
def compare_latency():
|
||||||
|
"""对比KLC和KLU方法的延迟差异"""
|
||||||
|
|
||||||
|
print("\n=== 延迟对比分析 ===")
|
||||||
|
print("假设场景:连续包含关系的K线序列")
|
||||||
|
print("原始K线: K1, K2(包含K1), K3(包含K2), K4(突破), K5, K6")
|
||||||
|
print()
|
||||||
|
|
||||||
|
print("KLC方法:")
|
||||||
|
print("- 需要等待K4确认包含关系结束")
|
||||||
|
print("- KLC1 = [K1+K2+K3], 在K4完成时才确定")
|
||||||
|
print("- 分型检测: 需要等待KLC1, KLC2, KLC3")
|
||||||
|
print("- 实际延迟: 可能6-8根原始K线")
|
||||||
|
print()
|
||||||
|
|
||||||
|
print("KLU实时方法:")
|
||||||
|
print("- 每根K线完成时立即检测")
|
||||||
|
print("- K3完成时就能检测K2的分型状态")
|
||||||
|
print("- 实际延迟: 最多1根K线")
|
||||||
|
print()
|
||||||
|
|
||||||
|
print("延迟改善: 从6-8根K线缩短到1根K线")
|
||||||
|
print("时间价值: 在5分钟K线下,可节省25-40分钟的反应时间")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# 运行模拟
|
||||||
|
simulate_realtime_trading()
|
||||||
|
|
||||||
|
# 显示延迟对比
|
||||||
|
compare_latency()
|
||||||
@@ -14,16 +14,19 @@ import talib.abstract as ta
|
|||||||
from pandas import DataFrame
|
from pandas import DataFrame
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from freqtrade.persistence import Trade
|
from freqtrade.persistence import Trade
|
||||||
from typing import Optional
|
from typing import Optional, List, Dict
|
||||||
import logging
|
import logging
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
from freqtrade.optimize.space import Categorical, Dimension, Integer, SKDecimal
|
||||||
### Now you can use logger.info('asfd') to log
|
### Now you can use logger.info('asfd') to log
|
||||||
# freqtrade plot-dataframe --strategy ChanLun_BTC_15 --datadir user_data/data/binance -c ./user_data/ChanLun_SOL_15.json --timerange=20250309-
|
# freqtrade plot-dataframe --strategy ChanLun_BTC_15 --datadir user_data/data/binance -c ./user_data/ChanLun_SOL_15.json --timerange=20250309-
|
||||||
|
|
||||||
# freqtrade trade -c ./user_data/Chan/config/ChanLun_BTC_15.json --strategy ChanLun_BTC_15 --strategy-path ./user_data/Chan/strategies
|
# freqtrade trade -c ./user_data/Chan/config/ChanLun_BTC_15.json --strategy ChanLun_BTC_15 --strategy-path ./user_data/Chan/strategies
|
||||||
|
# freqtrade backtesting -c ./user_data/Chan/config/ChanLun_BTC_15.json --strategy ChanLun_BTC_15 --export none --strategy-path ./user_data/Chan/strategies --timerange=20250525-
|
||||||
# freqtrade backtesting -c ./user_data/Chan/config/ChanLun_BTC_15.json --strategy ChanLun_BTC_15 --strategy-path ./user_data/Chan/strategies --timerange=20250525-
|
# freqtrade backtesting -c ./user_data/Chan/config/ChanLun_BTC_15.json --strategy ChanLun_BTC_15 --strategy-path ./user_data/Chan/strategies --timerange=20250525-
|
||||||
# freqtrade download-data -c ./user_data/Chan/config/ChanLun_BTC_15.json -t 1m --pairs BTC/USDT:USDT --timerange=20250405-
|
# freqtrade download-data -c ./user_data/Chan/config/ChanLun_BTC_15.json -t 1m --pairs SOL/USDT:USDT --timerange=20250405-
|
||||||
# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLossDaily --spaces roi stoploss --strategy ChanLun_BTC_15 --strategy-path ./user_data/Chan/strategies -c ./user_data/Chan/config/ChanLun_BTC_15.json -e 200 --timerange=20250201-20250401
|
# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLossDaily --spaces stoploss --strategy ChanLun_BTC_15 --strategy-path ./user_data/Chan/strategies -c ./user_data/Chan/config/ChanLun_BTC_15.json -e 200 --timerange=20250201-20250501
|
||||||
|
# freqtrade live-backtest -c ./user_data/Chan/config/ChanLun_BTC_15.json --strategy ChanLun_BTC_15 --strategy-path ./user_data/Chan/strategies --timerange=20250525-
|
||||||
|
|
||||||
# sudo docker compose run --rm chanlun_btc backtesting -c ./user_data/Chan/config/ChanLun_BTC_15.json --strategy ChanLun_BTC_15 --strategy-path ./user_data/Chan/strategies --timerange=20250525-
|
# sudo docker compose run --rm chanlun_btc backtesting -c ./user_data/Chan/config/ChanLun_BTC_15.json --strategy ChanLun_BTC_15 --strategy-path ./user_data/Chan/strategies --timerange=20250525-
|
||||||
# sudo docker compose run --rm chanlun_btc download-data -c ./user_data/Chan/config/ChanLun_BTC_15.json --pairs BTC/USDT:USDT -t 1m --timerange 20240101-
|
# sudo docker compose run --rm chanlun_btc download-data -c ./user_data/Chan/config/ChanLun_BTC_15.json --pairs BTC/USDT:USDT -t 1m --timerange 20240101-
|
||||||
@@ -35,10 +38,10 @@ class ChanLun_BTC_15(IStrategy):
|
|||||||
# This attribute will be overridden if the config file contains "minimal_roi"
|
# This attribute will be overridden if the config file contains "minimal_roi"
|
||||||
# 30m and 1h
|
# 30m and 1h
|
||||||
minimal_roi = {
|
minimal_roi = {
|
||||||
"0": 0.60,
|
"0": 0.15,
|
||||||
"360": 0.2,
|
"240": 0.1,
|
||||||
"640": 0.1,
|
"480": 0.02,
|
||||||
"1200": 0
|
"960": 0
|
||||||
}
|
}
|
||||||
# 5m and 15m
|
# 5m and 15m
|
||||||
minimal_roi_1 = {
|
minimal_roi_1 = {
|
||||||
@@ -61,14 +64,13 @@ class ChanLun_BTC_15(IStrategy):
|
|||||||
"3600": 0
|
"3600": 0
|
||||||
}
|
}
|
||||||
can_short = True
|
can_short = True
|
||||||
lev = 50.0
|
lev = 10
|
||||||
stoploss = -0.3
|
stoploss = -0.8
|
||||||
trailing_stop = False
|
trailing_stop = False
|
||||||
trailing_stop_positive = 0.025
|
trailing_stop_positive = 0.025
|
||||||
trailing_stop_positive_offset = 0.045
|
trailing_stop_positive_offset = 0.045
|
||||||
trailing_only_offset_is_reached = False
|
trailing_only_offset_is_reached = False
|
||||||
|
|
||||||
position_adjustment_enable = True
|
|
||||||
startup_candle_count = 600
|
startup_candle_count = 600
|
||||||
|
|
||||||
time5 = 5
|
time5 = 5
|
||||||
@@ -174,8 +176,8 @@ class ChanLun_BTC_15(IStrategy):
|
|||||||
dataframe.loc[
|
dataframe.loc[
|
||||||
(
|
(
|
||||||
#(dataframe['state'] == "-30")
|
#(dataframe['state'] == "-30")
|
||||||
(dataframe[state_str].shift(self.time5) > 1.0) &
|
(dataframe[state_str].shift(self.time5*2) > 0) &
|
||||||
(dataframe[fx_str].shift(self.time5) == -1)
|
(dataframe[fx_str].shift(self.time5*2) == -1)
|
||||||
#(dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time5)].shift(self.time5) == "-10") &
|
#(dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time5)].shift(self.time5) == "-10") &
|
||||||
#(dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time30)] == "-10") &
|
#(dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time30)] == "-10") &
|
||||||
#(dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time5)].shift(self.time5) == "-10")
|
#(dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time5)].shift(self.time5) == "-10")
|
||||||
@@ -185,8 +187,8 @@ class ChanLun_BTC_15(IStrategy):
|
|||||||
dataframe.loc[
|
dataframe.loc[
|
||||||
(
|
(
|
||||||
#(dataframe['state'] == "-30")
|
#(dataframe['state'] == "-30")
|
||||||
(dataframe[state_str].shift(self.time5) > 1.0) &
|
(dataframe[state_str].shift(self.time5*2) > 0) &
|
||||||
(dataframe[fx_str].shift(self.time5) == 1)
|
(dataframe[fx_str].shift(self.time5*2) == 1)
|
||||||
#(dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time5)].shift(self.time5) == "-10") &
|
#(dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time5)].shift(self.time5) == "-10") &
|
||||||
#(dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time30)] == "-10") &
|
#(dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time30)] == "-10") &
|
||||||
#(dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time5)].shift(self.time5) == "-10")
|
#(dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time5)].shift(self.time5) == "-10")
|
||||||
@@ -200,8 +202,8 @@ class ChanLun_BTC_15(IStrategy):
|
|||||||
dataframe.loc[
|
dataframe.loc[
|
||||||
(
|
(
|
||||||
#(dataframe['state']== "30")
|
#(dataframe['state']== "30")
|
||||||
(dataframe[state_str].shift(self.time5) > 1.0) &
|
(dataframe[state_str].shift(self.time5*2) > 0) &
|
||||||
(dataframe[fx_str].shift(self.time5) == 1)
|
(dataframe[fx_str].shift(self.time5*2) == 1)
|
||||||
#(dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time30)] == "10") &
|
#(dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time30)] == "10") &
|
||||||
#(dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time60)] == "10")
|
#(dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time60)] == "10")
|
||||||
),
|
),
|
||||||
@@ -209,8 +211,8 @@ class ChanLun_BTC_15(IStrategy):
|
|||||||
dataframe.loc[
|
dataframe.loc[
|
||||||
(
|
(
|
||||||
#(dataframe['state']== "30")
|
#(dataframe['state']== "30")
|
||||||
(dataframe[state_str].shift(self.time5) > 1.0) &
|
(dataframe[state_str].shift(self.time5*2) > 0) &
|
||||||
(dataframe[fx_str].shift(self.time5) == -1)
|
(dataframe[fx_str].shift(self.time5*2) == -1)
|
||||||
#(dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time30)] == "10") &
|
#(dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time30)] == "10") &
|
||||||
#(dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time60)] == "10")
|
#(dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time60)] == "10")
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
2025/05/27 01:57:54 [notice] 1#1: using the "epoll" event method
|
||||||
|
2025/05/27 01:57:54 [notice] 1#1: nginx/1.27.5
|
||||||
|
2025/05/27 01:57:54 [notice] 1#1: built by gcc 12.2.0 (Debian 12.2.0-14)
|
||||||
|
2025/05/27 01:57:54 [notice] 1#1: OS: Linux 6.10.14-linuxkit
|
||||||
|
2025/05/27 01:57:54 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576
|
||||||
|
2025/05/27 01:57:54 [notice] 1#1: start worker processes
|
||||||
|
2025/05/27 01:57:54 [notice] 1#1: start worker process 20
|
||||||
|
2025/05/27 01:57:54 [notice] 1#1: start worker process 21
|
||||||
|
2025/05/27 01:57:54 [notice] 1#1: start worker process 22
|
||||||
|
2025/05/27 01:57:54 [notice] 1#1: start worker process 23
|
||||||
|
2025/05/27 01:57:54 [notice] 1#1: start worker process 24
|
||||||
|
2025/05/27 01:57:54 [notice] 1#1: start worker process 25
|
||||||
|
2025/05/27 01:57:54 [notice] 1#1: start worker process 26
|
||||||
|
2025/05/27 01:57:54 [notice] 1#1: start worker process 27
|
||||||
|
2025/05/27 01:57:54 [notice] 1#1: start worker process 28
|
||||||
|
2025/05/27 01:57:54 [notice] 1#1: start worker process 29
|
||||||
|
2025/05/27 01:57:54 [notice] 1#1: start worker process 30
|
||||||
|
2025/05/27 01:57:54 [notice] 1#1: start worker process 31
|
||||||
@@ -2941,7 +2941,7 @@
|
|||||||
is_strong_fx: fx.is_strong_fx
|
is_strong_fx: fx.is_strong_fx
|
||||||
});
|
});
|
||||||
const displayText = `${fx.fx_strength_level} ${fx.fx_strength.toFixed(1)}`;
|
const displayText = `${fx.fx_strength_level} ${fx.fx_strength.toFixed(1)}`;
|
||||||
if (fx.fx_strength < 1) {
|
if (fx.fx_strength < 1.4) {
|
||||||
displayText = ''
|
displayText = ''
|
||||||
}
|
}
|
||||||
console.log('显示文本:', displayText);
|
console.log('显示文本:', displayText);
|
||||||
|
|||||||
Reference in New Issue
Block a user