Files
aiagents-stock/longhubang_scoring.py
Eikwang 91d32c6ffa 增加更多的历史记录,修正部份API数据获取错误,增加备用API (#5)
* 增加更多的历史记录,修正部份数据获取错误

* 增加更多的历史记录,修正部份API数据获取错误,增加备用API

---------

Co-authored-by: bathfire <>
2025-10-29 16:22:18 +08:00

585 lines
20 KiB
Python
Raw Permalink 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.
"""
智瞰龙虎AI智能评分模块
对龙虎榜上榜股票进行综合评分排名
"""
import pandas as pd
from typing import Dict, List
from collections import Counter
class LonghubangScoring:
"""龙虎榜股票智能评分系统"""
def __init__(self):
"""初始化评分系统"""
# 顶级游资名单(根据市场知名度和历史战绩)
self.top_youzi = [
'赵老哥', '章盟主', '92科比', '瑞鹤仙', '小鳄鱼',
'养家心法', '欢乐海岸', '古北路', '成都系', '佛山系',
'方新侠', '乔帮主', '淮海路', '东方财富',
'国信深圳', '华泰深圳', '中信杭州', '招商深圳'
]
# 知名游资(次一级)
self.famous_youzi = [
'深股通', '沪股通', '北向资金',
'中金公司', '中信证券', '国泰君安', '海通证券',
'广发证券', '华泰证券', '招商证券'
]
# 机构关键词
self.institution_keywords = [
'机构专用', '机构', '基金', '保险', '社保',
'QFII', 'RQFII', '券商', '信托'
]
print("[智瞰龙虎] 评分系统初始化完成")
def calculate_stock_score(self, stock_data: List[Dict]) -> float:
"""
计算单个股票的综合评分
Args:
stock_data: 该股票的所有龙虎榜记录
Returns:
综合评分 (0-100分)
"""
if not stock_data:
return 0.0
# 1. 买入资金含金量评分 (0-30分)
capital_quality_score = self._calculate_capital_quality(stock_data)
# 2. 净买入额评分 (0-25分)
net_inflow_score = self._calculate_net_inflow_score(stock_data)
# 3. 卖出压力评分 (0-20分)
sell_pressure_score = self._calculate_sell_pressure_score(stock_data)
# 4. 机构共振评分 (0-15分)
institution_score = self._calculate_institution_score(stock_data)
# 5. 其他加分项 (0-10分)
bonus_score = self._calculate_bonus_score(stock_data)
# 综合评分
total_score = (
capital_quality_score +
net_inflow_score +
sell_pressure_score +
institution_score +
bonus_score
)
return round(total_score, 1)
def _calculate_capital_quality(self, stock_data: List[Dict]) -> float:
"""
计算买入资金含金量评分 (0-30分)
顶级游资加分多,普通游资加分少
"""
score = 0.0
max_score = 30.0
buyers = []
for record in stock_data:
buy_amount = record.get('买入金额', 0) or record.get('mrje', 0)
# 确保转换为数值类型
try:
buy_amount = float(buy_amount) if buy_amount else 0
except (ValueError, TypeError):
buy_amount = 0
if buy_amount > 0:
youzi_name = record.get('游资名称', '') or record.get('yzmc', '')
yingye_bu = record.get('营业部', '') or record.get('yyb', '')
buyers.append({
'name': youzi_name,
'yingye_bu': yingye_bu,
'amount': float(buy_amount)
})
if not buyers:
return 0.0
# 顶级游资:每个加8-10分
top_youzi_count = 0
for buyer in buyers:
for top in self.top_youzi:
if top in buyer['name'] or top in buyer['yingye_bu']:
top_youzi_count += 1
score += 10.0
break
# 知名游资:每个加4-6分
famous_youzi_count = 0
for buyer in buyers:
is_top = any(top in buyer['name'] or top in buyer['yingye_bu']
for top in self.top_youzi)
if not is_top:
for famous in self.famous_youzi:
if famous in buyer['name'] or famous in buyer['yingye_bu']:
famous_youzi_count += 1
score += 5.0
break
# 普通游资:每个加1-2分
ordinary_count = len(buyers) - top_youzi_count - famous_youzi_count
score += ordinary_count * 1.5
# 限制最高分
return min(score, max_score)
def _calculate_net_inflow_score(self, stock_data: List[Dict]) -> float:
"""
计算净买入额评分 (0-25分)
真金白银越多分数越高
"""
max_score = 25.0
# 计算总净流入
total_net_inflow = 0.0
for record in stock_data:
net_inflow = record.get('净流入金额', 0) or record.get('jlrje', 0)
try:
net_inflow = float(net_inflow) if net_inflow else 0
total_net_inflow += net_inflow
except (ValueError, TypeError):
pass
if total_net_inflow <= 0:
return 0.0
# 净流入分段评分
# 1000万以下:0-10分
# 1000-5000万:10-18分
# 5000万-1亿:18-22分
# 1亿以上:22-25分
net_inflow_wan = total_net_inflow / 10000 # 转换为万元
if net_inflow_wan < 1000:
score = (net_inflow_wan / 1000) * 10
elif net_inflow_wan < 5000:
score = 10 + ((net_inflow_wan - 1000) / 4000) * 8
elif net_inflow_wan < 10000:
score = 18 + ((net_inflow_wan - 5000) / 5000) * 4
else:
score = 22 + min((net_inflow_wan - 10000) / 10000, 1) * 3
return min(score, max_score)
def _calculate_sell_pressure_score(self, stock_data: List[Dict]) -> float:
"""
计算卖出压力评分 (0-20分)
卖出压力越小分数越高
"""
max_score = 20.0
total_buy = 0.0
total_sell = 0.0
for record in stock_data:
buy_amount = record.get('买入金额', 0) or record.get('mrje', 0)
sell_amount = record.get('卖出金额', 0) or record.get('mcje', 0)
try:
buy_amount = float(buy_amount) if buy_amount else 0
total_buy += buy_amount
except (ValueError, TypeError):
pass
try:
sell_amount = float(sell_amount) if sell_amount else 0
total_sell += sell_amount
except (ValueError, TypeError):
pass
if total_buy == 0:
return 0.0
# 计算卖出比例
sell_ratio = total_sell / total_buy if total_buy > 0 else 1.0
# 卖出压力评分
# 卖出比例0-10%20分
# 卖出比例10-30%15-20分
# 卖出比例30-50%10-15分
# 卖出比例50-80%5-10分
# 卖出比例80%以上:0-5分
if sell_ratio < 0.1:
score = 20.0
elif sell_ratio < 0.3:
score = 20.0 - (sell_ratio - 0.1) / 0.2 * 5
elif sell_ratio < 0.5:
score = 15.0 - (sell_ratio - 0.3) / 0.2 * 5
elif sell_ratio < 0.8:
score = 10.0 - (sell_ratio - 0.5) / 0.3 * 5
else:
score = 5.0 - min(sell_ratio - 0.8, 0.2) / 0.2 * 5
return max(0, min(score, max_score))
def _calculate_institution_score(self, stock_data: List[Dict]) -> float:
"""
计算机构共振评分 (0-15分)
机构+游资共振最高分
"""
max_score = 15.0
has_institution = False
has_youzi = False
institution_count = 0
youzi_count = 0
for record in stock_data:
buy_amount = record.get('买入金额', 0) or record.get('mrje', 0)
try:
buy_amount = float(buy_amount) if buy_amount else 0
except (ValueError, TypeError):
buy_amount = 0
if buy_amount <= 0:
continue
youzi_name = record.get('游资名称', '') or record.get('yzmc', '')
yingye_bu = record.get('营业部', '') or record.get('yyb', '')
# 检查是否是机构
if any(keyword in youzi_name or keyword in yingye_bu
for keyword in self.institution_keywords):
has_institution = True
institution_count += 1
else:
has_youzi = True
youzi_count += 1
# 评分逻辑
if has_institution and has_youzi:
# 机构+游资共振:最高分
score = 15.0
elif has_institution:
# 仅机构:8-12分
score = min(8 + institution_count * 2, 12)
elif has_youzi:
# 仅游资:5-10分
score = min(5 + youzi_count * 1, 10)
else:
score = 0.0
return min(score, max_score)
def _calculate_bonus_score(self, stock_data: List[Dict]) -> float:
"""
计算其他加分项 (0-10分)
买卖比例、主力集中度、热门概念等
"""
max_score = 10.0
score = 0.0
if not stock_data:
return 0.0
# 1. 主力集中度加分 (0-3分)
# 如果资金集中在少数几个席位,说明主力信心强
seat_count = len(stock_data)
if seat_count == 1:
score += 3.0
elif seat_count == 2:
score += 2.5
elif seat_count == 3:
score += 2.0
elif seat_count <= 5:
score += 1.5
else:
score += 1.0
# 2. 热门概念加分 (0-3分)
all_concepts = []
for record in stock_data:
concepts = record.get('概念', '') or record.get('gl', '')
if concepts:
all_concepts.extend([c.strip() for c in str(concepts).split(',')])
hot_keywords = [
'人工智能', 'AI', 'ChatGPT', '算力', '新能源', '芯片', '半导体',
'军工', '医药', '消费', '5G', '新材料', '量子', '光伏',
'储能', '锂电池', '汽车', '游戏', '传媒', '元宇宙'
]
concept_score = 0
for concept in all_concepts:
if any(keyword in concept for keyword in hot_keywords):
concept_score += 0.3
score += min(concept_score, 3.0)
# 3. 连续上榜加分 (0-2分)
# 这里简化处理,如果有多条记录可能表示连续上榜
if len(stock_data) >= 3:
score += 2.0
elif len(stock_data) == 2:
score += 1.0
# 4. 买卖比例优秀加分 (0-2分)
total_buy = 0.0
total_sell = 0.0
for r in stock_data:
try:
buy = float(r.get('买入金额', 0) or r.get('mrje', 0) or 0)
total_buy += buy
except (ValueError, TypeError):
pass
try:
sell = float(r.get('卖出金额', 0) or r.get('mcje', 0) or 0)
total_sell += sell
except (ValueError, TypeError):
pass
if total_buy > 0:
buy_sell_ratio = total_buy / (total_sell + 1)
if buy_sell_ratio >= 10:
score += 2.0
elif buy_sell_ratio >= 5:
score += 1.5
elif buy_sell_ratio >= 3:
score += 1.0
return min(score, max_score)
def score_all_stocks(self, data_list: List[Dict]) -> pd.DataFrame:
"""
对所有上榜股票进行评分排名
Args:
data_list: 龙虎榜数据列表
Returns:
评分排名DataFrame
"""
if not data_list:
return pd.DataFrame()
# 按股票代码分组
stocks_dict = {}
for record in data_list:
code = record.get('股票代码') or record.get('gpdm')
name = record.get('股票名称') or record.get('gpmc')
if not code:
continue
if code not in stocks_dict:
stocks_dict[code] = {
'code': code,
'name': name,
'records': []
}
stocks_dict[code]['records'].append(record)
# 计算每只股票的评分
results = []
for code, stock_info in stocks_dict.items():
records = stock_info['records']
# 计算各维度评分
capital_quality = self._calculate_capital_quality(records)
net_inflow = self._calculate_net_inflow_score(records)
sell_pressure = self._calculate_sell_pressure_score(records)
institution = self._calculate_institution_score(records)
bonus = self._calculate_bonus_score(records)
total_score = capital_quality + net_inflow + sell_pressure + institution + bonus
# 计算实际数据(安全转换)
total_buy = 0.0
total_sell = 0.0
total_net = 0.0
for r in records:
try:
buy = float(r.get('买入金额', 0) or r.get('mrje', 0) or 0)
total_buy += buy
except (ValueError, TypeError):
pass
try:
sell = float(r.get('卖出金额', 0) or r.get('mcje', 0) or 0)
total_sell += sell
except (ValueError, TypeError):
pass
try:
net = float(r.get('净流入金额', 0) or r.get('jlrje', 0) or 0)
total_net += net
except (ValueError, TypeError):
pass
# 统计买入席位数(安全比较)
buy_seats = 0
for r in records:
try:
buy = float(r.get('买入金额', 0) or r.get('mrje', 0) or 0)
if buy > 0:
buy_seats += 1
except (ValueError, TypeError):
pass
# 统计机构数量
institution_count = sum(1 for r in records
if any(kw in (r.get('游资名称', '') or r.get('yzmc', '')) or
kw in (r.get('营业部', '') or r.get('yyb', ''))
for kw in self.institution_keywords))
# 获取概念
concepts_list = []
for r in records:
concepts = r.get('概念', '') or r.get('gl', '')
if concepts:
concepts_list.extend([c.strip() for c in str(concepts).split(',')])
top_concepts = Counter(concepts_list).most_common(3)
concept_str = ','.join([c[0] for c in top_concepts]) if top_concepts else ''
# 判断机构参与
has_institution = institution_count > 0
results.append({
'排名': 0, # 稍后填充
'排名_display': '', # 用于显示奖牌
'股票名称': stock_info['name'],
'股票代码': code,
'综合评分': round(total_score, 1),
'资金含金量': round(capital_quality, 0),
'净买入额': round(net_inflow, 0),
'卖出压力': round(sell_pressure, 0),
'机构共振': round(institution, 0),
'加分项': round(bonus, 0),
'顶级游资': self._count_top_youzi(records),
'买方数': buy_seats,
'机构参与': '✅' if has_institution else '❌',
'净流入': round(total_net, 2)
})
# 转换为DataFrame并排序
df = pd.DataFrame(results)
if df.empty:
return df
df = df.sort_values('综合评分', ascending=False).reset_index(drop=True)
df['排名'] = range(1, len(df) + 1)
# 添加奖牌显示
df['排名_display'] = df['排名'].astype(str)
if len(df) >= 1:
df.loc[0, '排名_display'] = '🥇 1'
if len(df) >= 2:
df.loc[1, '排名_display'] = '🥈 2'
if len(df) >= 3:
df.loc[2, '排名_display'] = '🥉 3'
return df
def _count_top_youzi(self, records: List[Dict]) -> int:
"""统计顶级游资数量"""
count = 0
for record in records:
buy_amount = record.get('买入金额', 0) or record.get('mrje', 0)
try:
buy_amount = float(buy_amount) if buy_amount else 0
except (ValueError, TypeError):
buy_amount = 0
if buy_amount <= 0:
continue
youzi_name = record.get('游资名称', '') or record.get('yzmc', '')
yingye_bu = record.get('营业部', '') or record.get('yyb', '')
if any(top in youzi_name or top in yingye_bu for top in self.top_youzi):
count += 1
return count
def get_score_explanation(self) -> str:
"""获取评分维度说明"""
explanation = """
【AI智能评分维度说明】
📊 总分100分,由5个维度组成:
1️⃣ 买入资金含金量 (0-30分)
- 顶级游资(赵老哥、章盟主等):每个+10分
- 知名游资(深股通、中信等):每个+5分
- 普通游资:每个+1.5分
2️⃣ 净买入额评分 (0-25分)
- 净流入1000万以下:0-10分
- 净流入1000-5000万:10-18分
- 净流入5000万-1亿:18-22分
- 净流入1亿以上:22-25分
3️⃣ 卖出压力评分 (0-20分)
- 卖出比例0-10%:20分(压力极小)
- 卖出比例10-30%15-20分(压力较小)
- 卖出比例30-50%10-15分(压力中等)
- 卖出比例50-80%:5-10分(压力较大)
- 卖出比例80%以上:0-5分(压力极大)
4️⃣ 机构共振评分 (0-15分)
- 机构+游资共振:15分(最强信号)
- 仅机构买入:8-12分
- 仅游资买入:5-10分
5️⃣ 其他加分项 (0-10分)
- 主力集中度:席位越少越集中,+1-3分
- 热门概念:AI、新能源、芯片等,+0-3分
- 连续上榜:连续多日上榜,+0-2分
- 买卖比例优秀:买入远大于卖出,+0-2分
💡 评分越高,表示该股票受到资金青睐程度越高,
但仍需结合市场环境、技术面等因素综合判断!
"""
return explanation
# 测试函数
if __name__ == "__main__":
print("=" * 60)
print("测试智瞰龙虎评分系统")
print("=" * 60)
# 创建测试数据
test_data = [
{
'股票代码': '001337',
'股票名称': '四川黄金',
'游资名称': '92科比',
'营业部': '兴业证券股份有限公司南京天元东路证券营业部',
'买入金额': 14470401,
'卖出金额': 15080,
'净流入金额': 14455321,
'概念': '贵金属,黄金概念,次新股'
},
{
'股票代码': '001337',
'股票名称': '四川黄金',
'游资名称': '赵老哥',
'营业部': '某证券公司',
'买入金额': 10000000,
'卖出金额': 0,
'净流入金额': 10000000,
'概念': '贵金属,黄金概念'
}
]
scoring = LonghubangScoring()
# 测试评分
df_result = scoring.score_all_stocks(test_data)
print("\n评分结果:")
print(df_result)
print("\n" + scoring.get_score_explanation())