stock-analyzer-skill 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/README.md +110 -39
  3. package/data/reports/202506_Stock_Analysis_Summary.md +271 -0
  4. package/experts/README.md +23 -2
  5. package/experts/buffett.md +44 -1
  6. package/experts/chaogu_yangjia.md +50 -0
  7. package/experts/decide.md +54 -2
  8. package/experts/duan_yongping.md +45 -0
  9. package/experts/lynch.md +48 -1
  10. package/experts/soros.md +43 -0
  11. package/experts/xu_xiang.md +44 -0
  12. package/experts/zhao_laoge.md +53 -0
  13. package/experts/zuoshou_xinyi.md +66 -0
  14. package/methodology.md +313 -13
  15. package/package.json +1 -1
  16. package/scripts/__pycache__/screener.cpython-314.pyc +0 -0
  17. package/scripts/api/__init__.py +22 -0
  18. package/scripts/api/__pycache__/__init__.cpython-314.pyc +0 -0
  19. package/scripts/api/__pycache__/quote_cli.cpython-314.pyc +0 -0
  20. package/scripts/api/__pycache__/screener_cli.cpython-314.pyc +0 -0
  21. package/scripts/api/quote_cli.py +106 -0
  22. package/scripts/api/screener_cli.py +149 -0
  23. package/scripts/business/__init__.py +15 -0
  24. package/scripts/business/__pycache__/__init__.cpython-314.pyc +0 -0
  25. package/scripts/business/__pycache__/screening_service.cpython-314.pyc +0 -0
  26. package/scripts/business/__pycache__/stock_analysis.cpython-314.pyc +0 -0
  27. package/scripts/business/screening_service.py +267 -0
  28. package/scripts/business/stock_analysis.py +183 -0
  29. package/scripts/common/__init__.py +334 -0
  30. package/scripts/common/__pycache__/__init__.cpython-314.pyc +0 -0
  31. package/scripts/common/__pycache__/http.cpython-314.pyc +0 -0
  32. package/scripts/common/__pycache__/parsers.cpython-314.pyc +0 -0
  33. package/scripts/common/__pycache__/utils.cpython-314.pyc +0 -0
  34. package/scripts/common/__pycache__/validators.cpython-314.pyc +0 -0
  35. package/scripts/common/exceptions/__init__.py +172 -0
  36. package/scripts/common/exceptions/__pycache__/__init__.cpython-314.pyc +0 -0
  37. package/scripts/common/http.py +79 -0
  38. package/scripts/common/metrics.py +92 -0
  39. package/scripts/common/parsers.py +125 -0
  40. package/scripts/common/utils.py +195 -0
  41. package/scripts/common/validators.py +219 -0
  42. package/scripts/config/__init__.py +24 -0
  43. package/scripts/config/__pycache__/__init__.cpython-314.pyc +0 -0
  44. package/scripts/config/__pycache__/loader.cpython-314.pyc +0 -0
  45. package/scripts/config/data_source.yaml +126 -0
  46. package/scripts/config/industry_thresholds.yaml +158 -0
  47. package/scripts/config/limits.yaml +48 -0
  48. package/scripts/config/loader.py +141 -0
  49. package/scripts/config/notification.yaml +57 -0
  50. package/scripts/config/scoring.yaml +159 -0
  51. package/scripts/data/__pycache__/config.cpython-314.pyc +0 -0
  52. package/scripts/data/__pycache__/types.cpython-314.pyc +0 -0
  53. package/scripts/data/config.py +56 -4
  54. package/scripts/data/portfolio.json +100 -0
  55. package/scripts/data/portfolio_example.json +66 -11
  56. package/scripts/data/sector_stocks.json +244 -80
  57. package/scripts/data/types.py +3 -3
  58. package/scripts/fetchers/__init__.py +54 -0
  59. package/scripts/fetchers/__pycache__/__init__.cpython-314.pyc +0 -0
  60. package/scripts/fetchers/__pycache__/akshare_quote.cpython-314.pyc +0 -0
  61. package/scripts/fetchers/__pycache__/eastmoney_event.cpython-314.pyc +0 -0
  62. package/scripts/fetchers/__pycache__/eastmoney_flow.cpython-314.pyc +0 -0
  63. package/scripts/fetchers/__pycache__/eastmoney_lhb.cpython-314.pyc +0 -0
  64. package/scripts/fetchers/__pycache__/eastmoney_quote.cpython-314.pyc +0 -0
  65. package/scripts/fetchers/__pycache__/efinance_quote.cpython-314.pyc +0 -0
  66. package/scripts/fetchers/__pycache__/sina_quote.cpython-314.pyc +0 -0
  67. package/scripts/fetchers/__pycache__/tencent_quote.cpython-314.pyc +0 -0
  68. package/scripts/fetchers/akshare_quote.py +17 -3
  69. package/scripts/fetchers/eastmoney_event.py +148 -0
  70. package/scripts/fetchers/eastmoney_flow.py +118 -0
  71. package/scripts/fetchers/eastmoney_lhb.py +134 -0
  72. package/scripts/fetchers/eastmoney_quote.py +3 -3
  73. package/scripts/fetchers/efinance_quote.py +17 -3
  74. package/scripts/fetchers/sina_quote.py +5 -9
  75. package/scripts/fetchers/tencent_quote.py +3 -1
  76. package/scripts/monitor/__init__.py +13 -0
  77. package/scripts/monitor/__pycache__/__init__.cpython-314.pyc +0 -0
  78. package/scripts/monitor/__pycache__/manager.cpython-314.pyc +0 -0
  79. package/scripts/monitor/channels/__init__.py +6 -0
  80. package/scripts/monitor/channels/__pycache__/__init__.cpython-314.pyc +0 -0
  81. package/scripts/monitor/channels/__pycache__/bark.cpython-314.pyc +0 -0
  82. package/scripts/monitor/channels/__pycache__/base.cpython-314.pyc +0 -0
  83. package/scripts/monitor/channels/bark.py +75 -0
  84. package/scripts/monitor/channels/base.py +36 -0
  85. package/scripts/monitor/health.py +148 -0
  86. package/scripts/monitor/manager.py +229 -0
  87. package/scripts/portfolio/__init__.py +13 -0
  88. package/scripts/portfolio/__pycache__/__init__.cpython-314.pyc +0 -0
  89. package/scripts/portfolio/__pycache__/manager.cpython-314.pyc +0 -0
  90. package/scripts/portfolio/manager.py +329 -0
  91. package/scripts/portfolio/performance.py +209 -0
  92. package/scripts/screener.py +78 -23
  93. package/scripts/strategies/factors/__pycache__/liquidity.cpython-314.pyc +0 -0
  94. package/scripts/strategies/factors/liquidity.py +1 -1
  95. package/skills/backtest/SKILL.md +57 -0
  96. package/skills/help/SKILL.md +69 -5
  97. package/skills/monitor/SKILL.md +98 -0
  98. package/skills/portfolio/SKILL.md +135 -40
  99. package/skills/stock/SKILL.md +99 -1
  100. package/skills/{init → stock-init}/SKILL.md +5 -5
  101. package/workflow.md +36 -2
  102. package/scripts/common.py +0 -507
