"""趋势相关 API。""" from flask import Blueprint, jsonify, request from services.runtime import * # noqa: F403 from services import runtime as R bp = Blueprint("trend", __name__) @bp.route('/api/trend_filter', methods=['GET']) def trend_filter(): """趋势筛选接口(币对) 参数: timeframe: K线周期 start_time, end_time: 毫秒时间戳,可选 direction: bull/bear/sideways 可选 stage: early/mid/late 可选 min_strength: 0-100 可选 symbols: 逗号分隔列表,可选;不传则自动加载部分USDT币对 返回符合条件的币对与简要统计 """ timeframe = request.args.get('timeframe', '1h') start_time = request.args.get('start_time') end_time = request.args.get('end_time') want_direction = request.args.get('direction') # 可为 None want_stage = request.args.get('stage') # 可为 None try: min_strength = float(request.args.get('min_strength', '0')) except ValueError: min_strength = 0.0 symbols_param = request.args.get('symbols') if symbols_param: symbols_list = [s.strip() for s in symbols_param.split(',') if s.strip()] else: symbols_list = load_crypto_symbols(limit=150) results = [] for sym in symbols_list: try: df = get_crypto_kl_data(sym, timeframe, start_time=start_time, end_time=end_time) if df is None or len(df) < 60: continue df = add_indicators(df) direction, stage, strength = classify_trend_stage(df) if want_direction and direction != want_direction: continue if want_stage and stage != want_stage: continue if strength < min_strength: continue last_row = df.iloc[-1] results.append({ 'symbol': sym, 'time': int(last_row['timestamp']), 'close': float(last_row['close']), 'direction': direction, 'stage': stage, 'strength': float(round(strength, 2)), 'ema5': float(last_row['ema5']), 'ema10': float(last_row['ema10']), 'ema24': float(last_row['ema24']), 'ema52': float(last_row['ema52']) }) except Exception: continue # 按强度降序 results.sort(key=lambda x: x['strength'], reverse=True) return jsonify({ 'count': len(results), 'results': results }) @bp.route('/api/trend_detail', methods=['GET']) def trend_detail(): """返回单个币对的K线与EMA、用于前端绘制趋势线 参数: symbol, timeframe, start_time, end_time """ symbol = request.args.get('symbol') timeframe = request.args.get('timeframe', '1h') start_time = request.args.get('start_time') end_time = request.args.get('end_time') timezone_name = request.args.get('timezone', 'Asia/Shanghai') if not symbol: return jsonify({'error': 'symbol不能为空'}) df = get_crypto_kl_data(symbol, timeframe, start_time=start_time, end_time=end_time) if df is None or len(df) == 0: return jsonify({'error': '获取数据失败'}) df = add_indicators(df) direction, stage, strength = classify_trend_stage(df) # 简单趋势线: 用最近N根收盘价做线性拟合 N = min(80, len(df)) sub = df.tail(N) y = sub['close'].values x = np.arange(len(y)) denom = np.dot(x - x.mean(), x - x.mean()) if denom != 0: m = float(np.dot(y - y.mean(), x - x.mean()) / denom) b = float(y.mean() - m * x.mean()) else: m, b = 0.0, float(y[-1]) client_tz = timezone(timezone_name) return jsonify({ 'symbol': symbol, 'timeframe': timeframe, 'timezone': timezone_name, 'direction': direction, 'stage': stage, 'strength': float(round(strength, 2)), 'kline_data': clean_dataframe_for_json(df)[['timestamp','open','high','low','close','volume','ema5','ema10','ema24','ema52']].to_dict('records'), 'trend_line': { 'offset': int(df.index[-N]), 'slope': m, 'intercept': b, 'length': int(N) } })