79e869eeda
- P0资金面:DB数据过期时用麦蕊API获取资金流向 - P1市场情绪:去掉turnover字段依赖(DB无此字段) - P2南向资金:北向实时数据已停公布,改用南向资金(港股通)替代 - P3美股:用腾讯财经API替代失效的AKShare接口 - P4大宗商品:用腾讯财经API替代,修复var_name解析 - P7汇率:用新浪财经API替代失效的AKShare接口 - 修复评分详情API技术得分硬编码问题 - 前端传入tech_score参数确保明细与列表分数一致
627 lines
23 KiB
Python
627 lines
23 KiB
Python
"""
|
||
外部因素分析模块(P2/P3/P4/P7)
|
||
|
||
包含:
|
||
- P2: 南向资金(港股通跨境资金动向)
|
||
- P3: 美股隔夜板块变化
|
||
- P4: 大宗商品价格
|
||
- P7: 汇率变化
|
||
|
||
数据源:
|
||
- P2: AKShare stock_hsgt_hist_em(南向资金历史)+ stock_hsgt_fund_flow_summary_em(今日汇总)
|
||
- P3: 腾讯财经API(美股指数实时)
|
||
- P4: 腾讯财经API(商品期货实时)
|
||
- P7: 新浪财经API(人民币汇率)
|
||
|
||
所有数据采集均带超时和异常处理,失败时返回中性评分不影响主流程。
|
||
"""
|
||
import logging
|
||
import requests
|
||
from datetime import datetime, timedelta
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
# 缓存(当日有效)
|
||
_cache = {}
|
||
_cache_date = {}
|
||
|
||
|
||
def _get_cache(key):
|
||
"""获取当日缓存"""
|
||
today = datetime.now().strftime('%Y-%m-%d')
|
||
if _cache_date.get(key) == today:
|
||
return _cache.get(key)
|
||
return None
|
||
|
||
|
||
def _set_cache(key, value):
|
||
"""设置当日缓存"""
|
||
today = datetime.now().strftime('%Y-%m-%d')
|
||
_cache[key] = value
|
||
_cache_date[key] = today
|
||
|
||
|
||
# ═══════════════════════════════════════════════
|
||
# P2: 南向资金
|
||
# ═══════════════════════════════════════════════
|
||
|
||
def get_southbound_capital():
|
||
"""
|
||
获取跨境资金流向数据
|
||
|
||
南向资金(港股通)反映内地资金配置港股的意愿,是跨境资金情绪的重要指标:
|
||
- 南向净流入 > 0:内地资金积极配置港股,大中华区risk-on,对A股偏正面
|
||
- 南向净流入 < 0:内地资金撤出港股,risk-off,对A股偏负面
|
||
|
||
数据源:
|
||
1. AKShare stock_hsgt_fund_flow_summary_em(今日汇总)
|
||
2. AKShare stock_hsgt_hist_em(南向资金历史,用于连续天数计算)
|
||
|
||
返回:
|
||
dict: {
|
||
'net_inflow': float, # 今日南向净流入(亿)
|
||
'score': int, # 评分增减(-10 ~ +10)
|
||
'summary': str, # 白话总结
|
||
'reasons': list, # 评分原因
|
||
}
|
||
"""
|
||
cached = _get_cache('southbound')
|
||
if cached:
|
||
return cached
|
||
|
||
try:
|
||
import akshare as ak
|
||
import pandas as pd
|
||
|
||
# 1. 用 stock_hsgt_fund_flow_summary_em 获取今日汇总
|
||
today_inflow = 0
|
||
try:
|
||
df_summary = ak.stock_hsgt_fund_flow_summary_em()
|
||
if df_summary is not None and not df_summary.empty:
|
||
# 筛选南向资金行
|
||
south_rows = df_summary[df_summary['资金方向'] == '南向']
|
||
if not south_rows.empty:
|
||
# 成交净买额列求和
|
||
vals = south_rows['成交净买额'].tolist()
|
||
today_inflow = float(sum(v for v in vals if pd.notna(v) and v != 0))
|
||
except Exception as e:
|
||
logger.debug(f"stock_hsgt_fund_flow_summary_em失败: {e}")
|
||
|
||
# 2. 用 stock_hsgt_hist_em 获取南向资金历史(计算连续天数)
|
||
consecutive_inflow = 0
|
||
consecutive_outflow = 0
|
||
try:
|
||
df = ak.stock_hsgt_hist_em(symbol="南向资金")
|
||
if df is not None and not df.empty:
|
||
recent = df.tail(5)
|
||
for _, row in recent[::-1].iterrows():
|
||
val = float(row.get('当日成交净买额', 0) or 0)
|
||
if str(val) == 'nan' or pd.isna(val):
|
||
val = 0
|
||
if val > 0:
|
||
if consecutive_outflow > 0:
|
||
break
|
||
consecutive_inflow += 1
|
||
elif val < 0:
|
||
if consecutive_inflow > 0:
|
||
break
|
||
consecutive_outflow += 1
|
||
except Exception as e:
|
||
logger.debug(f"stock_hsgt_hist_em南向失败: {e}")
|
||
|
||
# 如果今日数据也为0或NaN,说明无法获取
|
||
if str(today_inflow) == 'nan' or today_inflow == 0:
|
||
# 尝试从历史数据取最新值
|
||
try:
|
||
df = ak.stock_hsgt_hist_em(symbol="南向资金")
|
||
if df is not None and not df.empty:
|
||
last_val = float(df.iloc[-1].get('当日成交净买额', 0) or 0)
|
||
if str(last_val) != 'nan' and not pd.isna(last_val) and last_val != 0:
|
||
today_inflow = last_val
|
||
except Exception:
|
||
pass
|
||
|
||
# 评分(基于南向资金,逻辑与北向一致:净流入=正面,净流出=负面)
|
||
score = 0
|
||
reasons = []
|
||
summary_parts = []
|
||
|
||
if today_inflow > 80:
|
||
score += 5
|
||
reasons.append(f'南向今日净流入{today_inflow:.1f}亿(+5)')
|
||
summary_parts.append(f'南向资金大幅流入{today_inflow:.1f}亿元,跨境资金情绪偏暖')
|
||
elif today_inflow > 30:
|
||
score += 3
|
||
reasons.append(f'南向今日净流入{today_inflow:.1f}亿(+3)')
|
||
summary_parts.append(f'南向资金净流入{today_inflow:.1f}亿元')
|
||
elif today_inflow < -80:
|
||
score -= 5
|
||
reasons.append(f'南向今日净流出{abs(today_inflow):.1f}亿(-5)')
|
||
summary_parts.append(f'南向资金大幅流出{abs(today_inflow):.1f}亿元,跨境资金情绪偏冷')
|
||
elif today_inflow < -30:
|
||
score -= 3
|
||
reasons.append(f'南向今日净流出{abs(today_inflow):.1f}亿(-3)')
|
||
summary_parts.append(f'南向资金净流出{abs(today_inflow):.1f}亿元')
|
||
else:
|
||
summary_parts.append(f'南向资金净流入{today_inflow:.1f}亿元,方向不明')
|
||
|
||
if consecutive_inflow >= 3:
|
||
score += 3
|
||
reasons.append(f'南向连续{consecutive_inflow}日净流入(+3)')
|
||
summary_parts.append(f'已连续{consecutive_inflow}天流入')
|
||
|
||
if consecutive_outflow >= 3:
|
||
score -= 3
|
||
reasons.append(f'南向连续{consecutive_outflow}日净流出(-3)')
|
||
summary_parts.append(f'已连续{consecutive_outflow}天流出')
|
||
|
||
score = max(-10, min(10, score))
|
||
|
||
result = {
|
||
'net_inflow': round(today_inflow, 2),
|
||
'consecutive_inflow': consecutive_inflow,
|
||
'consecutive_outflow': consecutive_outflow,
|
||
'score': score,
|
||
'summary': ','.join(summary_parts),
|
||
'reasons': reasons,
|
||
}
|
||
_set_cache('southbound', result)
|
||
return result
|
||
|
||
except Exception as e:
|
||
logger.warning(f"获取跨境资金数据失败: {e}")
|
||
return _neutral_result('南向资金数据获取失败')
|
||
|
||
|
||
# ═══════════════════════════════════════════════
|
||
# P3: 美股隔夜板块变化
|
||
# ═══════════════════════════════════════════════
|
||
|
||
# 美股板块 → A股板块映射
|
||
US_A_SECTOR_MAP = {
|
||
'科技': ['半导体', '软件', '消费电子', '芯片', 'IT服务'],
|
||
'新能源车': ['锂电池', '汽车零部件', '新能源车', '充电桩'],
|
||
'金融': ['银行', '保险', '券商'],
|
||
'能源': ['石油开采', '化工', '页岩气'],
|
||
'医药': ['创新药', '医疗器械', '生物制品', 'CXO'],
|
||
'消费': ['白酒', '食品饮料', '零售', '免税'],
|
||
'房地产': ['房地产', '建材', '家居'],
|
||
'工业': ['机械', '军工', '工业4.0'],
|
||
'材料': ['有色金属', '钢铁', '化工新材料'],
|
||
'公用事业': ['电力', '环保', '水务'],
|
||
}
|
||
|
||
|
||
def get_us_market_overview():
|
||
"""
|
||
获取美股隔夜收盘数据,计算外盘情绪
|
||
|
||
数据源:腾讯财经API(qt.gtimg.cn)
|
||
获取道琼斯、纳斯达克、标普500三大指数实时行情。
|
||
|
||
返回:
|
||
dict: {
|
||
'indices': dict, # 三大指数涨跌
|
||
'score': int, # 评分增减(-10 ~ +10)
|
||
'summary': str, # 白话总结
|
||
'reasons': list, # 评分原因
|
||
'affected_a_sectors': dict, # 对A股板块的影响
|
||
}
|
||
"""
|
||
cached = _get_cache('us_market')
|
||
if cached:
|
||
return cached
|
||
|
||
try:
|
||
# 腾讯财经API获取美股指数
|
||
# 格式: v_usDJI="200~道琼斯~.DJI~price~...~change_pct~..."
|
||
url = 'https://qt.gtimg.cn/q=usDJI,usIXIC,usSPX'
|
||
resp = requests.get(url, timeout=10)
|
||
text = resp.content.decode('gbk', errors='replace')
|
||
|
||
us_indices = {}
|
||
for line in text.strip().split(';'):
|
||
line = line.strip()
|
||
if not line or 'v_pv_none_match' in line:
|
||
continue
|
||
# 解析 v_usDJI="..."
|
||
if '=' not in line:
|
||
continue
|
||
var_name = line.split('=')[0].strip().replace('var ', '').replace('v_', '')
|
||
value = line.split('"')[1] if '"' in line else ''
|
||
fields = value.split('~')
|
||
if len(fields) < 33:
|
||
continue
|
||
|
||
name = fields[1]
|
||
change_pct = float(fields[32]) if fields[32] else 0
|
||
|
||
if 'DJI' in var_name.upper() or '道琼斯' in name:
|
||
us_indices['dow'] = {'name': name, 'change_pct': change_pct}
|
||
elif 'IXIC' in var_name.upper() or '纳斯达克' in name:
|
||
us_indices['nasdaq'] = {'name': name, 'change_pct': change_pct}
|
||
elif 'SPX' in var_name.upper() or '标普' in name:
|
||
us_indices['sp500'] = {'name': name, 'change_pct': change_pct}
|
||
|
||
if not us_indices:
|
||
return _neutral_result('未找到美股指数')
|
||
|
||
# 计算综合涨跌
|
||
avg_change = sum(v['change_pct'] for v in us_indices.values()) / len(us_indices)
|
||
|
||
# 评分
|
||
score = 0
|
||
reasons = []
|
||
summary_parts = []
|
||
|
||
if avg_change > 2:
|
||
score += 5
|
||
reasons.append(f'美股三大指数平均涨幅{avg_change:.1f}%(+5)')
|
||
summary_parts.append(f'美股大涨,平均涨幅{avg_change:.1f}%')
|
||
elif avg_change > 0.5:
|
||
score += 2
|
||
reasons.append(f'美股偏强,平均涨幅{avg_change:.1f}%(+2)')
|
||
summary_parts.append(f'美股小幅上涨,平均{avg_change:.1f}%')
|
||
elif avg_change < -2:
|
||
score -= 5
|
||
reasons.append(f'美股三大指数平均跌幅{abs(avg_change):.1f}%(-5)')
|
||
summary_parts.append(f'美股大跌,平均跌幅{abs(avg_change):.1f}%')
|
||
elif avg_change < -0.5:
|
||
score -= 2
|
||
reasons.append(f'美股偏弱,平均跌幅{abs(avg_change):.1f}%(-2)')
|
||
summary_parts.append(f'美股小幅下跌,平均{avg_change:.1f}%')
|
||
else:
|
||
summary_parts.append(f'美股基本平盘,平均变化{avg_change:.1f}%')
|
||
|
||
# 对A股板块的影响
|
||
affected_sectors = {}
|
||
if avg_change > 1:
|
||
for us_sector, a_sectors in US_A_SECTOR_MAP.items():
|
||
affected_sectors[us_sector] = {
|
||
'a_sectors': a_sectors,
|
||
'direction': '利好',
|
||
'note': f'美股{us_sector}板块偏强,A股{"、".join(a_sectors[:3])}可能高开',
|
||
}
|
||
elif avg_change < -1:
|
||
for us_sector, a_sectors in US_A_SECTOR_MAP.items():
|
||
affected_sectors[us_sector] = {
|
||
'a_sectors': a_sectors,
|
||
'direction': '利空',
|
||
'note': f'美股{us_sector}板块偏弱,A股{"、".join(a_sectors[:3])}可能低开',
|
||
}
|
||
|
||
score = max(-10, min(10, score))
|
||
|
||
result = {
|
||
'indices': us_indices,
|
||
'avg_change': round(avg_change, 2),
|
||
'score': score,
|
||
'summary': ','.join(summary_parts),
|
||
'reasons': reasons,
|
||
'affected_a_sectors': affected_sectors,
|
||
}
|
||
_set_cache('us_market', result)
|
||
return result
|
||
|
||
except Exception as e:
|
||
logger.warning(f"获取美股数据失败: {e}")
|
||
return _neutral_result('美股数据获取失败')
|
||
|
||
|
||
# ═══════════════════════════════════════════════
|
||
# P4: 大宗商品价格
|
||
# ═══════════════════════════════════════════════
|
||
|
||
# 大宗商品 → A股板块影响映射
|
||
COMMODITY_A_SECTOR_MAP = {
|
||
'原油': {
|
||
'beneficiary': ['石油开采', '油服', '化工'],
|
||
'victim': ['航空', '物流', '化工下游'],
|
||
'direction': '油价涨→开采受益,航空受损',
|
||
},
|
||
'黄金': {
|
||
'beneficiary': ['黄金股', '珠宝', '有色'],
|
||
'victim': [],
|
||
'direction': '金价涨→黄金企业受益',
|
||
},
|
||
'铜': {
|
||
'beneficiary': ['铜矿', '有色', '电缆'],
|
||
'victim': [],
|
||
'direction': '铜价涨→铜企受益',
|
||
},
|
||
'螺纹钢': {
|
||
'beneficiary': ['钢铁', '钢矿'],
|
||
'victim': ['基建', '地产'],
|
||
'direction': '钢价涨→钢企受益,基建成本增',
|
||
},
|
||
'碳酸锂': {
|
||
'beneficiary': ['锂矿', '锂电池'],
|
||
'victim': ['新能源车'],
|
||
'direction': '锂价涨→锂矿受益,新能源车成本增',
|
||
},
|
||
}
|
||
|
||
|
||
def get_commodity_overview():
|
||
"""
|
||
获取主要大宗商品价格变化
|
||
|
||
数据源:腾讯财经API(qt.gtimg.cn)
|
||
获取纽约黄金、纽约原油、美铜等商品期货实时行情。
|
||
|
||
返回:
|
||
dict: {
|
||
'commodities': dict, # 各商品涨跌
|
||
'score': int, # 评分增减(-5 ~ +5)
|
||
'summary': str, # 白话总结
|
||
'reasons': list, # 评分原因
|
||
'affected_sectors': dict, # 对A股板块影响
|
||
}
|
||
"""
|
||
cached = _get_cache('commodity')
|
||
if cached:
|
||
return cached
|
||
|
||
try:
|
||
# 腾讯财经API获取商品期货
|
||
# hf_GC=纽约黄金, hf_CL=纽约原油, hf_HG=美铜
|
||
url = 'https://qt.gtimg.cn/q=hf_GC,hf_CL,hf_HG'
|
||
resp = requests.get(url, timeout=10)
|
||
text = resp.content.decode('gbk', errors='replace')
|
||
|
||
# 商品名称映射
|
||
symbol_map = {
|
||
'hf_GC': '黄金',
|
||
'hf_CL': '原油',
|
||
'hf_HG': '铜',
|
||
}
|
||
|
||
commodities = {}
|
||
for line in text.strip().split(';'):
|
||
line = line.strip()
|
||
if not line or 'v_pv_none_match' in line:
|
||
continue
|
||
if '=' not in line:
|
||
continue
|
||
var_name = line.split('=')[0].strip().replace('var ', '').replace('v_', '')
|
||
value = line.split('"')[1] if '"' in line else ''
|
||
fields = value.split(',')
|
||
if len(fields) < 10:
|
||
continue
|
||
|
||
target = symbol_map.get(var_name)
|
||
if not target:
|
||
continue
|
||
|
||
# 腾讯商品格式: price,change_pct,prev_close,open,high,low,time,...,name
|
||
current_price = float(fields[0]) if fields[0] else 0
|
||
change_pct = float(fields[1]) if fields[1] else 0
|
||
name = fields[-1].rstrip(';"')
|
||
|
||
commodities[target] = {
|
||
'price': current_price,
|
||
'change_pct': round(change_pct, 2),
|
||
'name': name,
|
||
}
|
||
|
||
if not commodities:
|
||
return _neutral_result('未找到关注的大宗商品')
|
||
|
||
# 评分和影响
|
||
score = 0
|
||
reasons = []
|
||
summary_parts = []
|
||
affected = {}
|
||
|
||
for name, data in commodities.items():
|
||
pct = data['change_pct']
|
||
if abs(pct) < 0.5:
|
||
continue
|
||
|
||
mapping = COMMODITY_A_SECTOR_MAP.get(name)
|
||
if not mapping:
|
||
continue
|
||
|
||
if pct > 2:
|
||
score += 1
|
||
reasons.append(f'{name}涨{pct:.1f}%,利好{"、".join(mapping["beneficiary"][:2])}(+1)')
|
||
summary_parts.append(f'{name}大涨{pct:.1f}%')
|
||
affected[name] = {
|
||
'direction': '利好',
|
||
'beneficiary': mapping['beneficiary'],
|
||
'victim': mapping['victim'],
|
||
'note': mapping['direction'],
|
||
}
|
||
elif pct < -2:
|
||
score -= 1
|
||
reasons.append(f'{name}跌{abs(pct):.1f}%,利空{"、".join(mapping["beneficiary"][:2])}(-1)')
|
||
summary_parts.append(f'{name}大跌{pct:.1f}%')
|
||
affected[name] = {
|
||
'direction': '利空',
|
||
'beneficiary': mapping['victim'],
|
||
'victim': mapping['beneficiary'],
|
||
'note': mapping['direction'],
|
||
}
|
||
|
||
score = max(-5, min(5, score))
|
||
|
||
result = {
|
||
'commodities': commodities,
|
||
'score': score,
|
||
'summary': ','.join(summary_parts) if summary_parts else '大宗商品整体平稳',
|
||
'reasons': reasons,
|
||
'affected_sectors': affected,
|
||
}
|
||
_set_cache('commodity', result)
|
||
return result
|
||
|
||
except Exception as e:
|
||
logger.warning(f"获取大宗商品数据失败: {e}")
|
||
return _neutral_result('大宗商品数据获取失败')
|
||
|
||
|
||
# ═══════════════════════════════════════════════
|
||
# P7: 汇率变化
|
||
# ═══════════════════════════════════════════════
|
||
|
||
# 汇率 → A股板块影响
|
||
FX_SECTOR_MAP = {
|
||
'升值': {
|
||
'beneficiary': ['航空', '造纸', '房地产'],
|
||
'victim': ['纺织', '家电出口', '电子代工'],
|
||
},
|
||
'贬值': {
|
||
'beneficiary': ['纺织', '家电', '电子代工'],
|
||
'victim': ['航空', '造纸'],
|
||
},
|
||
}
|
||
|
||
|
||
def get_fx_overview():
|
||
"""
|
||
获取人民币汇率变化
|
||
|
||
数据源:新浪财经API(hq.sinajs.cn)
|
||
获取在岸人民币兑美元实时汇率。
|
||
|
||
返回:
|
||
dict: {
|
||
'usd_cny': float, # 美元兑人民币汇率
|
||
'change_pct': float, # 涨跌幅
|
||
'direction': str, # 升值/贬值/稳定
|
||
'score': int, # 评分增减(-3 ~ +3)
|
||
'summary': str, # 白话总结
|
||
'reasons': list, # 评分原因
|
||
'affected_sectors': dict, # 对A股板块影响
|
||
}
|
||
"""
|
||
cached = _get_cache('fx')
|
||
if cached:
|
||
return cached
|
||
|
||
try:
|
||
# 新浪财经API获取在岸人民币汇率
|
||
# 格式: var hq_str_fx_susdcny="time,bid,ask,prev_close,...,name,change_pct,..."
|
||
url = 'https://hq.sinajs.cn/list=fx_susdcny'
|
||
resp = requests.get(url, timeout=10, headers={'Referer': 'https://finance.sina.com.cn'})
|
||
text = resp.content.decode('gbk', errors='replace')
|
||
|
||
# 解析汇率数据
|
||
if 'hq_str_fx_susdcny' not in text:
|
||
return _neutral_result('汇率数据解析失败')
|
||
|
||
value = text.split('"')[1] if '"' in text else ''
|
||
fields = value.split(',')
|
||
if len(fields) < 11:
|
||
return _neutral_result('汇率数据格式异常')
|
||
|
||
# 新浪汇率格式: time,bid,ask,prev_close,?,mid,?,?,?,name,change_pct,...
|
||
today_rate = float(fields[5]) if fields[5] else 0 # 中间价
|
||
prev_rate = float(fields[3]) if fields[3] else 0 # 昨收价
|
||
change_pct = float(fields[10]) if fields[10] else 0 # 涨跌幅
|
||
|
||
if today_rate == 0:
|
||
today_rate = float(fields[1]) if fields[1] else 0
|
||
if prev_rate == 0:
|
||
prev_rate = float(fields[3]) if fields[3] else 0
|
||
|
||
if today_rate == 0 or prev_rate == 0:
|
||
return _neutral_result('汇率数据异常')
|
||
|
||
# 如果涨跌幅为0,自行计算
|
||
if change_pct == 0:
|
||
change_pct = round((today_rate / prev_rate - 1) * 100, 3)
|
||
|
||
# 判断方向(美元兑人民币:涨=人民币贬值,跌=人民币升值)
|
||
if change_pct > 0.1:
|
||
direction = '贬值'
|
||
score = -2
|
||
summary = f'人民币贬值{abs(change_pct):.3f}%'
|
||
reasons = [f'人民币贬值{abs(change_pct):.3f}%(-2)']
|
||
affected = FX_SECTOR_MAP['贬值']
|
||
elif change_pct < -0.1:
|
||
direction = '升值'
|
||
score = 2
|
||
summary = f'人民币升值{abs(change_pct):.3f}%'
|
||
reasons = [f'人民币升值{abs(change_pct):.3f}%(+2)']
|
||
affected = FX_SECTOR_MAP['升值']
|
||
else:
|
||
direction = '稳定'
|
||
score = 0
|
||
summary = '人民币汇率基本稳定'
|
||
reasons = []
|
||
affected = {}
|
||
|
||
score = max(-3, min(3, score))
|
||
|
||
result = {
|
||
'usd_cny': round(today_rate, 4),
|
||
'change_pct': change_pct,
|
||
'direction': direction,
|
||
'score': score,
|
||
'summary': summary,
|
||
'reasons': reasons,
|
||
'affected_sectors': affected,
|
||
}
|
||
_set_cache('fx', result)
|
||
return result
|
||
|
||
except Exception as e:
|
||
logger.warning(f"获取汇率数据失败: {e}")
|
||
return _neutral_result('汇率数据获取失败')
|
||
|
||
|
||
# ═══════════════════════════════════════════════
|
||
# 综合外部因素
|
||
# ═══════════════════════════════════════════════
|
||
|
||
def get_all_external_factors():
|
||
"""
|
||
获取所有外部因素数据,返回综合结果
|
||
|
||
返回:
|
||
dict: 包含南向资金、美股、大宗商品、汇率的综合数据
|
||
"""
|
||
southbound = get_southbound_capital()
|
||
us_market = get_us_market_overview()
|
||
commodity = get_commodity_overview()
|
||
fx = get_fx_overview()
|
||
|
||
total_score = (
|
||
southbound.get('score', 0) +
|
||
us_market.get('score', 0) +
|
||
commodity.get('score', 0) +
|
||
fx.get('score', 0)
|
||
)
|
||
|
||
all_reasons = []
|
||
all_reasons.extend(southbound.get('reasons', []))
|
||
all_reasons.extend(us_market.get('reasons', []))
|
||
all_reasons.extend(commodity.get('reasons', []))
|
||
all_reasons.extend(fx.get('reasons', []))
|
||
|
||
summaries = []
|
||
for name, data in [('南向资金', southbound), ('美股', us_market), ('大宗商品', commodity), ('汇率', fx)]:
|
||
s = data.get('summary', '')
|
||
if s and '失败' not in s and '为空' not in s:
|
||
summaries.append(f'{name}:{s}')
|
||
|
||
return {
|
||
'southbound_capital': southbound,
|
||
'us_market': us_market,
|
||
'commodity': commodity,
|
||
'fx': fx,
|
||
'total_score': total_score,
|
||
'all_reasons': all_reasons,
|
||
'summary': ' | '.join(summaries),
|
||
}
|
||
|
||
|
||
def _neutral_result(reason):
|
||
"""返回中性结果"""
|
||
return {
|
||
'score': 0,
|
||
'summary': reason,
|
||
'reasons': [],
|
||
}
|