feat: 新增外部因素分析模块+综合评分引擎+算法文档重构
新增模块: - fund_flow_analyzer.py: 主力资金流向分析(P0, ±20) - market_sentiment.py: 市场情绪指标(P1, ±10) - external_factors.py: 北向资金/美股/大宗商品/汇率(P2-P4,P7) - news_analyzer.py: 公告/并购/政策面LLM分析(P5-P6) - score_engine.py: 综合评分引擎,整合技术面+外部因素 路由更新: - analysis.py: deep_analyze接入综合评分,根据最终评级修正买卖建议 - market.py: 新增4个外部因素API端点 - trades.py: 交易路由更新 算法文档重构: - 章节重排: 技术面(二三)→外部因素(四)→买卖决策(五)→数据源(六)→性能(七) - 架构图更新为五层,标注章节对应 - 5.1/5.2标注纯技术面,5.3整合外部因素修正推荐
This commit is contained in:
@@ -168,22 +168,23 @@ def _get_kline_from_local_db(stock_code, days=120):
|
||||
"""从本地数据库读取K线(最快,毫秒级)"""
|
||||
import pandas as pd
|
||||
try:
|
||||
import psycopg2
|
||||
conn = psycopg2.connect(
|
||||
host=Config.DB_HOST, port=Config.DB_PORT,
|
||||
dbname=Config.DB_NAME, user=Config.DB_USER, password=Config.DB_PASSWORD,
|
||||
)
|
||||
from db import get_db, put_db
|
||||
conn = get_db()
|
||||
if not conn:
|
||||
return None
|
||||
conn.autocommit = True
|
||||
start_date = (datetime.now() - timedelta(days=days)).strftime('%Y-%m-%d')
|
||||
with conn.cursor() as cur:
|
||||
cur.execute("""
|
||||
SELECT trade_date, open, high, low, close, volume
|
||||
FROM stock_kline_daily
|
||||
WHERE code = %s AND trade_date >= %s
|
||||
ORDER BY trade_date
|
||||
""", (stock_code, start_date))
|
||||
rows = cur.fetchall()
|
||||
conn.close()
|
||||
try:
|
||||
with conn.cursor() as cur:
|
||||
cur.execute("""
|
||||
SELECT trade_date, open, high, low, close, volume
|
||||
FROM stock_kline_daily
|
||||
WHERE code = %s AND trade_date >= %s
|
||||
ORDER BY trade_date
|
||||
""", (stock_code, start_date))
|
||||
rows = cur.fetchall()
|
||||
finally:
|
||||
put_db(conn)
|
||||
|
||||
if rows and len(rows) >= 30:
|
||||
df = pd.DataFrame(rows, columns=['date', 'open', 'high', 'low', 'close', 'volume'])
|
||||
@@ -625,9 +626,9 @@ def compute_recommend(signal_status, indicators, triggered_count, is_holding):
|
||||
if has_real_dragon:
|
||||
return ('watch', '关注', '真龙出现 → 趋势启动,等待龙抬头确认', 65)
|
||||
|
||||
# MACD死叉 → 卖出/回避
|
||||
# MACD死叉 → 回避(非持仓不能卖出,应为回避/观望)
|
||||
if dif is not None and dea is not None and dif < dea:
|
||||
return ('sell', '卖出', f"MACD死叉(DIF={dif:.3f}<DEA={dea:.3f})", 75)
|
||||
return ('watch', '回避', f"MACD死叉(DIF={dif:.3f}<DEA={dea:.3f}),趋势偏弱", 25)
|
||||
|
||||
# 底背离 → 关注(suanfa.md 步骤1: 纳入关注范围)
|
||||
if has_divergence:
|
||||
@@ -652,23 +653,23 @@ def get_latest_price(stock_code):
|
||||
返回:
|
||||
float: 最新价格, 失败返回 0
|
||||
"""
|
||||
from db import get_db, put_db
|
||||
conn = get_db()
|
||||
if not conn:
|
||||
return 0
|
||||
try:
|
||||
import psycopg2
|
||||
conn = psycopg2.connect(
|
||||
host=Config.DB_HOST, port=Config.DB_PORT,
|
||||
dbname=Config.DB_NAME, user=Config.DB_USER, password=Config.DB_PASSWORD,
|
||||
)
|
||||
cur = conn.cursor()
|
||||
cur.execute("""
|
||||
SELECT price FROM stock_realtime_price
|
||||
WHERE code = %s AND price > 0
|
||||
""", (stock_code,))
|
||||
row = cur.fetchone()
|
||||
conn.close()
|
||||
if row:
|
||||
return float(row[0])
|
||||
except Exception:
|
||||
pass
|
||||
finally:
|
||||
put_db(conn)
|
||||
return 0
|
||||
|
||||
|
||||
@@ -905,3 +906,393 @@ def find_bull_stocks(scan_rows, holding_codes=None):
|
||||
'total': total,
|
||||
'stage_info': BULL_STAGES,
|
||||
}
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════
|
||||
# 9. 单股深度分析(价格位置、压力支撑、量价、空间估算)
|
||||
# ═══════════════════════════════════════════════
|
||||
|
||||
def _generate_plain_summary(price, change_pct, ma_trend, position, supports,
|
||||
resistances, vol_ratio, vol_trend, patterns,
|
||||
space, score, verdict, reasons):
|
||||
"""根据技术分析结果生成通俗易懂的中文解说"""
|
||||
parts = []
|
||||
|
||||
# 1. 当前走势概况
|
||||
if change_pct > 3:
|
||||
trend_desc = f'今天涨了{change_pct:.1f}%,涨势比较猛'
|
||||
elif change_pct > 0:
|
||||
trend_desc = f'今天小涨{change_pct:.1f}%'
|
||||
elif change_pct > -3:
|
||||
trend_desc = f'今天小跌{abs(change_pct):.1f}%'
|
||||
else:
|
||||
trend_desc = f'今天跌了{abs(change_pct):.1f}%,跌幅较大'
|
||||
|
||||
if ma_trend == 'bullish':
|
||||
trend_desc += ',均线呈多头排列,说明中短期整体向上'
|
||||
elif ma_trend == 'bearish':
|
||||
trend_desc += ',均线呈空头排列,中短期趋势偏弱'
|
||||
else:
|
||||
trend_desc += ',均线交叉纠缠,短期方向还不太明确'
|
||||
parts.append(trend_desc + '。')
|
||||
|
||||
# 2. 价格位置(用大白话)
|
||||
pos_20 = position.get('20d', {})
|
||||
pct_20 = pos_20.get('pct', 50)
|
||||
if pct_20 > 80:
|
||||
parts.append(f'当前股价处于近20天的高位区间({pct_20:.0f}%位置),已经涨了不少,追高要小心。')
|
||||
elif pct_20 > 50:
|
||||
parts.append(f'股价在近20天的中高位置({pct_20:.0f}%),还有一定上涨空间。')
|
||||
elif pct_20 > 20:
|
||||
parts.append(f'股价在近20天的中低位置({pct_20:.0f}%),相对安全。')
|
||||
else:
|
||||
parts.append(f'股价处于近20天的低位区间({pct_20:.0f}%),可能存在反弹机会。')
|
||||
|
||||
# 3. 上方压力和下方支撑
|
||||
if resistances:
|
||||
nearest_r = resistances[0]
|
||||
r_gap = round((nearest_r['level'] - price) / price * 100, 1) if price > 0 else 0
|
||||
if r_gap > 0:
|
||||
parts.append(f'往上最近的压力位在{nearest_r["level"]:.2f}元({nearest_r["name"]}),距离约{r_gap:.1f}%。')
|
||||
if supports:
|
||||
nearest_s = supports[0]
|
||||
s_gap = round((price - nearest_s['level']) / price * 100, 1) if price > 0 else 0
|
||||
if s_gap > 0:
|
||||
parts.append(f'往下最近的支撑位在{nearest_s["level"]:.2f}元({nearest_s["name"]}),有{s_gap:.1f}%的安全垫。')
|
||||
|
||||
# 4. 成交量情况
|
||||
if vol_ratio >= 2:
|
||||
parts.append(f'成交量明显放大(量比{vol_ratio:.1f}倍),市场关注度很高,要留意是主力进场还是出货。')
|
||||
elif vol_ratio >= 1.3:
|
||||
parts.append(f'成交量温和放大(量比{vol_ratio:.1f}倍),有资金在活跃参与。')
|
||||
elif vol_ratio < 0.6:
|
||||
parts.append(f'成交量萎缩(量比{vol_ratio:.1f}倍),市场比较冷清,短期可能震荡。')
|
||||
else:
|
||||
parts.append(f'成交量正常(量比{vol_ratio:.1f}倍)。')
|
||||
|
||||
# 5. 形态识别
|
||||
if patterns:
|
||||
pattern_names = [p['name'] for p in patterns]
|
||||
bullish_p = [p['name'] for p in patterns if p.get('bullish') is True]
|
||||
bearish_p = [p['name'] for p in patterns if p.get('bullish') is False]
|
||||
if bullish_p:
|
||||
parts.append(f'发现看涨信号:{"、".join(bullish_p)},这是积极的技术形态。')
|
||||
if bearish_p:
|
||||
parts.append(f'注意看跌信号:{"、".join(bearish_p)},需要警惕。')
|
||||
|
||||
# 6. 综合建议(大白话)
|
||||
action_tip = ''
|
||||
if score >= 75:
|
||||
action_tip = '综合来看比较乐观,可以考虑逢低关注或适量参与,但注意控制仓位。'
|
||||
elif score >= 60:
|
||||
action_tip = '整体偏积极,可以少量关注,等回调到支撑位附近再考虑。'
|
||||
elif score >= 45:
|
||||
action_tip = '目前多空力量比较均衡,建议观望为主,等方向更明确再做决定。'
|
||||
elif score >= 30:
|
||||
action_tip = '目前偏弱势,不建议急于买入。如果持有,可以在反弹时适当减仓。'
|
||||
else:
|
||||
action_tip = '当前走势比较弱,建议回避。已经持有的可以考虑止损或等待反弹减仓。'
|
||||
|
||||
# 7. 空间估算
|
||||
rr = space.get('risk_reward', 0)
|
||||
if rr and rr > 0:
|
||||
if rr >= 2:
|
||||
parts.append(f'从空间来看,潜在收益是风险的{rr:.1f}倍,性价比不错。')
|
||||
elif rr >= 1:
|
||||
parts.append(f'收益风险比{rr:.1f}:1,性价比一般。')
|
||||
else:
|
||||
parts.append(f'收益风险比仅{rr:.1f}:1,下行风险大于上涨空间,不太划算。')
|
||||
|
||||
summary_text = ''.join(parts)
|
||||
|
||||
return {
|
||||
'text': summary_text,
|
||||
'action_tip': action_tip,
|
||||
'confidence': '高' if score >= 70 or score <= 30 else '中',
|
||||
}
|
||||
|
||||
|
||||
def compute_deep_analysis(df, signal_result=None, realtime_info=None):
|
||||
"""
|
||||
对单只股票进行深度分析,返回结构化的分析报告。
|
||||
|
||||
参数:
|
||||
df: DataFrame (含技术指标的K线数据)
|
||||
signal_result: dict (detect_all_signals 返回的结果,可选)
|
||||
realtime_info: dict (stock_realtime_price 行数据,可选)
|
||||
|
||||
返回:
|
||||
dict: 完整的深度分析报告
|
||||
"""
|
||||
import numpy as np
|
||||
if df is None or len(df) < 30:
|
||||
return {'error': 'K线数据不足(需要至少30天)'}
|
||||
|
||||
last = df.iloc[-1]
|
||||
cl = float(last['close'])
|
||||
n = len(df)
|
||||
|
||||
# ---- 1. 均线系统 ----
|
||||
ma_data = {}
|
||||
for period in [5, 10, 20, 60]:
|
||||
col = f'ma{period}'
|
||||
if col in df.columns and n >= period:
|
||||
ma_data[f'ma{period}'] = round(float(df[col].iloc[-1]), 2)
|
||||
|
||||
ma_list = sorted(ma_data.items(), key=lambda x: x[1], reverse=True)
|
||||
ma_trend = 'bullish' if all(
|
||||
ma_data.get(f'ma{a}', 0) >= ma_data.get(f'ma{b}', 0)
|
||||
for a, b in [(5, 10), (10, 20)]
|
||||
) else 'bearish' if all(
|
||||
ma_data.get(f'ma{a}', 0) <= ma_data.get(f'ma{b}', 0)
|
||||
for a, b in [(5, 10), (10, 20)]
|
||||
) else 'mixed'
|
||||
|
||||
ma_trend_label = {'bullish': '多头排列', 'bearish': '空头排列', 'mixed': '交叉整理'}
|
||||
|
||||
# ---- 2. 价格位置分析 ----
|
||||
position = {}
|
||||
for days in [20, 60, 120]:
|
||||
subset = df.tail(days) if n >= days else df
|
||||
h = float(subset['high'].max())
|
||||
l = float(subset['low'].min())
|
||||
rng = h - l
|
||||
pct = round((cl - l) / rng * 100, 0) if rng > 0 else 50
|
||||
position[f'd{days}'] = {
|
||||
'high': round(h, 2), 'low': round(l, 2),
|
||||
'range_pct': pct,
|
||||
'up_space': round((h / cl - 1) * 100, 1),
|
||||
'down_risk': round((1 - l / cl) * 100, 1),
|
||||
}
|
||||
|
||||
# ---- 3. 支撑与压力位 ----
|
||||
supports = []
|
||||
resistances = []
|
||||
|
||||
for name, val in ma_data.items():
|
||||
if val < cl:
|
||||
supports.append({'level': val, 'type': 'ma', 'name': name.upper()})
|
||||
elif val > cl:
|
||||
resistances.append({'level': val, 'type': 'ma', 'name': name.upper()})
|
||||
|
||||
for days_key in ['d20', 'd60', 'd120']:
|
||||
p = position.get(days_key, {})
|
||||
label = days_key.replace('d', '') + '日'
|
||||
if p.get('low', 0) < cl:
|
||||
supports.append({'level': p['low'], 'type': 'low', 'name': f'{label}低点'})
|
||||
if p.get('high', 0) > cl:
|
||||
resistances.append({'level': p['high'], 'type': 'high', 'name': f'{label}高点'})
|
||||
|
||||
supports.sort(key=lambda x: x['level'], reverse=True)
|
||||
resistances.sort(key=lambda x: x['level'])
|
||||
|
||||
# ---- 4. 成交量分析 ----
|
||||
vol = float(last['volume'])
|
||||
vol_5 = float(df['volume'].tail(5).mean()) if n >= 5 else vol
|
||||
vol_20 = float(df['volume'].tail(20).mean()) if n >= 20 else vol
|
||||
vol_ratio = round(vol / vol_20, 1) if vol_20 > 0 else 1.0
|
||||
|
||||
vol_trend = '缩量' if vol_ratio < 0.7 else '平量' if vol_ratio < 1.3 else '温和放量' if vol_ratio < 2.0 else '大幅放量'
|
||||
|
||||
# ---- 5. 形态识别(增强版) ----
|
||||
patterns = []
|
||||
closes_10 = [float(x) for x in df['close'].tail(10)]
|
||||
if n >= 10:
|
||||
std_10 = np.std(closes_10)
|
||||
mean_10 = np.mean(closes_10)
|
||||
cv_10 = std_10 / mean_10 if mean_10 > 0 else 0
|
||||
|
||||
if cv_10 < 0.015 and cl > max(closes_10[:-1]):
|
||||
patterns.append({'name': '平台突破', 'bullish': True,
|
||||
'desc': f'近10日波动率仅{cv_10*100:.1f}%,今日突破平台'})
|
||||
elif cv_10 < 0.015:
|
||||
patterns.append({'name': '窄幅整理', 'bullish': None,
|
||||
'desc': f'近10日波动率{cv_10*100:.1f}%,蓄势待变'})
|
||||
|
||||
if n >= 20:
|
||||
h20 = float(df.tail(20)['high'].max())
|
||||
if cl >= h20 * 0.99:
|
||||
patterns.append({'name': '创20日新高', 'bullish': True,
|
||||
'desc': f'触及20日高点{h20:.2f}'})
|
||||
|
||||
# 双底形态:近30日内两个低点价格接近(差异<3%),且当前价格高于两低点之间的高点
|
||||
if n >= 30:
|
||||
lows_30 = [float(x) for x in df['low'].tail(30)]
|
||||
# 找最低点和次低点
|
||||
min_idx = int(np.argmin(lows_30))
|
||||
min_val = lows_30[min_idx]
|
||||
# 在最低点之前找次低点
|
||||
if min_idx > 5:
|
||||
before_lows = lows_30[:min_idx]
|
||||
second_min_idx = int(np.argmin(before_lows))
|
||||
second_min_val = before_lows[second_min_idx]
|
||||
if abs(min_val - second_min_val) / min_val < 0.03:
|
||||
# 两低点之间的高点
|
||||
between_high = max(lows_30[second_min_idx:min_idx])
|
||||
if cl > between_high:
|
||||
patterns.append({'name': '双底突破', 'bullish': True,
|
||||
'desc': f'双底形态(低点{min_val:.2f}和{second_min_val:.2f}),已突破颈线{between_high:.2f}'})
|
||||
|
||||
# 量价齐升:近5日成交量递增且价格递增
|
||||
if n >= 5:
|
||||
vols_5 = [float(x) for x in df['volume'].tail(5)]
|
||||
closes_5 = [float(x) for x in df['close'].tail(5)]
|
||||
if all(vols_5[i] <= vols_5[i+1] for i in range(len(vols_5)-1)) and \
|
||||
all(closes_5[i] <= closes_5[i+1] for i in range(len(closes_5)-1)):
|
||||
patterns.append({'name': '量价齐升', 'bullish': True,
|
||||
'desc': '近5日成交量与价格同步递增,强势特征'})
|
||||
|
||||
# 均线粘合后发散:MA5/10/20 三线粘合后开始发散
|
||||
if n >= 20:
|
||||
ma5_val = ma_data.get('ma5', 0)
|
||||
ma10_val = ma_data.get('ma10', 0)
|
||||
ma20_val = ma_data.get('ma20', 0)
|
||||
if ma5_val and ma10_val and ma20_val:
|
||||
ma_spread = max(ma5_val, ma10_val, ma20_val) - min(ma5_val, ma10_val, ma20_val)
|
||||
ma_pct = ma_spread / cl * 100
|
||||
if ma_pct < 1.0 and ma5_val > ma10_val > ma20_val:
|
||||
patterns.append({'name': '均线粘合发散', 'bullish': True,
|
||||
'desc': f'MA5/10/20粘合(离散{ma_pct:.1f}%)后多头排列'})
|
||||
|
||||
# 涨跌幅计算:如果最后一条是今天(可能未收盘),用前一日收盘价计算
|
||||
from datetime import date
|
||||
last_date_str = str(df['date'].values[-1])[:10]
|
||||
today_str = date.today().isoformat()
|
||||
if last_date_str == today_str and n >= 3:
|
||||
# 今天未收盘,用倒数第二根K线的收盘价对比倒数第三根
|
||||
change_today = round((cl / float(df.iloc[-2]['close']) - 1) * 100, 2)
|
||||
else:
|
||||
change_today = round((cl / float(df.iloc[-2]['close']) - 1) * 100, 2) if n >= 2 else 0
|
||||
if change_today >= 5:
|
||||
patterns.append({'name': '大阳线', 'bullish': True,
|
||||
'desc': f'涨幅{change_today:.1f}%'})
|
||||
elif change_today <= -5:
|
||||
patterns.append({'name': '大阴线', 'bullish': False,
|
||||
'desc': f'跌幅{change_today:.1f}%'})
|
||||
|
||||
# ---- 6. 空间估算 ----
|
||||
first_resist = resistances[0] if resistances else None
|
||||
first_support = supports[0] if supports else None
|
||||
|
||||
space = {
|
||||
'nearest_resist': first_resist,
|
||||
'nearest_support': first_support,
|
||||
'risk_reward': None,
|
||||
}
|
||||
if first_resist and first_support:
|
||||
upside = first_resist['level'] - cl
|
||||
downside = cl - first_support['level']
|
||||
space['risk_reward'] = round(upside / downside, 1) if downside > 0 else 99
|
||||
|
||||
# ---- 7. 综合评估 ----
|
||||
score = 50
|
||||
reasons = []
|
||||
|
||||
if ma_trend == 'bullish':
|
||||
score += 10
|
||||
reasons.append('均线多头排列(+10)')
|
||||
elif ma_trend == 'bearish':
|
||||
score -= 10
|
||||
reasons.append('均线空头排列(-10)')
|
||||
|
||||
if vol_ratio >= 1.3:
|
||||
score += 5
|
||||
reasons.append(f'放量{vol_ratio}倍(+5)')
|
||||
elif vol_ratio < 0.6:
|
||||
score -= 3
|
||||
reasons.append(f'缩量{vol_ratio}倍(-3)')
|
||||
|
||||
any_breakout = any(p['name'] == '平台突破' for p in patterns)
|
||||
if any_breakout:
|
||||
score += 10
|
||||
reasons.append('平台突破(+10)')
|
||||
|
||||
any_new_high = any(p['name'] == '创20日新高' for p in patterns)
|
||||
if any_new_high:
|
||||
score += 5
|
||||
reasons.append('创20日新高(+5)')
|
||||
|
||||
any_double_bottom = any(p['name'] == '双底突破' for p in patterns)
|
||||
if any_double_bottom:
|
||||
score += 10
|
||||
reasons.append('双底突破(+10)')
|
||||
|
||||
any_vol_price_rise = any(p['name'] == '量价齐升' for p in patterns)
|
||||
if any_vol_price_rise:
|
||||
score += 8
|
||||
reasons.append('量价齐升(+8)')
|
||||
|
||||
any_ma_converge = any(p['name'] == '均线粘合发散' for p in patterns)
|
||||
if any_ma_converge:
|
||||
score += 7
|
||||
reasons.append('均线粘合发散(+7)')
|
||||
|
||||
pos_120 = position.get('d120', {}).get('range_pct', 50)
|
||||
if pos_120 < 30:
|
||||
score += 5
|
||||
reasons.append(f'120日位置偏低{pos_120}%(+5)')
|
||||
elif pos_120 > 80:
|
||||
score -= 5
|
||||
reasons.append(f'120日位置偏高{pos_120}%(-5)')
|
||||
|
||||
# 20日位置也纳入评分
|
||||
pos_20 = position.get('d20', {}).get('range_pct', 50)
|
||||
if pos_20 < 25:
|
||||
score += 3
|
||||
reasons.append(f'20日位置偏低{pos_20}%(+3)')
|
||||
elif pos_20 > 85:
|
||||
score -= 3
|
||||
reasons.append(f'20日位置偏高{pos_20}%(-3)')
|
||||
|
||||
if signal_result:
|
||||
sig_count = signal_result.get('signal_summary', {}).get('total_signals', 0)
|
||||
if sig_count >= 3:
|
||||
score += 15
|
||||
reasons.append(f'{sig_count}信号共振(+15)')
|
||||
elif sig_count >= 2:
|
||||
score += 10
|
||||
reasons.append(f'{sig_count}信号叠加(+10)')
|
||||
elif sig_count >= 1:
|
||||
score += 5
|
||||
reasons.append(f'{sig_count}个信号(+5)')
|
||||
|
||||
if space.get('risk_reward') and space['risk_reward'] >= 2:
|
||||
score += 5
|
||||
reasons.append(f'风险收益比{space["risk_reward"]}:1(+5)')
|
||||
elif space.get('risk_reward') and space['risk_reward'] < 0.8:
|
||||
score -= 5
|
||||
reasons.append(f'风险收益比{space["risk_reward"]}:1(-5)')
|
||||
|
||||
score = max(0, min(100, score))
|
||||
|
||||
verdict = '强烈看多' if score >= 80 else '看多' if score >= 65 else '中性偏多' if score >= 50 else '中性偏空' if score >= 35 else '看空'
|
||||
|
||||
ai_summary = _generate_plain_summary(
|
||||
cl, change_today, ma_trend, position, supports, resistances,
|
||||
vol_ratio, vol_trend, patterns, space, score, verdict, reasons
|
||||
)
|
||||
|
||||
return {
|
||||
'price': cl,
|
||||
'change_pct': change_today,
|
||||
'ma': ma_data,
|
||||
'ma_trend': ma_trend,
|
||||
'ma_trend_label': ma_trend_label[ma_trend],
|
||||
'position': position,
|
||||
'supports': supports[:5],
|
||||
'resistances': resistances[:5],
|
||||
'volume': {
|
||||
'today': vol,
|
||||
'avg_5': round(vol_5),
|
||||
'avg_20': round(vol_20),
|
||||
'ratio': vol_ratio,
|
||||
'trend': vol_trend,
|
||||
},
|
||||
'patterns': patterns,
|
||||
'space': space,
|
||||
'deep_score': score,
|
||||
'verdict': verdict,
|
||||
'score_reasons': reasons,
|
||||
'ai_summary': ai_summary,
|
||||
'kline_days': n,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user