-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmarket_risk_analysis.py
More file actions
332 lines (282 loc) · 13.1 KB
/
Copy pathmarket_risk_analysis.py
File metadata and controls
332 lines (282 loc) · 13.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
"""Deep market trend analysis for risk assessment"""
import baostock as bs
import pandas as pd
import numpy as np
lg = bs.login()
# ============================================================
# Part 1: Major Index Deep Analysis (120 days)
# ============================================================
print("=" * 90)
print("PART 1: MAJOR INDEX DEEP ANALYSIS (120 Days)")
print("=" * 90)
indices = [
("sh.000001", "上证指数"),
("sz.399001", "深证成指"),
("sz.399006", "创业板指"),
("sh.000300", "沪深300"),
("sh.000016", "上证50"),
("sz.399673", "创业板50"),
]
for code, name in indices:
rs = bs.query_history_k_data_plus(code,
"date,close,high,low,volume,amount,pctChg",
start_date="2026-01-01", end_date="2026-05-19",
frequency="d", adjustflag="3")
data_list = []
while (rs.error_code == "0") & rs.next():
data_list.append(rs.get_row_data())
if data_list:
df = pd.DataFrame(data_list, columns=rs.fields)
df["date"] = pd.to_datetime(df["date"])
df = df.sort_values("date")
for col in ["close", "high", "low", "volume", "amount", "pctChg"]:
df[col] = df[col].astype(float)
# Moving averages
df["MA5"] = df["close"].rolling(5).mean()
df["MA10"] = df["close"].rolling(10).mean()
df["MA20"] = df["close"].rolling(20).mean()
df["MA60"] = df["close"].rolling(60).mean()
df["MA120"] = df["close"].rolling(120).mean()
# Bollinger Bands (20,2)
df["BB_mid"] = df["close"].rolling(20).mean()
df["BB_std"] = df["close"].rolling(20).std()
df["BB_upper"] = df["BB_mid"] + 2 * df["BB_std"]
df["BB_lower"] = df["BB_mid"] - 2 * df["BB_std"]
# RSI
delta = df["close"].diff()
gain = delta.where(delta > 0, 0).rolling(14).mean()
loss = (-delta.where(delta < 0, 0)).rolling(14).mean()
rs_val = gain / loss.replace(0, 0.001)
df["RSI14"] = 100 - (100 / (1 + rs_val))
# MACD
df["EMA12"] = df["close"].ewm(span=12).mean()
df["EMA26"] = df["close"].ewm(span=26).mean()
df["DIF"] = df["EMA12"] - df["EMA26"]
df["DEA"] = df["DIF"].ewm(span=9).mean()
df["MACD"] = (df["DIF"] - df["DEA"]) * 2
last = df.iloc[-1]
prev = df.iloc[-2]
# Key levels
recent_high = df["high"].tail(20).max()
recent_low = df["low"].tail(20).min()
# Trend assessment
ma_bullish = 0
if last["close"] > last["MA5"]: ma_bullish += 1
if last["close"] > last["MA20"]: ma_bullish += 1
if last["close"] > last["MA60"]: ma_bullish += 1
if last["MA5"] > last["MA20"]: ma_bullish += 1
if last["MA20"] > last["MA60"]: ma_bullish += 1
# MACD signal
if last["DIF"] > last["DEA"] and prev["DIF"] <= prev["DEA"]:
macd_signal = "GOLDEN CROSS"
elif last["DIF"] < last["DEA"] and prev["DIF"] >= prev["DEA"]:
macd_signal = "DEAD CROSS"
elif last["DIF"] > last["DEA"] and last["MACD"] > 0:
macd_signal = "BULLISH"
elif last["DIF"] > last["DEA"] and last["MACD"] < 0:
macd_signal = "WEAKENING"
elif last["DIF"] < last["DEA"] and last["MACD"] < 0:
macd_signal = "BEARISH"
else:
macd_signal = "RECOVERING"
# Bollinger position
bb_pos = (last["close"] - last["BB_lower"]) / (last["BB_upper"] - last["BB_lower"]) * 100
# Volatility
vol_20d = df["pctChg"].tail(20).std()
# Drawdown from recent high
max_close = df["close"].tail(60).max()
drawdown = (last["close"] / max_close - 1) * 100
print(f"\n {name} ({code})")
print(f" Latest: {last['close']:.2f} | RSI14: {last['RSI14']:.1f}")
print(f" MA5: {last['MA5']:.2f} | MA20: {last['MA20']:.2f} | MA60: {last['MA60']:.2f}")
print(f" MA Score: {ma_bullish}/5 | MACD: {macd_signal}")
print(f" Bollinger: {bb_pos:.0f}% (0%=lower, 100%=upper)")
print(f" 20d Vol: {vol_20d:.2f}% | Drawdown from 60d high: {drawdown:+.2f}%")
print(f" 20d High: {recent_high:.2f} | 20d Low: {recent_low:.2f}")
print(f" DIF: {last['DIF']:.2f} | DEA: {last['DEA']:.2f} | MACD: {last['MACD']:.4f}")
# ============================================================
# Part 2: Sector/Theme Analysis - Computing Power Chain
# ============================================================
print(f"\n{'='*90}")
print("PART 2: COMPUTING POWER CHAIN ANALYSIS")
print("=" * 90)
# Key sectors and representative stocks
sector_stocks = {
"GPU/AI芯片": ["sh.688256", "sz.002049"], # 寒武纪, 紫光国微
"HBM/存储": ["sh.603501", "sz.002077"], # 韦尔股份, 大港股份
"封测(先进封装)": ["sh.600584", "sz.002185"], # 长电科技, 华天科技
"光模块": ["sz.003031", "sz.300308"], # 中瓷电子, 中际旭创
"PCB": ["sz.002938", "sz.300623"], # 鹏鼎控股, 捷捷微电
"服务器": ["sz.000977", "sh.600756"], # 浪潮信息, 浪潮软件
"运营商(Token经济)": ["sh.600941", "sh.601728"], # 中国移动, 中国电信
"电力设备": ["sh.600406", "sz.002050"], # 国电南瑞, 三花智控
}
sector_names = {
"sh.688256": "寒武纪", "sz.002049": "紫光国微",
"sh.603501": "韦尔股份", "sz.002077": "大港股份",
"sh.600584": "长电科技", "sz.002185": "华天科技",
"sz.003031": "中瓷电子", "sz.300308": "中际旭创",
"sz.002938": "鹏鼎控股", "sz.300623": "捷捷微电",
"sz.000977": "浪潮信息", "sh.600756": "浪潮软件",
"sh.600941": "中国移动", "sh.601728": "中国电信",
"sh.600406": "国电南瑞", "sz.002050": "三花智控",
}
print(f"\n {'Sector':<20} {'Stock':<10} {'5d Ret':>8} {'20d Ret':>8} {'RSI14':>6} {'NetFlow5d':>12} {'Signal'}")
print(f" {'-'*20} {'-'*10} {'-'*8} {'-'*8} {'-'*6} {'-'*12} {'-'*20}")
sector_results = {}
for sector, codes in sector_stocks.items():
sector_results[sector] = []
for code in codes:
name = sector_names.get(code, code)
rs = bs.query_history_k_data_plus(code,
"date,close,pctChg,amount",
start_date="2026-03-01", end_date="2026-05-19",
frequency="d", adjustflag="3")
data_list = []
while (rs.error_code == "0") & rs.next():
data_list.append(rs.get_row_data())
if data_list:
df = pd.DataFrame(data_list, columns=rs.fields)
df["date"] = pd.to_datetime(df["date"])
df = df.sort_values("date")
df["close"] = df["close"].astype(float)
df["pctChg"] = df["pctChg"].astype(float)
df["amount"] = df["amount"].astype(float)
ret_5d = (df["close"].iloc[-1] / df["close"].iloc[-5] - 1) * 100 if len(df) >= 5 else 0
ret_20d = (df["close"].iloc[-1] / df["close"].iloc[-20] - 1) * 100 if len(df) >= 20 else 0
# RSI
delta = df["close"].diff()
gain = delta.where(delta > 0, 0).rolling(14).mean()
loss_val = (-delta.where(delta < 0, 0)).rolling(14).mean()
rs_val = gain / loss_val.replace(0, 0.001)
df["RSI14"] = 100 - (100 / (1 + rs_val))
last_rsi = df["RSI14"].iloc[-1]
# Net flow
df["inflow"] = df.apply(lambda r: r["amount"] if r["pctChg"] > 0 else 0, axis=1)
df["outflow"] = df.apply(lambda r: r["amount"] if r["pctChg"] < 0 else 0, axis=1)
net_5d = (df["inflow"].tail(5).sum() - df["outflow"].tail(5).sum()) / 1e8
# Signal
signals = []
if last_rsi > 80: signals.append("EXTREME OB")
elif last_rsi > 70: signals.append("OVERBOUGHT")
elif last_rsi < 30: signals.append("OVERSOLD")
if ret_5d > 15: signals.append("HOT")
elif ret_5d > 5: signals.append("STRONG")
elif ret_5d < -5: signals.append("WEAK")
signal_str = " | ".join(signals) if signals else "NEUTRAL"
sector_results[sector].append({
"name": name, "ret_5d": ret_5d, "ret_20d": ret_20d,
"rsi": last_rsi, "net_5d": net_5d, "signal": signal_str
})
print(f" {sector:<20} {name:<10} {ret_5d:>+7.2f}% {ret_20d:>+7.2f}% {last_rsi:>6.1f} {net_5d:>+10.2f}亿 {signal_str}")
# Sector summary
print(f"\n {'='*80}")
print(f" SECTOR ROTATION SUMMARY")
print(f" {'='*80}")
for sector, stocks_data in sector_results.items():
avg_5d = np.mean([s["ret_5d"] for s in stocks_data])
avg_rsi = np.mean([s["rsi"] for s in stocks_data])
avg_net = np.sum([s["net_5d"] for s in stocks_data])
if avg_rsi > 75:
status = "OVERHEATED - 高位风险"
elif avg_rsi > 65:
status = "STRONG - 强势但注意回调"
elif avg_rsi > 50:
status = "HEALTHY - 健康上涨"
elif avg_rsi > 35:
status = "NEUTRAL - 中性"
else:
status = "WEAK - 弱势"
print(f" {sector:<20} 5d Avg: {avg_5d:>+6.2f}% | RSI: {avg_rsi:>5.1f} | NetFlow: {avg_net:>+8.2f}亿 | {status}")
# ============================================================
# Part 3: Market Risk Indicators
# ============================================================
print(f"\n{'='*90}")
print("PART 3: MARKET RISK INDICATORS")
print("=" * 90)
# Up/Down ratio
rs = bs.query_history_k_data_plus("sh.000001",
"date,close,pctChg,volume,amount",
start_date="2026-04-01", end_date="2026-05-19",
frequency="d", adjustflag="3")
idx_data = []
while (rs.error_code == "0") & rs.next():
idx_data.append(rs.get_row_data())
if idx_data:
df_idx = pd.DataFrame(idx_data, columns=rs.fields)
df_idx["date"] = pd.to_datetime(df_idx["date"])
df_idx = df_idx.sort_values("date")
df_idx["close"] = df_idx["close"].astype(float)
df_idx["pctChg"] = df_idx["pctChg"].astype(float)
df_idx["volume"] = df_idx["volume"].astype(float)
# Recent trend analysis
print(f"\n 上证指数 Daily Changes (Recent 20 days):")
print(f" {'Date':<12} {'Close':>10} {'Chg%':>8} {'Vol(亿)':>12} {'Signal'}")
print(f" {'-'*12} {'-'*10} {'-'*8} {'-'*12} {'-'*20}")
recent = df_idx.tail(20)
for _, row in recent.iterrows():
vol_yi = row["volume"] / 1e8
signal = ""
if row["pctChg"] > 1: signal = "STRONG UP"
elif row["pctChg"] > 0: signal = "UP"
elif row["pctChg"] > -1: signal = "DOWN"
else: signal = "STRONG DOWN"
print(f" {row['date'].strftime('%Y-%m-%d'):<12} {row['close']:>10.2f} {row['pctChg']:>+7.2f}% {vol_yi:>10.2f} {signal}")
# Key support/resistance
df_idx["MA5"] = df_idx["close"].rolling(5).mean()
df_idx["MA20"] = df_idx["close"].rolling(20).mean()
df_idx["MA60"] = df_idx["close"].rolling(60).mean()
last = df_idx.iloc[-1]
print(f"\n Key Levels:")
print(f" Current: {last['close']:.2f}")
print(f" MA5: {last['MA5']:.2f} ({'SUPPORT' if last['close'] > last['MA5'] else 'RESISTANCE'})")
print(f" MA20: {last['MA20']:.2f} ({'SUPPORT' if last['close'] > last['MA20'] else 'RESISTANCE'})")
print(f" MA60: {last['MA60']:.2f} ({'SUPPORT' if last['close'] > last['MA60'] else 'RESISTANCE'})")
# Count up/down days in recent 20
up_days = (recent["pctChg"] > 0).sum()
down_days = (recent["pctChg"] < 0).sum()
print(f"\n Recent 20 days: {up_days} UP / {down_days} DOWN")
# Volume trend
vol_5d = df_idx["volume"].tail(5).mean()
vol_20d = df_idx["volume"].tail(20).mean()
vol_ratio = vol_5d / vol_20d
print(f" Volume 5d/20d ratio: {vol_ratio:.2f} ({'SHRINKING' if vol_ratio < 0.8 else 'EXPANDING' if vol_ratio > 1.2 else 'NORMAL'})")
# Consecutive down days
consec_down = 0
for i in range(len(df_idx)-1, -1, -1):
if df_idx.iloc[i]["pctChg"] < 0:
consec_down += 1
else:
break
print(f" Consecutive down days: {consec_down}")
# ============================================================
# Part 4: Risk Assessment Summary
# ============================================================
print(f"\n{'='*90}")
print("PART 4: RISK ASSESSMENT SUMMARY")
print("=" * 90)
print("""
综合研判:
1. 大盘趋势:
- 上证4169站在MA20(4135)和MA60之上,中期趋势未破
- 但6大指数5日全部下跌,短期处于调整中
- MACD信号需要关注是否出现死叉
2. 算力主线:
- GPU/AI芯片: 已大幅上涨,RSI偏高,风险较大
- HBM/存储: 跟涨阶段,仍有空间
- 封测(先进封装): 刚启动,资金流入强劲,空间最大
- 光模块: 第二波上涨,RSI偏高但资金仍在流入
- PCB: 跟随算力需求,鹏鼎控股HDI板技术壁垒高
- 运营商: Token经济逻辑,低位轮动,空间有限
3. 风险信号:
- 算力链多只股票RSI>80,极度超买
- 市场成交量萎缩,5d/20d量比<1
- 连续下跌天数增加
4. 操作建议:
- 短期市场面临调整压力
- 超买股(盛合晶微RSI83.5、中瓷电子RSI81.2)建议减仓1/3
- 封测(长电科技)和PCB(鹏鼎)仍有上涨逻辑,可持有
- 运营商(Token经济)已轮动到位,不宜追高
- 等待大盘调整结束后再考虑加仓
""")
bs.logout()