chanlun/indicators/ta.py 接口兼容 talib.abstract,实现代码实际用到的 SMA/MA/EMA/RSI/ATR/MACD/BBANDS;chanlun/pipeline/resample.py 替代 technical.util.resample_to_interval。调用点只改 import,逻辑未动。 暖机长度与平滑种子按 TA-Lib 的约定实现,差一根 K 线就会让下游所有 笔/线段/中枢整体位移。其中 MACD 需特别处理:TA-Lib 让快慢两条 EMA 在同一根 K 线出首值,因而快线的种子取 x[slow-fast:slow] 的均值,而非 从 fastperiod-1 一路递推——两者在百元价位上相差约 0.17。 BBANDS 是有意的分歧:TA-Lib 用 sumsq/n - mean² 求方差,短窗口远离零 时灾难性抵消(timeperiod=2 误差 8.7e-7),本实现用 rolling std,对 50 位精度基准误差为 0。项目实际使用的周期两者一致到 1e-10。 顺带清理 12 个文件中 16 处从未调用的 talib/technical 导入。 验证:9440 组随机对拨;真实 K 线端到端比对 add_indicators 全部 33 个 指标列,NaN 模式一致、MACD 柱符号 100% 相同;屏蔽两个包后 60 个模块 均可导入。新增 test_ta_compat.py 将输出逐 bar 钉在 TA-Lib 上,但该文件 在 TA-Lib 缺失时静默跳过,改动 ta.py 需在装有 TA-Lib 的环境复跑。 Co-authored-by: Cursor <cursoragent@cursor.com>
681 lines
29 KiB
Python
681 lines
29 KiB
Python
"""TF_DF builder mixin — 由 split_tfdf_builders 自动生成,逻辑与原 TF_DF 一致。"""
|
||
from __future__ import annotations
|
||
|
||
from datetime import timedelta
|
||
from decimal import Decimal
|
||
|
||
import numpy as np
|
||
import pandas as pd
|
||
from pandas import DataFrame
|
||
|
||
from chanlun.core.ChanBI import ChanBI
|
||
from chanlun.core.ChanBIZS import ChanBIZS
|
||
from chanlun.core.ChanBSP import ChanBSP
|
||
from chanlun.core.ChanEnum import (
|
||
Chan_BI_DIR,
|
||
Chan_BSP_DIR,
|
||
Chan_BSP_TYPE,
|
||
Chan_FX_TYPE,
|
||
Chan_K_DIR,
|
||
Chan_KLC_FX,
|
||
Chan_KLC_STATE,
|
||
Chan_KLINE_DIR,
|
||
Chan_KLU_PATTERN,
|
||
Chan_PRICE_TREND,
|
||
Chan_SEG_DIR,
|
||
Chan_ZS_DIR,
|
||
)
|
||
from chanlun.core.ChanKLC import ChanKLC
|
||
from chanlun.core.ChanKLU import ChanKLU
|
||
from chanlun.core.ChanSBI import ChanSBI
|
||
from chanlun.core.ChanSEG import ChanSEG
|
||
from chanlun.core.ChanZS import ChanZS, ChanZS_Big
|
||
from chanlun.indicators.ChanMACD import ChanMACD
|
||
|
||
|
||
class BiBuilderMixin:
|
||
def cal_trend(self, klc_list):
|
||
"""
|
||
基于价格与EMA24/EMA52的位置关系、以及MACD/Signal/Hist的方向,
|
||
为每个KLC打上趋势标签:'UP' / 'DOWN' / 'FLAT'。
|
||
仅设置 klc.trend,不影响其它字段。
|
||
"""
|
||
if not klc_list:
|
||
return klc_list
|
||
last_trend = Chan_PRICE_TREND.UNKNOWN
|
||
# 趋势延续性:参考近 N 根已完成的KLC
|
||
lookback_n = 5
|
||
prev_klcs = []
|
||
for klc in klc_list:
|
||
price = getattr(klc, 'close', None)
|
||
ema24 = getattr(klc, 'ema24', None)
|
||
ema52 = getattr(klc, 'ema52', None)
|
||
macd_raw = getattr(klc, 'macd', None)
|
||
signal_raw = getattr(klc, 'signal', None)
|
||
hist_raw = getattr(klc, 'macdhist', None)
|
||
macd = macd_raw if macd_raw is not None else 0
|
||
signal = signal_raw if signal_raw is not None else 0
|
||
hist = hist_raw if hist_raw is not None else 0
|
||
rsi = getattr(klc, 'rsi', None)
|
||
macd_ready = macd_raw is not None and signal_raw is not None
|
||
hist_ready = hist_raw is not None
|
||
trend = Chan_PRICE_TREND.UNKNOWN
|
||
score = 0
|
||
try:
|
||
# 有效性
|
||
price_valid = price is not None and price != 0
|
||
ema24_valid = ema24 is not None and ema24 != 0
|
||
ema52_valid = ema52 is not None and ema52 != 0
|
||
# 多因子投票
|
||
|
||
# 1) 均线结构 + 价位
|
||
if ema24_valid or ema52_valid:
|
||
ma_votes = 0
|
||
if ema24_valid and ema52_valid:
|
||
ma_votes += 1 if ema24 > ema52 else -1
|
||
if price_valid and ema24_valid:
|
||
ma_votes += 1 if price > ema24 else 0
|
||
if price_valid and ema52_valid:
|
||
ma_votes += 1 if price > ema52 else -1
|
||
# 限幅,避免相关因子重复计分
|
||
score += max(-2, min(2, ma_votes))
|
||
# 2) MACD结构
|
||
if macd_ready:
|
||
score += 1 if macd >= signal else -1
|
||
if hist_ready and hist != 0:
|
||
score += 1 if hist > 0 else -1
|
||
# 3) 动量与均线差分斜率
|
||
pre = getattr(klc, 'pre', None)
|
||
pre_hist = getattr(pre, 'macdhist', None) if pre else None
|
||
if pre:
|
||
pre_close = getattr(pre, 'close', None)
|
||
if price_valid and pre_close is not None:
|
||
score += 1 if price >= pre_close else -1
|
||
pre_ema24 = getattr(pre, 'ema24', None)
|
||
pre_ema52 = getattr(pre, 'ema52', None)
|
||
if ema24_valid and ema52_valid and pre_ema24 not in (None, 0) and pre_ema52 not in (None, 0):
|
||
spread_now = ema24 - ema52
|
||
spread_pre = pre_ema24 - pre_ema52
|
||
score += 1 if spread_now >= spread_pre else -1
|
||
# 3.1) MACD柱体动量趋势:考虑 macdhist 的斜率与过零
|
||
if hist_ready and pre_hist is not None:
|
||
# 柱体斜率:上升加分,下降减分
|
||
if hist > pre_hist:
|
||
score += 1
|
||
elif hist < pre_hist:
|
||
score -= 1
|
||
# 过零加权:负转正更偏多,正转负更偏空
|
||
if pre_hist < 0 and hist > 0:
|
||
score += 1
|
||
elif pre_hist > 0 and hist < 0:
|
||
score -= 1
|
||
# 3.2) EMA52 突破/跌破加权
|
||
if ema52_valid and price_valid and pre_close is not None and pre_ema52 not in (None, 0):
|
||
# 看多突破:从均线下方上破且动量配合
|
||
if pre_close <= pre_ema52 and price > ema52 and (hist is None or pre_hist is None or hist >= pre_hist):
|
||
score += 1
|
||
# 看空跌破:从均线上方下破且动量配合
|
||
if pre_close >= pre_ema52 and price < ema52 and (hist is None or pre_hist is None or hist <= pre_hist):
|
||
score -= 1
|
||
# 3.3) EMA52 支撑/阻力触碰(非强穿越)
|
||
if ema52_valid and price_valid:
|
||
low_v = getattr(klc, 'low', None)
|
||
high_v = getattr(klc, 'high', None)
|
||
if low_v is not None and high_v is not None and ema52 not in (None, 0):
|
||
# 触碰容差(相对EMA52的0.15%)
|
||
touch_tol = 0.0015
|
||
# 作为支撑:收盘在上,最低靠近EMA52
|
||
near_support_touch = (price > ema52) and (abs(low_v - ema52) / abs(ema52) <= touch_tol)
|
||
# 作为阻力:收盘在下,最高靠近EMA52
|
||
near_resistance_touch = (price < ema52) and (abs(high_v - ema52) / abs(ema52) <= touch_tol)
|
||
if near_support_touch:
|
||
# 若动量不弱,则更偏多
|
||
score += 1 if (hist is None or pre_hist is None or hist >= pre_hist) else 0
|
||
if near_resistance_touch:
|
||
# 若动量不强,则更偏空
|
||
score -= 1 if (hist is None or pre_hist is None or hist <= pre_hist) else 0
|
||
# 3.4) 多次对 EMA52 的"拒绝"配合 MACD 逆向:易形成压/支并反向
|
||
# 统计近窗口内的上/下拒绝次数:
|
||
# - 上拒绝:价格位于 EMA52 下方,最高触及/越过 EMA52 但收盘仍在下方
|
||
# - 下拒绝:价格位于 EMA52 上方,最低触及/跌破 EMA52 但收盘仍在上方
|
||
recent_up_rejects = 0
|
||
recent_down_rejects = 0
|
||
if ema52_valid:
|
||
window_rej = prev_klcs[-lookback_n:] if len(prev_klcs) > 0 else []
|
||
rej_tol = 0.0015
|
||
for wk in window_rej:
|
||
wk_close = getattr(wk, 'close', None)
|
||
wk_ema52 = getattr(wk, 'ema52', None)
|
||
wk_high = getattr(wk, 'high', None)
|
||
wk_low = getattr(wk, 'low', None)
|
||
if wk_close is None or wk_ema52 in (None, 0):
|
||
continue
|
||
# 上拒绝(阻力):下方多次试图上破但未站上
|
||
if wk_close < wk_ema52 and wk_high is not None:
|
||
if wk_high >= wk_ema52 or abs(wk_high - wk_ema52) / abs(wk_ema52) <= rej_tol:
|
||
recent_up_rejects += 1
|
||
# 下拒绝(支撑):上方多次试图下破但未跌破
|
||
if wk_close > wk_ema52 and wk_low is not None:
|
||
if wk_low <= wk_ema52 or abs(wk_low - wk_ema52) / abs(wk_ema52) <= rej_tol:
|
||
recent_down_rejects += 1
|
||
# 定义 MACD 的方向偏好
|
||
macd_bias_up = macd_ready and (macd >= signal) and (not hist_ready or pre_hist is None or hist >= pre_hist)
|
||
macd_bias_down = macd_ready and (macd <= signal) and (not hist_ready or pre_hist is None or hist <= pre_hist)
|
||
# 若多次上拒绝且 MACD 偏空,则更偏向下行;若多次下拒绝且 MACD 偏多,则更偏向上行
|
||
if recent_up_rejects >= 2 and macd_bias_down:
|
||
score -= 2
|
||
if recent_down_rejects >= 2 and macd_bias_up:
|
||
score += 2
|
||
# 4) RSI 辅助
|
||
if rsi is not None:
|
||
if rsi >= 55:
|
||
score += 1
|
||
elif rsi <= 45:
|
||
score -= 1
|
||
# 5) 指标未就绪回退(EMA/MACD缺失时,用动量与RSI辅助,延续趋势)
|
||
has_full_ind = ema24_valid and ema52_valid and not (macd == 0 and signal == 0 and hist == 0)
|
||
if not has_full_ind:
|
||
# 仅根据价动量/RSI做轻量判断,默认延续 last_trend,除非出现强反向
|
||
strong_up = False
|
||
strong_down = False
|
||
pre = getattr(klc, 'pre', None)
|
||
if pre:
|
||
pre_close = getattr(pre, 'close', None)
|
||
if price_valid and pre_close is not None:
|
||
strong_up = (price >= pre_close)
|
||
strong_down = (price < pre_close)
|
||
if rsi is not None:
|
||
if rsi >= 60:
|
||
strong_up = True
|
||
elif rsi <= 40:
|
||
strong_down = True
|
||
if last_trend == Chan_PRICE_TREND.UP and not strong_down:
|
||
trend = Chan_PRICE_TREND.UP
|
||
elif last_trend == Chan_PRICE_TREND.DOWN and not strong_up:
|
||
trend = Chan_PRICE_TREND.DOWN
|
||
else:
|
||
trend = Chan_PRICE_TREND.UP if strong_up and not strong_down else (Chan_PRICE_TREND.DOWN if strong_down and not strong_up else Chan_PRICE_TREND.FLAT)
|
||
else:
|
||
# 6) 震荡过滤(仅当极近EMA52且MACD贴合时判作震荡)
|
||
near_flat = False
|
||
if price_valid and ema52_valid:
|
||
near_ema52 = abs(price - ema52) / abs(ema52) <= 0.0005 # 0.05%
|
||
if macd_ready:
|
||
macd_scale = max(abs(macd), abs(signal), 1e-6)
|
||
near_macd = abs(macd - signal) / macd_scale <= 0.05
|
||
else:
|
||
near_macd = False
|
||
near_flat = near_ema52 and near_macd
|
||
# 7) 动态阈值 + 趋势记忆(更强粘滞:趋势中容忍小幅反分)
|
||
# 引入过去 N 根KLC 的趋势延续性来动态调整翻转阈值,并结合 EMA52 支撑/阻力触碰强化门槛
|
||
force_flip_down = False
|
||
force_flip_up = False
|
||
if near_flat:
|
||
trend = Chan_PRICE_TREND.FLAT
|
||
else:
|
||
# 计算过去窗口的趋势一致性
|
||
window = prev_klcs[-lookback_n:] if len(prev_klcs) > 0 else []
|
||
persist_up = 0
|
||
persist_down = 0
|
||
for wk in window:
|
||
if getattr(wk, 'trend', None) == Chan_PRICE_TREND.UP:
|
||
persist_up += 1
|
||
elif getattr(wk, 'trend', None) == Chan_PRICE_TREND.DOWN:
|
||
persist_down += 1
|
||
persist_ratio_up = (persist_up / len(window)) if len(window) > 0 else 0
|
||
persist_ratio_down = (persist_down / len(window)) if len(window) > 0 else 0
|
||
# 基准阈值
|
||
down_flip_threshold = -2
|
||
up_flip_threshold = 2
|
||
# 若最近多为UP,则从UP翻转需更强反向信号;同理对DOWN
|
||
if last_trend == Chan_PRICE_TREND.UP and persist_ratio_up >= 0.6:
|
||
down_flip_threshold = -3
|
||
elif last_trend == Chan_PRICE_TREND.DOWN and persist_ratio_down >= 0.6:
|
||
up_flip_threshold = 3
|
||
# EMA52 触碰强化门槛:UP时若出现支撑触碰,下翻更难;DOWN时若出现阻力触碰,上翻更难
|
||
if ema52_valid and price_valid:
|
||
low_v = getattr(klc, 'low', None)
|
||
high_v = getattr(klc, 'high', None)
|
||
if low_v is not None and high_v is not None and ema52 not in (None, 0):
|
||
touch_tol = 0.0015
|
||
near_support_touch = (price > ema52) and (abs(low_v - ema52) / abs(ema52) <= touch_tol)
|
||
near_resistance_touch = (price < ema52) and (abs(high_v - ema52) / abs(ema52) <= touch_tol)
|
||
if last_trend == Chan_PRICE_TREND.UP and near_support_touch:
|
||
# 强化维持UP:进一步降低向下翻转阈值
|
||
down_flip_threshold = min(down_flip_threshold - 1, -3)
|
||
if last_trend == Chan_PRICE_TREND.DOWN and near_resistance_touch:
|
||
# 强化维持DOWN:进一步提高向上翻转阈值
|
||
up_flip_threshold = max(up_flip_threshold + 1, 3)
|
||
# 7.1) 复合拐头信号:MACD/Signal 同向拐头 + hist 连续减弱 + 多次未能越过 EMA52
|
||
pre_macd = getattr(pre, 'macd', None) if pre else None
|
||
pre_signal = getattr(pre, 'signal', None) if pre else None
|
||
macd_slope = (macd - pre_macd) if (macd_ready and pre_macd is not None) else 0
|
||
signal_slope = (signal - pre_signal) if (macd_ready and pre_signal is not None) else 0
|
||
# hist 连续减弱(绝对值缩小)
|
||
hist_seq = []
|
||
for wk in prev_klcs[-2:]:
|
||
val = getattr(wk, 'macdhist', None)
|
||
if val is not None:
|
||
hist_seq.append(val)
|
||
if hist is not None:
|
||
hist_seq.append(hist)
|
||
weaken_steps = 0
|
||
for i in range(1, len(hist_seq)):
|
||
if abs(hist_seq[i]) < abs(hist_seq[i-1]):
|
||
weaken_steps += 1
|
||
# 近窗口对 EMA52 的"未能站上/跌破"统计(放宽窗口与条件)
|
||
window_ema = prev_klcs[-4:] if len(prev_klcs) > 0 else []
|
||
no_up_break = False
|
||
no_down_break = False
|
||
if ema52_valid:
|
||
# 未能有效上破:最近若干根收盘大多数不在 EMA52 上方,且高点多次触及/接近
|
||
cnt_touch_up = 0
|
||
cnt_close_above = 0
|
||
for wk in window_ema:
|
||
wk_close = getattr(wk, 'close', None)
|
||
wk_high = getattr(wk, 'high', None)
|
||
wk_ema = getattr(wk, 'ema52', None)
|
||
if wk_close is not None and wk_ema not in (None, 0):
|
||
if wk_close > wk_ema:
|
||
cnt_close_above += 1
|
||
if wk_high is not None and (wk_high >= wk_ema or abs(wk_high - wk_ema) / abs(wk_ema) <= 0.0015):
|
||
cnt_touch_up += 1
|
||
no_up_break = (cnt_close_above <= 1 and cnt_touch_up >= 1 and price <= ema52)
|
||
# 未能有效下破:最近若干根收盘大多数不在 EMA52 下方,且低点多次触及/接近
|
||
cnt_touch_down = 0
|
||
cnt_close_below = 0
|
||
for wk in window_ema:
|
||
wk_close = getattr(wk, 'close', None)
|
||
wk_low = getattr(wk, 'low', None)
|
||
wk_ema = getattr(wk, 'ema52', None)
|
||
if wk_close is not None and wk_ema not in (None, 0):
|
||
if wk_close < wk_ema:
|
||
cnt_close_below += 1
|
||
if wk_low is not None and (wk_low <= wk_ema or abs(wk_low - wk_ema) / abs(wk_ema) <= 0.0015):
|
||
cnt_touch_down += 1
|
||
no_down_break = (cnt_close_below <= 1 and cnt_touch_down >= 1 and price >= ema52)
|
||
# 若当前为UP趋势,出现明显拐头+hist减弱+未能上破EMA52,则加速看空
|
||
if last_trend == Chan_PRICE_TREND.UP and macd_slope < 0 and signal_slope < 0 and weaken_steps >= 1 and no_up_break and macd_bias_down:
|
||
score -= 3
|
||
down_flip_threshold = max(down_flip_threshold, 0)
|
||
force_flip_down = True
|
||
# 若当前为DOWN趋势,出现明显拐头+hist减弱+未能下破EMA52,则加速看多
|
||
if last_trend == Chan_PRICE_TREND.DOWN and macd_slope > 0 and signal_slope > 0 and weaken_steps >= 1 and no_down_break and macd_bias_up:
|
||
score += 3
|
||
up_flip_threshold = min(up_flip_threshold, 0)
|
||
force_flip_up = True
|
||
# 多次对 EMA52 的拒绝配合 MACD 逆向:加速反向翻转(降低相反方向阈值)
|
||
if recent_up_rejects >= 2 and macd_bias_down:
|
||
# 从 UP 向 DOWN 的翻转更容易
|
||
down_flip_threshold = max(down_flip_threshold, -1)
|
||
if recent_down_rejects >= 2 and macd_bias_up:
|
||
# 从 DOWN 向 UP 的翻转更容易
|
||
up_flip_threshold = min(up_flip_threshold, 1)
|
||
if force_flip_down:
|
||
trend = Chan_PRICE_TREND.DOWN
|
||
elif force_flip_up:
|
||
trend = Chan_PRICE_TREND.UP
|
||
elif last_trend == Chan_PRICE_TREND.UP:
|
||
if score <= down_flip_threshold:
|
||
trend = Chan_PRICE_TREND.DOWN
|
||
else:
|
||
trend = Chan_PRICE_TREND.UP
|
||
elif last_trend == Chan_PRICE_TREND.DOWN:
|
||
if score >= up_flip_threshold:
|
||
trend = Chan_PRICE_TREND.UP
|
||
else:
|
||
trend = Chan_PRICE_TREND.DOWN
|
||
else:
|
||
# 初始无记忆时,降低进入门槛
|
||
if score >= 1:
|
||
trend = Chan_PRICE_TREND.UP
|
||
elif score <= -1:
|
||
trend = Chan_PRICE_TREND.DOWN
|
||
else:
|
||
trend = Chan_PRICE_TREND.FLAT
|
||
except Exception:
|
||
trend = Chan_PRICE_TREND.UNKNOWN
|
||
# 写回趋势
|
||
if klc.end_time is None:
|
||
trend = Chan_PRICE_TREND.FLAT
|
||
if hasattr(klc, 'set_trend'):
|
||
klc.set_trend(trend)
|
||
else:
|
||
setattr(klc, 'trend', trend)
|
||
last_trend = trend
|
||
# 更新滑窗:仅向后看
|
||
prev_klcs.append(klc)
|
||
price_diff = klc.close - klc.pre.close if klc.pre else 0
|
||
#if klc.index > len(klc_list) - 10:
|
||
#print(klc.start_time, klc.end_time, klc.close, klc.ema24, klc.ema52, klc.macd, klc.signal, klc.macdhist, klc.trend, price_diff, score)
|
||
#print(klc.start_time, klc.end_time, klc.trend, price_diff, score)
|
||
return klc_list
|
||
|
||
def get_bi_list(self, dataframe):
|
||
bi_list = self.cal_bi_list(self.get_klc_list(dataframe))
|
||
#bi_list = self.cal_bi_list_chanlun(self.get_klc_list(dataframe))
|
||
return bi_list
|
||
|
||
def cal_bi_list(self, klc_list):
|
||
bi_list = []
|
||
last_top = None
|
||
last_bottom = None
|
||
bi_klc_min = 4
|
||
last_fx_klc = None
|
||
for klc in klc_list:
|
||
if last_fx_klc:
|
||
klc.check_klc_state(last_fx_klc)
|
||
klc.check_fx_confirmed(last_top, last_bottom)
|
||
fx = self.check_fx(klc)
|
||
if fx == Chan_FX_TYPE.TOP:
|
||
if last_bottom:
|
||
if self.check_top_fx(last_bottom, klc) == False:
|
||
fx = Chan_FX_TYPE.UNKNOWN
|
||
if fx == Chan_FX_TYPE.BOTTOM:
|
||
if last_top:
|
||
if self.check_bottom_fx(last_top, klc) == False:
|
||
#print(klc.end_time, last_top.end_time, "---")
|
||
fx = Chan_FX_TYPE.UNKNOWN
|
||
# Do nothing
|
||
if fx == Chan_FX_TYPE.UNKNOWN:
|
||
if len(bi_list) > 0:
|
||
bi_list[-1].add_klc(klc)
|
||
klc.set_bi(bi_list[-1])
|
||
#continue
|
||
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:
|
||
if last_bottom and klc.high > last_bi.high:
|
||
#print(klc.end_time, "Top 7, 1", last_bi.start_time, klc.high, last_bi.high)
|
||
#klc.klc_fx_type = Chan_KLC_FX.TOP7
|
||
#klc.fx = Chan_FX_TYPE.TOP
|
||
"""
|
||
last_bi.set_end_klc(last_bottom, klc)
|
||
bi = ChanBI(last_bottom, len(bi_list), Chan_BI_DIR.UP)
|
||
#klc.set_klc_fx_type(Chan_KLC_FX.BOTTOM7)
|
||
#klc.bb_out = True
|
||
last_bi.set_next(bi)
|
||
bi.set_pre(last_bi)
|
||
for klc_index in range(last_bi.end_klc.index, len(klc_list)):
|
||
bi.add_klc(klc_list[klc_index])
|
||
bi_list.append(bi)
|
||
last_top = klc
|
||
klc.set_bi(bi)
|
||
#print(klc.start_time, bi.start_time, bi.end_time, bi.dir, bi.high, bi.low, bi.is_sure)
|
||
"""
|
||
else:
|
||
if last_bottom and last_bi.dir == Chan_BI_DIR.UP:
|
||
if last_top and klc.low < last_bi.low:
|
||
#print(klc.end_time, "Bottom 8, 2", last_bi.start_time)
|
||
#klc.klc_fx_type = Chan_KLC_FX.BOTTOM8
|
||
#klc.fx = Chan_FX_TYPE.BOTTOM
|
||
"""
|
||
last_bi.set_end_klc(last_top, klc)
|
||
bi = ChanBI(last_top, len(bi_list), Chan_BI_DIR.DOWN)
|
||
#klc.set_klc_fx_type(Chan_KLC_FX.TOP6)
|
||
#klc.bb_out = True
|
||
last_bi.set_next(bi)
|
||
bi.set_pre(last_bi)
|
||
for klc_index in range(last_bi.end_klc.index, len(klc_list)):
|
||
bi.add_klc(klc_list[klc_index])
|
||
bi_list.append(bi)
|
||
last_bottom = klc
|
||
klc.set_bi(bi)
|
||
#print(klc.start_time, bi.start_time, bi.end_time, bi.dir, bi.high, bi.low, bi.is_sure)
|
||
"""
|
||
else:
|
||
last_fx_klc = klc
|
||
if fx == Chan_FX_TYPE.TOP:
|
||
#print(klc.end_time, fx, klc.pre.high, klc.high, klc.pre.start_time, klc.pre.end_time)
|
||
if last_top:
|
||
if last_bottom:
|
||
#print(klc.start_time, last_bottom.start_time, last_top.start_time)
|
||
if last_bottom.index < last_top.index:
|
||
# Second top lower to be second sell point
|
||
if last_top.high > klc.high:
|
||
bi_list[-1].add_klc(klc)
|
||
klc.set_bi(bi_list[-1])
|
||
#klc.set_klc_fx_type(Chan_KLC_FX.TOP3)
|
||
#print(klc.end_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, bi_list[-1].dir, "Last Top Change 1")
|
||
klc.set_klc_fx_type(Chan_KLC_FX.TOP1)
|
||
self.check_fx_pattern(klc)
|
||
#print(klc.end_time, klc.fx, "一类卖点Sell 1")
|
||
bi_list[-1].add_klc(klc)
|
||
klc.set_bi(bi_list[-1])
|
||
# 不满足结合律的分型
|
||
else:
|
||
#klc.set_klc_fx_type(Chan_KLC_FX.TOP0)
|
||
#print(klc.end_time, klc.klc_fx_type)
|
||
if last_bottom.index + bi_klc_min > klc.index:
|
||
if last_top.high > klc.high:
|
||
#print(klc.start_time, klc.fx, "二类卖点Sell 1")
|
||
#klc.set_klc_fx_type(Chan_KLC_FX.TOP8)
|
||
bi_list[-1].add_klc(klc)
|
||
klc.set_bi(bi_list[-1])
|
||
# New TOP Found前面的UKNOWN可能出现TOP7,但是这里的也可能出现TOP8分型
|
||
else:
|
||
# 顶分型在出现2之前超过前一个笔的顶 TOP8
|
||
if last_top.index + bi_klc_min < klc.index and len(bi_list) > 1:
|
||
pre_last_bi = bi_list[-2]
|
||
last_bi = bi_list[-1]
|
||
if pre_last_bi.is_sure and not last_bi.is_sure and pre_last_bi.dir == Chan_BI_DIR.UP and False:
|
||
#pre_last_bi.update_bi(klc)
|
||
bi_list.remove(last_bi)
|
||
pre_last_bi.set_next(None)
|
||
#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")
|
||
###klc.set_klc_fx_type(Chan_KLC_FX.TOP2) # when bi is down but the fx is top
|
||
bi_list[-1].add_klc(klc)
|
||
klc.set_bi(bi_list[-1])
|
||
#klc.set_klc_fx_type(Chan_KLC_FX.TOP8)
|
||
#print(klc.start_time, last_bi.start_klc.start_time, "New TOP Found reset last bi")
|
||
else:
|
||
#klc.set_fx(Chan_FX_TYPE.PTOP)
|
||
bi_list[-1].add_klc(klc)
|
||
klc.set_bi(bi_list[-1])
|
||
#print(klc.end_time, klc.fx, "无效顶分型")
|
||
# 满足结合律
|
||
else:
|
||
# New Temp TOP and last bottom confirmed ***** confirm last down bi(last bottom and last top)
|
||
last_bi = bi_list[-1]
|
||
if not last_bi.is_sure:
|
||
last_bi.set_end_klc(last_bottom, klc)
|
||
bi = ChanBI(last_bottom, len(bi_list), Chan_BI_DIR.UP)
|
||
last_bi.set_next(bi)
|
||
bi.set_pre(last_bi)
|
||
bi.add_klc(klc)
|
||
bi_list.append(bi)
|
||
last_top = klc
|
||
#print(klc.end_time, klc.fx, bi_list[-1].dir, "Last Top Change 2")
|
||
klc.set_klc_fx_type(Chan_KLC_FX.TOP2)
|
||
self.check_fx_pattern(klc)
|
||
#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")
|
||
# last bottom = None 初始化的时候用,其他时间不用
|
||
else:
|
||
# 初始化的时候用,其他时间不用
|
||
if last_top.high < klc.high:
|
||
last_bi = bi_list[-1]
|
||
last_bi.set_start_klc(klc, Chan_BI_DIR.DOWN)
|
||
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])
|
||
# 初始化的时候用,其他时间不用
|
||
else:
|
||
#klc.set_fx(Chan_FX_TYPE.TT)
|
||
#print(klc.start_time, klc.fx, "二类卖点Sell 2")
|
||
bi_list[-1].add_klc(klc)
|
||
klc.set_bi(bi_list[-1])
|
||
# last_top == None 初始化的时候用,其他时间不用
|
||
else:
|
||
if last_bottom:
|
||
# 不满足结合律的分型
|
||
if last_bottom.index + bi_klc_min > klc.index:
|
||
#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])
|
||
# 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.add_klc(klc)
|
||
klc.set_bi(bi_list[-1])
|
||
#print(klc.start_time, klc.fx, bi_list[-1].dir, "Last Top Change 5")
|
||
#klc.fx = Bottom ========================
|
||
else:
|
||
if last_bottom:
|
||
if last_top:
|
||
# Bottom after top and find a new bottom
|
||
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:
|
||
bi_list[-1].add_klc(klc)
|
||
klc.set_bi(bi_list[-1])
|
||
#klc.set_klc_fx_type(Chan_KLC_FX.BOTTOM3)
|
||
#print(last_bottom.start_time, last_bottom.end_time, "--------------------------------1")
|
||
#print(klc.end_time, klc.fx, "二类买点Buy 1")
|
||
else:
|
||
# A new bottom found
|
||
last_bottom = klc
|
||
#print(klc.end_time, klc.fx, bi_list[-1].dir, "Last Bottom Change 1")
|
||
klc.set_klc_fx_type(Chan_KLC_FX.BOTTOM1)
|
||
self.check_fx_pattern(klc)
|
||
#print(klc.end_time, klc.fx, "一类买点Buy 1")
|
||
bi_list[-1].add_klc(klc)
|
||
klc.set_bi(bi_list[-1])
|
||
# 不满足结合律的分型
|
||
else:
|
||
#klc.set_klc_fx_type(Chan_KLC_FX.BOTTOM0)
|
||
#print(klc.end_time, klc.klc_fx_type)
|
||
if last_top.index + bi_klc_min > klc.index:
|
||
if last_bottom.low < klc.low:
|
||
#print(klc.end_time, klc.fx, "中枢买点Buy 1")
|
||
bi_list[-1].add_klc(klc)
|
||
klc.set_bi(bi_list[-1])
|
||
#klc.set_klc_fx_type(Chan_KLC_FX.BOTTOM8)
|
||
# Found new bottom没有意义,上面UNKNOWN的时候已经是笔破坏了
|
||
else:
|
||
#print(klc.end_time, last_bottom.end_time, "Found a new bottom")
|
||
if last_bottom.index + bi_klc_min < klc.index and len(bi_list) > 1:
|
||
pre_last_bi = bi_list[-2]
|
||
last_bi = bi_list[-1]
|
||
if pre_last_bi.is_sure and not last_bi.is_sure and pre_last_bi.dir == Chan_BI_DIR.DOWN and False:
|
||
#pre_last_bi.update_bi(klc)
|
||
bi_list.remove(last_bi)
|
||
pre_last_bi.set_next(None)
|
||
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")
|
||
#print(klc.start_time, klc.fx, "笔买点Buy 1")
|
||
###klc.set_klc_fx_type(Chan_KLC_FX.BOTTOM2) # when bi is up but the fx is bottom
|
||
bi_list[-1].add_klc(klc)
|
||
klc.set_bi(bi_list[-1])
|
||
#klc.set_klc_fx_type(Chan_KLC_FX.BOTTOM8)
|
||
else:
|
||
#klc.set_fx(Chan_FX_TYPE.UNKNOWN)
|
||
bi_list[-1].add_klc(klc)
|
||
klc.set_bi(bi_list[-1])
|
||
print(klc.end_time, klc.fx, "无效底分型")
|
||
# 满足结合律的分型
|
||
else:
|
||
# New Temp Bottom and last top confirmed ***** confirm last up bi(last bottom and last top)
|
||
last_bi = bi_list[-1]
|
||
if not last_bi.is_sure:
|
||
last_bi.set_end_klc(last_top, klc)
|
||
bi = ChanBI(last_top, len(bi_list), Chan_BI_DIR.DOWN)
|
||
last_bi.set_next(bi)
|
||
bi.set_pre(last_bi)
|
||
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)
|
||
self.check_fx_pattern(klc)
|
||
#bi_list[-1].add_klc(klc)
|
||
klc.set_bi(bi_list[-1])
|
||
#print(klc.start_time, last_top.start_time, "Normal Bottom Found, Confirm up bi 6")
|
||
# last_top = None 初始化的时候用,其他时间不用
|
||
else:
|
||
if last_bottom.low > klc.low:
|
||
last_bi = bi_list[-1]
|
||
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")
|
||
bi_list[-1].add_klc(klc)
|
||
klc.set_bi(bi_list[-1])
|
||
# last_bottom = None 初始化的时候用,其他时间不用
|
||
else:
|
||
if last_top:
|
||
# 不满足结合律的分型
|
||
if last_top.index + bi_klc_min > klc.index:
|
||
#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
|
||
last_bottom = klc
|
||
bi = ChanBI(klc, len(bi_list), Chan_BI_DIR.UP)
|
||
#klc.set_klc_fx_type(Chan_KLC_FX.BOTTOM7)
|
||
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 Bottom Change 5")
|
||
#print(klc.start_time, klc.fx, "笔买点Buy 4")
|
||
self.get_above_zero_bsp(klc_list)
|
||
#print(bi_list[-1].start_time, bi_list[-1].end_time, len(bi_list[-1].klc_list))
|
||
return bi_list
|
||
|
||
def check_top_fx(self, last_bottom, klc):
|
||
if (last_bottom.high > klc.pre.low or last_bottom.high > klc.next.low) and (klc.index - last_bottom.index < 100):
|
||
return False
|
||
return True
|
||
|
||
|
||
def check_bottom_fx(self, last_top, klc):
|
||
if (last_top.low < klc.pre.high or last_top.low < klc.next.high) and (klc.index - last_top.index < 100):
|
||
return False
|
||
return True
|
||
# 线段内的中枢
|