@@ -0,0 +1,158 @@
1
+ # 行业差异化财务阈值
2
+
3
+ thresholds:
4
+ 银行:
5
+ pe_undervalued: 8
6
+ pe_reasonable: 12
7
+ pe_expensive: 18
8
+ peg_undervalued: 0.5
9
+ peg_reasonable: 1.0
10
+ roe_excellent: 12
11
+ gross_margin_min: 30
12
+ debt_ratio_max: 90
13
+ profit_growth_excellent: 10
14
+ revenue_growth_excellent: 5
15
+
16
+ 券商:
17
+ pe_undervalued: 15
18
+ pe_reasonable: 25
19
+ pe_expensive: 35
20
+ peg_undervalued: 0.8
21
+ peg_reasonable: 1.5
22
+ roe_excellent: 10
23
+ gross_margin_min: 25
24
+ debt_ratio_max: 80
25
+ profit_growth_excellent: 15
26
+ revenue_growth_excellent: 10
27
+
28
+ 地产:
29
+ pe_undervalued: 6
30
+ pe_reasonable: 10
31
+ pe_expensive: 15
32
+ peg_undervalued: 0.4
33
+ peg_reasonable: 0.8
34
+ roe_excellent: 10
35
+ gross_margin_min: 20
36
+ debt_ratio_max: 85
37
+ profit_growth_excellent: 8
38
+ revenue_growth_excellent: 10
39
+
40
+ 医药:
41
+ pe_undervalued: 25
42
+ pe_reasonable: 40
43
+ pe_expensive: 60
44
+ peg_undervalued: 1.0
45
+ peg_reasonable: 2.0
46
+ roe_excellent: 18
47
+ gross_margin_min: 50
48
+ debt_ratio_max: 60
49
+ profit_growth_excellent: 25
50
+ revenue_growth_excellent: 20
51
+
52
+ 半导体:
53
+ pe_undervalued: 30
54
+ pe_reasonable: 50
55
+ pe_expensive: 80
56
+ peg_undervalued: 1.0
57
+ peg_reasonable: 2.0
58
+ roe_excellent: 15
59
+ gross_margin_min: 40
60
+ debt_ratio_max: 70
61
+ profit_growth_excellent: 30
62
+ revenue_growth_excellent: 25
63
+
64
+ 软件:
65
+ pe_undervalued: 35
66
+ pe_reasonable: 55
67
+ pe_expensive: 90
68
+ peg_undervalued: 1.2
69
+ peg_reasonable: 2.5
70
+ roe_excellent: 15
71
+ gross_margin_min: 45
72
+ debt_ratio_max: 65
73
+ profit_growth_excellent: 35
74
+ revenue_growth_excellent: 30
75
+
76
+ 消费:
77
+ pe_undervalued: 20
78
+ pe_reasonable: 30
79
+ pe_expensive: 45
80
+ peg_undervalued: 0.8
81
+ peg_reasonable: 1.5
82
+ roe_excellent: 20
83
+ gross_margin_min: 35
84
+ debt_ratio_max: 60
85
+ profit_growth_excellent: 20
86
+ revenue_growth_excellent: 15
87
+
88
+ 能源:
89
+ pe_undervalued: 10
90
+ pe_reasonable: 18
91
+ pe_expensive: 25
92
+ peg_undervalued: 0.5
93
+ peg_reasonable: 1.0
94
+ roe_excellent: 12
95
+ gross_margin_min: 15
96
+ debt_ratio_max: 70
97
+ profit_growth_excellent: 15
98
+ revenue_growth_excellent: 10
99
+
100
+ 周期:
101
+ pe_undervalued: 12
102
+ pe_reasonable: 20
103
+ pe_expensive: 30
104
+ peg_undervalued: 0.6
105
+ peg_reasonable: 1.2
106
+ roe_excellent: 15
107
+ gross_margin_min: 15
108
+ debt_ratio_max: 70
109
+ profit_growth_excellent: 25
110
+ revenue_growth_excellent: 20
111
+
112
+ 制造:
113
+ pe_undervalued: 18
114
+ pe_reasonable: 28
115
+ pe_expensive: 40
116
+ peg_undervalued: 0.8
117
+ peg_reasonable: 1.5
118
+ roe_excellent: 15
119
+ gross_margin_min: 20
120
+ debt_ratio_max: 65
121
+ profit_growth_excellent: 25
122
+ revenue_growth_excellent: 20
123
+
124
+ 军工:
125
+ pe_undervalued: 50
126
+ pe_reasonable: 70
127
+ pe_expensive: 100
128
+ peg_undervalued: 1.5
129
+ peg_reasonable: 2.5
130
+ roe_excellent: 12
131
+ gross_margin_min: 30
132
+ debt_ratio_max: 70
133
+ profit_growth_excellent: 20
134
+ revenue_growth_excellent: 15
135
+
136
+ 科技:
137
+ pe_undervalued: 30
138
+ pe_reasonable: 50
139
+ pe_expensive: 80
140
+ peg_undervalued: 1.0
141
+ peg_reasonable: 2.0
142
+ roe_excellent: 15
143
+ gross_margin_min: 40
144
+ debt_ratio_max: 70
145
+ profit_growth_excellent: 30
146
+ revenue_growth_excellent: 25
147
+
148
+ 默认:
149
+ pe_undervalued: 15
150
+ pe_reasonable: 25
151
+ pe_expensive: 40
152
+ peg_undervalued: 0.8
153
+ peg_reasonable: 1.5
154
+ roe_excellent: 20
155
+ gross_margin_min: 20
156
+ debt_ratio_max: 60
157
+ profit_growth_excellent: 40
158
+ revenue_growth_excellent: 30
@@ -0,0 +1,48 @@
1
+ # A 股市场限制配置
2
+
3
+ # 涨跌停限制 (%)
4
+ board_limits:
5
+ 主板: 9.5
6
+ 创业板: 19.5
7
+ 科创板: 19.5
8
+ 北交所: 29.5
9
+
10
+ # 最低市值要求 (亿元)
11
+ min_total_cap:
12
+ 主板: 40
13
+ 创业板: 20
14
+ 科创板: 20
15
+ 北交所: 10
16
+
17
+ # 最低成交额要求 (万元)
18
+ min_amount:
19
+ 主板: 5000
20
+ 创业板: 3000
21
+ 科创板: 3000
22
+ 北交所: 1000
23
+
24
+ # 退市风险市值 (亿元)
25
+ min_survival_cap:
26
+ 主板: 3
27
+ 创业板: 2
28
+ 科创板: 2
29
+ 北交所: 1
30
+
31
+ # ST 风险检测 (名称前缀)
32
+ st_prefixes:
33
+ - "ST"
34
+ - "*ST"
35
+ - "S*ST"
36
+ - "SST"
37
+
38
+ # 商誉减值风险阈值 (%)
39
+ goodwill_ratio_warning: 30
40
+ goodwill_ratio_danger: 50
41
+
42
+ # 股权质押风险阈值 (%)
43
+ pledge_ratio_warning: 70
44
+ pledge_ratio_danger: 85
45
+
46
+ # 连续亏损检测
47
+ loss_years_warning: 2
48
+ loss_years_danger: 3
@@ -0,0 +1,141 @@
1
+ """
2
+ 配置加载器,支持 YAML 配置文件。
3
+
4
+ 配置文件目录: scripts/config/
5
+ 配置文件:
6
+ - scoring.yaml: 评分配置
7
+ - limits.yaml: 涨跌停/市值限制配置
8
+ - data_source.yaml: 数据源配置
9
+ - industry_thresholds.yaml: 行业差异化阈值
10
+ """
11
+ import yaml
12
+ from pathlib import Path
13
+ from typing import Any, Optional
14
+
15
+
16
+ class ConfigLoader:
17
+ """配置加载器,支持 YAML 配置文件(带缓存)。"""
18
+
19
+ _cache: dict = {}
20
+ _config_dir: Path = Path(__file__).parent
21
+
22
+ @classmethod
23
+ def load(cls, filename: str, use_cache: bool = True) -> dict:
24
+ """
25
+ 加载配置文件。
26
+
27
+ Args:
28
+ filename: 配置文件名 (如 "scoring.yaml")
29
+ use_cache: 是否使用缓存
30
+
31
+ Returns:
32
+ 配置字典
33
+ """
34
+ if use_cache and filename in cls._cache:
35
+ return cls._cache[filename]
36
+
37
+ config_path = cls._config_dir / filename
38
+ if not config_path.exists():
39
+ return {}
40
+
41
+ with open(config_path, "r", encoding="utf-8") as f:
42
+ config = yaml.safe_load(f) or {}
43
+
44
+ cls._cache[filename] = config
45
+ return config
46
+
47
+ @classmethod
48
+ def get(cls, filename: str, key_path: str, default: Any = None) -> Any:
49
+ """
50
+ 获取配置值,支持嵌套键路径。
51
+
52
+ Args:
53
+ filename: 配置文件名
54
+ key_path: 键路径,如 "alignment_scores.多头排列"
55
+ default: 默认值
56
+
57
+ Returns:
58
+ 配置值
59
+ """
60
+ config = cls.load(filename)
61
+ keys = key_path.split(".")
62
+
63
+ value = config
64
+ for key in keys:
65
+ if isinstance(value, dict):
66
+ value = value.get(key)
67
+ else:
68
+ return default
69
+
70
+ return value if value is not None else default
71
+
72
+ @classmethod
73
+ def reload(cls, filename: str = None):
74
+ """重新加载配置。"""
75
+ if filename:
76
+ cls._cache.pop(filename, None)
77
+ else:
78
+ cls._cache.clear()
79
+
80
+
81
+ def get_scoring_config(key: str = None, default: Any = None) -> Any:
82
+ """
83
+ 获取评分配置。
84
+
85
+ Args:
86
+ key: 键路径(如 "alignment_scores.多头排列"),为空时返回整个配置
87
+ default: 默认值
88
+
89
+ Returns:
90
+ 配置值
91
+ """
92
+ if key is None:
93
+ return ConfigLoader.load("scoring.yaml")
94
+ return ConfigLoader.get("scoring.yaml", key, default)
95
+
96
+
97
+ def get_limit_config(key: str = None, default: Any = None) -> Any:
98
+ """
99
+ 获取涨跌停限制配置。
100
+
101
+ Args:
102
+ key: 键路径,为空时返回整个配置
103
+ default: 默认值
104
+ """
105
+ if key is None:
106
+ return ConfigLoader.load("limits.yaml")
107
+ return ConfigLoader.get("limits.yaml", key, default)
108
+
109
+
110
+ def get_industry_threshold(industry: str, metric: str, default: float) -> float:
111
+ """
112
+ 获取行业差异化阈值。
113
+
114
+ Args:
115
+ industry: 行业名称
116
+ metric: 指标名称 (如 "pe_undervalued")
117
+ default: 默认值
118
+
119
+ Returns:
120
+ 阈值
121
+ """
122
+ # 先尝试从行业阈���配置获取
123
+ value = ConfigLoader.get(
124
+ "industry_thresholds.yaml",
125
+ f"thresholds.{industry}.{metric}",
126
+ None
127
+ )
128
+ if value is not None:
129
+ return float(value)
130
+
131
+ # 回退到默认行业配置
132
+ return ConfigLoader.get(
133
+ "scoring.yaml",
134
+ f"industry_defaults.{metric}",
135
+ default
136
+ )
137
+
138
+
139
+ def reload_config(filename: str = None):
140
+ """重新加载配置。"""
141
+ ConfigLoader.reload(filename)
@@ -0,0 +1,57 @@
1
+ # 盘中消息推送配置
2
+ # 文档: https://github.com/user/stock-analyzer-skill/wiki/notifications
3
+
4
+ # 推送通道配置
5
+ channels:
6
+ bark:
7
+ enabled: false
8
+ server: "https://api.day.app" # Bark 服务器地址(支持自托管)
9
+ key: "" # Bark 推送 Key
10
+ group: "stock" # iOS 通知分组名
11
+
12
+ # wechat_work:
13
+ # enabled: false
14
+ # webhook: "" # 企业微信机器人 Webhook URL
15
+
16
+ # dingtalk:
17
+ # enabled: false
18
+ # webhook: "" # 钉钉机器人 Webhook URL
19
+
20
+ # webhook:
21
+ # enabled: false
22
+ # url: "" # 自定义 Webhook URL
23
+
24
+ # 预警规则
25
+ rules:
26
+ price_alert:
27
+ enabled: true
28
+ thresholds:
29
+ default: 3.0 # 默认涨跌幅阈值 (%)
30
+ overrides: {} # 个股自定义: {"sh600989": 5.0}
31
+ support_resistance: true # 触及支撑/压力位推送
32
+ near_limit: true # 涨跌停附近推送
33
+
34
+ technical_alert:
35
+ enabled: true
36
+ macd_cross: true # MACD 金叉/死叉
37
+ ma_break: [20, 60] # 均线突破
38
+ volume_surge: 2.0 # 量比阈值
39
+ chan_signal: true # 缠论买卖点
40
+
41
+ portfolio_alert:
42
+ enabled: true
43
+ risk_change: true # 风险状态变更
44
+ underperform_days: 2 # 连续跑输天数
45
+ concentration: true # 集中度超标
46
+
47
+ market_alert:
48
+ enabled: true
49
+ index_change: 2.0 # 大盘涨跌幅阈值 (%)
50
+ sector_moved: true # 持仓板块异动
51
+ northbound_flow: 50 # 北向资金净流入/出 (亿)
52
+
53
+ # 频率控制
54
+ throttle:
55
+ dedup_window: 15 # 同类消息去重窗口 (分钟)
56
+ daily_limit: 20 # 每日推送上限
57
+ quiet_hours: "15:05-09:25" # 非交易时段静默
@@ -0,0 +1,159 @@
1
+ # A 股评分系统配置
2
+
3
+ # 均线排列评分
4
+ alignment_scores:
5
+ 多头排列: 20
6
+ 交叉震荡: 12
7
+ 空头排列: 3
8
+ 数据不足: 7
9
+
10
+ # MACD 评分
11
+ macd_scores:
12
+ 金叉放大: 15
13
+ 金叉: 10
14
+ 死叉: 3
15
+ 中性: 7
16
+
17
+ # 市场环境权重调整
18
+ market_weights:
19
+ 牛市:
20
+ bullish_bias: 1.3
21
+ trend_following: 1.4
22
+ breakout: 1.3
23
+ divergence_bottom: 0.5
24
+ buy_point_1: 0.5
25
+ buy_point_3: 1.3
26
+ overbought: 0.8
27
+ desc: "牛市:趋势跟随加权,底背离/一买降权"
28
+
29
+ 熊市:
30
+ bullish_bias: 1.5
31
+ trend_following: 0.6
32
+ breakout: 0.6
33
+ divergence_bottom: 1.5
34
+ buy_point_1: 1.5
35
+ buy_point_3: 0.5
36
+ overbought: 1.3
37
+ desc: "熊市:反转信号加权,追涨信号降权"
38
+
39
+ 震荡:
40
+ bullish_bias: 1.0
41
+ trend_following: 0.8
42
+ breakout: 0.8
43
+ divergence_bottom: 1.2
44
+ buy_point_1: 1.1
45
+ buy_point_3: 1.2
46
+ overbought: 1.0
47
+ desc: "震荡:反转+区间交易加权,趋势信号降权"
48
+
49
+ 冰点:
50
+ bullish_bias: 1.8
51
+ trend_following: 0.3
52
+ breakout: 0.4
53
+ divergence_bottom: 1.8
54
+ buy_point_1: 2.0
55
+ buy_point_3: 0.3
56
+ overbought: 1.5
57
+ desc: "冰点:极度超卖反转加权,趋势信号大幅降权"
58
+
59
+ 亢奋:
60
+ bullish_bias: 0.6
61
+ trend_following: 0.5
62
+ breakout: 0.5
63
+ divergence_bottom: 0.4
64
+ buy_point_1: 0.3
65
+ buy_point_3: 0.5
66
+ overbought: 0.3
67
+ desc: "亢奋:全面保守,警惕反转"
68
+
69
+ # 个股类型 × 指标权重
70
+ stock_type_weights:
71
+ 题材股:
72
+ ma: 0.6
73
+ macd: 0.5
74
+ kdj: 0.5
75
+ boll: 0.8
76
+ rsi: 1.0
77
+ volume: 1.3
78
+ pattern: 1.5
79
+ limit: 1.5
80
+ chan: 0.5
81
+
82
+ 蓝筹股:
83
+ ma: 1.3
84
+ macd: 1.1
85
+ kdj: 0.4
86
+ boll: 1.2
87
+ rsi: 0.9
88
+ volume: 0.8
89
+ pattern: 0.7
90
+ limit: 0.3
91
+ chan: 0.8
92
+
93
+ 强成长股:
94
+ ma: 0.9
95
+ macd: 1.3
96
+ kdj: 0.4
97
+ boll: 1.2
98
+ rsi: 0.9
99
+ volume: 1.2
100
+ pattern: 0.8
101
+ limit: 0.5
102
+ chan: 0.7
103
+
104
+ 周期股:
105
+ ma: 0.6
106
+ macd: 1.3
107
+ kdj: 1.2
108
+ boll: 1.0
109
+ rsi: 0.9
110
+ volume: 0.9
111
+ pattern: 0.7
112
+ limit: 0.4
113
+ chan: 1.3
114
+
115
+ 稳成长股:
116
+ ma: 1.2
117
+ macd: 1.1
118
+ kdj: 0.5
119
+ boll: 1.0
120
+ rsi: 1.0
121
+ volume: 0.9
122
+ pattern: 1.0
123
+ limit: 0.3
124
+ chan: 0.8
125
+
126
+ 防御股:
127
+ ma: 0.8
128
+ macd: 0.9
129
+ kdj: 0.6
130
+ boll: 1.1
131
+ rsi: 1.1
132
+ volume: 0.7
133
+ pattern: 0.7
134
+ limit: 0.3
135
+ chan: 0.9
136
+
137
+ 普通股:
138
+ ma: 1.0
139
+ macd: 1.0
140
+ kdj: 1.0
141
+ boll: 1.0
142
+ rsi: 1.0
143
+ volume: 1.0
144
+ pattern: 1.0
145
+ limit: 1.0
146
+ chan: 1.0
147
+
148
+ # 默认行业阈值
149
+ industry_defaults:
150
+ pe_undervalued: 15
151
+ pe_reasonable: 25
152
+ pe_expensive: 40
153
+ peg_undervalued: 0.8
154
+ peg_reasonable: 1.5
155
+ roe_excellent: 20
156
+ gross_margin_min: 20
157
+ debt_ratio_max: 60
158
+ profit_growth_excellent: 40
159
+ revenue_growth_excellent: 30
@@ -1,13 +1,27 @@
1
- """统一配置管理。"""
1
+ """统一配置管理。
2
+
3
+ 配置优先级:环境变量 > YAML 配置 > 代码默认值
4
+ """
2
5
  import os
