Import 9 alphaear finance skills

- alphaear-deepear-lite: DeepEar Lite API integration
- alphaear-logic-visualizer: Draw.io XML finance diagrams
- alphaear-news: Real-time finance news (10+ sources)
- alphaear-predictor: Kronos time-series forecasting
- alphaear-reporter: Professional financial reports
- alphaear-search: Web search + local RAG
- alphaear-sentiment: FinBERT/LLM sentiment analysis
- alphaear-signal-tracker: Signal evolution tracking
- alphaear-stock: A-Share/HK/US stock data

Updates:
- All scripts updated to use universal .env path
- Added JINA_API_KEY, LLM_*, DEEPSEEK_API_KEY to .env.example
- Updated load_dotenv() to use ~/.config/opencode/.env
This commit is contained in:
Kunthawat Greethong
2026-03-27 10:11:37 +07:00
parent 7edf5bc4d0
commit 58f9380ec4
149 changed files with 26867 additions and 0 deletions

View File

@@ -0,0 +1,381 @@
"""
ISQ (Investment Signal Quality) 评估框架 Template
统一定义 ISQ 的各个维度、评分标准、和使用方法。
支持默认 template 和自定义 template。
"""
from typing import Dict, List, Any, Optional
from pydantic import BaseModel, Field
from enum import Enum
from pathlib import Path
import json
class ISQDimension(str, Enum):
"""ISQ 评估维度"""
SENTIMENT = "sentiment" # 情绪/走势方向
CONFIDENCE = "confidence" # 确定性/可信度
INTENSITY = "intensity" # 强度/影响量级
EXPECTATION_GAP = "expectation_gap" # 预期差/市场认知差
TIMELINESS = "timeliness" # 时效性/窗口紧迫度
TRANSMISSION = "transmission" # 逻辑传导清晰度
class ISQDimensionSpec(BaseModel):
"""ISQ 单个维度的定义规范"""
name: str = Field(..., description="维度名称")
key: str = Field(..., description="维度键名")
description: str = Field(..., description="维度描述")
range_type: str = Field(default="0-1", description="取值范围 (0-1 或 1-5 等)")
scale_factor: float = Field(default=1.0, description="显示时的缩放因子")
examples: Dict[str, str] = Field(default_factory=dict, description="不同分值的示例解释")
visualization_color: Optional[str] = Field(default=None, description="可视化颜色")
class ISQTemplate(BaseModel):
"""ISQ 评估框架 Template"""
template_id: str = Field(..., description="模板 ID")
template_name: str = Field(..., description="模板名称")
description: str = Field(..., description="模板描述")
# 核心维度定义
dimensions: Dict[str, ISQDimensionSpec] = Field(..., description="维度定义字典")
# 评分指导
scoring_guide: str = Field(..., description="评分指导说明")
# 应用场景
applicable_scenarios: List[str] = Field(default_factory=list, description="适用场景")
# 聚合算法
aggregation_method: str = Field(default="weighted_average", description="聚合方法 (weighted_average, product 等)")
dimension_weights: Dict[str, float] = Field(default_factory=dict, description="维度权重")
class ISQScore(BaseModel):
"""单个信号的 ISQ 评分结果"""
signal_id: str = Field(..., description="信号 ID")
template_id: str = Field(..., description="使用的模板 ID")
# 各维度评分
scores: Dict[str, float] = Field(..., description="各维度评分")
# 总分
overall_score: float = Field(..., description="综合评分")
# 评分理由
rationale: Dict[str, str] = Field(default_factory=dict, description="各维度评分理由")
# 时间戳
timestamp: str = Field(..., description="评分时间")
# =====================================================
# 默认 Template
# =====================================================
DEFAULT_ISQ_TEMPLATE = ISQTemplate(
template_id="default_isq_v1",
template_name="标准投资信号质量评估框架 (ISQ v1.0)",
description="AlphaEar 默认的 ISQ 评估框架,用于标准化评估投资信号的质量维度",
dimensions={
"sentiment": ISQDimensionSpec(
name="情绪/走势",
key="sentiment",
description="基础情绪偏向和市场走势判断",
range_type="-1.0 到 1.0",
scale_factor=1.0,
examples={
"-1.0": "极度悲观/极度看空",
"-0.5": "明显看空",
"0.0": "中性/没有明确方向",
"0.5": "明显看多",
"1.0": "极度乐观/极度看多"
},
visualization_color="#ef4444" # 红色表示负面,绿色表示正面
),
"confidence": ISQDimensionSpec(
name="确定性",
key="confidence",
description="信号的可信度和确定性程度",
range_type="0.0 到 1.0",
scale_factor=1.0,
examples={
"0.0-0.3": "信息来源不可靠/传言多/逻辑推导牵强",
"0.3-0.6": "信息相对可靠/有一定逻辑/但仍有不确定性",
"0.6-0.8": "信息来源权威/逻辑清晰/高度可信",
"0.8-1.0": "官方确认/数据明确/完全确定"
},
visualization_color="#3b82f6" # 蓝色
),
"intensity": ISQDimensionSpec(
name="强度/影响量级",
key="intensity",
description="信号对相关板块/个股的潜在影响程度",
range_type="1 到 5",
scale_factor=20.0, # 用于雷达图缩放 (5 -> 100)
examples={
"1": "影响微弱,可能被市场忽略",
"2": "小幅影响,短期可能有波动",
"3": "中等影响,值得重点关注",
"4": "强烈影响,可能成为市场焦点",
"5": "极强影响,市场预期明显变化"
},
visualization_color="#f97316" # 橙色
),
"expectation_gap": ISQDimensionSpec(
name="预期差",
key="expectation_gap",
description="市场预期与现实之间的差距",
range_type="0.0 到 1.0",
scale_factor=1.0,
examples={
"0.0-0.2": "市场充分认知,预期差小",
"0.2-0.5": "市场部分认知,存在一定预期差",
"0.5-0.8": "市场认知不足,预期差较大,存在博弈空间",
"0.8-1.0": "市场严重低估/高估,巨大预期差"
},
visualization_color="#22c55e" # 绿色
),
"timeliness": ISQDimensionSpec(
name="时效性",
key="timeliness",
description="信号的时间窗口紧迫度",
range_type="0.0 到 1.0",
scale_factor=1.0,
examples={
"0.0-0.2": "长期信号,反应窗口 > 3 月",
"0.2-0.5": "中期信号,反应窗口 1-3 月",
"0.5-0.8": "短期信号,反应窗口 1 周 - 1 月",
"0.8-1.0": "超短期信号,反应窗口 < 1 周(需立即行动)"
},
visualization_color="#a855f7" # 紫色
),
},
scoring_guide="""
### ISQ 评分指导 (Investment Signal Quality)
ISQ 框架用于多维度评估投资信号的质量。每个信号由 5 个维度组成:
1. **情绪 (Sentiment)**: -1.0 到 1.0,表示看空(-)/中性(0)/看多(+)
2. **确定性 (Confidence)**: 0.0 到 1.0,数值越高越确定
3. **强度 (Intensity)**: 1 到 5数值越高影响越大
4. **预期差 (Expectation Gap)**: 0.0 到 1.0,市场预期与现实的差距
5. **时效性 (Timeliness)**: 0.0 到 1.0,反应窗口的紧迫程度
### 综合评分算法
综合评分 = 确定性 × 0.35 + 强度/5 × 0.30 + 预期差 × 0.20 + 时效性 × 0.15
范围: 0.0 到 1.0
- 0.0-0.3: 信号质量较差,不建议跟进
- 0.3-0.6: 信号质量一般,可作参考
- 0.6-0.8: 信号质量良好,值得跟进
- 0.8-1.0: 信号质量优异,强烈推荐
### 评分时的注意事项
- **不要混淆方向和强度**:情绪可以是看空,但确定性和强度仍可能很高
- **预期差往往是 Alpha 来源**:高预期差 + 高确定性 = 最佳博弈机会
- **考虑时间成本**:长期信号需要更高的确定性才值得跟进
- **数据为王**:所有评分必须有具体数据支撑
""",
applicable_scenarios=[
"上市公司基本面变化分析",
"产业政策与监管事件评估",
"地缘政治与宏观经济影响",
"技术进步与产业升级",
"突发事件与应急响应"
],
aggregation_method="weighted_average",
dimension_weights={
"confidence": 0.35,
"intensity": 0.30,
"expectation_gap": 0.20,
"timeliness": 0.15
}
)
# =====================================================
# ISQ Template 管理系统
# =====================================================
class ISQTemplateManager:
"""ISQ Template 管理器"""
def __init__(self):
self.templates: Dict[str, ISQTemplate] = {
DEFAULT_ISQ_TEMPLATE.template_id: DEFAULT_ISQ_TEMPLATE
}
def register_template(self, template: ISQTemplate) -> None:
"""注册新的 template"""
self.templates[template.template_id] = template
def register_template_dict(self, template_dict: Dict[str, Any]) -> ISQTemplate:
"""从 dict 注册模板,返回实例。"""
tpl = ISQTemplate(**template_dict)
self.register_template(tpl)
return tpl
def get_template(self, template_id: str) -> ISQTemplate:
"""获取指定 template"""
if template_id not in self.templates:
return DEFAULT_ISQ_TEMPLATE
return self.templates[template_id]
def list_templates(self) -> List[Dict[str, str]]:
"""列出所有可用 template"""
return [
{
"id": t.template_id,
"name": t.template_name,
"description": t.description,
"dimensions": list(t.dimensions.keys())
}
for t in self.templates.values()
]
def get_dimension(self, template_id: str, dimension_key: str) -> ISQDimensionSpec:
"""获取指定 template 的某个维度定义"""
template = self.get_template(template_id)
return template.dimensions.get(dimension_key)
def get_scoring_prompt(self, template_id: str) -> str:
"""获取用于 LLM 的评分 prompt"""
template = self.get_template(template_id)
dimensions_desc = "\n".join([
f"- **{d.name} ({d.key})**\n"
f" 范围: {d.range_type}\n"
f" 说明: {d.description}\n"
f" 示例: {', '.join(f'{k}={v}' for k, v in list(d.examples.items())[:3])}"
for d in template.dimensions.values()
])
return f"""
### ISQ 评估指导 ({template.template_name})
使用以下 {len(template.dimensions)} 个维度评估信号质量:
{dimensions_desc}
### 评分标准
{template.scoring_guide}
### 输出格式 (JSON)
请输出以下 JSON 格式的评分结果:
{{
"sentiment": <float>,
"confidence": <float>,
"intensity": <int>,
"expectation_gap": <float>,
"timeliness": <float>,
"rationale": {{
"sentiment": "评分理由",
"confidence": "评分理由",
"intensity": "评分理由",
"expectation_gap": "评分理由",
"timeliness": "评分理由"
}}
}}
"""
# 全局 template 管理器实例
isq_template_manager = ISQTemplateManager()
# =====================================================
# 配置加载
# =====================================================
def load_templates_from_config(config_path: Optional[str] = None) -> None:
"""从配置目录加载所有 JSON 模板文件,未找到则跳过,不影响默认模板。
支持单个 JSON 文件或目录(目录下的所有 .json 文件)。
"""
if config_path:
path = Path(config_path)
else:
# 默认目录config/isq_templates/
# __file__ = src/schema/isq_template.py
# parent = src/schema, parent.parent = src, parent.parent.parent = 项目根目录
path = Path(__file__).resolve().parent.parent.parent / "config"
if not path.exists():
return
# 如果是目录,扫描所有 .json 文件
if path.is_dir():
json_files = list(path.glob("*.json"))
else:
json_files = [path]
for json_file in json_files:
try:
data = json.loads(json_file.read_text(encoding="utf-8"))
# 如果是单个模板对象,转为列表
if isinstance(data, dict):
templates = [data]
elif isinstance(data, list):
templates = data
else:
continue
# 注册所有模板
for tpl_dict in templates:
if not isinstance(tpl_dict, dict):
continue
try:
isq_template_manager.register_template_dict(tpl_dict)
except Exception:
# 忽略单个模板的加载错误,继续其他模板
continue
except Exception:
# JSON 解析失败,跳过该文件
continue
# 在模块加载时自动尝试加载配置模板
load_templates_from_config()
# =====================================================
# 便利函数
# =====================================================
def get_isq_template(template_id: str = "default_isq_v1") -> ISQTemplate:
"""获取 ISQ template"""
return isq_template_manager.get_template(template_id)
def get_isq_scoring_prompt(template_id: str = "default_isq_v1") -> str:
"""获取用于 LLM 的 ISQ 评分 prompt"""
return isq_template_manager.get_scoring_prompt(template_id)
def calculate_isq_overall_score(scores: Dict[str, float], template_id: str = "default_isq_v1") -> float:
"""计算 ISQ 综合评分"""
template = get_isq_template(template_id)
overall = 0.0
for dim_key, weight in template.dimension_weights.items():
if dim_key in scores:
score = scores[dim_key]
# 处理强度维度的特殊缩放 (1-5 -> 0-1)
if dim_key == "intensity":
score = score / 5.0
overall += score * weight
return min(1.0, max(0.0, overall)) # 限制在 0-1 之间

