stock-analyzer-skill 1.1.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.
- package/.claude-plugin/marketplace.json +19 -0
- package/.claude-plugin/plugin.json +21 -0
- package/CHANGELOG.md +93 -0
- package/CONTRIBUTING.md +331 -0
- package/README.md +259 -0
- package/experts/README.md +119 -0
- package/experts/buffett.md +91 -0
- package/experts/chaogu_yangjia.md +125 -0
- package/experts/decide.md +212 -0
- package/experts/duan_yongping.md +106 -0
- package/experts/lynch.md +127 -0
- package/experts/soros.md +89 -0
- package/experts/xu_xiang.md +107 -0
- package/experts/zhao_laoge.md +143 -0
- package/experts/zuoshou_xinyi.md +144 -0
- package/install-plugin.js +69 -0
- package/methodology.md +455 -0
- package/package.json +43 -0
- package/scripts/__pycache__/announcements.cpython-314.pyc +0 -0
- package/scripts/__pycache__/backtest.cpython-314.pyc +0 -0
- package/scripts/__pycache__/chan.cpython-314.pyc +0 -0
- package/scripts/__pycache__/classifier.cpython-314.pyc +0 -0
- package/scripts/__pycache__/common.cpython-314.pyc +0 -0
- package/scripts/__pycache__/finance.cpython-314.pyc +0 -0
- package/scripts/__pycache__/init_pool.cpython-314.pyc +0 -0
- package/scripts/__pycache__/kline.cpython-314.pyc +0 -0
- package/scripts/__pycache__/patterns_local.cpython-314.pyc +0 -0
- package/scripts/__pycache__/quote.cpython-314.pyc +0 -0
- package/scripts/__pycache__/refresh_pool.cpython-314.pyc +0 -0
- package/scripts/__pycache__/screener.cpython-314.pyc +0 -0
- package/scripts/__pycache__/technical.cpython-314.pyc +0 -0
- package/scripts/announcements.py +118 -0
- package/scripts/backtest.py +528 -0
- package/scripts/chan.py +591 -0
- package/scripts/classifier.py +302 -0
- package/scripts/common.py +507 -0
- package/scripts/data/__init__.py +208 -0
- package/scripts/data/__pycache__/__init__.cpython-314.pyc +0 -0
- package/scripts/data/__pycache__/cache.cpython-314.pyc +0 -0
- package/scripts/data/__pycache__/config.cpython-314.pyc +0 -0
- package/scripts/data/__pycache__/types.cpython-314.pyc +0 -0
- package/scripts/data/cache.py +99 -0
- package/scripts/data/config.py +49 -0
- package/scripts/data/industry_thresholds.json +199 -0
- package/scripts/data/portfolio_example.json +14 -0
- package/scripts/data/sector_etf.csv +14 -0
- package/scripts/data/sector_mapping.json +64 -0
- package/scripts/data/sector_stocks.json +135 -0
- package/scripts/data/types.py +66 -0
- package/scripts/fetchers/__init__.py +130 -0
- package/scripts/fetchers/__pycache__/__init__.cpython-314.pyc +0 -0
- package/scripts/fetchers/__pycache__/akshare_finance.cpython-314.pyc +0 -0
- package/scripts/fetchers/__pycache__/akshare_kline.cpython-314.pyc +0 -0
- package/scripts/fetchers/__pycache__/akshare_quote.cpython-314.pyc +0 -0
- package/scripts/fetchers/__pycache__/baostock_kline.cpython-314.pyc +0 -0
- package/scripts/fetchers/__pycache__/eastmoney_finance.cpython-314.pyc +0 -0
- package/scripts/fetchers/__pycache__/eastmoney_kline.cpython-314.pyc +0 -0
- package/scripts/fetchers/__pycache__/eastmoney_quote.cpython-314.pyc +0 -0
- package/scripts/fetchers/__pycache__/efinance_finance.cpython-314.pyc +0 -0
- package/scripts/fetchers/__pycache__/efinance_kline.cpython-314.pyc +0 -0
- package/scripts/fetchers/__pycache__/efinance_quote.cpython-314.pyc +0 -0
- package/scripts/fetchers/__pycache__/pytdx_quote.cpython-314.pyc +0 -0
- package/scripts/fetchers/__pycache__/sina_kline.cpython-314.pyc +0 -0
- package/scripts/fetchers/__pycache__/sina_quote.cpython-314.pyc +0 -0
- package/scripts/fetchers/__pycache__/tencent_kline.cpython-314.pyc +0 -0
- package/scripts/fetchers/__pycache__/tencent_quote.cpython-314.pyc +0 -0
- package/scripts/fetchers/__pycache__/tushare_kline.cpython-314.pyc +0 -0
- package/scripts/fetchers/__pycache__/tushare_quote.cpython-314.pyc +0 -0
- package/scripts/fetchers/__pycache__/yfinance_kline.cpython-314.pyc +0 -0
- package/scripts/fetchers/akshare_finance.py +35 -0
- package/scripts/fetchers/akshare_kline.py +59 -0
- package/scripts/fetchers/akshare_quote.py +52 -0
- package/scripts/fetchers/baostock_kline.py +64 -0
- package/scripts/fetchers/eastmoney_finance.py +29 -0
- package/scripts/fetchers/eastmoney_kline.py +48 -0
- package/scripts/fetchers/eastmoney_quote.py +68 -0
- package/scripts/fetchers/efinance_finance.py +32 -0
- package/scripts/fetchers/efinance_kline.py +46 -0
- package/scripts/fetchers/efinance_quote.py +53 -0
- package/scripts/fetchers/pytdx_kline.py +70 -0
- package/scripts/fetchers/pytdx_quote.py +78 -0
- package/scripts/fetchers/sina_kline.py +30 -0
- package/scripts/fetchers/sina_quote.py +35 -0
- package/scripts/fetchers/tencent_kline.py +52 -0
- package/scripts/fetchers/tencent_quote.py +29 -0
- package/scripts/fetchers/tushare_kline.py +62 -0
- package/scripts/fetchers/tushare_quote.py +62 -0
- package/scripts/fetchers/yfinance_kline.py +66 -0
- package/scripts/finance.py +92 -0
- package/scripts/init_pool.py +105 -0
- package/scripts/kline.py +62 -0
- package/scripts/monitor.py +107 -0
- package/scripts/patterns_local.py +599 -0
- package/scripts/quote.py +69 -0
- package/scripts/refresh_pool.py +328 -0
- package/scripts/screener.py +434 -0
- package/scripts/strategies/__init__.py +11 -0
- package/scripts/strategies/__pycache__/__init__.cpython-314.pyc +0 -0
- package/scripts/strategies/__pycache__/registry.cpython-314.pyc +0 -0
- package/scripts/strategies/__pycache__/thresholds.cpython-314.pyc +0 -0
- package/scripts/strategies/factors/__init__.py +8 -0
- package/scripts/strategies/factors/__pycache__/__init__.cpython-314.pyc +0 -0
- package/scripts/strategies/factors/__pycache__/liquidity.cpython-314.pyc +0 -0
- package/scripts/strategies/factors/__pycache__/momentum.cpython-314.pyc +0 -0
- package/scripts/strategies/factors/__pycache__/quality.cpython-314.pyc +0 -0
- package/scripts/strategies/factors/__pycache__/valuation.cpython-314.pyc +0 -0
- package/scripts/strategies/factors/__pycache__/volatility.cpython-314.pyc +0 -0
- package/scripts/strategies/factors/liquidity.py +49 -0
- package/scripts/strategies/factors/momentum.py +45 -0
- package/scripts/strategies/factors/quality.py +54 -0
- package/scripts/strategies/factors/valuation.py +76 -0
- package/scripts/strategies/factors/volatility.py +89 -0
- package/scripts/strategies/registry.py +87 -0
- package/scripts/strategies/thresholds.py +28 -0
- package/scripts/technical/__init__.py +116 -0
- package/scripts/technical/__pycache__/__init__.cpython-314.pyc +0 -0
- package/scripts/technical/__pycache__/astock.cpython-314.pyc +0 -0
- package/scripts/technical/__pycache__/boll.cpython-314.pyc +0 -0
- package/scripts/technical/__pycache__/candlestick.cpython-314.pyc +0 -0
- package/scripts/technical/__pycache__/core.cpython-314.pyc +0 -0
- package/scripts/technical/__pycache__/kdj.cpython-314.pyc +0 -0
- package/scripts/technical/__pycache__/macd.cpython-314.pyc +0 -0
- package/scripts/technical/__pycache__/moving_average.cpython-314.pyc +0 -0
- package/scripts/technical/__pycache__/report.cpython-314.pyc +0 -0
- package/scripts/technical/__pycache__/rsi.cpython-314.pyc +0 -0
- package/scripts/technical/__pycache__/scoring.cpython-314.pyc +0 -0
- package/scripts/technical/__pycache__/signals.cpython-314.pyc +0 -0
- package/scripts/technical/__pycache__/trend.cpython-314.pyc +0 -0
- package/scripts/technical/__pycache__/volume.cpython-314.pyc +0 -0
- package/scripts/technical/astock.py +98 -0
- package/scripts/technical/boll.py +49 -0
- package/scripts/technical/candlestick.py +151 -0
- package/scripts/technical/core.py +92 -0
- package/scripts/technical/kdj.py +68 -0
- package/scripts/technical/macd.py +97 -0
- package/scripts/technical/moving_average.py +59 -0
- package/scripts/technical/report.py +221 -0
- package/scripts/technical/rsi.py +37 -0
- package/scripts/technical/scoring.py +392 -0
- package/scripts/technical/signals.py +70 -0
- package/scripts/technical/trend.py +143 -0
- package/scripts/technical/volume.py +113 -0
- package/scripts/technical.py +215 -0
- package/skills/financial-analyst/SKILL.md +141 -0
- package/skills/help/SKILL.md +188 -0
- package/skills/init/SKILL.md +66 -0
- package/skills/investment-researcher/SKILL.md +152 -0
- package/skills/market/SKILL.md +99 -0
- package/skills/portfolio/SKILL.md +96 -0
- package/skills/screener/SKILL.md +128 -0
- package/skills/sector/SKILL.md +102 -0
- package/skills/stock/SKILL.md +148 -0
- package/skills/technical/SKILL.md +168 -0
- package/workflow.md +91 -0
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
"""
|
|
2
|
+
报告渲染。
|
|
3
|
+
无内部依赖。
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def _fmt(val, default="-"):
|
|
8
|
+
return str(val) if val is not None else default
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def render_report(features, score, signals, meta):
|
|
12
|
+
"""完整技术分析报告。"""
|
|
13
|
+
lines = []
|
|
14
|
+
ma = features.get("ma_system", {})
|
|
15
|
+
macd = features.get("macd") or {}
|
|
16
|
+
kdj = features.get("kdj") or {}
|
|
17
|
+
boll = features.get("bollinger") or {}
|
|
18
|
+
rsi_data = features.get("rsi", {})
|
|
19
|
+
vol = features.get("volume") or {}
|
|
20
|
+
sr = features.get("support_resistance", {})
|
|
21
|
+
box = features.get("box")
|
|
22
|
+
breakout = features.get("breakout", {})
|
|
23
|
+
wave = features.get("wave", "")
|
|
24
|
+
patterns = features.get("patterns", [])
|
|
25
|
+
limit_data = features.get("limit_analysis") or {}
|
|
26
|
+
|
|
27
|
+
lines.append("═" * 72)
|
|
28
|
+
lines.append(f" {meta['name']} ({meta['code']}) 现价: {meta['price']} 涨跌: {meta['change_pct']}% 板块: {meta['board']} 时间: {meta['timestamp']}")
|
|
29
|
+
lines.append("═" * 72)
|
|
30
|
+
|
|
31
|
+
# ── 综合评分 ──
|
|
32
|
+
lines.append(f"\n## 综合评分: {score['score']}/100 -- {score['grade']}")
|
|
33
|
+
if score["buy_signals"]:
|
|
34
|
+
lines.append(f" 买入信号: {', '.join(score['buy_signals'])}")
|
|
35
|
+
if score["sell_signals"]:
|
|
36
|
+
lines.append(f" 卖出信号: {', '.join(score['sell_signals'])}")
|
|
37
|
+
if sr.get("nearest_support"):
|
|
38
|
+
lines.append(f" 关键支撑: {sr['nearest_support']} 关键阻力: {sr.get('nearest_resistance', '-')}")
|
|
39
|
+
|
|
40
|
+
# ── 均线系统 ──
|
|
41
|
+
lines.append(f"\n## 均线系统")
|
|
42
|
+
ma_parts = []
|
|
43
|
+
for p in [5, 10, 20, 60, 120, 250]:
|
|
44
|
+
v = ma.get(f"ma{p}")
|
|
45
|
+
if v is not None:
|
|
46
|
+
ma_parts.append(f"MA{p}: {v}")
|
|
47
|
+
lines.append(f" {', '.join(ma_parts)}")
|
|
48
|
+
lines.append(f" 排列: {ma.get('alignment', '-')} | 粘合度: {ma.get('convergence_desc', '-')}")
|
|
49
|
+
|
|
50
|
+
# ── MACD ──
|
|
51
|
+
lines.append(f"\n## MACD")
|
|
52
|
+
lines.append(f" DIF: {_fmt(macd.get('dif'))} DEA: {_fmt(macd.get('dea'))} BAR: {_fmt(macd.get('macd_bar'))}")
|
|
53
|
+
lines.append(f" 信号: {macd.get('signal_desc', '-')} | 柱趋势: {macd.get('bar_trend', '-')}")
|
|
54
|
+
if macd.get("divergence"):
|
|
55
|
+
lines.append(f" 背离: **{macd['divergence']}**")
|
|
56
|
+
|
|
57
|
+
# ── KDJ ──
|
|
58
|
+
lines.append(f"\n## KDJ")
|
|
59
|
+
lines.append(f" K: {_fmt(kdj.get('k'))} D: {_fmt(kdj.get('d'))} J: {_fmt(kdj.get('j'))}")
|
|
60
|
+
lines.append(f" 信号: {kdj.get('signal', '-')}")
|
|
61
|
+
if kdj.get("钝化"):
|
|
62
|
+
lines.append(f" ⚠ KDJ钝化中,超买超卖信号暂停参考")
|
|
63
|
+
|
|
64
|
+
# ── BOLL ──
|
|
65
|
+
lines.append(f"\n## BOLL")
|
|
66
|
+
lines.append(f" 上轨: {_fmt(boll.get('upper'))} 中轨: {_fmt(boll.get('mid'))} 下轨: {_fmt(boll.get('lower'))}")
|
|
67
|
+
lines.append(f" 带宽: {boll.get('bandwidth_desc', '-')} | 价格: {boll.get('position_desc', '-')}")
|
|
68
|
+
|
|
69
|
+
# ── 成交量 ──
|
|
70
|
+
lines.append(f"\n## 成交量")
|
|
71
|
+
lines.append(f" 量比: {_fmt(vol.get('volume_ratio'))} ({vol.get('volume_ratio_desc', '-')})")
|
|
72
|
+
lines.append(f" 量价: {vol.get('volume_price', '-')}")
|
|
73
|
+
if vol.get("obv_divergence"):
|
|
74
|
+
lines.append(f" OBV: {vol['obv_divergence']}")
|
|
75
|
+
|
|
76
|
+
# ── RSI ──
|
|
77
|
+
lines.append(f"\n## RSI")
|
|
78
|
+
rsi_desc = {1: "超卖", -1: "超买"}.get(rsi_data.get("signal", 0), "正常")
|
|
79
|
+
lines.append(f" RSI-{rsi_data.get('period', 14)}: {rsi_data.get('rsi', 50)} ({rsi_desc})")
|
|
80
|
+
|
|
81
|
+
# ── K线形态 ──
|
|
82
|
+
if patterns:
|
|
83
|
+
lines.append(f"\n## K线形态")
|
|
84
|
+
for p in patterns:
|
|
85
|
+
lines.append(f" {p['position']} [{p['date']}] {p['type']}")
|
|
86
|
+
else:
|
|
87
|
+
lines.append(f"\n## K线形态\n (无明显形态)")
|
|
88
|
+
|
|
89
|
+
# ── 个股分类 ──
|
|
90
|
+
classification = features.get("classification")
|
|
91
|
+
if classification:
|
|
92
|
+
lines.append(f"\n## 个股分类")
|
|
93
|
+
lines.append(f" 类型: {classification['type']} (置信度: {classification['confidence']})")
|
|
94
|
+
if classification.get("reasons"):
|
|
95
|
+
lines.append(f" 依据: {'; '.join(classification['reasons'])}")
|
|
96
|
+
if classification.get("priority_indicators"):
|
|
97
|
+
lines.append(f" 推荐指标: {', '.join(classification['priority_indicators'])}")
|
|
98
|
+
|
|
99
|
+
# ── 缠论分析 ──
|
|
100
|
+
chan = features.get("chan_theory") or {}
|
|
101
|
+
if chan.get("valid"):
|
|
102
|
+
lines.append(f"\n## 缠论分析")
|
|
103
|
+
lines.append(f" 处理后K线: {chan.get('merged_count', '-')}/{chan.get('original_count', '-')}"
|
|
104
|
+
f" (合并率{chan.get('merge_ratio_pct', '-')}%)")
|
|
105
|
+
lines.append(f" 分型: {chan.get('fenxing_count', 0)} (顶{chan.get('top_fenxing', 0)}/底{chan.get('bottom_fenxing', 0)})"
|
|
106
|
+
f" → 笔: {chan.get('bi_count', 0)} (↑{chan.get('up_bi', 0)} ↓{chan.get('down_bi', 0)})"
|
|
107
|
+
f" → 线段: {chan.get('xianduan_count', 0)}")
|
|
108
|
+
zs_list = chan.get("zhongshu_list", [])
|
|
109
|
+
if zs_list:
|
|
110
|
+
zs_desc = "; ".join(f"[{z['zd']}-{z['zg']}]" for z in zs_list[-2:])
|
|
111
|
+
lines.append(f" 中枢({chan.get('zhongshu_count', 0)}): {zs_desc}")
|
|
112
|
+
beichi = chan.get("beichi", {})
|
|
113
|
+
if beichi.get("summary"):
|
|
114
|
+
lines.append(f" 背驰: {beichi['summary']}")
|
|
115
|
+
maidain = chan.get("maidian", {})
|
|
116
|
+
buy_pts = maidain.get("buy_points", [])
|
|
117
|
+
sell_pts = maidain.get("sell_points", [])
|
|
118
|
+
if buy_pts:
|
|
119
|
+
bp_desc = "; ".join(bp["type"] + "(" + bp.get("confidence", "") + ")" for bp in buy_pts)
|
|
120
|
+
lines.append(f" 买点: {bp_desc}")
|
|
121
|
+
if sell_pts:
|
|
122
|
+
sp_desc = "; ".join(sp["type"] + "(" + sp.get("confidence", "") + ")" for sp in sell_pts)
|
|
123
|
+
lines.append(f" 卖点: {sp_desc}")
|
|
124
|
+
if not buy_pts and not sell_pts:
|
|
125
|
+
lines.append(f" 买卖点: 当前无明确缠论买卖点")
|
|
126
|
+
lines.append(f" 当前位置: {chan.get('current_position', '-')}")
|
|
127
|
+
|
|
128
|
+
# ── A股本土战法 ──
|
|
129
|
+
local_p = features.get("local_patterns") or {}
|
|
130
|
+
if local_p.get("patterns"):
|
|
131
|
+
lines.append(f"\n## A股本土战法")
|
|
132
|
+
for lp in local_p["patterns"]:
|
|
133
|
+
icon = "↑" if lp["type"] == "看涨" else "↓"
|
|
134
|
+
lines.append(f" {icon} {lp['name']} ({lp['confidence']}): {lp['desc']}")
|
|
135
|
+
lines.append(f" {local_p.get('summary', '')}")
|
|
136
|
+
elif local_p:
|
|
137
|
+
lines.append(f"\n## A股本土战法\n {local_p.get('summary', '未检测到形态')}")
|
|
138
|
+
|
|
139
|
+
# ── 市场环境自适应 ──
|
|
140
|
+
market_env = features.get("market_environment") or {}
|
|
141
|
+
if market_env.get("state") and market_env["state"] != "震荡":
|
|
142
|
+
lines.append(f"\n## 市场环境自适应")
|
|
143
|
+
lines.append(f" 市场状态: {market_env['state']} (置信度: {market_env.get('confidence', '-')})")
|
|
144
|
+
adj_info = market_env.get("weight_adjustments", {})
|
|
145
|
+
if adj_info.get("desc"):
|
|
146
|
+
lines.append(f" 权重调整: {adj_info['desc']}")
|
|
147
|
+
|
|
148
|
+
# ── 支撑与阻力 ──
|
|
149
|
+
lines.append(f"\n## 支撑与阻力")
|
|
150
|
+
lines.append(f" {'支撑位':<10} {'来源':<8} {'强度'}")
|
|
151
|
+
for s in sr.get("supports", []):
|
|
152
|
+
lines.append(f" {s['level']:<10} {s['source']:<8} {s['strength']}")
|
|
153
|
+
lines.append(f" {'阻力位':<10} {'来源':<8} {'强度'}")
|
|
154
|
+
for r in sr.get("resistances", []):
|
|
155
|
+
lines.append(f" {r['level']:<10} {r['source']:<8} {r['strength']}")
|
|
156
|
+
|
|
157
|
+
# ── 趋势结构 ──
|
|
158
|
+
lines.append(f"\n## 趋势结构")
|
|
159
|
+
lines.append(f" 波浪状态: {wave}")
|
|
160
|
+
if box:
|
|
161
|
+
lines.append(f" 箱体: {box['top']}-{box['bottom']} 震荡 (幅度{box['range_pct']}%, {box['days']}日)")
|
|
162
|
+
if breakout and breakout.get("status", "未突破") != "未突破":
|
|
163
|
+
lines.append(f" 突破: {breakout.get('status')}")
|
|
164
|
+
|
|
165
|
+
# ── A 股特化 ──
|
|
166
|
+
if limit_data:
|
|
167
|
+
lines.append(f"\n## A股特化分析")
|
|
168
|
+
lines.append(f" 板块制度: {limit_data.get('board', '-')} (涨跌停{limit_data.get('limit_ratio', 10)}%)")
|
|
169
|
+
lines.append(f" 涨跌停价: 涨停{limit_data.get('limit_up_price', '-')} / 跌停{limit_data.get('limit_down_price', '-')}")
|
|
170
|
+
lines.append(f" 当前状态: {limit_data.get('board_status', '-')}")
|
|
171
|
+
if limit_data.get("limit_streak", 0) > 0:
|
|
172
|
+
lines.append(f" 连板: {limit_data.get('limit_streak')}连板 ({limit_data.get('streak_type')})")
|
|
173
|
+
if limit_data.get("streak_volume"):
|
|
174
|
+
lines.append(f" 连板量能: {limit_data['streak_volume']}")
|
|
175
|
+
if limit_data.get("t1_risk"):
|
|
176
|
+
lines.append(f" ⚠ {limit_data['t1_risk']}")
|
|
177
|
+
|
|
178
|
+
# ── 综合建议止损 ──
|
|
179
|
+
lines.append(f"\n## 仓位参考(技术面)")
|
|
180
|
+
nearest_support = sr.get("nearest_support")
|
|
181
|
+
if nearest_support:
|
|
182
|
+
stop_pct = round((meta['price_num'] - nearest_support) / meta['price_num'] * 100, 1)
|
|
183
|
+
lines.append(f" 止损位: {nearest_support} (距现价 -{abs(stop_pct)}%)")
|
|
184
|
+
nearest_resistance = sr.get("nearest_resistance")
|
|
185
|
+
if nearest_resistance:
|
|
186
|
+
tp_pct = round((nearest_resistance - meta['price_num']) / meta['price_num'] * 100, 1)
|
|
187
|
+
lines.append(f" 止盈位: {nearest_resistance} (距现价 +{tp_pct}%)")
|
|
188
|
+
lines.append(f" 纯技术视角,不构成投资建议。需结合基本面、市场环境综合判断。")
|
|
189
|
+
|
|
190
|
+
lines.append("═" * 72)
|
|
191
|
+
return "\n".join(lines)
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def render_quick(features, score, meta):
|
|
195
|
+
"""快速技术摘要。"""
|
|
196
|
+
ma = features.get("ma_system", {})
|
|
197
|
+
macd = features.get("macd") or {}
|
|
198
|
+
vol = features.get("volume") or {}
|
|
199
|
+
sr = features.get("support_resistance", {})
|
|
200
|
+
limit_data = features.get("limit_analysis") or {}
|
|
201
|
+
|
|
202
|
+
lines = []
|
|
203
|
+
lines.append(f"## 技术面快扫: {meta['name']} ({meta['code']})")
|
|
204
|
+
lines.append(f"现价: {meta['price']} | 涨跌: {meta['change_pct']}% | 板块: {meta['board']} | {meta['timestamp']}")
|
|
205
|
+
lines.append("")
|
|
206
|
+
lines.append(f"评分: {score['score']}/100 ({score['grade']})")
|
|
207
|
+
lines.append(f"趋势: {ma.get('alignment', '-')}")
|
|
208
|
+
macd_icon = "↑金叉" if macd.get('signal') == 1 else "↓死叉" if macd.get('signal') == -1 else "→"
|
|
209
|
+
lines.append(f"MACD: {macd_icon} | {macd.get('bar_trend', '-')}")
|
|
210
|
+
if macd.get("divergence"):
|
|
211
|
+
lines.append(f" ⚠ {macd['divergence']}")
|
|
212
|
+
lines.append(f"量能: {vol.get('volume_ratio_desc', '-')} | {vol.get('volume_price', '-')}")
|
|
213
|
+
lines.append(f"支撑: {sr.get('nearest_support', '-')} | 阻力: {sr.get('nearest_resistance', '-')}")
|
|
214
|
+
if limit_data and limit_data.get("limit_streak", 0) > 0:
|
|
215
|
+
lines.append(f"连板: {limit_data['limit_streak']}板 ({limit_data.get('board_status')})")
|
|
216
|
+
if score["buy_signals"]:
|
|
217
|
+
lines.append(f"买入: {', '.join(score['buy_signals'])}")
|
|
218
|
+
if score["sell_signals"]:
|
|
219
|
+
lines.append(f"卖出: {', '.join(score['sell_signals'])}")
|
|
220
|
+
lines.append(f"⚠ 纯技术视角,不构成投资建议")
|
|
221
|
+
return "\n".join(lines)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""
|
|
2
|
+
RSI 指标(Wilder 平滑方法)。
|
|
3
|
+
无内部依赖。
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def rsi_features(closes, period=14):
|
|
8
|
+
"""RSI 计算(Wilder 指数平滑,与通达信/同花顺一致)。"""
|
|
9
|
+
if len(closes) < period + 1:
|
|
10
|
+
return {"rsi": 50, "signal": 0}
|
|
11
|
+
|
|
12
|
+
# 计算涨跌序列
|
|
13
|
+
gains, losses = [], []
|
|
14
|
+
for i in range(1, len(closes)):
|
|
15
|
+
chg = closes[i] - closes[i - 1]
|
|
16
|
+
gains.append(max(chg, 0))
|
|
17
|
+
losses.append(max(-chg, 0))
|
|
18
|
+
|
|
19
|
+
# Wilder 平滑:初始值用 SMA,后续用指数平滑
|
|
20
|
+
avg_gain = sum(gains[:period]) / period
|
|
21
|
+
avg_loss = sum(losses[:period]) / period
|
|
22
|
+
for i in range(period, len(gains)):
|
|
23
|
+
avg_gain = (avg_gain * (period - 1) + gains[i]) / period
|
|
24
|
+
avg_loss = (avg_loss * (period - 1) + losses[i]) / period
|
|
25
|
+
|
|
26
|
+
if avg_loss == 0:
|
|
27
|
+
rsi = 100
|
|
28
|
+
else:
|
|
29
|
+
rs = avg_gain / avg_loss
|
|
30
|
+
rsi = 100 - 100 / (1 + rs)
|
|
31
|
+
|
|
32
|
+
signal = 0
|
|
33
|
+
if rsi < 30:
|
|
34
|
+
signal = 1
|
|
35
|
+
elif rsi > 70:
|
|
36
|
+
signal = -1
|
|
37
|
+
return {"rsi": round(rsi, 1), "signal": signal}
|
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
"""
|
|
2
|
+
综合评分引擎和市场环境检测。
|
|
3
|
+
依赖: common (to_float, clamp), signals (_generate_signals)
|
|
4
|
+
"""
|
|
5
|
+
from common import clamp, to_float
|
|
6
|
+
|
|
7
|
+
from .signals import _generate_signals
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
# 个股类型 × 指标权重矩阵
|
|
11
|
+
_STOCK_TYPE_WEIGHTS = {
|
|
12
|
+
"题材股": {
|
|
13
|
+
"ma": 0.6, "macd": 0.5, "kdj": 0.5,
|
|
14
|
+
"boll": 0.8, "rsi": 1.0, "volume": 1.3,
|
|
15
|
+
"pattern": 1.5, "limit": 1.5, "chan": 0.5,
|
|
16
|
+
},
|
|
17
|
+
"蓝筹股": {
|
|
18
|
+
"ma": 1.3, "macd": 1.1, "kdj": 0.4,
|
|
19
|
+
"boll": 1.2, "rsi": 0.9, "volume": 0.8,
|
|
20
|
+
"pattern": 0.7, "limit": 0.3, "chan": 0.8,
|
|
21
|
+
},
|
|
22
|
+
"强成长股": {
|
|
23
|
+
"ma": 0.9, "macd": 1.3, "kdj": 0.4,
|
|
24
|
+
"boll": 1.2, "rsi": 0.9, "volume": 1.2,
|
|
25
|
+
"pattern": 0.8, "limit": 0.5, "chan": 0.7,
|
|
26
|
+
},
|
|
27
|
+
"周期股": {
|
|
28
|
+
"ma": 0.6, "macd": 1.3, "kdj": 1.2,
|
|
29
|
+
"boll": 1.0, "rsi": 0.9, "volume": 0.9,
|
|
30
|
+
"pattern": 0.7, "limit": 0.4, "chan": 1.3,
|
|
31
|
+
},
|
|
32
|
+
"稳成长股": {
|
|
33
|
+
"ma": 1.2, "macd": 1.1, "kdj": 0.5,
|
|
34
|
+
"boll": 1.0, "rsi": 1.0, "volume": 0.9,
|
|
35
|
+
"pattern": 1.0, "limit": 0.3, "chan": 0.8,
|
|
36
|
+
},
|
|
37
|
+
"防御股": {
|
|
38
|
+
"ma": 0.8, "macd": 0.9, "kdj": 0.6,
|
|
39
|
+
"boll": 1.1, "rsi": 1.1, "volume": 0.7,
|
|
40
|
+
"pattern": 0.7, "limit": 0.3, "chan": 0.9,
|
|
41
|
+
},
|
|
42
|
+
"普通股": {
|
|
43
|
+
"ma": 1.0, "macd": 1.0, "kdj": 1.0,
|
|
44
|
+
"boll": 1.0, "rsi": 1.0, "volume": 1.0,
|
|
45
|
+
"pattern": 1.0, "limit": 1.0, "chan": 1.0,
|
|
46
|
+
},
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def composite_score(features, stock_type="普通股", market_state=None):
|
|
51
|
+
"""自适应多指标共振评分 0-100,按个股类型和市场环境调整权重。"""
|
|
52
|
+
score = 0
|
|
53
|
+
ma = features.get("ma_system", {})
|
|
54
|
+
macd = features.get("macd") or {}
|
|
55
|
+
kdj = features.get("kdj") or {}
|
|
56
|
+
boll = features.get("bollinger") or {}
|
|
57
|
+
rsi_data = features.get("rsi", {})
|
|
58
|
+
vol = features.get("volume") or {}
|
|
59
|
+
patterns = features.get("patterns", [])
|
|
60
|
+
|
|
61
|
+
# 获取权重
|
|
62
|
+
type_w = _STOCK_TYPE_WEIGHTS.get(stock_type, _STOCK_TYPE_WEIGHTS["普通股"])
|
|
63
|
+
adj = {}
|
|
64
|
+
if market_state:
|
|
65
|
+
adj = _market_weight_adjustments(market_state)
|
|
66
|
+
else:
|
|
67
|
+
adj = _market_weight_adjustments("震荡")
|
|
68
|
+
|
|
69
|
+
# 1. 均线 20 分 × 类型权重 × 市场趋势权重
|
|
70
|
+
alignment = ma.get("alignment", "")
|
|
71
|
+
alignment_scores = {"多头排列": 20, "交叉震荡": 12, "空头排列": 3, "数据不足": 7}
|
|
72
|
+
ma_base = alignment_scores.get(alignment, 7)
|
|
73
|
+
ma_score = ma_base * type_w["ma"] * (adj.get("trend_following", 1.0) if alignment == "多头排列" else 1.0)
|
|
74
|
+
score += clamp(ma_score, 0, 30)
|
|
75
|
+
|
|
76
|
+
# 2. MACD 15 分(上限 20 分,下限 0 分)
|
|
77
|
+
macd_signal = macd.get("signal", 0)
|
|
78
|
+
bar_trend = macd.get("bar_trend", "")
|
|
79
|
+
divergence = macd.get("divergence", "")
|
|
80
|
+
macd_base = 7
|
|
81
|
+
if macd_signal == 1 and "放大" in bar_trend:
|
|
82
|
+
macd_base = 15
|
|
83
|
+
elif macd_signal == 1:
|
|
84
|
+
macd_base = 10
|
|
85
|
+
elif macd_signal == -1:
|
|
86
|
+
macd_base = 3
|
|
87
|
+
macd_score = macd_base * type_w["macd"]
|
|
88
|
+
if divergence == "底背离(看涨)":
|
|
89
|
+
macd_score += 8 * adj.get("divergence_bottom", 1.0)
|
|
90
|
+
elif divergence == "顶背离(看跌)":
|
|
91
|
+
macd_score -= 8 * adj.get("overbought", 1.0)
|
|
92
|
+
score += clamp(macd_score, 0, 20)
|
|
93
|
+
|
|
94
|
+
# 3. KDJ 10 分(辅助信号:仅在震荡市/盘整时生效,其他市场降权)
|
|
95
|
+
# KDJ 与 RSI 功能重叠(超买超卖),KDJ 更适合短线震荡
|
|
96
|
+
market_state_for_kdj = adj.get("trend_following", 1.0)
|
|
97
|
+
kdj_active = market_state_for_kdj < 1.0 # 震荡/熊市时 KDJ 更有效
|
|
98
|
+
kdj_weight = 5 if kdj.get("钝化") else (10 if kdj_active else 5)
|
|
99
|
+
kdj_sig = kdj.get("signal", "")
|
|
100
|
+
# 按关键词匹配评分(支持组合信号如"金叉+超卖"、"死叉+超买"等)
|
|
101
|
+
if "金叉" in kdj_sig and "超卖" in kdj_sig:
|
|
102
|
+
kdj_base = kdj_weight
|
|
103
|
+
elif "金叉" in kdj_sig:
|
|
104
|
+
kdj_base = kdj_weight * 0.8
|
|
105
|
+
elif "死叉" in kdj_sig and "超买" in kdj_sig:
|
|
106
|
+
kdj_base = kdj_weight * 0.1
|
|
107
|
+
elif "死叉" in kdj_sig:
|
|
108
|
+
kdj_base = kdj_weight * 0.2
|
|
109
|
+
elif "超卖" in kdj_sig:
|
|
110
|
+
kdj_base = kdj_weight * 0.6
|
|
111
|
+
elif "超买" in kdj_sig:
|
|
112
|
+
kdj_base = kdj_weight * 0.3
|
|
113
|
+
else:
|
|
114
|
+
kdj_base = kdj_weight * 0.45
|
|
115
|
+
kdj_score = kdj_base * type_w["kdj"]
|
|
116
|
+
score += clamp(kdj_score, 0, 15)
|
|
117
|
+
|
|
118
|
+
# 4. BOLL 10 分
|
|
119
|
+
pos = boll.get("position", 0.5)
|
|
120
|
+
bw = boll.get("bandwidth_desc", "")
|
|
121
|
+
boll_base = 7
|
|
122
|
+
if pos < 0.3 and "收窄" in bw:
|
|
123
|
+
boll_base = 10
|
|
124
|
+
elif 0.3 <= pos <= 0.7:
|
|
125
|
+
boll_base = 7
|
|
126
|
+
elif pos > 0.7:
|
|
127
|
+
boll_base = 4
|
|
128
|
+
else:
|
|
129
|
+
boll_base = 5
|
|
130
|
+
score += boll_base * type_w["boll"]
|
|
131
|
+
|
|
132
|
+
# 5. RSI 10 分
|
|
133
|
+
rsi = rsi_data.get("rsi", 50)
|
|
134
|
+
rsi_base = 7
|
|
135
|
+
if 30 <= rsi <= 40:
|
|
136
|
+
rsi_base = 10
|
|
137
|
+
elif 40 < rsi <= 60:
|
|
138
|
+
rsi_base = 7
|
|
139
|
+
elif 20 <= rsi < 30:
|
|
140
|
+
rsi_base = 8
|
|
141
|
+
elif 60 < rsi <= 70:
|
|
142
|
+
rsi_base = 5
|
|
143
|
+
elif rsi > 70:
|
|
144
|
+
rsi_base = 3
|
|
145
|
+
else:
|
|
146
|
+
rsi_base = 5
|
|
147
|
+
score += rsi_base * type_w["rsi"]
|
|
148
|
+
|
|
149
|
+
# 6. 成交量 15 分
|
|
150
|
+
vp_signal = vol.get("volume_price_signal", 0)
|
|
151
|
+
vr = vol.get("volume_ratio", 1)
|
|
152
|
+
vol_base = 7
|
|
153
|
+
if vp_signal == 1:
|
|
154
|
+
vol_base = 12
|
|
155
|
+
elif vp_signal == -1:
|
|
156
|
+
vol_base = 3
|
|
157
|
+
vol_score = vol_base * type_w["volume"]
|
|
158
|
+
if vr < 0.3:
|
|
159
|
+
vol_score += 3
|
|
160
|
+
score += clamp(vol_score, 0, 20)
|
|
161
|
+
|
|
162
|
+
# 7. K线形态 15 分 × 类型权重 × 市场牛市偏向
|
|
163
|
+
bullish_patterns = ["早晨之星", "阳包阴", "锤子线", "红三兵", "假阴真阳"]
|
|
164
|
+
bearish_patterns = ["黄昏之星", "阴包阳", "倒锤子", "三只乌鸦", "假阳真阴"]
|
|
165
|
+
pattern_score = 7
|
|
166
|
+
for p in patterns:
|
|
167
|
+
ptype = p.get("type", "")
|
|
168
|
+
if any(b in ptype for b in bullish_patterns):
|
|
169
|
+
pattern_score = max(pattern_score, 13)
|
|
170
|
+
if any(b in ptype for b in bearish_patterns):
|
|
171
|
+
pattern_score = min(pattern_score, 3)
|
|
172
|
+
score += clamp(pattern_score * type_w["pattern"] * adj.get("bullish_bias", 1.0), 0, 25)
|
|
173
|
+
|
|
174
|
+
# 8. 缠论加分项(上限 15 分)
|
|
175
|
+
chan_bonus = 0
|
|
176
|
+
chan_data = features.get("chan_theory") or {}
|
|
177
|
+
if chan_data.get("valid"):
|
|
178
|
+
maidain = chan_data.get("maidian", {})
|
|
179
|
+
buy_points = maidain.get("buy_points", [])
|
|
180
|
+
for bp in buy_points:
|
|
181
|
+
bpt = bp.get("type", "")
|
|
182
|
+
if bpt == "一买":
|
|
183
|
+
chan_bonus += 10 * adj.get("buy_point_1", 1.0)
|
|
184
|
+
elif bpt == "二买":
|
|
185
|
+
chan_bonus += 5
|
|
186
|
+
elif bpt == "三买":
|
|
187
|
+
chan_bonus += 8 * adj.get("buy_point_3", 1.0)
|
|
188
|
+
beichi = chan_data.get("beichi", {})
|
|
189
|
+
if beichi.get("summary", "").startswith("检测到底背驰"):
|
|
190
|
+
chan_bonus += 8 * adj.get("divergence_bottom", 1.0)
|
|
191
|
+
score += clamp(chan_bonus, 0, 15)
|
|
192
|
+
|
|
193
|
+
# 9. 本土战法加分(上限 10 分)
|
|
194
|
+
local_bonus = 0
|
|
195
|
+
local_patterns_data = features.get("local_patterns") or {}
|
|
196
|
+
for lp in local_patterns_data.get("patterns", []):
|
|
197
|
+
pname = lp.get("name", "")
|
|
198
|
+
pconf = lp.get("confidence", "中")
|
|
199
|
+
bonus = 0
|
|
200
|
+
if pname == "老鸭头":
|
|
201
|
+
bonus = 8
|
|
202
|
+
elif pname == "美人肩":
|
|
203
|
+
bonus = 6
|
|
204
|
+
elif pname == "三阴一阳":
|
|
205
|
+
bonus = 5
|
|
206
|
+
elif pname == "涨停双响炮":
|
|
207
|
+
bonus = 7
|
|
208
|
+
elif pname == "底部首板":
|
|
209
|
+
bonus = 6
|
|
210
|
+
elif pname == "双针探底":
|
|
211
|
+
bonus = 5
|
|
212
|
+
if pconf == "高":
|
|
213
|
+
bonus *= 1.2
|
|
214
|
+
local_bonus += bonus
|
|
215
|
+
score += clamp(local_bonus, 0, 10)
|
|
216
|
+
|
|
217
|
+
score = clamp(score, 0, 100)
|
|
218
|
+
|
|
219
|
+
# 定级(含模糊区间:边界附近标注"边界")
|
|
220
|
+
if score >= 80:
|
|
221
|
+
grade = "强烈看多"
|
|
222
|
+
elif score >= 75:
|
|
223
|
+
grade = "偏多(强)" # 模糊区间:75-80
|
|
224
|
+
elif score >= 60:
|
|
225
|
+
grade = "偏多"
|
|
226
|
+
elif score >= 55:
|
|
227
|
+
grade = "中性(偏多)" # 模糊区间:55-65
|
|
228
|
+
elif score >= 40:
|
|
229
|
+
grade = "中性"
|
|
230
|
+
elif score >= 35:
|
|
231
|
+
grade = "中性(偏空)" # 模糊区间:35-45
|
|
232
|
+
elif score >= 20:
|
|
233
|
+
grade = "偏空"
|
|
234
|
+
elif score >= 15:
|
|
235
|
+
grade = "偏空(强)" # 模糊区间:15-25
|
|
236
|
+
else:
|
|
237
|
+
grade = "强烈看空"
|
|
238
|
+
|
|
239
|
+
buy_signals, sell_signals = _generate_signals(features)
|
|
240
|
+
|
|
241
|
+
return {
|
|
242
|
+
"score": round(score, 1),
|
|
243
|
+
"grade": grade,
|
|
244
|
+
"buy_signals": buy_signals,
|
|
245
|
+
"sell_signals": sell_signals,
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def detect_market_environment(index_quote=None, recent_quotes=None):
|
|
250
|
+
"""
|
|
251
|
+
检测当前市场环境(牛市/熊市/震荡/冰点/亢奋)。
|
|
252
|
+
优先使用大盘数据(涨跌停家数),不可得时用指数技术指标推断。
|
|
253
|
+
支持多日窗口判断,避免单日噪声。
|
|
254
|
+
|
|
255
|
+
Args:
|
|
256
|
+
index_quote: 大盘指数行情 dict(可选)
|
|
257
|
+
recent_quotes: 近期大盘行情列表(可选,用于多日均值判断)
|
|
258
|
+
|
|
259
|
+
Returns:
|
|
260
|
+
{
|
|
261
|
+
"state": "牛市"|"熊市"|"震荡"|"冰点"|"亢奋",
|
|
262
|
+
"confidence": "高"|"中"|"低",
|
|
263
|
+
"signals": [...],
|
|
264
|
+
"weight_adjustments": {...},
|
|
265
|
+
}
|
|
266
|
+
"""
|
|
267
|
+
state = "震荡"
|
|
268
|
+
confidence = "低"
|
|
269
|
+
signals = []
|
|
270
|
+
|
|
271
|
+
if index_quote and isinstance(index_quote, dict):
|
|
272
|
+
price = to_float(index_quote.get("price"))
|
|
273
|
+
change_pct = to_float(index_quote.get("change_pct"))
|
|
274
|
+
turnover = to_float(index_quote.get("turnover"))
|
|
275
|
+
|
|
276
|
+
# 多日窗口:用近期数据的均值平滑单日噪声
|
|
277
|
+
if recent_quotes and len(recent_quotes) > 1:
|
|
278
|
+
recent_changes = [to_float(q.get("change_pct")) for q in recent_quotes]
|
|
279
|
+
recent_turnovers = [to_float(q.get("turnover")) for q in recent_quotes]
|
|
280
|
+
avg_change = sum(recent_changes) / len(recent_changes)
|
|
281
|
+
avg_turnover = sum(recent_turnovers) / len(recent_turnovers)
|
|
282
|
+
window_days = len(recent_quotes)
|
|
283
|
+
signals.append(f"近{window_days}日均涨跌{avg_change:.2f}%")
|
|
284
|
+
else:
|
|
285
|
+
avg_change = change_pct
|
|
286
|
+
avg_turnover = turnover
|
|
287
|
+
|
|
288
|
+
# 用多日均值判断趋势
|
|
289
|
+
if avg_change > 1.5:
|
|
290
|
+
state = "牛市"
|
|
291
|
+
confidence = "高" if avg_change > 2.5 else "中"
|
|
292
|
+
signals.append(f"持续上涨(均值{avg_change:.1f}%)")
|
|
293
|
+
elif avg_change < -1.5:
|
|
294
|
+
state = "熊市"
|
|
295
|
+
confidence = "高" if avg_change < -2.5 else "中"
|
|
296
|
+
signals.append(f"持续下跌(均值{avg_change:.1f}%)")
|
|
297
|
+
elif avg_change > 0.3:
|
|
298
|
+
state = "牛市"
|
|
299
|
+
confidence = "低"
|
|
300
|
+
signals.append(f"温和上涨(均值{avg_change:.1f}%)")
|
|
301
|
+
elif avg_change < -0.3:
|
|
302
|
+
state = "熊市"
|
|
303
|
+
confidence = "低"
|
|
304
|
+
signals.append(f"温和下跌(均值{avg_change:.1f}%)")
|
|
305
|
+
else:
|
|
306
|
+
signals.append(f"窄幅震荡(均值{avg_change:.1f}%)")
|
|
307
|
+
|
|
308
|
+
# 用当日数据补充极端信号
|
|
309
|
+
if change_pct > 2:
|
|
310
|
+
signals.append(f"当日大涨{change_pct:.1f}%")
|
|
311
|
+
elif change_pct < -2:
|
|
312
|
+
signals.append(f"当日大跌{change_pct:.1f}%")
|
|
313
|
+
|
|
314
|
+
if avg_turnover > 5:
|
|
315
|
+
signals.append("高换手率")
|
|
316
|
+
if state == "牛市":
|
|
317
|
+
state = "亢奋"
|
|
318
|
+
signals.append("亢奋信号")
|
|
319
|
+
elif avg_turnover < 0.5:
|
|
320
|
+
signals.append("极度缩量")
|
|
321
|
+
if state in ("熊市", "震荡"):
|
|
322
|
+
state = "冰点"
|
|
323
|
+
signals.append("冰点信号")
|
|
324
|
+
else:
|
|
325
|
+
signals.append("大盘数据缺失,默认震荡")
|
|
326
|
+
|
|
327
|
+
# 市场 → 信号权重调整
|
|
328
|
+
adjustments = _market_weight_adjustments(state)
|
|
329
|
+
|
|
330
|
+
return {
|
|
331
|
+
"state": state,
|
|
332
|
+
"confidence": confidence,
|
|
333
|
+
"signals": signals,
|
|
334
|
+
"weight_adjustments": adjustments,
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
def _market_weight_adjustments(state):
|
|
339
|
+
"""市场环境 → 信号权重因子。"""
|
|
340
|
+
adjustments = {
|
|
341
|
+
"牛市": {
|
|
342
|
+
"bullish_bias": 1.3,
|
|
343
|
+
"trend_following": 1.4,
|
|
344
|
+
"breakout": 1.3,
|
|
345
|
+
"divergence_bottom": 0.5,
|
|
346
|
+
"buy_point_1": 0.5,
|
|
347
|
+
"buy_point_3": 1.3,
|
|
348
|
+
"overbought": 0.8,
|
|
349
|
+
"desc": "牛市:趋势跟随加权,底背离/一买降权",
|
|
350
|
+
},
|
|
351
|
+
"熊市": {
|
|
352
|
+
"bullish_bias": 1.5,
|
|
353
|
+
"trend_following": 0.6,
|
|
354
|
+
"breakout": 0.6,
|
|
355
|
+
"divergence_bottom": 1.5,
|
|
356
|
+
"buy_point_1": 1.5,
|
|
357
|
+
"buy_point_3": 0.5,
|
|
358
|
+
"overbought": 1.3,
|
|
359
|
+
"desc": "熊市:反转信号加权,追涨信号降权",
|
|
360
|
+
},
|
|
361
|
+
"震荡": {
|
|
362
|
+
"bullish_bias": 1.0,
|
|
363
|
+
"trend_following": 0.8,
|
|
364
|
+
"breakout": 0.8,
|
|
365
|
+
"divergence_bottom": 1.2,
|
|
366
|
+
"buy_point_1": 1.1,
|
|
367
|
+
"buy_point_3": 1.2,
|
|
368
|
+
"overbought": 1.0,
|
|
369
|
+
"desc": "震荡:反转+区间交易加权,趋势信号降权",
|
|
370
|
+
},
|
|
371
|
+
"冰点": {
|
|
372
|
+
"bullish_bias": 1.8,
|
|
373
|
+
"trend_following": 0.3,
|
|
374
|
+
"breakout": 0.4,
|
|
375
|
+
"divergence_bottom": 1.8,
|
|
376
|
+
"buy_point_1": 2.0,
|
|
377
|
+
"buy_point_3": 0.3,
|
|
378
|
+
"overbought": 1.5,
|
|
379
|
+
"desc": "冰点:极度超卖反转加权,趋势信号大幅降权",
|
|
380
|
+
},
|
|
381
|
+
"亢奋": {
|
|
382
|
+
"bullish_bias": 0.6,
|
|
383
|
+
"trend_following": 0.5,
|
|
384
|
+
"breakout": 0.5,
|
|
385
|
+
"divergence_bottom": 0.4,
|
|
386
|
+
"buy_point_1": 0.3,
|
|
387
|
+
"buy_point_3": 0.5,
|
|
388
|
+
"overbought": 0.3,
|
|
389
|
+
"desc": "亢奋:全面保守,警惕反转",
|
|
390
|
+
},
|
|
391
|
+
}
|
|
392
|
+
return adjustments.get(state, adjustments["震荡"])
|