6
+ import time
3
7
  from dataclasses import dataclass
4
8
 
5
9
 
10
+ def _load_yaml_config() -> dict:
11
+ """从 ConfigLoader 加载 YAML 配置(延迟导入避免循环依赖)。"""
12
+ try:
13
+ from config.loader import ConfigLoader
14
+ return ConfigLoader.load("data_source.yaml")
15
+ except Exception:
16
+ return {}
17
+
18
+
6
19
  @dataclass
7
20
  class DataConfig:
8
21
  """数据层配置。"""
9
22
  # 缓存 TTL (秒)
10
- quote_cache_ttl: int = 900 # 15 分钟
23
+ quote_cache_ttl: int = 900 # 15 分钟(盘后)
24
+ intraday_quote_cache_ttl: int = 90 # 90 秒(盘中)
11
25
  kline_cache_ttl: int = 21600 # 6 小时
12
26
  finance_cache_ttl: int = 21600 # 6 小时
13
27
  ann_cache_ttl: int = 1800 # 30 分钟
@@ -20,10 +34,24 @@ class DataConfig:
20
34
  max_workers: int = 8
21
35
 
22
36
  @classmethod
23
- def from_env(cls) -> "DataConfig":
24
- """从环境变量加载配置。"""
37
+ def from_yaml_and_env(cls) -> "DataConfig":
38
+ """从 YAML 配置加载默认值,环境变量覆盖。"""
39
+ yaml_cfg = _load_yaml_config()
40
+ cache_cfg = yaml_cfg.get("cache", {})
41
+ cb_cfg = yaml_cfg.get("circuit_breaker", {})
42
+
25
43
  cfg = cls()
