Files
Chan/chanlun/pipeline/orchestrator.py
T
jackyu66gitandCursor 9b72173285 feat: 第四类买卖点(B4/S4)融入缠论引擎与 web 展示
研究侧的 fast_bsp3 一直只活在 research/lib/ 里,web 端看不到,回测与目视
两条线对不上。这次把它搬进引擎,作为独立的第四类买卖点。

之所以单独立类而不是当作 B3/S3 的低滞后版:step30/31 显示引擎原生的
B3/S3 统计上呈逆势、显著亏损(胜率 27.4%、PF 0.66、t −18.76),而同一组
过滤器把 B4 从 PF 1.59 提到 2.26 却对它无效(0.66→0.71)。两者选的是
不同的交易群体,不是同一信号的早晚两版。

- chanlun/analysis/fast_bsp.py 原样搬入 find_fast_bsp3 与 build_htf_zones,
  另加 add_zone_ladder / htf_fx_timeline / attach_htf_agree
- research/lib/ 两个模块改为转发,所有 step 脚本导入不变,信号逐条比对一致
- 大级别上下文用 resample 从同一份 df 构建,不额外拉数据,因此与界面上选的
  周期和时间范围无关
- 前端三个复选框 + 过滤模式下拉;未过滤的原始信号用浅色,避免与主口径混淆

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-28 00:05:26 +08:00

