daxiapi-cli 1.2.0 → 2.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/lib/output.js CHANGED
@@ -1,328 +1,7 @@
1
- const chalk = require('chalk');
2
- const consoleTable = require('console.table');
3
-
4
- /**
5
- * 错误输出
6
- */
7
- function error(err) {
8
- console.log(chalk.red.bold('\n❌ 错误:'), err.message);
9
- if (err.response?.status === 401) {
10
- console.log(chalk.yellow(' Token无效或已过期,请重新配置'));
11
- } else if (err.response?.status === 429) {
12
- console.log(chalk.yellow(' 请求频率超限,请稍后重试'));
13
- }
14
- }
15
-
16
- /**
17
- * 纯文本输出
18
- */
19
- function text(data) {
20
- if (typeof data === 'string') {
21
- console.log(data);
22
- } else {
23
- console.log(JSON.stringify(data, null, 2));
24
- }
25
- }
26
-
27
- /**
28
- * 表格输出
29
- */
30
- function table(data) {
31
- if (typeof data === 'string') {
32
- // Markdown表格直接输出
33
- console.log(data);
34
- } else if (Array.isArray(data)) {
35
- console.table(data);
36
- } else {
37
- console.log(JSON.stringify(data, null, 2));
38
- }
39
- }
40
-
41
- /**
42
- * 市场概览
43
- */
44
- function marketOverview(data) {
45
- console.log(chalk.bold.cyan('📈 市场概览\n'));
46
-
47
- if (data.index && data.index.length > 0) {
48
- console.log(chalk.bold('主要指数:'));
49
- const indexData = data.index.map(item => ({
50
- '名称': item.name,
51
- '涨跌幅': formatPercent(item.zdf),
52
- 'CS': item.cs?.toFixed(2) || '-',
53
- '5日': formatPercent(item.zdf5),
54
- '20日': formatPercent(item.zdf20)
55
- }));
56
- console.table(indexData);
57
- }
58
-
59
- if (data.above_ma200_ratio !== undefined) {
60
- console.log(chalk.bold('\n市场宽度:'));
61
- console.log(` 200日均线上方: ${chalk.cyan((data.above_ma200_ratio * 100).toFixed(1) + '%')}`);
62
- }
63
-
64
- if (data.zdfRange) {
65
- console.log(chalk.bold('\n涨跌分布:'));
66
- const range = data.zdfRange;
67
- console.log(` 涨停(>10%): ${chalk.red(range.zdf_10 || 0)}`);
68
- console.log(` 大涨(7-10%): ${chalk.red(range.zdf_10z7 || 0)}`);
69
- console.log(` 上涨(5-7%): ${chalk.red(range.zdf_7z5 || 0)}`);
70
- console.log(` 微涨(2-5%): ${chalk.red(range.zdf_5z2 || 0)}`);
71
- console.log(` 平盘: ${chalk.gray(range.zdf_0 || 0)}`);
72
- console.log(` 微跌(0-2%): ${chalk.green(range.zdf_2z0 || 0)}`);
73
- console.log(` 下跌(2-5%): ${chalk.green(range.zdf_0z_2 || 0)}`);
74
- console.log(` 大跌(5-7%): ${chalk.green(range.zdf_7z_5 || 0)}`);
75
- console.log(` 跌停(<-10%): ${chalk.green(range.zdf__10 || 0)}`);
76
- }
77
- }
78
-
79
- /**
80
- * 指数估值表格
81
- */
82
- function valueTable(data) {
83
- console.log(chalk.bold.cyan('💰 指数估值\n'));
84
-
85
- if (data.items && data.items.length > 0) {
86
- const tableData = data.items.map(item => ({
87
- 名称: item.name,
88
- PE: item.PE?.toFixed(2) || '-',
89
- PE分位: (item.PEPercentile * 100).toFixed(1) + '%',
90
- PB: item.PB?.toFixed(2) || '-',
91
- PB分位: (item.PBPercentile * 100).toFixed(1) + '%',
92
- 温度: formatTemp(item.wendu)
93
- }));
94
- console.table(tableData);
95
- console.log(chalk.gray(`\n数据日期: ${data.fullDate}`));
96
- }
97
- }
98
-
99
- /**
100
- * 新闻列表
101
- */
102
- function newsList(data) {
103
- console.log(chalk.bold.cyan('📰 收盘新闻\n'));
104
-
105
- if (data.title) {
106
- console.log(chalk.bold(data.title));
107
- if (data.summary) {
108
- console.log(chalk.gray(data.summary));
109
- }
110
- if (data.showTime) {
111
- console.log(chalk.gray(`时间: ${data.showTime}`));
112
- }
113
- if (data.uniqueUrl) {
114
- console.log(chalk.blue(`链接: ${data.uniqueUrl}`));
115
- }
116
- }
117
- }
118
-
119
- /**
120
- * K线数据
121
- */
122
- function klines(data) {
123
- console.log(chalk.bold.cyan(`📊 ${data.name} K线数据\n`));
124
-
125
- if (data.klines && data.klines.length > 0) {
126
- const tableData = data.klines.slice(-10).map(item => ({
127
- 日期: item.date,
128
- 开盘: item.open?.toFixed(2),
129
- 收盘: item.close?.toFixed(2),
130
- 最高: item.high?.toFixed(2),
131
- 最低: item.low?.toFixed(2),
132
- 成交量: formatVolume(item.vol)
133
- }));
134
- console.table(tableData);
135
- }
136
- }
137
-
138
- /**
139
- * 板块热力图
140
- */
141
- function sectorHeatmap(data) {
142
- console.log(chalk.bold.cyan('🔥 板块热力图\n'));
143
-
144
- if (data.csHeatmap) {
145
- console.log(chalk.bold('CS动量热力图:'));
146
- console.log(data.csHeatmap);
147
- }
148
-
149
- if (data.crossover) {
150
- console.log(chalk.bold('\n突破箱体板块:'));
151
- console.log(data.crossover);
152
- }
153
- }
154
-
155
- /**
156
- * 股票列表
157
- */
158
- function stockList(data) {
159
- console.log(chalk.bold.cyan('📋 股票列表\n'));
160
-
161
- if (Array.isArray(data) && data.length > 0) {
162
- const tableData = data.slice(0, 20).map(item => ({
163
- 代码: item.code || item.stockId,
164
- 名称: item.name,
165
- 收盘价: item.close?.toFixed(2) || '-',
166
- 涨跌幅: formatPercent(item.zdf),
167
- CS: item.cs?.toFixed(2) || '-',
168
- RPS: item.rps_score?.toFixed(0) || '-',
169
- 标签: (item.tags || []).join(',')
170
- }));
171
- console.table(tableData);
172
- }
173
- }
174
-
175
- /**
176
- * 股票详情
177
- */
178
- function stockDetail(data) {
179
- console.log(chalk.bold.cyan('📊 个股详情\n'));
180
-
181
- if (Array.isArray(data)) {
182
- data.forEach(stock => {
183
- console.log(chalk.bold.blue(`${stock.name} (${stock.code})`));
184
- console.log(chalk.gray('─'.repeat(40)));
185
-
186
- console.log(`价格: ${stock.close?.toFixed(2)} 涨跌幅: ${formatPercent(stock.zdf)}`);
187
- console.log(`今开: ${stock.open?.toFixed(2)} 最高: ${stock.high?.toFixed(2)} 最低: ${stock.low?.toFixed(2)}`);
188
- console.log('');
189
-
190
- console.log(chalk.bold('动量指标:'));
191
- console.log(` CS(短期): ${stock.cs?.toFixed(2)} SM(中期): ${stock.sm?.toFixed(2)} ML(长期): ${stock.ml?.toFixed(2)}`);
192
- console.log(` RPS: ${stock.rps_score?.toFixed(0)} SCTR: ${stock.sctr?.toFixed(0)}`);
193
- console.log('');
194
-
195
- console.log(chalk.bold('均线系统:'));
196
- console.log(` MA20: ${stock.ma20?.toFixed(2)} MA50: ${stock.ma50?.toFixed(2)}`);
197
- console.log(` MA150: ${stock.ma150?.toFixed(2)} MA200: ${stock.ma200?.toFixed(2)}`);
198
- console.log('');
199
-
200
- if (stock.tags && stock.tags.length > 0) {
201
- console.log(chalk.bold('形态标签:'), stock.tags.join(', '));
202
- }
203
-
204
- const signals = [];
205
- if (stock.isVCP) signals.push('VCP');
206
- if (stock.isSOS) signals.push('SOS');
207
- if (stock.isLPS) signals.push('LPS');
208
- if (stock.isSpring) signals.push('Spring');
209
- if (stock.isCrossoverBox) signals.push('突破箱体');
210
- if (signals.length > 0) {
211
- console.log(chalk.bold('技术形态:'), chalk.green(signals.join(', ')));
212
- }
213
-
214
- console.log('');
215
- });
216
- }
217
- }
218
-
219
- /**
220
- * 搜索结果
221
- */
222
- function searchResult(data) {
223
- console.log(chalk.bold.cyan('🔍 搜索结果\n'));
224
-
225
- if (Array.isArray(data) && data.length > 0) {
226
- const tableData = data.map(item => ({
227
- 代码: item.code,
228
- 名称: item.name,
229
- 拼音: item.pinyin,
230
- 类型: item.type === 'hy' ? '行业' : '个股'
231
- }));
232
- console.table(tableData);
233
- } else {
234
- console.log(chalk.yellow('未找到匹配结果'));
235
- }
236
- }
237
-
238
- /**
239
- * K线数据输出(增强版)
240
- */
241
- function klineData(data, limit = 20) {
242
- console.log(chalk.bold.cyan(`📊 ${data.name} (${data.code}) K线数据\n`));
243
- console.log(chalk.gray(`数据源: ${data.source === 'qq' ? '腾讯财经' : '东方财富'}`));
244
- console.log(chalk.gray(`数据条数: ${data.count}\n`));
245
-
246
- if (data.klines && data.klines.length > 0) {
247
- const displayData = data.klines.slice(-limit);
248
- const tableData = displayData.map(item => ({
249
- '日期': item.date,
250
- '开盘': item.open?.toFixed(2),
251
- '收盘': item.close?.toFixed(2),
252
- '最高': item.high?.toFixed(2),
253
- '最低': item.low?.toFixed(2),
254
- '涨跌幅': formatPercent(item.close && item.open ? ((item.close - item.open) / item.open * 100) : null),
255
- '成交量': formatVolume(item.volume),
256
- '成交额': formatAmount(item.amount)
257
- }));
258
- console.table(tableData);
259
- }
260
- }
261
-
262
- /**
263
- * K线简单输出(最近N天)
264
- */
265
- function klineSimple(data, limit = 10) {
266
- if (data.klines && data.klines.length > 0) {
267
- const displayData = data.klines.slice(-limit);
268
- console.log(chalk.bold(`${data.name} (${data.code}) 最近${displayData.length}日K线 [${data.source}]:`));
269
-
270
- displayData.forEach(item => {
271
- const change = item.close && item.open ? ((item.close - item.open) / item.open * 100) : 0;
272
- const changeStr = change >= 0 ? chalk.red(`+${change.toFixed(2)}%`) : chalk.green(`${change.toFixed(2)}%`);
273
- console.log(` ${item.date} | 开${item.open?.toFixed(2)} 收${item.close?.toFixed(2)} 高${item.high?.toFixed(2)} 低${item.low?.toFixed(2)} | ${changeStr}`);
274
- });
275
- }
276
- }
277
-
278
- // ==================== 辅助函数 ====================
279
-
280
- function formatPercent(value) {
281
- if (value === null || value === undefined) return '-';
282
- const num = typeof value === 'string' ? parseFloat(value) : value;
283
- if (isNaN(num)) return '-';
284
- const str = num.toFixed(2) + '%';
285
- return num >= 0 ? chalk.red(str) : chalk.green(str);
286
- }
287
-
288
- function formatTemp(value) {
289
- if (value === null || value === undefined) return '-';
290
- const num = typeof value === 'string' ? parseFloat(value) : value;
291
- if (isNaN(num)) return '-';
292
-
293
- if (num < 20) return chalk.blue(num.toFixed(1) + '°C');
294
- if (num < 40) return chalk.cyan(num.toFixed(1) + '°C');
295
- if (num < 60) return chalk.yellow(num.toFixed(1) + '°C');
296
- if (num < 80) return chalk.rgb(255, 165, 0)(num.toFixed(1) + '°C');
297
- return chalk.red(num.toFixed(1) + '°C');
298
- }
299
-
300
- function formatVolume(vol) {
301
- if (!vol) return '-';
302
- if (vol >= 100000000) return (vol / 100000000).toFixed(2) + '亿';
303
- if (vol >= 10000) return (vol / 10000).toFixed(2) + '万';
304
- return vol.toString();
305
- }
306
-
307
- function formatAmount(amount) {
308
- if (!amount) return '-';
309
- if (amount >= 100000000) return (amount / 100000000).toFixed(2) + '亿';
310
- if (amount >= 10000) return (amount / 10000).toFixed(2) + '万';
311
- return amount.toFixed(2);
1
+ function output(data) {
2
+ console.log(JSON.stringify(data, null, 2));
312
3
  }
313
4
 
314
5
  module.exports = {
315
- error,
316
- text,
317
- table,
318
- marketOverview,
319
- valueTable,
320
- newsList,
321
- klines,
322
- sectorHeatmap,
323
- stockList,
324
- stockDetail,
325
- searchResult,
326
- klineData,
327
- klineSimple
6
+ output
328
7
  };
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "daxiapi-cli",
3
- "version": "1.2.0",
4
- "description": "大虾皮(daxiapi.com)金融数据API命令行工具",
3
+ "version": "2.0.0",
4
+ "description": "大虾皮金融数据API命令行工具",
5
5
  "bin": {
6
6
  "daxiapi": "./bin/index.js",
7
7
  "dxp": "./bin/index.js"
8
8
  },
9
9
  "scripts": {
10
- "test": "node bin/index.js"
10
+ "test": "node bin/index.js --help"
11
11
  },
12
12
  "keywords": [
13
13
  "stock",
@@ -22,7 +22,9 @@
22
22
  "axios": "^1.6.0",
23
23
  "chalk": "^4.1.2",
24
24
  "commander": "^11.1.0",
25
- "conf": "^10.2.0",
26
- "console.table": "^0.10.0"
25
+ "conf": "^10.2.0"
26
+ },
27
+ "engines": {
28
+ "node": ">=14.0.0"
27
29
  }
28
- }
30
+ }