Add replay good now
This commit is contained in:
+255
-47
@@ -130,45 +130,36 @@ def get_crypto_kl_data(symbol, timeframe, limit=1000, start_time=None, end_time=
|
||||
request_count = 0
|
||||
max_requests = 50 # 最大请求次数,防止无限循环
|
||||
|
||||
print(f"开始分批获取加密货币数据: {symbol}, {timeframe}")
|
||||
|
||||
# 分页加载数据
|
||||
while request_count < max_requests:
|
||||
request_count += 1
|
||||
|
||||
print(f"批次 {request_count}: 获取数据 since={current_since}, limit={batch_size}")
|
||||
|
||||
try:
|
||||
# 获取当前页的数据
|
||||
ohlcv = exchange.fetch_ohlcv(symbol, timeframe, since=current_since, limit=batch_size)
|
||||
|
||||
# 如果没有获取到数据,结束循环
|
||||
if not ohlcv or len(ohlcv) == 0:
|
||||
print(f"批次 {request_count}: 未获取到数据,结束")
|
||||
break
|
||||
|
||||
# 将获取到的数据添加到总列表中
|
||||
all_ohlcv.extend(ohlcv)
|
||||
print(f"批次 {request_count}: 获取到 {len(ohlcv)} 条记录")
|
||||
|
||||
# 获取最后一条数据的时间戳
|
||||
last_timestamp = ohlcv[-1][0]
|
||||
|
||||
# 如果已达到结束时间,结束循环
|
||||
if until and last_timestamp >= until:
|
||||
print(f"批次 {request_count}: 已达到结束时间,结束")
|
||||
break
|
||||
|
||||
# 如果获取的数据条数小于限制数,说明已经获取完所有数据
|
||||
if len(ohlcv) < batch_size:
|
||||
print(f"批次 {request_count}: 数据不足批次大小,已获取完所有数据")
|
||||
break
|
||||
|
||||
# 更新下一页的开始时间(加1毫秒避免重复)
|
||||
current_since = last_timestamp + 1
|
||||
|
||||
except Exception as e:
|
||||
print(f"批次 {request_count} 获取失败: {e}")
|
||||
# 如果单个批次失败,继续尝试下一个批次
|
||||
if current_since:
|
||||
# 尝试增加时间跳过可能的问题时间点
|
||||
@@ -181,7 +172,6 @@ def get_crypto_kl_data(symbol, timeframe, limit=1000, start_time=None, end_time=
|
||||
|
||||
# 数据为空的情况
|
||||
if not all_ohlcv or len(all_ohlcv) == 0:
|
||||
print(f"未获取到数据: {symbol}, {timeframe}")
|
||||
return None
|
||||
|
||||
# 转换为DataFrame
|
||||
@@ -201,21 +191,16 @@ def get_crypto_kl_data(symbol, timeframe, limit=1000, start_time=None, end_time=
|
||||
# 限制数据条数的逻辑 - 优先考虑时间范围
|
||||
if start_time and end_time:
|
||||
# 如果指定了明确的时间范围,返回该时间范围内的所有数据
|
||||
print(f"用户指定了时间范围,返回完整数据 {len(df)} 条记录")
|
||||
if len(df) > 10000: # 防止数据量过大,设置一个合理的上限
|
||||
print(f"警告:数据量过大({len(df)}条),为保证性能将限制为最新的10000条记录")
|
||||
df = df.tail(10000).reset_index(drop=True)
|
||||
elif limit and len(df) > limit:
|
||||
# 如果没有指定明确时间范围,使用默认的limit限制
|
||||
print(f"未指定明确时间范围,应用默认限制,返回最新的 {limit} 条记录")
|
||||
df = df.tail(limit).reset_index(drop=True)
|
||||
|
||||
# 如果过滤后没有数据,返回None
|
||||
if len(df) == 0:
|
||||
print("过滤后无数据")
|
||||
return None
|
||||
|
||||
print(f"成功获取加密货币数据: {len(df)} 条记录 (共 {request_count} 个批次)")
|
||||
return df
|
||||
|
||||
except Exception as e:
|
||||
@@ -265,17 +250,14 @@ def get_a_stock_kl_data(symbol, timeframe, limit=1000, start_time=None, end_time
|
||||
# 如果用户指定了时间范围,优先获取该范围内的所有数据
|
||||
actual_limit = limit
|
||||
if start_date and end_date:
|
||||
print(f"用户指定了时间范围 {start_date} 到 {end_date},将获取该范围内的所有数据")
|
||||
actual_limit = None # 不限制数据条数,获取完整时间范围数据
|
||||
|
||||
# 调用A股数据获取器
|
||||
df = china_stock.get_kl_data(symbol, timeframe, start_date, end_date, actual_limit)
|
||||
|
||||
if df is None:
|
||||
print(f"未获取到A股数据: {symbol}")
|
||||
return None
|
||||
|
||||
print(f"获取到A股数据: {len(df)} 条记录")
|
||||
return df
|
||||
|
||||
except Exception as e:
|
||||
@@ -385,21 +367,11 @@ def analyze_chan(df, timeframe='1d'):
|
||||
"zs_algo": "normal", # 使用标准中枢算法提高稳定性 over_seg, normal, auto
|
||||
"bi_algo": "normal",
|
||||
})
|
||||
print(f"[{timeframe}] 使用配置: {config.__dict__}")
|
||||
|
||||
# 检查数据量是否足够进行缠论分析
|
||||
if len(df) < 30:
|
||||
print(f"[{timeframe}] 警告:数据量过少({len(df)}条),可能影响买卖点识别准确性")
|
||||
else:
|
||||
print(f"[{timeframe}] 数据量充足:{len(df)}条K线数据")
|
||||
|
||||
# 将数据设置到WebDataAPI
|
||||
from DataAPI.WebDataAPI import WebDataAPI
|
||||
WebDataAPI.set_data("WEB_DATA", df)
|
||||
print(f"[{timeframe}] 数据已设置到WebDataAPI,时间范围: {df.iloc[0]['date']} ~ {df.iloc[-1]['date']}")
|
||||
|
||||
# 创建CChan实例,使用自定义数据源
|
||||
print(f"[{timeframe}] 创建CChan实例,级别列表: {[kl_type]}")
|
||||
chan = CChan(
|
||||
code="WEB_DATA",
|
||||
begin_time=None,
|
||||
@@ -410,12 +382,8 @@ def analyze_chan(df, timeframe='1d'):
|
||||
autype=AUTYPE.QFQ,
|
||||
)
|
||||
|
||||
# CChan会自动加载数据,无需手动触发
|
||||
print(f"[{timeframe}] CChan实例创建完成")
|
||||
|
||||
# 获取分析结果
|
||||
kline_list = chan[kl_type]
|
||||
print(f"[{timeframe}] 获取到kline_list,类型: {type(kline_list)}")
|
||||
|
||||
# 提取笔列表
|
||||
bi_list = []
|
||||
@@ -435,10 +403,8 @@ def analyze_chan(df, timeframe='1d'):
|
||||
# 直接从KLine_List获取买卖点
|
||||
buy_sell_points = []
|
||||
try:
|
||||
print(f"[{timeframe}] 分析买卖点,kl_type={kl_type}")
|
||||
if hasattr(kline_list, 'bs_point_lst') and kline_list.bs_point_lst:
|
||||
bsp_list = sorted(kline_list.bs_point_lst.lst, key=lambda x: x.klu.time)
|
||||
print(f"[{timeframe}] 从KLine_List获取到的买卖点数量: {len(bsp_list)}")
|
||||
for i, bsp in enumerate(bsp_list):
|
||||
# 根据买卖点类型选择正确的价格
|
||||
if bsp.is_buy:
|
||||
@@ -454,25 +420,14 @@ def analyze_chan(df, timeframe='1d'):
|
||||
'price': price,
|
||||
'desc': f"{bsp.type2str()}"
|
||||
})
|
||||
|
||||
# 添加调试信息,打印前5个买卖点和最后2个买卖点
|
||||
if i < 5 or i >= len(bsp_list) - 2:
|
||||
time_str = str(bsp.klu.time) if hasattr(bsp.klu.time, '__str__') else 'N/A'
|
||||
print(f"[{timeframe}] 买卖点{i+1}/{len(bsp_list)}: 类型={'买点' if bsp.is_buy else '卖点'}, 时间={time_str}, 价格={price}, KLU价格范围=[{bsp.klu.low}, {bsp.klu.high}], 描述={bsp.type2str()}")
|
||||
else:
|
||||
print(f"[{timeframe}] KLine_List没有买卖点列表或bs_point_lst为空")
|
||||
print(f"[{timeframe}] kline_list属性: {[attr for attr in dir(kline_list) if not attr.startswith('_')]}")
|
||||
buy_sell_points = []
|
||||
except Exception as e:
|
||||
print(f"[{timeframe}] 获取买卖点失败: {e}")
|
||||
traceback.print_exc()
|
||||
buy_sell_points = []
|
||||
# 提取分型信息
|
||||
klc_fx_info = []
|
||||
klu_fx_info = []
|
||||
bsp_list = chan.get_bsp()
|
||||
for bsp in bsp_list:
|
||||
print(bsp.klu.time, bsp.type2str(), bsp.is_buy)
|
||||
# 从K线列表中提取分型信息
|
||||
if hasattr(kline_list, 'lst'):
|
||||
for klc in kline_list.lst:
|
||||
@@ -502,8 +457,10 @@ def analyze_chan(df, timeframe='1d'):
|
||||
'fx_confirmed': True
|
||||
})
|
||||
|
||||
print(f"chan.py分析完成: 笔{len(bi_list)}个, 线段{len(seg_list)}个, 中枢{len(zs_list)}个, 买卖点{len(buy_sell_points)}个")
|
||||
print(f"kline_list类型: {type(kline_list)}, 属性: {dir(kline_list) if hasattr(kline_list, '__dict__') else 'N/A'}")
|
||||
# 只在数据量较少时打印分析结果摘要,避免在回放时产生过多日志
|
||||
if len(df) <= 50:
|
||||
print(f"chan.py分析完成: 笔{len(bi_list)}个, 线段{len(seg_list)}个, 中枢{len(zs_list)}个, 买卖点{len(buy_sell_points)}个")
|
||||
|
||||
|
||||
return {
|
||||
'klc_list': kline_list.lst if hasattr(kline_list, 'lst') else [],
|
||||
@@ -1195,5 +1152,256 @@ def calculate_bi_macd_divergence(bi_list):
|
||||
|
||||
return bi_macd_divs
|
||||
|
||||
@app.route('/api/replay_data')
|
||||
def get_replay_data():
|
||||
"""生成回放数据接口 - 为每个时间点计算当时可用的数据"""
|
||||
symbol = request.args.get('symbol', 'SOL/USDT:USDT')
|
||||
timeframe = request.args.get('timeframe', '5m')
|
||||
|
||||
# 验证交易对不为空
|
||||
if not symbol or symbol.strip() == '':
|
||||
print(f"错误: 空交易对")
|
||||
return jsonify({'error': '交易对不能为空'})
|
||||
|
||||
# 获取时间范围参数
|
||||
start_time = request.args.get('start_time')
|
||||
end_time = request.args.get('end_time')
|
||||
|
||||
# 获取客户端请求的时区
|
||||
client_timezone = request.args.get('timezone', 'Asia/Shanghai')
|
||||
|
||||
# 获取分形元素时间周期
|
||||
element_timeframe = request.args.get('element_timeframe')
|
||||
|
||||
print(f"回放数据API请求参数: symbol={symbol}, timeframe={timeframe}, element_timeframe={element_timeframe}")
|
||||
print(f"时间范围: start_time={start_time}, end_time={end_time}")
|
||||
|
||||
# 验证小周期是否小于主周期
|
||||
if element_timeframe and not is_smaller_or_equal_timeframe(element_timeframe, timeframe):
|
||||
print(f"错误: 元素周期 {element_timeframe} 大于主周期 {timeframe}")
|
||||
return jsonify({'error': '分形元素时间周期必须小于或等于主图表时间周期'})
|
||||
|
||||
# 获取完整数据
|
||||
df = get_kl_data(symbol, timeframe, start_time=start_time, end_time=end_time)
|
||||
if df is None:
|
||||
print(f"错误: 获取数据失败 - symbol={symbol}, timeframe={timeframe}")
|
||||
return jsonify({'error': '获取数据失败'})
|
||||
|
||||
if len(df) == 0:
|
||||
print(f"错误: 所选时间范围内没有数据 - symbol={symbol}, timeframe={timeframe}")
|
||||
return jsonify({'error': '所选时间范围内没有数据'})
|
||||
|
||||
# 使用客户端指定的时区
|
||||
client_tz = timezone(client_timezone)
|
||||
|
||||
# 预先获取小周期数据(如果需要的话),避免在循环中重复拉取
|
||||
element_df = None
|
||||
if element_timeframe:
|
||||
element_df = get_kl_data(symbol, element_timeframe, start_time=start_time, end_time=end_time)
|
||||
|
||||
# 生成回放数据
|
||||
replay_data = {}
|
||||
|
||||
print(f"开始生成回放数据,主周期: {len(df)} 条记录")
|
||||
|
||||
# 为每个时间点计算当时可用的数据
|
||||
for i in range(len(df)):
|
||||
# 获取到当前时间点的数据子集
|
||||
current_df = df.iloc[:i+1].copy()
|
||||
|
||||
# 添加技术指标(包括布林带)
|
||||
current_df = add_indicators(current_df)
|
||||
|
||||
# 进行缠论分析
|
||||
analysis_result = analyze_chan(current_df, timeframe)
|
||||
|
||||
# 计算MACD
|
||||
macd_data = calculate_macd(current_df)
|
||||
|
||||
# 计算笔的MACD背离值
|
||||
bi_macd_divs = calculate_bi_macd_divergence(analysis_result['bi_list'])
|
||||
|
||||
# 获取买卖点
|
||||
all_trade_points = analysis_result['trade_points']
|
||||
|
||||
# 构建当前时间点的数据
|
||||
current_data = {
|
||||
'kline_data': clean_dataframe_for_json(current_df).to_dict('records'),
|
||||
'bi_list': [{
|
||||
'start_time': format_time_safely(bi.begin_klc.time_end, client_tz),
|
||||
'end_time': format_time_safely(bi.end_klc.time_end, client_tz) if bi.end_klc else None,
|
||||
'start_price': bi.begin_klc.low if convert_direction(bi.dir) == 1 else bi.begin_klc.high,
|
||||
'end_price': bi.end_klc.high if convert_direction(bi.dir) == 1 else bi.end_klc.low if bi.end_klc else None,
|
||||
'direction': convert_direction(bi.dir),
|
||||
'macd_div': float(bi_macd_divs.get(idx, 0))
|
||||
} for idx, bi in enumerate(analysis_result['bi_list']) if bi.end_klc],
|
||||
'seg_list': [{
|
||||
'start_time': format_time_safely(seg.start_bi.begin_klc.time_end, client_tz),
|
||||
'end_time': format_time_safely(seg.end_bi.end_klc.time_end, client_tz) if seg.end_bi else None,
|
||||
'start_price': seg.start_bi.begin_klc.low if convert_direction(seg.dir) == 1 else seg.start_bi.begin_klc.high,
|
||||
'end_price': seg.end_bi.end_klc.high if convert_direction(seg.dir) == 1 else seg.end_bi.end_klc.low if seg.end_bi else None,
|
||||
'direction': convert_direction(seg.dir)
|
||||
} for seg in analysis_result['seg_list'] if seg.end_bi],
|
||||
'zs_list': [{
|
||||
'start_time': format_time_safely(zs.begin.time, client_tz),
|
||||
'end_time': format_time_safely(zs.end.time, client_tz) if zs.end else None,
|
||||
'zg': zs.high,
|
||||
'zd': zs.low,
|
||||
'is_sure': zs.is_sure
|
||||
} for zs in analysis_result['zs_list'] if zs.end],
|
||||
'uncompleted_zs_list': [{
|
||||
'start_time': format_time_safely(zs.begin.time, client_tz),
|
||||
'end_time': None,
|
||||
'zg': zs.high,
|
||||
'zd': zs.low,
|
||||
'is_sure': zs.is_sure
|
||||
} for zs in analysis_result['zs_list'] if not zs.is_sure],
|
||||
'trade_points': [{
|
||||
'type': point['type'],
|
||||
'time': format_time_safely(point['time'], client_tz),
|
||||
'price': point['price'],
|
||||
'desc': point['desc']
|
||||
} for point in all_trade_points],
|
||||
'macd': macd_data,
|
||||
'bollinger': {
|
||||
'upper': current_df['bb_upper'].tolist(),
|
||||
'middle': current_df['bb_middle'].tolist(),
|
||||
'lower': current_df['bb_lower'].tolist()
|
||||
},
|
||||
'element_bollinger': {
|
||||
'upper': current_df['element_bb_upper'].tolist(),
|
||||
'middle': current_df['element_bb_middle'].tolist(),
|
||||
'lower': current_df['element_bb_lower'].tolist()
|
||||
},
|
||||
'klc_fx_info': [{
|
||||
'time': format_time_safely(point['time'], client_tz),
|
||||
'price': float(point['price']),
|
||||
'fx_type': point['fx_type'],
|
||||
'is_bottom': bool(point['is_bottom']),
|
||||
'fx_strength': float(point['fx_strength']),
|
||||
'fx_strength_level': str(point['fx_strength_level']),
|
||||
'is_strong_fx': bool(point['is_strong_fx'])
|
||||
} for point in analysis_result['klc_fx_info']],
|
||||
'klu_fx_info': [{
|
||||
'time': format_time_safely(point['time'], client_tz),
|
||||
'price': float(point['price']),
|
||||
'fx_type': point['fx_type'],
|
||||
'is_bottom': bool(point['is_bottom']),
|
||||
'fx_strength': float(point['fx_strength']),
|
||||
'fx_strength_level': str(point['fx_strength_level']),
|
||||
'is_strong_fx': bool(point['is_strong_fx']),
|
||||
'fx_confirmed': bool(point['fx_confirmed'])
|
||||
} for point in analysis_result['klu_fx_info']]
|
||||
}
|
||||
|
||||
# 如果有小周期数据,也要计算小周期的历史数据
|
||||
if element_timeframe and element_df is not None and len(element_df) > 0:
|
||||
# 找到对应当前主周期时间点的小周期数据截止位置
|
||||
current_time = current_df['date'].iloc[-1]
|
||||
element_subset = element_df[element_df['date'] <= current_time]
|
||||
|
||||
if len(element_subset) > 0:
|
||||
# 添加小周期技术指标
|
||||
element_subset = add_indicators(element_subset)
|
||||
|
||||
# 对小周期数据进行缠论分析
|
||||
element_analysis = analyze_chan(element_subset, element_timeframe)
|
||||
|
||||
# 计算小周期MACD数据
|
||||
element_macd_data = calculate_macd(element_subset)
|
||||
|
||||
# 计算小周期笔的MACD背离值
|
||||
element_bi_macd_divs = calculate_bi_macd_divergence(element_analysis['bi_list'])
|
||||
|
||||
# 获取小周期买卖点
|
||||
element_all_trade_points = element_analysis['trade_points']
|
||||
|
||||
# 添加小周期数据到当前时间点
|
||||
current_data.update({
|
||||
'element_timeframe': element_timeframe,
|
||||
'element_macd': element_macd_data,
|
||||
'element_bollinger': {
|
||||
'upper': element_subset['bb_upper'].tolist(),
|
||||
'middle': element_subset['bb_middle'].tolist(),
|
||||
'lower': element_subset['bb_lower'].tolist()
|
||||
},
|
||||
'element_element_bollinger': {
|
||||
'upper': element_subset['element_bb_upper'].tolist(),
|
||||
'middle': element_subset['element_bb_middle'].tolist(),
|
||||
'lower': element_subset['element_bb_lower'].tolist()
|
||||
},
|
||||
'element_bi_list': [{
|
||||
'start_time': format_time_safely(bi.begin_klc.time_end, client_tz),
|
||||
'end_time': format_time_safely(bi.end_klc.time_end, client_tz) if bi.end_klc else None,
|
||||
'start_price': bi.begin_klc.low if convert_direction(bi.dir) == 1 else bi.begin_klc.high,
|
||||
'end_price': bi.end_klc.high if convert_direction(bi.dir) == 1 else bi.end_klc.low if bi.end_klc else None,
|
||||
'direction': convert_direction(bi.dir),
|
||||
'macd_div': float(element_bi_macd_divs.get(idx, 0))
|
||||
} for idx, bi in enumerate(element_analysis['bi_list']) if bi.end_klc],
|
||||
'element_kline_data': clean_dataframe_for_json(element_subset).to_dict('records'),
|
||||
'element_seg_list': [{
|
||||
'start_time': format_time_safely(seg.start_bi.begin_klc.time_end, client_tz),
|
||||
'end_time': format_time_safely(seg.end_bi.end_klc.time_end, client_tz) if seg.end_bi else None,
|
||||
'start_price': seg.start_bi.begin_klc.low if convert_direction(seg.dir) == 1 else seg.start_bi.begin_klc.high,
|
||||
'end_price': seg.end_bi.end_klc.high if convert_direction(seg.dir) == 1 else seg.end_bi.end_klc.low if seg.end_bi else None,
|
||||
'direction': convert_direction(seg.dir)
|
||||
} for seg in element_analysis['seg_list'] if seg.end_bi],
|
||||
'element_zs_list': [{
|
||||
'start_time': format_time_safely(zs.begin.time, client_tz),
|
||||
'end_time': format_time_safely(zs.end.time, client_tz) if zs.end else None,
|
||||
'zg': zs.high,
|
||||
'zd': zs.low,
|
||||
'is_sure': zs.is_sure
|
||||
} for zs in element_analysis['zs_list'] if zs.end],
|
||||
'element_uncompleted_zs_list': [{
|
||||
'start_time': format_time_safely(zs.begin.time, client_tz),
|
||||
'end_time': None,
|
||||
'zg': zs.high,
|
||||
'zd': zs.low,
|
||||
'is_sure': zs.is_sure
|
||||
} for zs in element_analysis['zs_list'] if not zs.is_sure],
|
||||
'element_trade_points': [{
|
||||
'type': point['type'],
|
||||
'time': format_time_safely(point['time'], client_tz),
|
||||
'price': point['price'],
|
||||
'desc': point['desc']
|
||||
} for point in element_all_trade_points],
|
||||
'element_klc_fx_info': [{
|
||||
'time': format_time_safely(point['time'], client_tz),
|
||||
'price': float(point['price']),
|
||||
'fx_type': point['fx_type'],
|
||||
'is_bottom': bool(point['is_bottom']),
|
||||
'fx_strength': float(point['fx_strength']),
|
||||
'fx_strength_level': str(point['fx_strength_level']),
|
||||
'is_strong_fx': bool(point['is_strong_fx'])
|
||||
} for point in element_analysis['klc_fx_info']],
|
||||
'element_klu_fx_info': [{
|
||||
'time': format_time_safely(point['time'], client_tz),
|
||||
'price': float(point['price']),
|
||||
'fx_type': point['fx_type'],
|
||||
'is_bottom': bool(point['is_bottom']),
|
||||
'fx_strength': float(point['fx_strength']),
|
||||
'fx_strength_level': str(point['fx_strength_level']),
|
||||
'is_strong_fx': bool(point['is_strong_fx']),
|
||||
'fx_confirmed': bool(point['fx_confirmed'])
|
||||
} for point in element_analysis['klu_fx_info']]
|
||||
})
|
||||
|
||||
replay_data[i] = current_data
|
||||
|
||||
# 打印进度(减少频率)
|
||||
if (i + 1) % 100 == 0 or i == len(df) - 1:
|
||||
percent = int((i + 1) / len(df) * 100)
|
||||
print(f"回放数据生成进度: {percent}% ({i + 1}/{len(df)})")
|
||||
|
||||
result = {
|
||||
'replay_data': replay_data,
|
||||
'total_length': len(df),
|
||||
'timezone': client_timezone
|
||||
}
|
||||
|
||||
print(f"回放数据生成完成: {len(df)} 条记录")
|
||||
return jsonify(result)
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(debug=True, host='0.0.0.0', port=8120)
|
||||
Reference in New Issue
Block a user