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/README.md +75 -67
- package/bin/index.js +15 -203
- package/commands/config.js +67 -0
- package/commands/kline.js +34 -0
- package/commands/market.js +87 -0
- package/commands/search.js +35 -0
- package/commands/secid.js +34 -0
- package/commands/sector.js +120 -0
- package/commands/stock.js +79 -0
- package/commands/zdt.js +38 -0
- package/lib/api.js +43 -201
- package/lib/config.js +0 -15
- package/lib/error.js +80 -0
- package/lib/output.js +3 -324
- package/package.json +8 -6
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
const config = require('../lib/config');
|
|
2
|
+
const api = require('../lib/api');
|
|
3
|
+
const {handleError, createParameterError} = require('../lib/error');
|
|
4
|
+
const {output} = require('../lib/output');
|
|
5
|
+
|
|
6
|
+
module.exports = function (program) {
|
|
7
|
+
const sectorCmd = program.command('sector');
|
|
8
|
+
|
|
9
|
+
sectorCmd
|
|
10
|
+
.description('板块数据')
|
|
11
|
+
.option('--order <field>', '排序字段', 'cs')
|
|
12
|
+
.option('--limit <num>', '数量限制', 5)
|
|
13
|
+
.action(async options => {
|
|
14
|
+
try {
|
|
15
|
+
const token = config.getToken();
|
|
16
|
+
if (!token) {
|
|
17
|
+
const error = new Error('未配置 API Token');
|
|
18
|
+
error.response = {status: 401};
|
|
19
|
+
throw error;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const data = await api.getSectorData(token, options.order, options.limit);
|
|
23
|
+
output(data);
|
|
24
|
+
} catch (error) {
|
|
25
|
+
handleError(error);
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
sectorCmd
|
|
31
|
+
.command('bk')
|
|
32
|
+
.description('行业板块数据')
|
|
33
|
+
.action(async () => {
|
|
34
|
+
try {
|
|
35
|
+
const token = config.getToken();
|
|
36
|
+
if (!token) {
|
|
37
|
+
const error = new Error('未配置 API Token');
|
|
38
|
+
error.response = {status: 401};
|
|
39
|
+
throw error;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const data = await api.getBkData(token);
|
|
43
|
+
output(data);
|
|
44
|
+
} catch (error) {
|
|
45
|
+
handleError(error);
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
sectorCmd
|
|
51
|
+
.command('stocks')
|
|
52
|
+
.description('板块个股排名')
|
|
53
|
+
.requiredOption('--code <bkCode>', '板块代码')
|
|
54
|
+
.option('--order <field>', '排序字段', 'cs')
|
|
55
|
+
.action(async options => {
|
|
56
|
+
try {
|
|
57
|
+
const token = config.getToken();
|
|
58
|
+
if (!token) {
|
|
59
|
+
const error = new Error('未配置 API Token');
|
|
60
|
+
error.response = {status: 401};
|
|
61
|
+
throw error;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const data = await api.getSectorRankStock(token, options.code, options.order);
|
|
65
|
+
output(data);
|
|
66
|
+
} catch (error) {
|
|
67
|
+
handleError(error);
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
sectorCmd
|
|
73
|
+
.command('top')
|
|
74
|
+
.description('热门股票')
|
|
75
|
+
.action(async () => {
|
|
76
|
+
try {
|
|
77
|
+
const token = config.getToken();
|
|
78
|
+
if (!token) {
|
|
79
|
+
const error = new Error('未配置 API Token');
|
|
80
|
+
error.response = {status: 401};
|
|
81
|
+
throw error;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const data = await api.getTopStocks(token);
|
|
85
|
+
output(data);
|
|
86
|
+
} catch (error) {
|
|
87
|
+
handleError(error);
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
sectorCmd
|
|
93
|
+
.command('gn')
|
|
94
|
+
.description('热门概念')
|
|
95
|
+
.option('--type <type>', '数据源类型 (dfcf|ths)', 'ths')
|
|
96
|
+
.action(async options => {
|
|
97
|
+
try {
|
|
98
|
+
const token = config.getToken();
|
|
99
|
+
if (!token) {
|
|
100
|
+
const error = new Error('未配置 API Token');
|
|
101
|
+
error.response = {status: 401};
|
|
102
|
+
throw error;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (!['dfcf', 'ths'].includes(options.type)) {
|
|
106
|
+
throw createParameterError(
|
|
107
|
+
'参数无效',
|
|
108
|
+
["参数 'type' 必须是 dfcf 或 ths"],
|
|
109
|
+
['daxiapi sector gn --type ths', 'daxiapi sector gn --type dfcf']
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const data = await api.getGnHot(token, options.type);
|
|
114
|
+
output(data);
|
|
115
|
+
} catch (error) {
|
|
116
|
+
handleError(error);
|
|
117
|
+
process.exit(1);
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
const config = require('../lib/config');
|
|
2
|
+
const api = require('../lib/api');
|
|
3
|
+
const {handleError, createParameterError} = require('../lib/error');
|
|
4
|
+
const {output} = require('../lib/output');
|
|
5
|
+
|
|
6
|
+
module.exports = function(program) {
|
|
7
|
+
const stockCmd = program.command('stock');
|
|
8
|
+
|
|
9
|
+
stockCmd
|
|
10
|
+
.description('股票数据查询')
|
|
11
|
+
.argument('<codes...>', '股票代码(支持多个)')
|
|
12
|
+
.action(async (codes) => {
|
|
13
|
+
try {
|
|
14
|
+
const token = config.getToken();
|
|
15
|
+
if (!token) {
|
|
16
|
+
const error = new Error('未配置 API Token');
|
|
17
|
+
error.response = {status: 401};
|
|
18
|
+
throw error;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (!codes || codes.length === 0) {
|
|
22
|
+
throw createParameterError(
|
|
23
|
+
'参数无效',
|
|
24
|
+
["参数 'codes' 不能为空"],
|
|
25
|
+
[
|
|
26
|
+
'daxiapi stock 000001',
|
|
27
|
+
'daxiapi stock 000001 600031 300750'
|
|
28
|
+
]
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const data = await api.getStockData(token, codes);
|
|
33
|
+
output(data);
|
|
34
|
+
} catch (error) {
|
|
35
|
+
handleError(error);
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
stockCmd
|
|
41
|
+
.command('gn <gnId>')
|
|
42
|
+
.description('概念股查询')
|
|
43
|
+
.option('--type <type>', '数据源类型 (dfcf|ths)', 'ths')
|
|
44
|
+
.action(async (gnId, options) => {
|
|
45
|
+
try {
|
|
46
|
+
const token = config.getToken();
|
|
47
|
+
if (!token) {
|
|
48
|
+
const error = new Error('未配置 API Token');
|
|
49
|
+
error.response = {status: 401};
|
|
50
|
+
throw error;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (!gnId) {
|
|
54
|
+
throw createParameterError(
|
|
55
|
+
'参数无效',
|
|
56
|
+
["参数 'gnId' 不能为空"],
|
|
57
|
+
['daxiapi stock gn GN1234']
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (!['dfcf', 'ths'].includes(options.type)) {
|
|
62
|
+
throw createParameterError(
|
|
63
|
+
'参数无效',
|
|
64
|
+
["参数 'type' 必须是 dfcf 或 ths"],
|
|
65
|
+
[
|
|
66
|
+
'daxiapi stock gn GN1234 --type ths',
|
|
67
|
+
'daxiapi stock gn GN1234 --type dfcf'
|
|
68
|
+
]
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const data = await api.getGainianStock(token, gnId, options.type);
|
|
73
|
+
output(data);
|
|
74
|
+
} catch (error) {
|
|
75
|
+
handleError(error);
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
};
|
package/commands/zdt.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
const config = require('../lib/config');
|
|
2
|
+
const api = require('../lib/api');
|
|
3
|
+
const {handleError, createParameterError} = require('../lib/error');
|
|
4
|
+
const {output} = require('../lib/output');
|
|
5
|
+
|
|
6
|
+
module.exports = function(program) {
|
|
7
|
+
program
|
|
8
|
+
.command('zdt')
|
|
9
|
+
.description('涨跌停池')
|
|
10
|
+
.option('--type <type>', '类型 (zt|dt)', 'zt')
|
|
11
|
+
.action(async (options) => {
|
|
12
|
+
try {
|
|
13
|
+
const token = config.getToken();
|
|
14
|
+
if (!token) {
|
|
15
|
+
const error = new Error('未配置 API Token');
|
|
16
|
+
error.response = {status: 401};
|
|
17
|
+
throw error;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (!['zt', 'dt'].includes(options.type)) {
|
|
21
|
+
throw createParameterError(
|
|
22
|
+
'参数无效',
|
|
23
|
+
["参数 'type' 必须是 zt 或 dt"],
|
|
24
|
+
[
|
|
25
|
+
'daxiapi zdt --type zt',
|
|
26
|
+
'daxiapi zdt --type dt'
|
|
27
|
+
]
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const data = await api.getZdtPool(token, options.type);
|
|
32
|
+
output(data);
|
|
33
|
+
} catch (error) {
|
|
34
|
+
handleError(error);
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
};
|
package/lib/api.js
CHANGED
|
@@ -1,286 +1,128 @@
|
|
|
1
1
|
const axios = require('axios');
|
|
2
2
|
const config = require('./config');
|
|
3
3
|
|
|
4
|
-
const BASE_URL = config.
|
|
4
|
+
const BASE_URL = config.get('baseUrl') || 'https://daxiapi.com';
|
|
5
5
|
|
|
6
|
-
/**
|
|
7
|
-
* 获取市场类型前缀
|
|
8
|
-
*/
|
|
9
|
-
function getMarketPrefix(code) {
|
|
10
|
-
if (code.startsWith('688') || code.startsWith('689')) {
|
|
11
|
-
return 'sh';
|
|
12
|
-
}
|
|
13
|
-
if (code.startsWith('60')) {
|
|
14
|
-
return 'sh';
|
|
15
|
-
}
|
|
16
|
-
return 'sz';
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* 创建请求实例
|
|
21
|
-
*/
|
|
22
6
|
function createClient(token) {
|
|
23
7
|
return axios.create({
|
|
24
8
|
baseURL: `${BASE_URL}/coze`,
|
|
25
9
|
headers: {
|
|
26
|
-
'
|
|
10
|
+
'Authorization': `Bearer ${token}`,
|
|
27
11
|
'Content-Type': 'application/json'
|
|
28
12
|
},
|
|
29
13
|
timeout: 30000
|
|
30
14
|
});
|
|
31
15
|
}
|
|
32
16
|
|
|
33
|
-
/**
|
|
34
|
-
* GET 请求
|
|
35
|
-
*/
|
|
36
17
|
async function get(client, path) {
|
|
37
18
|
const {data} = await client.get(path);
|
|
38
19
|
if (data.errCode !== 0) {
|
|
39
|
-
|
|
20
|
+
const error = new Error(data.errMsg || `API Error: ${data.errCode}`);
|
|
21
|
+
error.response = {status: data.errCode};
|
|
22
|
+
throw error;
|
|
40
23
|
}
|
|
41
24
|
return data.data;
|
|
42
25
|
}
|
|
43
26
|
|
|
44
|
-
/**
|
|
45
|
-
* POST 请求
|
|
46
|
-
*/
|
|
47
27
|
async function post(client, path, body = {}) {
|
|
48
28
|
const {data} = await client.post(path, body);
|
|
49
29
|
if (data.errCode !== 0) {
|
|
50
|
-
|
|
30
|
+
const error = new Error(data.errMsg || `API Error: ${data.errCode}`);
|
|
31
|
+
error.response = {status: data.errCode};
|
|
32
|
+
throw error;
|
|
51
33
|
}
|
|
52
34
|
return data.data;
|
|
53
35
|
}
|
|
54
36
|
|
|
55
|
-
// ==================== API 方法 ====================
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* 获取指数K线
|
|
59
|
-
*/
|
|
60
|
-
async function getIndexK(token) {
|
|
61
|
-
const client = createClient(token);
|
|
62
|
-
return get(client, '/get_index_k');
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* 获取市场数据
|
|
67
|
-
*/
|
|
68
37
|
async function getMarketData(token) {
|
|
69
38
|
const client = createClient(token);
|
|
70
|
-
return get(client, '/
|
|
39
|
+
return get(client, '/get_index_data');
|
|
71
40
|
}
|
|
72
41
|
|
|
73
|
-
|
|
74
|
-
* 获取市场温度
|
|
75
|
-
*/
|
|
76
|
-
async function getMarketDegree(token) {
|
|
42
|
+
async function getMarketTemp(token) {
|
|
77
43
|
const client = createClient(token);
|
|
78
|
-
return get(client, '/
|
|
44
|
+
return get(client, '/get_market_temp');
|
|
79
45
|
}
|
|
80
46
|
|
|
81
|
-
/**
|
|
82
|
-
* 获取市场风格
|
|
83
|
-
*/
|
|
84
47
|
async function getMarketStyle(token) {
|
|
85
48
|
const client = createClient(token);
|
|
86
49
|
return get(client, '/get_market_style');
|
|
87
50
|
}
|
|
88
51
|
|
|
89
|
-
/**
|
|
90
|
-
* 获取指数估值
|
|
91
|
-
*/
|
|
92
52
|
async function getMarketValueData(token) {
|
|
93
53
|
const client = createClient(token);
|
|
94
54
|
return get(client, '/get_market_value_data');
|
|
95
55
|
}
|
|
96
56
|
|
|
97
|
-
|
|
98
|
-
* 获取收盘新闻
|
|
99
|
-
*/
|
|
100
|
-
async function getMarketNews(token) {
|
|
57
|
+
async function getBkData(token) {
|
|
101
58
|
const client = createClient(token);
|
|
102
|
-
return get(client, '/
|
|
59
|
+
return get(client, '/get_bk_data');
|
|
103
60
|
}
|
|
104
61
|
|
|
105
|
-
|
|
106
|
-
* 获取板块热力图
|
|
107
|
-
*/
|
|
108
|
-
async function getSectorData(token, orderBy = 'cs', lmt = 5) {
|
|
62
|
+
async function getSectorData(token, orderBy = 'cs', limit = 5) {
|
|
109
63
|
const client = createClient(token);
|
|
110
|
-
return post(client, '/get_sector_data', {orderBy, lmt});
|
|
64
|
+
return post(client, '/get_sector_data', {orderBy, lmt: limit});
|
|
111
65
|
}
|
|
112
66
|
|
|
113
|
-
/**
|
|
114
|
-
* 获取板块内个股排名
|
|
115
|
-
*/
|
|
116
67
|
async function getSectorRankStock(token, sectorCode, orderBy = 'cs') {
|
|
117
68
|
const client = createClient(token);
|
|
118
69
|
return post(client, '/get_sector_rank_stock', {sectorCode, orderBy});
|
|
119
70
|
}
|
|
120
71
|
|
|
121
|
-
|
|
122
|
-
* 获取概念内个股
|
|
123
|
-
*/
|
|
124
|
-
async function getGainianStock(token, gnId) {
|
|
72
|
+
async function getTopStocks(token) {
|
|
125
73
|
const client = createClient(token);
|
|
126
|
-
return post(client, '/
|
|
74
|
+
return post(client, '/get_top_stocks', {});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async function getGnHot(token, type = 'ths') {
|
|
78
|
+
const client = createClient(token);
|
|
79
|
+
return post(client, '/get_gn_hot', {type});
|
|
127
80
|
}
|
|
128
81
|
|
|
129
|
-
/**
|
|
130
|
-
* 获取个股数据
|
|
131
|
-
*/
|
|
132
82
|
async function getStockData(token, codes) {
|
|
133
83
|
const client = createClient(token);
|
|
134
84
|
return post(client, '/get_stock_data', {code: codes});
|
|
135
85
|
}
|
|
136
86
|
|
|
137
|
-
|
|
138
|
-
* 搜索股票/行业
|
|
139
|
-
*/
|
|
140
|
-
async function queryStock(token, keyword, type = 'stock') {
|
|
87
|
+
async function getGainianStock(token, gnId, type = 'ths') {
|
|
141
88
|
const client = createClient(token);
|
|
142
|
-
return post(client, '/
|
|
89
|
+
return post(client, '/get_gainian_stock', {gnId, type});
|
|
143
90
|
}
|
|
144
91
|
|
|
145
|
-
|
|
146
|
-
* 获取概念板块数据
|
|
147
|
-
*/
|
|
148
|
-
async function getGnTable(token) {
|
|
92
|
+
async function getKline(token, code) {
|
|
149
93
|
const client = createClient(token);
|
|
150
|
-
return post(client, '/
|
|
94
|
+
return post(client, '/get_kline', {code});
|
|
151
95
|
}
|
|
152
96
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
* 获取K线数据(首选腾讯,备选东方财富)
|
|
157
|
-
* @param {string} code - 股票代码
|
|
158
|
-
* @param {number} limit - 数据条数,默认300
|
|
159
|
-
* @param {string} type - K线类型: day(日线), week(周线), month(月线)
|
|
160
|
-
* @returns {Promise<Object>} K线数据
|
|
161
|
-
*/
|
|
162
|
-
async function getKline(code, limit = 300, type = 'day') {
|
|
163
|
-
// 首选腾讯
|
|
164
|
-
try {
|
|
165
|
-
const data = await getKlineFromQQ(code, limit, type);
|
|
166
|
-
return {source: 'qq', ...data};
|
|
167
|
-
} catch (err) {
|
|
168
|
-
console.log('腾讯数据源失败,切换到东方财富...');
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
// 备选东方财富
|
|
172
|
-
try {
|
|
173
|
-
const data = await getKlineFromEastmoney(code, limit, type);
|
|
174
|
-
return {source: 'eastmoney', ...data};
|
|
175
|
-
} catch (err) {
|
|
176
|
-
throw new Error('所有数据源均失败,请稍后重试');
|
|
177
|
-
}
|
|
97
|
+
async function getZdtPool(token, type = 'zt') {
|
|
98
|
+
const client = createClient(token);
|
|
99
|
+
return post(client, '/get_zdt_pool', {type});
|
|
178
100
|
}
|
|
179
101
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
* @param {number} limit - 数据条数
|
|
184
|
-
* @param {string} type - K线类型
|
|
185
|
-
*/
|
|
186
|
-
async function getKlineFromQQ(code, limit = 500, type = 'day') {
|
|
187
|
-
const market = getMarketPrefix(code);
|
|
188
|
-
const fullCode = `${market}${code}`;
|
|
189
|
-
|
|
190
|
-
// 腾讯接口限制最大2000条
|
|
191
|
-
if (limit > 2000) {
|
|
192
|
-
limit = 2000;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
const url = `https://proxy.finance.qq.com/ifzqgtimg/appstock/app/newfqkline/get?_var=¶m=${fullCode},${type},,,${limit},qfq&r=0.${Date.now()}`;
|
|
196
|
-
|
|
197
|
-
const {data} = await axios.get(url, {timeout: 10000});
|
|
198
|
-
|
|
199
|
-
if (!data.data || !data.data[fullCode]) {
|
|
200
|
-
throw new Error('腾讯数据源返回异常');
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
const klineData = data.data[fullCode].qfqday || data.data[fullCode].day;
|
|
204
|
-
if (!klineData || klineData.length === 0) {
|
|
205
|
-
throw new Error('无K线数据');
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
const klines = klineData.map(([date, open, close, high, low, volume, , , amount]) => ({
|
|
209
|
-
date,
|
|
210
|
-
open: parseFloat(open),
|
|
211
|
-
close: parseFloat(close),
|
|
212
|
-
high: parseFloat(high),
|
|
213
|
-
low: parseFloat(low),
|
|
214
|
-
volume: parseFloat(volume),
|
|
215
|
-
amount: parseFloat(amount)
|
|
216
|
-
}));
|
|
217
|
-
|
|
218
|
-
return {
|
|
219
|
-
code,
|
|
220
|
-
name: data.data[fullCode].qt?.[fullCode]?.[1] || code,
|
|
221
|
-
klines,
|
|
222
|
-
count: klines.length
|
|
223
|
-
};
|
|
102
|
+
async function getSecId(token, code) {
|
|
103
|
+
const client = createClient(token);
|
|
104
|
+
return post(client, '/get_sec_id', {code});
|
|
224
105
|
}
|
|
225
106
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
* @param {number} limit - 数据条数
|
|
230
|
-
* @param {string} type - K线类型: day=101, week=102, month=103
|
|
231
|
-
*/
|
|
232
|
-
async function getKlineFromEastmoney(code, limit = 300, type = 'day') {
|
|
233
|
-
// K线类型映射
|
|
234
|
-
const kltMap = {day: '101', week: '102', month: '103'};
|
|
235
|
-
const klt = kltMap[type] || '101';
|
|
236
|
-
|
|
237
|
-
// 市场代码
|
|
238
|
-
const secid = code.startsWith('6') ? `1.${code}` : `0.${code}`;
|
|
239
|
-
|
|
240
|
-
const url = `https://push2his.eastmoney.com/api/qt/stock/kline/get?fields1=f1,f2,f3,f4,f5,f6&fields2=f51,f52,f53,f54,f55,f56,f57,f58,f61&ut=7eea3edcaed734bea9cbfc24409ed989&end=29991010&klt=${klt}&secid=${secid}&fqt=1&lmt=${limit}`;
|
|
241
|
-
|
|
242
|
-
const {data} = await axios.get(url, {timeout: 10000});
|
|
243
|
-
|
|
244
|
-
if (!data.data || !data.data.klines) {
|
|
245
|
-
throw new Error('东方财富数据源返回异常');
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
const klines = data.data.klines.map(line => {
|
|
249
|
-
const [date, open, close, high, low, volume, amount, , turnover] = line.split(',');
|
|
250
|
-
return {
|
|
251
|
-
date,
|
|
252
|
-
open: parseFloat(open),
|
|
253
|
-
close: parseFloat(close),
|
|
254
|
-
high: parseFloat(high),
|
|
255
|
-
low: parseFloat(low),
|
|
256
|
-
volume: parseFloat(volume),
|
|
257
|
-
amount: parseFloat(amount),
|
|
258
|
-
turnover: parseFloat(turnover) || 0
|
|
259
|
-
};
|
|
260
|
-
});
|
|
261
|
-
|
|
262
|
-
return {
|
|
263
|
-
code,
|
|
264
|
-
name: data.data.name || code,
|
|
265
|
-
klines,
|
|
266
|
-
count: klines.length
|
|
267
|
-
};
|
|
107
|
+
async function queryStockData(token, q, type = 'stock') {
|
|
108
|
+
const client = createClient(token);
|
|
109
|
+
return post(client, '/query_stock_data', {q, type});
|
|
268
110
|
}
|
|
269
111
|
|
|
270
112
|
module.exports = {
|
|
271
|
-
getIndexK,
|
|
272
113
|
getMarketData,
|
|
273
|
-
|
|
114
|
+
getMarketTemp,
|
|
274
115
|
getMarketStyle,
|
|
275
116
|
getMarketValueData,
|
|
276
|
-
|
|
117
|
+
getBkData,
|
|
277
118
|
getSectorData,
|
|
278
119
|
getSectorRankStock,
|
|
279
|
-
|
|
120
|
+
getTopStocks,
|
|
121
|
+
getGnHot,
|
|
280
122
|
getStockData,
|
|
281
|
-
|
|
282
|
-
getGnTable,
|
|
123
|
+
getGainianStock,
|
|
283
124
|
getKline,
|
|
284
|
-
|
|
285
|
-
|
|
125
|
+
getZdtPool,
|
|
126
|
+
getSecId,
|
|
127
|
+
queryStockData
|
|
286
128
|
};
|
package/lib/config.js
CHANGED
|
@@ -17,30 +17,18 @@ const config = new Conf({
|
|
|
17
17
|
schema
|
|
18
18
|
});
|
|
19
19
|
|
|
20
|
-
/**
|
|
21
|
-
* 获取Token(优先环境变量)
|
|
22
|
-
*/
|
|
23
20
|
function getToken() {
|
|
24
21
|
return process.env.DAXIAPI_TOKEN || config.get('token');
|
|
25
22
|
}
|
|
26
23
|
|
|
27
|
-
/**
|
|
28
|
-
* 获取配置项
|
|
29
|
-
*/
|
|
30
24
|
function get(key) {
|
|
31
25
|
return config.get(key);
|
|
32
26
|
}
|
|
33
27
|
|
|
34
|
-
/**
|
|
35
|
-
* 设置配置项
|
|
36
|
-
*/
|
|
37
28
|
function set(key, value) {
|
|
38
29
|
config.set(key, value);
|
|
39
30
|
}
|
|
40
31
|
|
|
41
|
-
/**
|
|
42
|
-
* 获取所有配置
|
|
43
|
-
*/
|
|
44
32
|
function getAll() {
|
|
45
33
|
return {
|
|
46
34
|
token: getToken() ? '******' + getToken().slice(-4) : undefined,
|
|
@@ -48,9 +36,6 @@ function getAll() {
|
|
|
48
36
|
};
|
|
49
37
|
}
|
|
50
38
|
|
|
51
|
-
/**
|
|
52
|
-
* 删除配置项
|
|
53
|
-
*/
|
|
54
39
|
function del(key) {
|
|
55
40
|
config.delete(key);
|
|
56
41
|
}
|
package/lib/error.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
|
|
3
|
+
const errorMessages = {
|
|
4
|
+
401: {
|
|
5
|
+
title: 'API Token 无效或已过期',
|
|
6
|
+
solutions: [
|
|
7
|
+
'检查 Token 是否正确配置:\n daxiapi config set token YOUR_TOKEN',
|
|
8
|
+
'或设置环境变量:\n export DAXIAPI_TOKEN=YOUR_TOKEN',
|
|
9
|
+
'Token 可在 daxiapi.com 用户中心获取'
|
|
10
|
+
]
|
|
11
|
+
},
|
|
12
|
+
403: {
|
|
13
|
+
title: '无访问权限',
|
|
14
|
+
solutions: [
|
|
15
|
+
'检查您的账户是否有访问该接口的权限',
|
|
16
|
+
'联系管理员开通相关权限',
|
|
17
|
+
'访问 daxiapi.com 了解权限说明'
|
|
18
|
+
]
|
|
19
|
+
},
|
|
20
|
+
429: {
|
|
21
|
+
title: '请求频率超限',
|
|
22
|
+
solutions: [
|
|
23
|
+
'请稍后重试',
|
|
24
|
+
'检查您的请求频率是否过高',
|
|
25
|
+
'升级账户获取更高配额'
|
|
26
|
+
]
|
|
27
|
+
},
|
|
28
|
+
500: {
|
|
29
|
+
title: '服务器内部错误',
|
|
30
|
+
solutions: [
|
|
31
|
+
'请稍后重试',
|
|
32
|
+
'如果问题持续,请联系技术支持',
|
|
33
|
+
'访问 daxiapi.com 查看服务状态'
|
|
34
|
+
]
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
function handleError(error) {
|
|
39
|
+
console.log(chalk.red.bold('\n❌ 错误:'), error.message || error.title);
|
|
40
|
+
|
|
41
|
+
const statusCode = error.response?.status || error.statusCode;
|
|
42
|
+
const errorInfo = errorMessages[statusCode];
|
|
43
|
+
|
|
44
|
+
if (errorInfo) {
|
|
45
|
+
console.log(chalk.bold('\n解决方法:'));
|
|
46
|
+
errorInfo.solutions.forEach((solution, index) => {
|
|
47
|
+
console.log(` ${index + 1}. ${solution}`);
|
|
48
|
+
});
|
|
49
|
+
} else if (error.code === 'ECONNREFUSED' || error.code === 'ENOTFOUND') {
|
|
50
|
+
console.log(chalk.bold('\n解决方法:'));
|
|
51
|
+
console.log(' 1. 检查网络连接是否正常');
|
|
52
|
+
console.log(' 2. 检查 API 地址是否正确:\n daxiapi config set baseUrl https://daxiapi.com');
|
|
53
|
+
console.log(' 3. 如果使用代理,请检查代理配置');
|
|
54
|
+
} else if (error.details) {
|
|
55
|
+
console.log(chalk.bold('\n详细信息:'));
|
|
56
|
+
error.details.forEach(detail => {
|
|
57
|
+
console.log(` - ${detail}`);
|
|
58
|
+
});
|
|
59
|
+
if (error.examples) {
|
|
60
|
+
console.log(chalk.bold('\n使用示例:'));
|
|
61
|
+
error.examples.forEach(example => {
|
|
62
|
+
console.log(` ${example}`);
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
console.log('');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function createParameterError(message, details, examples) {
|
|
71
|
+
const error = new Error(message);
|
|
72
|
+
error.details = details;
|
|
73
|
+
error.examples = examples;
|
|
74
|
+
return error;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
module.exports = {
|
|
78
|
+
handleError,
|
|
79
|
+
createParameterError
|
|
80
|
+
};
|