197 lines
7.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import warnings
# 抑制 Docker 内 technical.util 的 fillna/ffill/bfill 的 pandas FutureWarningpandas 2.x 弃用 object 静默 downcast
warnings.filterwarnings(
"ignore",
category=FutureWarning,
message=".*Downcasting object dtype arrays on \\.fillna.*",
)
from datetime import timedelta
from pandas import DataFrame
from chanlun.core.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, Chan_MACD_STATE, Chan_PRICE_TREND, Chan_KLU_PATTERN
from chanlun.core.ChanKLU import ChanKLU
from chanlun.core.ChanKLC import ChanKLC
from chanlun.core.ChanBI import ChanBI
from chanlun.core.ChanSBI import ChanSBI
from chanlun.core.ChanSEG import ChanSEG
from chanlun.core.ChanZS import ChanZS
from chanlun.core.ChanBSP import ChanBSP
import pandas as pd
from decimal import Decimal
import numpy as np
from chanlun.indicators.ChanMACD import ChanMACD
from chanlun.pipeline.timeframe import TF_DF
from chanlun.analysis.ChanZone import StructureZone, StructureZoneConfig, analyze_structure_zones
class ChanLun():
def __init__(self):
self.time2m = 2
self.time3m = 3
self.time5m = 5
self.time10m = 10
self.time20m = 20
self.time_m_intervals = [2, 3, 5, 10, 20]
self.time_m_symbols = ['2m', '3m', '5m', '10m', '20m']
self.time30m = 30
self.time45m = 45
self.time_m15_intervals = [30, 45]
self.time_m15_symbols = ['30m', '45m']
self.time2h = 2*60
self.time4h = 4*60
self.time6h = 6*60
self.time8h = 8*60
self.time12h = 12*60
self.time16h = 16*60
self.time_h_intervals = [2*60, 4*60, 6*60, 8*60, 12*60, 16*60]
self.time_h_symbols = ['2h', '4h', '6h', '8h', '12h', '16h']
self.time2d = 2*24*60
self.time3d = 3*24*60
self.time_d_intervals = [2*24*60, 3*24*60]
self.time_d_symbols = ['2d', '3d']
self.time1w = 7*24*60
self.time2w = 14*24*60
self.time_w_intervals = [14*24*60]
self.time_w_symbols = ['2w']
self.time2M = 2*30*24*60
self.time3M = 3*30*24*60
self.time6M = 6*30*24*60
self.time1y = 12*30*24*60
self.time_M_intervals = [2*30*24*60, 3*30*24*60, 6*30*24*60, 12*30*24*60]
self.time_M_symbols = ['2M', '3M', '6M', '1y']
self.time_symbols = ['1m', '2m', '3m', '5m', '10m', '15m', '20m', '30m', '45m','1h', '2h', '4h', '6h', '8h', '12h', '16h', '1d', '2d', '3d']
self.tf_df_dict = {}
self.ema_symbols = ['5m', '15m', '30m', '45m', '1h', '2h', '4h', '8h', '12h', '1d', '2d', '3d']
self.tf_df = TF_DF()
def init_data(self, dataframe, intervals, timeframes):
for index in range(0, len(intervals)):
timeframe = timeframes[index]
interval = intervals[index]
self.tf_df_dict[timeframe] = TF_DF(dataframe, interval, timeframe)
def init_dataframes(self, dataframe_m=None, dataframe_15m=None, dataframe_h=None, dataframe_d=None, dataframe_w=None, dataframe_M=None):
self.tf_df_dict = {}
if dataframe_m is not None:
self.tf_df_dict['1m'] = TF_DF(dataframe_m, 1, '1m')
self.init_data(dataframe_m, self.time_m_intervals, self.time_m_symbols)
if dataframe_15m is not None:
self.tf_df_dict['15m'] = TF_DF(dataframe_15m, 1, '15m')
self.init_data(dataframe_15m, self.time_m15_intervals, self.time_m15_symbols)
if dataframe_h is not None:
self.tf_df_dict['1h'] = TF_DF(dataframe_h, 1, '1h')
self.init_data(dataframe_h, self.time_h_intervals, self.time_h_symbols)
if dataframe_d is not None:
self.tf_df_dict['1d'] = TF_DF(dataframe_d, 1, '1d')
self.init_data(dataframe_d, self.time_d_intervals, self.time_d_symbols)
if dataframe_w is not None and False:
self.tf_df_dict['1w'] = TF_DF(dataframe_w, 1, '1w')
self.init_data(dataframe_w, self.time_w_intervals, self.time_w_symbols)
if dataframe_M is not None and False:
self.tf_df_dict['1M'] = TF_DF(dataframe_M, 1, '1M')
self.init_data(dataframe_M, self.time_M_intervals, self.time_M_symbols)
def get_ema52_dict(self):
if len(self.tf_df_dict) > 0:
return {key: self.tf_df_dict[key].get_ema52() for key in self.ema_symbols}
return None
def get_ema24_dict(self):
if len(self.tf_df_dict) > 0:
return {key: self.tf_df_dict[key].get_ema24() for key in self.ema_symbols}
return None
def get_current_klc_dict(self):
if len(self.tf_df_dict) > 0:
return {key: self.tf_df_dict[key].get_current_klc() for key in self.ema_symbols}
return None
def get_tf_df_by_timeframe(self, timeframe):
if timeframe in self.tf_df_dict:
return self.tf_df_dict[timeframe]
return None
def check_price_ema52(self, price):
key_list = []
if len(self.tf_df_dict) > 0:
ema52_dict = self.get_ema52_dict()
for key in self.ema_symbols:
if ema52_dict[key] is not None:
if abs(price - ema52_dict[key]) < 100:
key_list.append(key)
return key_list
def get_ema_bsp(self, long_tf='1h', short_tf='15m'):
if long_tf in self.tf_df_dict and short_tf in self.tf_df_dict:
long_df = self.tf_df_dict[long_tf]
short_df = self.tf_df_dict[short_tf]
return long_df.get_ema_bsp(short_df)
return None
def get_bsp_state(self, dataframe):
return self.tf_df.get_bsp_state(dataframe)
def get_structure_zones(self, current_price=None, config=None):
if config is None:
config = StructureZoneConfig()
return analyze_structure_zones(
self.tf_df_dict,
self.ema_symbols,
current_price=current_price,
config=config,
)
# TF_DF methods ------------------------------------------
def get_ema_state(self, dataframe):
return self.tf_df.get_ema_state(dataframe)
def get_klu_state(self, dataframe):
return self.tf_df.get_klu_state(dataframe)
def check_fx(self, klc):
return self.tf_df.check_fx(klc)
def add_indicators1(self, df):
return self.tf_df.add_indicators(df)
def get_bi_list(self, dataframe):
return self.tf_df.get_bi_list(dataframe)
def get_kl_data(self, dataframe:DataFrame):
return self.tf_df.cal_kl_data(dataframe)
def cal_volume_ratio(self, dataframe, window=10):
return self.tf_df.cal_volume_ratio(dataframe, window)
def calculate_seg_zs(self, bi_list, seg_list):
return self.get_seg_zs_list(bi_list, seg_list)
def get_seg_list(self, bi_list):
return self.tf_df.get_seg_list(bi_list)
def cal_trend(self, klc_list):
return self.tf_df.cal_trend(klc_list)
def check_top_fx(self, last_bottom, klc):
return self.tf_df.check_top_fx(last_bottom, klc)
def check_bottom_fx(self, last_top, klc):
return self.tf_df.check_bottom_fx(last_top, klc)
def cal_bi_list(self, klc_list):
return self.tf_df.cal_bi_list(klc_list)
def find_first_bsp(self, bi_list, bi_zs_list):
return self.tf_df.find_first_bsp(bi_list, bi_zs_list)
def find_second_bsp(self, bi_list, first_bsp_list):
return self.tf_df.find_second_bsp(bi_list, first_bsp_list)
def find_all_bsp(self, bi_list, bi_zs_list):
return self.tf_df.find_all_bsp(bi_list, bi_zs_list)
def cal_fast_bsp(self, df=None, bi_zs_list=None, htf_chan=None, with_htf=True, timeframe=None, **kw):
return self.tf_df.cal_fast_bsp(df, bi_zs_list, htf_chan, with_htf, timeframe, **kw)
def get_zs_list(self, bi_list, seg_list):
return self.tf_df.get_zs_list(bi_list, seg_list)
def cal_bi_zs(self, seg_list):
return self.tf_df.cal_bi_zs(seg_list)
def cal_bi_zs_list(self, bi_list):
#return self.tf_df.cal_bi_zs(bi_list)
return self.tf_df.cal_bi_zs_list(bi_list)
def cal_bi_zs_list_pure(self, bi_list):
return self.tf_df.cal_bi_zs_list_pure(bi_list)
def init_stream(self, dataframe, interval=1, timeframe=None):
self.tf_df.init_stream(dataframe, interval, timeframe)
return self.tf_df
def append_bar(self, row):
return self.tf_df.append_bar(row)
def replace_last_bar(self, row):
return self.tf_df.replace_last_bar(row)
def get_bi_zs_list(self, bi_list):
return self.tf_df.get_bi_zs_list(bi_list)
def get_decimal(self, value):
return Decimal("{:.2f}".format(value))
def get_klc_list(self, klu_list):
return self.tf_df.get_klc_list(klu_list)
def get_klu_list(self, dataframe):
return self.tf_df.cal_klu_pattern(self.get_kl_data(dataframe))