View File

@@ -0,0 +1,100 @@
from pydantic import BaseModel, Field
from typing import List, Optional, Dict, Any
from datetime import datetime
class TransmissionNode(BaseModel):
node_name: str = Field(..., description="产业链节点名称")
impact_type: str = Field(..., description="利好/利空/中性")
logic: str = Field(..., description="该节点的传导逻辑")
class IntentAnalysis(BaseModel):
keywords: List[str] = Field(..., description="核心实体、事件或概念关键词")
search_queries: List[str] = Field(..., description="优化后的搜索引擎查询词")
is_specific_event: bool = Field(..., description="是否查询特定突发事件")
time_range: str = Field(..., description="时间范围 (recent/all/specific_date)")
intent_summary: str = Field(..., description="一句话意图描述")
class FilterResult(BaseModel):
"""LLM 筛选结果 - 快速判断是否有有效信号"""
has_valid_signals: bool = Field(..., description="列表中是否包含有效的金融信号")
selected_ids: List[int] = Field(default_factory=list, description="筛选出的有效信号 ID 列表")
themes: List[str] = Field(default_factory=list, description="信号涉及的主题")
reason: Optional[str] = Field(default=None, description="如果无有效信号,说明原因")
class InvestmentSignal(BaseModel):
# 核心元数据
signal_id: str = Field(default="unknown_sig", description="唯一信号 ID")
title: str = Field(..., description="信号标题")
summary: str = Field(default="暂无摘要分析", description="100 字核心观点快报")
reasoning: str = Field(default="", description="详细的推演逻辑和理由")
# 逻辑传导 (ISQ Key 1)
transmission_chain: List[TransmissionNode] = Field(default_factory=list, description="产业链传导逻辑链条")
# 信号质量 (ISQ Key 2) - 来自 isq_template.DEFAULT_ISQ_TEMPLATE
# 参考: src/schema/isq_template.py 的 DEFAULT_ISQ_TEMPLATE 定义
sentiment_score: float = Field(default=0.0, description="[ISQ] 情绪/走势 (-1.0=极度看空 ~ 0.0=中性 ~ 1.0=极度看多)")
confidence: float = Field(default=0.5, description="[ISQ] 确定性 (0.0=不可信 ~ 1.0=完全确定)")
intensity: int = Field(default=3, description="[ISQ] 强度/影响量级 (1=微弱 ~ 5=极强)")
expectation_gap: float = Field(default=0.5, description="[ISQ] 预期差/博弈空间 (0.0=充分定价 ~ 1.0=巨大预期差)")
timeliness: float = Field(default=0.8, description="[ISQ] 时效性 (0.0=长期 ~ 1.0=超短期)")
# 预测与博弈 (ISQ Key 3)
expected_horizon: str = Field(default="T+N", description="预期的反应时窗 (如: T+0, T+3, Long-term)")
price_in_status: str = Field(default="未知", description="市场预期消化程度 (未定价/部分定价/充分定价)")
# 关联实体
impact_tickers: List[Dict[str, Any]] = Field(default_factory=list, description="受影响的代码列表及其权重")
industry_tags: List[str] = Field(default_factory=list, description="关联行业标签")
# 溯源
sources: List[Dict[str, str]] = Field(default_factory=list, description="来源详情 (包含 title, url, source_name)")
class ResearchContext(BaseModel):
"""研究员搜集的背景信息结构"""
raw_signal: str = Field(..., description="原始信号内容")
tickers_found: List[Dict[str, Any]] = Field(default_factory=list, description="找到的相关标的及其基本面/股价信息")
industry_background: str = Field(..., description="行业背景及产业链现状")
latest_developments: List[str] = Field(default_factory=list, description="相关事件的最新进展")
key_risks: List[str] = Field(default_factory=list, description="潜在风险点")
search_results_summary: str = Field(..., description="搜索结果的综合摘要")
class ScanContext(BaseModel):
"""扫描员搜集的原始数据结构"""
hot_topics: List[str] = Field(..., description="当前市场热点话题")
news_summaries: List[Dict[str, Any]] = Field(..., description="关键新闻摘要列表")
market_data: Dict[str, Any] = Field(default_factory=dict, description="相关的市场行情数据")
sentiment_overview: str = Field(..., description="整体市场情绪概览")
raw_data_summary: str = Field(..., description="原始数据的综合摘要")
class SignalCluster(BaseModel):
theme_title: str = Field(..., description="主题名称")
signal_ids: List[int] = Field(..., description="包含的信号 ID 列表")
rationale: str = Field(..., description="聚类理由")
class ClusterContext(BaseModel):
"""信号聚类结果结构"""
clusters: List[SignalCluster] = Field(..., description="聚类列表")
class KLinePoint(BaseModel):
date: str = Field(..., description="日期")
open: float = Field(..., description="开盘价")
high: float = Field(..., description="最高价")
low: float = Field(..., description="最低价")
close: float = Field(..., description="收盘价")
volume: float = Field(..., description="成交量")
class ForecastResult(BaseModel):
ticker: str = Field(..., description="股票代码")
base_forecast: List[KLinePoint] = Field(default_factory=list, description="Kronos 模型原始预测")
adjusted_forecast: List[KLinePoint] = Field(default_factory=list, description="LLM 调整后的预测")
rationale: str = Field(default="", description="预测调整理由及逻辑说明")
timestamp: str = Field(default_factory=lambda: datetime.now().strftime("%Y-%m-%d %H:%M:%S"), description="生成时间")
class InvestmentReport(BaseModel):
overall_sentiment: str = Field(..., description="整体市场情绪评价")
market_entropy: float = Field(..., description="市场分歧度 (0-1, 1代表极高分歧)")
signals: List[InvestmentSignal] = Field(..., description="深度解析的投资信号列表")
forecasts: List[ForecastResult] = Field(default_factory=list, description="相关标的的预测结果")
timestamp: str = Field(..., description="报告生成时间")
meta_info: Optional[Dict[str, Any]] = Field(default_factory=dict, description="其他元数据")