aggroot 1.0.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/.env.example +173 -0
- package/README.md +43 -0
- package/dist/cli.cjs +22 -0
- package/dist/index.cjs +43931 -0
- package/dist/tree-sitter-c.wasm +0 -0
- package/dist/tree-sitter-cpp.wasm +0 -0
- package/dist/web-tree-sitter.wasm +0 -0
- package/install-linux.sh +107 -0
- package/install-windows.bat +103 -0
- package/package.json +151 -0
- package/share/agents/agent-ai-partner.json +66 -0
- package/share/agents/agent-ai.json +48 -0
- package/share/agents/agent-alpha-investor.json +97 -0
- package/share/agents/agent-coder.json +211 -0
- package/share/agents/agent-devops.json +59 -0
- package/share/agents/agent-resume.json +117 -0
- package/share/agents/agent-risk-control.json +84 -0
- package/share/agents/agent-writer.json +213 -0
- package/share/scripts/_utils.py +105 -0
- package/share/scripts/market_overview.py +244 -0
- package/share/scripts/stock_compare.py +244 -0
- package/share/scripts/stock_financial.py +211 -0
- package/share/scripts/stock_news.py +181 -0
- package/share/scripts/stock_quote.py +433 -0
- package/share/scripts/stock_technical.py +402 -0
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
"""
|
|
2
|
+
股票实时行情查询
|
|
3
|
+
|
|
4
|
+
用法: python stock_quote.py <stock_code> [--detail]
|
|
5
|
+
stock_code: 6位A股代码(如 600519) 或 5位港股代码(如 03888)
|
|
6
|
+
--detail: 获取详细信息(公司概况等)
|
|
7
|
+
|
|
8
|
+
AKShare接口:
|
|
9
|
+
A股主: ak.stock_zh_a_spot() — 新浪实时行情(稳定可用)
|
|
10
|
+
A股备: ak.stock_zh_a_spot_em() — 东方财富实时行情(含PE/PB/市值等)
|
|
11
|
+
A股补: ak.stock_individual_info_em(symbol) — 个股详细信息(行业/市值)
|
|
12
|
+
港股主: ak.stock_hk_hist_min_em(symbol) — 东方财富分钟线(实时,盘中最快)
|
|
13
|
+
港股备: ak.stock_hk_daily(symbol) — 新浪港股日线行情(T-1数据,稳定)
|
|
14
|
+
港股补: ak.stock_hk_financial_indicator_em(symbol) — PE/PB/市值/ROE
|
|
15
|
+
港股补: ak.stock_hk_company_profile_em(symbol) — 公司名称/行业
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import sys
|
|
19
|
+
import argparse
|
|
20
|
+
from datetime import datetime
|
|
21
|
+
|
|
22
|
+
sys.path.insert(0, '.')
|
|
23
|
+
from _utils import normalize_code, get_market_prefix, output_json, output_error, check_akshare, safe_float, is_hk_code
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _fetch_em(code):
|
|
27
|
+
"""东方财富A股接口:数据最全(含PE/PB/市值/换手率/量比等)"""
|
|
28
|
+
import akshare as ak
|
|
29
|
+
|
|
30
|
+
df = ak.stock_zh_a_spot_em()
|
|
31
|
+
row = df[df['代码'] == code]
|
|
32
|
+
|
|
33
|
+
if row.empty:
|
|
34
|
+
return None
|
|
35
|
+
|
|
36
|
+
r = row.iloc[0]
|
|
37
|
+
return {
|
|
38
|
+
"code": code,
|
|
39
|
+
"name": str(r.get('名称', '')),
|
|
40
|
+
"price": safe_float(r.get('最新价')),
|
|
41
|
+
"change_pct": safe_float(r.get('涨跌幅')),
|
|
42
|
+
"change_amt": safe_float(r.get('涨跌额')),
|
|
43
|
+
"volume": safe_float(r.get('成交量')),
|
|
44
|
+
"amount": safe_float(r.get('成交额')),
|
|
45
|
+
"amplitude": safe_float(r.get('振幅')),
|
|
46
|
+
"high": safe_float(r.get('最高')),
|
|
47
|
+
"low": safe_float(r.get('最低')),
|
|
48
|
+
"open": safe_float(r.get('今开')),
|
|
49
|
+
"prev_close": safe_float(r.get('昨收')),
|
|
50
|
+
"turnover_rate": safe_float(r.get('换手率')),
|
|
51
|
+
"pe_ratio": safe_float(r.get('市盈率-动态')),
|
|
52
|
+
"pb_ratio": safe_float(r.get('市净率')),
|
|
53
|
+
"total_market_cap": safe_float(r.get('总市值')),
|
|
54
|
+
"circulating_market_cap": safe_float(r.get('流通市值')),
|
|
55
|
+
"rise_speed": safe_float(r.get('5分钟涨速')),
|
|
56
|
+
"volume_ratio": safe_float(r.get('量比')),
|
|
57
|
+
"data_source": "eastmoney",
|
|
58
|
+
"data_time": datetime.now().strftime('%Y-%m-%d %H:%M'),
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _fetch_sina(code):
|
|
63
|
+
"""新浪A股日线接口:快速稳定(当日数据可用)"""
|
|
64
|
+
import akshare as ak
|
|
65
|
+
|
|
66
|
+
prefix = get_market_prefix(code)
|
|
67
|
+
sina_code = f"{prefix}{code}"
|
|
68
|
+
|
|
69
|
+
# 优先用 daily 获取当日数据(快速)
|
|
70
|
+
try:
|
|
71
|
+
df = ak.stock_zh_a_daily(symbol=sina_code, adjust="qfq")
|
|
72
|
+
if not df.empty and len(df) >= 2:
|
|
73
|
+
r = df.iloc[-1]
|
|
74
|
+
prev_r = df.iloc[-2]
|
|
75
|
+
prev_close = safe_float(prev_r['close'])
|
|
76
|
+
price = safe_float(r['close'])
|
|
77
|
+
change_amt = round(price - prev_close, 3) if prev_close and price else None
|
|
78
|
+
change_pct = round(change_amt / prev_close * 100, 3) if prev_close and change_amt is not None else None
|
|
79
|
+
return {
|
|
80
|
+
"code": code,
|
|
81
|
+
"name": "",
|
|
82
|
+
"price": price,
|
|
83
|
+
"change_pct": change_pct,
|
|
84
|
+
"change_amt": change_amt,
|
|
85
|
+
"volume": safe_float(r.get('volume')),
|
|
86
|
+
"amount": safe_float(r.get('amount')),
|
|
87
|
+
"high": safe_float(r.get('high')),
|
|
88
|
+
"low": safe_float(r.get('low')),
|
|
89
|
+
"open": safe_float(r.get('open')),
|
|
90
|
+
"prev_close": prev_close,
|
|
91
|
+
"date": str(r.get('date', '')),
|
|
92
|
+
"data_source": "sina_daily",
|
|
93
|
+
"data_time": str(r.get('date', '')) + " (收盘)",
|
|
94
|
+
}
|
|
95
|
+
except Exception:
|
|
96
|
+
pass
|
|
97
|
+
|
|
98
|
+
# 备选:全量spot接口(慢,但含名称)
|
|
99
|
+
df = ak.stock_zh_a_spot()
|
|
100
|
+
row = df[df['代码'] == sina_code]
|
|
101
|
+
|
|
102
|
+
if row.empty:
|
|
103
|
+
return None
|
|
104
|
+
|
|
105
|
+
r = row.iloc[0]
|
|
106
|
+
return {
|
|
107
|
+
"code": code,
|
|
108
|
+
"name": str(r.get('名称', '')),
|
|
109
|
+
"price": safe_float(r.get('最新价')),
|
|
110
|
+
"change_pct": safe_float(r.get('涨跌幅')),
|
|
111
|
+
"change_amt": safe_float(r.get('涨跌额')),
|
|
112
|
+
"volume": safe_float(r.get('成交量')),
|
|
113
|
+
"amount": safe_float(r.get('成交额')),
|
|
114
|
+
"high": safe_float(r.get('最高')),
|
|
115
|
+
"low": safe_float(r.get('最低')),
|
|
116
|
+
"open": safe_float(r.get('今开')),
|
|
117
|
+
"prev_close": safe_float(r.get('昨收')),
|
|
118
|
+
"data_source": "sina",
|
|
119
|
+
"data_time": datetime.now().strftime('%Y-%m-%d %H:%M'),
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _fetch_hk_realtime(code):
|
|
124
|
+
"""东方财富港股分钟线接口:盘中实时数据(优先使用)"""
|
|
125
|
+
import akshare as ak
|
|
126
|
+
|
|
127
|
+
try:
|
|
128
|
+
df = ak.stock_hk_hist_min_em(symbol=code, period='1')
|
|
129
|
+
if df.empty:
|
|
130
|
+
return None
|
|
131
|
+
|
|
132
|
+
last = df.iloc[-1]
|
|
133
|
+
price = safe_float(last.get('收盘'))
|
|
134
|
+
|
|
135
|
+
# 从分钟线构造今日行情
|
|
136
|
+
today_str = str(last.get('时间', ''))[:10]
|
|
137
|
+
today_data = df[df['时间'].astype(str).str.startswith(today_str)]
|
|
138
|
+
|
|
139
|
+
if today_data.empty:
|
|
140
|
+
return None
|
|
141
|
+
|
|
142
|
+
open_price = safe_float(today_data.iloc[0].get('开盘'))
|
|
143
|
+
high = max(safe_float(r.get('最高')) for _, r in today_data.iterrows() if safe_float(r.get('最高')) is not None)
|
|
144
|
+
low = min(safe_float(r.get('最低')) for _, r in today_data.iterrows() if safe_float(r.get('最低')) is not None)
|
|
145
|
+
volume = sum(safe_float(r.get('成交量')) or 0 for _, r in today_data.iterrows())
|
|
146
|
+
amount = sum(safe_float(r.get('成交额')) or 0 for _, r in today_data.iterrows())
|
|
147
|
+
|
|
148
|
+
# 计算涨跌需要昨日收盘
|
|
149
|
+
prev_close = None
|
|
150
|
+
change_pct = None
|
|
151
|
+
change_amt = None
|
|
152
|
+
try:
|
|
153
|
+
daily_df = ak.stock_hk_daily(symbol=code, adjust="qfq")
|
|
154
|
+
if len(daily_df) >= 2:
|
|
155
|
+
prev_close = safe_float(daily_df.iloc[-2]['close'])
|
|
156
|
+
if prev_close and price:
|
|
157
|
+
change_amt = round(price - prev_close, 3)
|
|
158
|
+
change_pct = round(change_amt / prev_close * 100, 3)
|
|
159
|
+
except Exception:
|
|
160
|
+
pass
|
|
161
|
+
|
|
162
|
+
return {
|
|
163
|
+
"code": code,
|
|
164
|
+
"name": "",
|
|
165
|
+
"price": price,
|
|
166
|
+
"change_pct": change_pct,
|
|
167
|
+
"change_amt": change_amt,
|
|
168
|
+
"volume": volume,
|
|
169
|
+
"amount": amount,
|
|
170
|
+
"high": high,
|
|
171
|
+
"low": low,
|
|
172
|
+
"open": open_price,
|
|
173
|
+
"prev_close": prev_close,
|
|
174
|
+
"date": today_str,
|
|
175
|
+
"data_source": "eastmoney_min",
|
|
176
|
+
"data_time": str(last.get('时间', '')),
|
|
177
|
+
}
|
|
178
|
+
except Exception:
|
|
179
|
+
return None
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def _fetch_hk_sina(code):
|
|
183
|
+
"""新浪港股接口:通过 stock_hk_daily 取最新数据(T-1,稳定)"""
|
|
184
|
+
import akshare as ak
|
|
185
|
+
|
|
186
|
+
df = ak.stock_hk_daily(symbol=code, adjust="qfq")
|
|
187
|
+
if df.empty:
|
|
188
|
+
return None
|
|
189
|
+
|
|
190
|
+
r = df.iloc[-1]
|
|
191
|
+
prev_r = df.iloc[-2] if len(df) >= 2 else None
|
|
192
|
+
prev_close = safe_float(prev_r['close']) if prev_r is not None else None
|
|
193
|
+
price = safe_float(r['close'])
|
|
194
|
+
change_amt = round(price - prev_close, 3) if prev_close and price else None
|
|
195
|
+
change_pct = round(change_amt / prev_close * 100, 3) if prev_close and change_amt is not None else None
|
|
196
|
+
|
|
197
|
+
return {
|
|
198
|
+
"code": code,
|
|
199
|
+
"name": "",
|
|
200
|
+
"price": price,
|
|
201
|
+
"change_pct": change_pct,
|
|
202
|
+
"change_amt": change_amt,
|
|
203
|
+
"volume": safe_float(r.get('volume')),
|
|
204
|
+
"amount": safe_float(r.get('amount')),
|
|
205
|
+
"high": safe_float(r.get('high')),
|
|
206
|
+
"low": safe_float(r.get('low')),
|
|
207
|
+
"open": safe_float(r.get('open')),
|
|
208
|
+
"prev_close": prev_close,
|
|
209
|
+
"date": str(r.get('date', '')),
|
|
210
|
+
"data_source": "sina_hk",
|
|
211
|
+
"data_time": str(r.get('date', '')) + " (收盘)",
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def _fetch_hk_em(code):
|
|
216
|
+
"""东方财富港股接口(含名称、振幅、换手率等更多字段)"""
|
|
217
|
+
import akshare as ak
|
|
218
|
+
|
|
219
|
+
df = ak.stock_hk_spot_em()
|
|
220
|
+
row = df[df['代码'] == code]
|
|
221
|
+
|
|
222
|
+
if row.empty:
|
|
223
|
+
return None
|
|
224
|
+
|
|
225
|
+
r = row.iloc[0]
|
|
226
|
+
return {
|
|
227
|
+
"code": code,
|
|
228
|
+
"name": str(r.get('名称', '')),
|
|
229
|
+
"price": safe_float(r.get('最新价')),
|
|
230
|
+
"change_pct": safe_float(r.get('涨跌幅')),
|
|
231
|
+
"change_amt": safe_float(r.get('涨跌额')),
|
|
232
|
+
"volume": safe_float(r.get('成交量')),
|
|
233
|
+
"amount": safe_float(r.get('成交额')),
|
|
234
|
+
"high": safe_float(r.get('最高')),
|
|
235
|
+
"low": safe_float(r.get('最低')),
|
|
236
|
+
"open": safe_float(r.get('今开')),
|
|
237
|
+
"prev_close": safe_float(r.get('昨收')),
|
|
238
|
+
"amplitude": safe_float(r.get('振幅')),
|
|
239
|
+
"turnover_rate": safe_float(r.get('换手率')),
|
|
240
|
+
"data_source": "eastmoney_hk",
|
|
241
|
+
"data_time": datetime.now().strftime('%Y-%m-%d %H:%M'),
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def _enrich_hk(data, code):
|
|
246
|
+
"""补充港股额外字段:PE/PB/市值/ROE/行业/名称"""
|
|
247
|
+
import akshare as ak
|
|
248
|
+
|
|
249
|
+
# 补充财务指标(PE/PB/市值/ROE等)
|
|
250
|
+
try:
|
|
251
|
+
fi = ak.stock_hk_financial_indicator_em(symbol=code)
|
|
252
|
+
if not fi.empty:
|
|
253
|
+
r = fi.iloc[0]
|
|
254
|
+
data.setdefault("pe_ratio", safe_float(r.get('市盈率')))
|
|
255
|
+
data.setdefault("pb_ratio", safe_float(r.get('市净率')))
|
|
256
|
+
data.setdefault("total_market_cap", safe_float(r.get('总市值(港元)')))
|
|
257
|
+
data["roe"] = safe_float(r.get('股东权益回报率(%)'))
|
|
258
|
+
data["net_profit"] = safe_float(r.get('净利润'))
|
|
259
|
+
data["net_profit_growth"] = safe_float(r.get('净利润滚动环比增长(%)'))
|
|
260
|
+
data["revenue"] = safe_float(r.get('营业总收入'))
|
|
261
|
+
data["revenue_growth"] = safe_float(r.get('营业总收入滚动环比增长(%)'))
|
|
262
|
+
data["net_margin"] = safe_float(r.get('销售净利率(%)'))
|
|
263
|
+
data["eps"] = safe_float(r.get('基本每股收益(元)'))
|
|
264
|
+
data["dividend_yield"] = safe_float(r.get('股息率TTM(%)'))
|
|
265
|
+
else:
|
|
266
|
+
data["financial_indicator_error"] = "东方财富港股指标接口返回空数据"
|
|
267
|
+
except Exception as e:
|
|
268
|
+
data["financial_indicator_error"] = f"获取财务指标失败: {str(e)}"
|
|
269
|
+
|
|
270
|
+
# 补充公司名称和行业
|
|
271
|
+
if not data.get("name") or not data.get("industry"):
|
|
272
|
+
try:
|
|
273
|
+
profile = ak.stock_hk_company_profile_em(symbol=code)
|
|
274
|
+
if not profile.empty:
|
|
275
|
+
p = profile.iloc[0]
|
|
276
|
+
if not data.get("name"):
|
|
277
|
+
data["name"] = str(p.get('公司名称', ''))
|
|
278
|
+
data["industry"] = str(p.get('所属行业', ''))
|
|
279
|
+
data["chairman"] = str(p.get('董事长', ''))
|
|
280
|
+
except Exception:
|
|
281
|
+
pass
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def main():
|
|
285
|
+
parser = argparse.ArgumentParser(description='股票实时行情查询')
|
|
286
|
+
parser.add_argument('stock_code', help='股票代码,如 600519(A股) 或 03888(港股)')
|
|
287
|
+
parser.add_argument('--detail', action='store_true', help='获取详细信息')
|
|
288
|
+
args = parser.parse_args()
|
|
289
|
+
|
|
290
|
+
# 检测AKShare
|
|
291
|
+
err = check_akshare()
|
|
292
|
+
if err:
|
|
293
|
+
output_error(err)
|
|
294
|
+
return
|
|
295
|
+
|
|
296
|
+
code = normalize_code(args.stock_code)
|
|
297
|
+
|
|
298
|
+
try:
|
|
299
|
+
import akshare as ak
|
|
300
|
+
|
|
301
|
+
# 港股:优先分钟线实时 → 新浪daily(T-1) → 东方财富spot → 补充丰富字段
|
|
302
|
+
if is_hk_code(code):
|
|
303
|
+
data = None
|
|
304
|
+
|
|
305
|
+
# 1. 优先:东方财富分钟线(盘中实时)
|
|
306
|
+
try:
|
|
307
|
+
data = _fetch_hk_realtime(code)
|
|
308
|
+
except Exception:
|
|
309
|
+
pass
|
|
310
|
+
|
|
311
|
+
# 2. 备选:新浪日线(T-1,稳定)
|
|
312
|
+
if data is None:
|
|
313
|
+
try:
|
|
314
|
+
data = _fetch_hk_sina(code)
|
|
315
|
+
except Exception:
|
|
316
|
+
pass
|
|
317
|
+
|
|
318
|
+
# 3. 兜底:东方财富港股spot
|
|
319
|
+
if data is None:
|
|
320
|
+
try:
|
|
321
|
+
data = _fetch_hk_em(code)
|
|
322
|
+
except Exception as e:
|
|
323
|
+
output_error(f"获取港股行情失败: {str(e)}")
|
|
324
|
+
return
|
|
325
|
+
|
|
326
|
+
if data is None:
|
|
327
|
+
output_error(f"未找到港股代码: {code},请确认代码正确(5位数字如 03888)")
|
|
328
|
+
return
|
|
329
|
+
|
|
330
|
+
# 补充PE/PB/市值/ROE/行业/名称
|
|
331
|
+
_enrich_hk(data, code)
|
|
332
|
+
|
|
333
|
+
# 时效性检查:标注数据是否过时
|
|
334
|
+
from datetime import date
|
|
335
|
+
today = date.today()
|
|
336
|
+
data_date_str = data.get('date', '') or data.get('data_time', '')[:10]
|
|
337
|
+
if data_date_str:
|
|
338
|
+
try:
|
|
339
|
+
data_date = date.fromisoformat(data_date_str[:10])
|
|
340
|
+
days_old = (today - data_date).days
|
|
341
|
+
if days_old > 0:
|
|
342
|
+
data["data_staleness"] = f"数据为{days_old}天前({data_date_str})"
|
|
343
|
+
if data.get("data_source", "").endswith("_daily") or data.get("data_source") == "sina_hk":
|
|
344
|
+
data["data_staleness"] += ",日线接口为T-1数据(最近交易日收盘)"
|
|
345
|
+
elif days_old > 3:
|
|
346
|
+
data["data_staleness"] += ",数据可能过时,请确认是否为非交易日"
|
|
347
|
+
except (ValueError, TypeError):
|
|
348
|
+
pass
|
|
349
|
+
|
|
350
|
+
# 可选:公司详细信息
|
|
351
|
+
if args.detail:
|
|
352
|
+
try:
|
|
353
|
+
profile = ak.stock_hk_company_profile_em(symbol=code)
|
|
354
|
+
if not profile.empty:
|
|
355
|
+
data["detail"] = {col: str(profile.iloc[0][col]) for col in profile.columns}
|
|
356
|
+
except Exception:
|
|
357
|
+
data["detail"] = None
|
|
358
|
+
|
|
359
|
+
output_json(data)
|
|
360
|
+
return
|
|
361
|
+
|
|
362
|
+
# A股:优先新浪(稳定),失败回退东方财富
|
|
363
|
+
data = None
|
|
364
|
+
try:
|
|
365
|
+
data = _fetch_sina(code)
|
|
366
|
+
except Exception:
|
|
367
|
+
pass
|
|
368
|
+
|
|
369
|
+
if data is None:
|
|
370
|
+
try:
|
|
371
|
+
data = _fetch_em(code)
|
|
372
|
+
except Exception as e2:
|
|
373
|
+
output_error(f"获取行情失败: {str(e2)}")
|
|
374
|
+
return
|
|
375
|
+
|
|
376
|
+
if data is None:
|
|
377
|
+
output_error(f"未找到股票代码: {code}")
|
|
378
|
+
return
|
|
379
|
+
|
|
380
|
+
# 非东方财富数据源时,尝试补充个股信息
|
|
381
|
+
if data.get("data_source") != "eastmoney":
|
|
382
|
+
try:
|
|
383
|
+
info_df = ak.stock_individual_info_em(symbol=code)
|
|
384
|
+
for _, row_info in info_df.iterrows():
|
|
385
|
+
key = str(row_info.iloc[0])
|
|
386
|
+
val = str(row_info.iloc[1])
|
|
387
|
+
if key == '总市值':
|
|
388
|
+
data["total_market_cap"] = safe_float(val)
|
|
389
|
+
elif key == '流通市值':
|
|
390
|
+
data["circulating_market_cap"] = safe_float(val)
|
|
391
|
+
elif key == '行业':
|
|
392
|
+
data["industry"] = val
|
|
393
|
+
except Exception:
|
|
394
|
+
pass
|
|
395
|
+
|
|
396
|
+
# 可选:获取详细信息
|
|
397
|
+
if args.detail:
|
|
398
|
+
try:
|
|
399
|
+
info_df = ak.stock_individual_info_em(symbol=code)
|
|
400
|
+
detail = {}
|
|
401
|
+
for _, row_info in info_df.iterrows():
|
|
402
|
+
item_key = str(row_info.iloc[0])
|
|
403
|
+
item_val = str(row_info.iloc[1])
|
|
404
|
+
detail[item_key] = item_val
|
|
405
|
+
data["detail"] = detail
|
|
406
|
+
except Exception:
|
|
407
|
+
data["detail"] = None
|
|
408
|
+
|
|
409
|
+
# A股时效性检查
|
|
410
|
+
from datetime import date as date_cls
|
|
411
|
+
today = date_cls.today()
|
|
412
|
+
data_date_str = data.get('date', '') or data.get('data_time', '')[:10]
|
|
413
|
+
if data_date_str:
|
|
414
|
+
try:
|
|
415
|
+
data_date = date_cls.fromisoformat(data_date_str[:10])
|
|
416
|
+
days_old = (today - data_date).days
|
|
417
|
+
if days_old > 0:
|
|
418
|
+
data["data_staleness"] = f"数据为{days_old}天前({data_date_str})"
|
|
419
|
+
if data.get("data_source") in ("sina_daily", "sina"):
|
|
420
|
+
data["data_staleness"] += ",日线接口为T-1数据(最近交易日收盘)"
|
|
421
|
+
elif days_old > 3:
|
|
422
|
+
data["data_staleness"] += ",数据可能过时,请确认是否为非交易日"
|
|
423
|
+
except (ValueError, TypeError):
|
|
424
|
+
pass
|
|
425
|
+
|
|
426
|
+
output_json(data)
|
|
427
|
+
|
|
428
|
+
except Exception as e:
|
|
429
|
+
output_error(f"获取行情失败: {str(e)}")
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
if __name__ == '__main__':
|
|
433
|
+
main()
|