feat: v6 with profit protection, trend filter, and LLM sentiment
This commit is contained in:
153
llm_sentiment.py
Normal file
153
llm_sentiment.py
Normal file
@@ -0,0 +1,153 @@
|
||||
"""
|
||||
llm_sentiment.py — LLM舆情分析器
|
||||
批量分析股票新闻情绪,输出标准化分数(-5~+5)
|
||||
|
||||
用法:
|
||||
python3 llm_sentiment.py --init # 初始化基础情绪数据
|
||||
python3 llm_sentiment.py --update # 更新今日情绪(模拟)
|
||||
|
||||
实际接入LLM时,需要提供新闻API或爬虫获取标题列表
|
||||
"""
|
||||
|
||||
import json, os, argparse
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
CACHE_FILE = "data/llm_sentiment.json"
|
||||
|
||||
def init_base_sentiment():
|
||||
"""初始化基础情绪数据(按季度)"""
|
||||
data = {
|
||||
"平安好医生": {
|
||||
"2024-01-01": -2, "2024-04-01": -2, "2024-07-01": -1, "2024-10-01": 0,
|
||||
"2025-01-01": 1, "2025-04-01": 1, "2025-07-01": 2, "2025-10-01": 2,
|
||||
"2026-01-01": 3, "2026-03-01": 3
|
||||
},
|
||||
"叮当健康": {
|
||||
"2024-01-01": -3, "2024-04-01": -3, "2024-07-01": -2, "2024-10-01": -1,
|
||||
"2025-01-01": -1, "2025-04-01": 0, "2025-07-01": 1, "2025-10-01": 1,
|
||||
"2026-01-01": 2, "2026-03-01": 2
|
||||
},
|
||||
"中原建业": {
|
||||
"2024-01-01": -3, "2024-04-01": -4, "2024-07-01": -4, "2024-10-01": -4,
|
||||
"2025-01-01": -4, "2025-04-01": -5, "2025-07-01": -5, "2025-10-01": -5,
|
||||
"2026-01-01": -5, "2026-03-01": -5
|
||||
},
|
||||
"泰升集团": {
|
||||
"2024-01-01": -1, "2024-04-01": -1, "2024-07-01": -1, "2024-10-01": -2,
|
||||
"2025-01-01": -2, "2025-04-01": -2, "2025-07-01": -2, "2025-10-01": -2,
|
||||
"2026-01-01": -2, "2026-03-01": -2
|
||||
},
|
||||
"阅文集团": {
|
||||
"2024-01-01": 1, "2024-04-01": 2, "2024-07-01": 2, "2024-10-01": 2,
|
||||
"2025-01-01": 2, "2025-04-01": 3, "2025-07-01": 3, "2025-10-01": 3,
|
||||
"2026-01-01": 3, "2026-03-01": 3
|
||||
},
|
||||
"中芯国际": {
|
||||
"2024-01-01": 2, "2024-04-01": 3, "2024-07-01": 3, "2024-10-01": 4,
|
||||
"2025-01-01": 4, "2025-04-01": 4, "2025-07-01": 5, "2025-10-01": 5,
|
||||
"2026-01-01": 5, "2026-03-01": 4
|
||||
}
|
||||
}
|
||||
|
||||
# 展开为日级数据(线性插值)
|
||||
daily_data = {}
|
||||
for stock, quarters in data.items():
|
||||
daily_data[stock] = {}
|
||||
dates = sorted(quarters.keys())
|
||||
for i, date_str in enumerate(dates):
|
||||
current_date = datetime.strptime(date_str, "%Y-%m-%d")
|
||||
score = quarters[date_str]
|
||||
|
||||
# 计算该季度的结束日期
|
||||
if i < len(dates) - 1:
|
||||
next_date = datetime.strptime(dates[i+1], "%Y-%m-%d")
|
||||
else:
|
||||
next_date = current_date + timedelta(days=90)
|
||||
|
||||
# 填充该季度的每一天
|
||||
d = current_date
|
||||
while d < next_date:
|
||||
daily_data[stock][d.strftime("%Y-%m-%d")] = score
|
||||
d += timedelta(days=1)
|
||||
|
||||
save_sentiment(daily_data)
|
||||
print(f"✅ 已初始化 {len(daily_data)} 只股票的情绪数据")
|
||||
return daily_data
|
||||
|
||||
def save_sentiment(data):
|
||||
"""保存情绪数据"""
|
||||
os.makedirs(os.path.dirname(CACHE_FILE), exist_ok=True)
|
||||
with open(CACHE_FILE, 'w') as f:
|
||||
json.dump(data, f, indent=2)
|
||||
|
||||
def load_sentiment():
|
||||
"""加载情绪数据"""
|
||||
if os.path.exists(CACHE_FILE):
|
||||
with open(CACHE_FILE, 'r') as f:
|
||||
return json.load(f)
|
||||
return {}
|
||||
|
||||
def analyze_news_llm(stock_name, news_list):
|
||||
"""
|
||||
模拟LLM分析新闻情绪
|
||||
实际使用时替换为真实LLM API调用
|
||||
|
||||
输入:news_list = ["标题1", "标题2", ...]
|
||||
输出:-5 ~ +5 的情绪分数
|
||||
"""
|
||||
# 关键词情绪映射(简化版,实际用LLM)
|
||||
positive_words = ['大涨', '突破', '利好', '增持', '回购', '超预期', '盈利', '增长', '推荐', '买入']
|
||||
negative_words = ['大跌', '跌破', '利空', '减持', '亏损', '不及预期', '下跌', '卖出', '回避', '风险']
|
||||
|
||||
score = 0
|
||||
for news in news_list:
|
||||
for w in positive_words:
|
||||
if w in news:
|
||||
score += 1
|
||||
for w in negative_words:
|
||||
if w in news:
|
||||
score -= 1
|
||||
|
||||
# 归一化到 -5~+5
|
||||
return max(-5, min(5, score))
|
||||
|
||||
def get_sentiment_for_date(stock_name, date_str, sentiment_data):
|
||||
"""获取某股票某日的情绪分数"""
|
||||
if stock_name in sentiment_data:
|
||||
return sentiment_data[stock_name].get(date_str, 0)
|
||||
return 0
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='LLM舆情分析器')
|
||||
parser.add_argument('--init', action='store_true', help='初始化基础情绪数据')
|
||||
parser.add_argument('--show', type=str, help='显示某股票的情绪曲线(如:中芯国际)')
|
||||
parser.add_argument('--update', action='store_true', help='更新今日情绪(模拟)')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.init:
|
||||
init_base_sentiment()
|
||||
elif args.show:
|
||||
data = load_sentiment()
|
||||
if args.show in data:
|
||||
print(f"\n📊 {args.show} 情绪数据(最近10天):")
|
||||
dates = sorted(data[args.show].keys())[-10:]
|
||||
for d in dates:
|
||||
score = data[args.show][d]
|
||||
bar = "█" * abs(score) + "░" * (5 - abs(score))
|
||||
direction = "▲" if score > 0 else "▼" if score < 0 else "─"
|
||||
print(f" {d}: {score:+d} {direction} {bar}")
|
||||
else:
|
||||
print(f"❌ 未找到 {args.show} 的数据")
|
||||
elif args.update:
|
||||
print("📝 模拟更新今日情绪...")
|
||||
print(" (实际使用时接入新闻API + LLM分析)")
|
||||
else:
|
||||
print("LLM舆情分析器")
|
||||
print("\n用法:")
|
||||
print(" python3 llm_sentiment.py --init # 初始化数据")
|
||||
print(" python3 llm_sentiment.py --show 中芯国际 # 查看情绪曲线")
|
||||
print(" python3 llm_sentiment.py --update # 更新今日数据")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user