44
+ # YAML 默认值
45
+ cfg.quote_cache_ttl = cache_cfg.get("quote_ttl", cfg.quote_cache_ttl)
46
+ cfg.kline_cache_ttl = cache_cfg.get("kline_ttl", cfg.kline_cache_ttl)
47
+ cfg.finance_cache_ttl = cache_cfg.get("finance_ttl", cfg.finance_cache_ttl)
48
+ cfg.ann_cache_ttl = cache_cfg.get("ann_ttl", cfg.ann_cache_ttl)
49
+ cfg.circuit_failure_threshold = cb_cfg.get("failure_threshold", cfg.circuit_failure_threshold)
50
+ cfg.circuit_recovery_timeout = cb_cfg.get("recovery_timeout", cfg.circuit_recovery_timeout)
51
+
52
+ # 环境变量覆盖
26
53
  cfg.quote_cache_ttl = int(os.getenv("DATA_QUOTE_TTL", cfg.quote_cache_ttl))
54
+ cfg.intraday_quote_cache_ttl = int(os.getenv("DATA_INTRADAY_QUOTE_TTL", cfg.intraday_quote_cache_ttl))
27
55
  cfg.kline_cache_ttl = int(os.getenv("DATA_KLINE_TTL", cfg.kline_cache_ttl))
28
56
  cfg.finance_cache_ttl = int(os.getenv("DATA_FINANCE_TTL", cfg.finance_cache_ttl))
29
57
  cfg.circuit_failure_threshold = int(os.getenv("DATA_CIRCUIT_THRESHOLD", cfg.circuit_failure_threshold))
@@ -31,6 +59,30 @@ class DataConfig:
31
59
  cfg.max_workers = int(os.getenv("DATA_MAX_WORKERS", cfg.max_workers))
32
60
  return cfg
33
61
 
62
+ @classmethod
63
+ def from_env(cls) -> "DataConfig":
64
+ """向后兼容:从环境变量加载配置。"""
65
+ return cls.from_yaml_and_env()
66
+
67
+
68
+ def is_trading_hours() -> bool:
69
+ """判断当前是否在 A 股交易时段(9:15-15:00,周一至周五)。"""
70
+ now = time.localtime()
71
+ # 周末不交易
72
+ if now.tm_wday >= 5:
73
+ return False
74
+ # 交易时段:9:15 - 15:00
75
+ current_minutes = now.tm_hour * 60 + now.tm_min
76
+ return 9 * 60 + 15 <= current_minutes <= 15 * 60
77
+
78
+
79
+ def get_quote_cache_ttl() -> int:
80
+ """获取行情缓存 TTL(盘中短,盘后长)。"""
81
+ cfg = get_config()
82
+ if is_trading_hours():
83
+ return cfg.intraday_quote_cache_ttl
84
+ return cfg.quote_cache_ttl
85
+
34
86
 
35
87
  _config = None
36
88