w-dwdata-hub 1.0.13 → 1.0.15
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/dist/w-dwdata-hub.umd.js +2 -2
- package/dist/w-dwdata-hub.umd.js.map +1 -1
- package/docs/WDwdataHub.mjs.html +1 -1
- package/docs/decodeEntities.mjs.html +1 -1
- package/docs/extractDivContent.mjs.html +1 -1
- package/docs/fetchAiNewsAggregator.mjs.html +2 -2
- package/docs/fetchAisixiang.mjs.html +13 -13
- package/docs/fetchCnyes.mjs.html +8 -8
- package/docs/fetchGuancha.mjs.html +15 -15
- package/docs/fetchHackerNews.mjs.html +2 -2
- package/docs/fetchMoneydj.mjs.html +4 -4
- package/docs/fetchMops.mjs.html +14 -14
- package/docs/fetchRSS.mjs.html +2 -2
- package/docs/fetchStatementdog.mjs.html +6 -6
- package/docs/fetchTaifex.mjs.html +14 -14
- package/docs/fetchTpex3insti.mjs.html +5 -5
- package/docs/fetchTpexMargin.mjs.html +5 -5
- package/docs/fetchTpexStock.mjs.html +4 -4
- package/docs/fetchTwDataHoliday.mjs.html +2 -2
- package/docs/fetchTwseMargin.mjs.html +5 -5
- package/docs/fetchTwseStock.mjs.html +4 -4
- package/docs/fetchTwseT86.mjs.html +5 -5
- package/docs/fetchWithRetry.mjs.html +7 -7
- package/docs/getOptFetch.mjs.html +9 -9
- package/docs/global.html +29 -29
- package/docs/htmlToMarkdown.mjs.html +1 -1
- package/docs/index.html +1 -1
- package/docs/isYmd.mjs.html +1 -1
- package/docs/parseIntComma.mjs.html +1 -1
- package/docs/safeFilename.mjs.html +1 -1
- package/docs/stripHtml.mjs.html +1 -1
- package/docs/toDatetimeUTC8.mjs.html +1 -1
- package/docs/toRocDate.mjs.html +1 -1
- package/g.mjs +13 -13
- package/package.json +5 -5
- package/src/fetchAiNewsAggregator.mjs +1 -1
- package/src/fetchAisixiang.mjs +12 -12
- package/src/fetchCnyes.mjs +7 -7
- package/src/fetchGuancha.mjs +14 -14
- package/src/fetchHackerNews.mjs +1 -1
- package/src/fetchMoneydj.mjs +3 -3
- package/src/fetchMops.mjs +13 -13
- package/src/fetchRSS.mjs +1 -1
- package/src/fetchStatementdog.mjs +5 -5
- package/src/fetchTaifex.mjs +13 -13
- package/src/fetchTpex3insti.mjs +4 -4
- package/src/fetchTpexMargin.mjs +4 -4
- package/src/fetchTpexStock.mjs +3 -3
- package/src/fetchTwDataHoliday.mjs +1 -1
- package/src/fetchTwseMargin.mjs +4 -4
- package/src/fetchTwseStock.mjs +3 -3
- package/src/fetchTwseT86.mjs +4 -4
- package/src/fetchWithRetry.mjs +6 -6
- package/src/getOptFetch.mjs +8 -8
- package/test/fetchAiNewsAggregator.test.mjs +5 -5
- package/test/fetchAisixiang.test.mjs +1 -1
- package/test/fetchCnyes.test.mjs +5 -5
- package/test/fetchGuancha.test.mjs +1 -1
- package/test/fetchHackerNews.test.mjs +5 -5
- package/test/fetchMoneydj.test.mjs +1 -1
- package/test/fetchMops.test.mjs +2 -2
- package/test/fetchRSS.test.mjs +11 -11
- package/test/fetchStatementdog.test.mjs +1 -1
- package/test/fetchTaifex.test.mjs +1 -1
- package/test/fetchTpex3insti.test.mjs +1 -1
- package/test/fetchTpexMargin.test.mjs +1 -1
- package/test/fetchTpexStock.test.mjs +1 -1
- package/test/fetchTwDataHoliday.test.mjs +1 -1
- package/test/fetchTwseMargin.test.mjs +1 -1
- package/test/fetchTwseStock.test.mjs +1 -1
- package/test/fetchTwseT86.test.mjs +1 -1
- package/test/fetchWithRetry.test.mjs +1 -1
package/src/fetchTaifex.mjs
CHANGED
|
@@ -144,7 +144,7 @@ function parseNum(str) {
|
|
|
144
144
|
async function fetchFuturesData(dateSlash, cfg) {
|
|
145
145
|
|
|
146
146
|
let url = `${cfg.baseUrl}/cht/3/futDataDown?down_type=1&queryStartDate=${dateSlash}&queryEndDate=${dateSlash}&commodity_id=TX`
|
|
147
|
-
if (cfg.
|
|
147
|
+
if (cfg.useShowLog) {
|
|
148
148
|
console.log(`Fetching futures data: ${url}`)
|
|
149
149
|
}
|
|
150
150
|
|
|
@@ -201,7 +201,7 @@ async function fetchFuturesData(dateSlash, cfg) {
|
|
|
201
201
|
result.afterHoursVolume = parseNum(afterHoursRow['成交量'])
|
|
202
202
|
}
|
|
203
203
|
|
|
204
|
-
if (cfg.
|
|
204
|
+
if (cfg.useShowLog) {
|
|
205
205
|
console.log(` 台指期近月 (${nearMonth}): 開${result.open} 高${result.high} 低${result.low} 收${result.close} 結算${result.settlement} 量${result.volume}`)
|
|
206
206
|
if (result.afterHoursClose !== null) {
|
|
207
207
|
console.log(` 盤後: 收${result.afterHoursClose} 結算${result.afterHoursSettlement} 量${result.afterHoursVolume}`)
|
|
@@ -222,7 +222,7 @@ async function fetchFuturesData(dateSlash, cfg) {
|
|
|
222
222
|
async function fetchInstitutionalData(dateSlash, cfg) {
|
|
223
223
|
|
|
224
224
|
let url = `${cfg.baseUrl}/cht/3/futContractsDateDown?queryStartDate=${dateSlash}&queryEndDate=${dateSlash}&commodityId=TXF`
|
|
225
|
-
if (cfg.
|
|
225
|
+
if (cfg.useShowLog) {
|
|
226
226
|
console.log(`Fetching institutional data: ${url}`)
|
|
227
227
|
}
|
|
228
228
|
|
|
@@ -253,7 +253,7 @@ async function fetchInstitutionalData(dateSlash, cfg) {
|
|
|
253
253
|
tradingNet: parseNum(row['多空交易口數淨額']),
|
|
254
254
|
}
|
|
255
255
|
|
|
256
|
-
if (cfg.
|
|
256
|
+
if (cfg.useShowLog) {
|
|
257
257
|
console.log(` ${identity}: 未平倉淨額 ${result[key].netContracts} 口`)
|
|
258
258
|
}
|
|
259
259
|
|
|
@@ -279,7 +279,7 @@ async function fetchInstitutionalData(dateSlash, cfg) {
|
|
|
279
279
|
async function fetchPCRatio(dateSlash, cfg) {
|
|
280
280
|
|
|
281
281
|
let url = `${cfg.baseUrl}/cht/3/pcRatioDown?queryStartDate=${dateSlash}&queryEndDate=${dateSlash}`
|
|
282
|
-
if (cfg.
|
|
282
|
+
if (cfg.useShowLog) {
|
|
283
283
|
console.log(`Fetching P/C ratio: ${url}`)
|
|
284
284
|
}
|
|
285
285
|
|
|
@@ -299,7 +299,7 @@ async function fetchPCRatio(dateSlash, cfg) {
|
|
|
299
299
|
openInterestRatio: parseNum(row['買賣權未平倉量比率%']),
|
|
300
300
|
}
|
|
301
301
|
|
|
302
|
-
if (cfg.
|
|
302
|
+
if (cfg.useShowLog) {
|
|
303
303
|
console.log(` Put ${result.putVolume} / Call ${result.callVolume} = ${result.ratio}%`)
|
|
304
304
|
}
|
|
305
305
|
|
|
@@ -321,7 +321,7 @@ async function fetchPCRatio(dateSlash, cfg) {
|
|
|
321
321
|
* @param {Integer} [opt.baseDelayMs=5000] 輸入重試之線性退避基礎毫秒整數,預設5000
|
|
322
322
|
* @param {Integer} [opt.maxDelayMs=30000] 輸入重試之線性退避上限毫秒整數,預設30000
|
|
323
323
|
* @param {Integer} [opt.interRequestDelayMs=1000] 輸入三支CSV之間隔毫秒整數,預設1000
|
|
324
|
-
* @param {Boolean} [opt.
|
|
324
|
+
* @param {Boolean} [opt.useShowLog=true] 輸入是否顯示過程訊息布林值,預設true
|
|
325
325
|
* @returns {Promise} 回傳Promise,resolve回傳結果物件{date,futures,institutional,pcRatio,errors},日期無效或三支資料全失敗時reject回傳錯誤物件
|
|
326
326
|
* @example
|
|
327
327
|
*
|
|
@@ -359,8 +359,8 @@ async function fetchTaifex(dateStr, opt = {}) {
|
|
|
359
359
|
|
|
360
360
|
//cfg
|
|
361
361
|
let optFetch = getOptFetch(opt, DFLT)
|
|
362
|
-
let
|
|
363
|
-
let cfg = { baseUrl,
|
|
362
|
+
let useShowLog = optFetch.useShowLog
|
|
363
|
+
let cfg = { baseUrl, useShowLog, optFetch }
|
|
364
364
|
|
|
365
365
|
//interRequestDelayMs
|
|
366
366
|
let interRequestDelayMs = get(opt, 'interRequestDelayMs')
|
|
@@ -373,7 +373,7 @@ async function fetchTaifex(dateStr, opt = {}) {
|
|
|
373
373
|
|
|
374
374
|
//dateSlash
|
|
375
375
|
let dateSlash = `${dateStr.substring(0, 4)}/${dateStr.substring(4, 6)}/${dateStr.substring(6, 8)}`
|
|
376
|
-
if (
|
|
376
|
+
if (useShowLog) {
|
|
377
377
|
console.log(`Fetching TAIFEX data for ${dateStr} (${dateSlash})`)
|
|
378
378
|
}
|
|
379
379
|
|
|
@@ -387,7 +387,7 @@ async function fetchTaifex(dateStr, opt = {}) {
|
|
|
387
387
|
}
|
|
388
388
|
catch (err) {
|
|
389
389
|
let msg = `台指期行情: ${get(err, 'message', err)}`
|
|
390
|
-
if (
|
|
390
|
+
if (useShowLog) {
|
|
391
391
|
console.error(msg)
|
|
392
392
|
}
|
|
393
393
|
errors.push(msg)
|
|
@@ -400,7 +400,7 @@ async function fetchTaifex(dateStr, opt = {}) {
|
|
|
400
400
|
}
|
|
401
401
|
catch (err) {
|
|
402
402
|
let msg = `三大法人: ${get(err, 'message', err)}`
|
|
403
|
-
if (
|
|
403
|
+
if (useShowLog) {
|
|
404
404
|
console.error(msg)
|
|
405
405
|
}
|
|
406
406
|
errors.push(msg)
|
|
@@ -413,7 +413,7 @@ async function fetchTaifex(dateStr, opt = {}) {
|
|
|
413
413
|
}
|
|
414
414
|
catch (err) {
|
|
415
415
|
let msg = `Put/Call Ratio: ${get(err, 'message', err)}`
|
|
416
|
-
if (
|
|
416
|
+
if (useShowLog) {
|
|
417
417
|
console.error(msg)
|
|
418
418
|
}
|
|
419
419
|
errors.push(msg)
|
package/src/fetchTpex3insti.mjs
CHANGED
|
@@ -99,7 +99,7 @@ function assertTpexFieldShape(fields) {
|
|
|
99
99
|
* @param {Integer} [opt.maxRetries=10] 輸入最大重試次數整數,含初始共執行maxRetries+1次,預設10
|
|
100
100
|
* @param {Integer} [opt.baseDelayMs=5000] 輸入重試之線性退避基礎毫秒整數,預設5000
|
|
101
101
|
* @param {Integer} [opt.maxDelayMs=30000] 輸入重試之線性退避上限毫秒整數,預設30000
|
|
102
|
-
* @param {Boolean} [opt.
|
|
102
|
+
* @param {Boolean} [opt.useShowLog=true] 輸入是否顯示過程訊息布林值,預設true
|
|
103
103
|
* @returns {Promise} 回傳Promise,resolve回傳結果物件{source,date,data},日期無效、無資料或欄位結構不符時reject回傳錯誤物件
|
|
104
104
|
* @example
|
|
105
105
|
*
|
|
@@ -138,7 +138,7 @@ async function fetchTpex3insti(dateStr, stockCodes, opt = {}) {
|
|
|
138
138
|
|
|
139
139
|
//optFetch
|
|
140
140
|
let optFetch = getOptFetch(opt, DFLT)
|
|
141
|
-
let
|
|
141
|
+
let useShowLog = optFetch.useShowLog
|
|
142
142
|
|
|
143
143
|
//targetCodes
|
|
144
144
|
let targetCodes = isearr(stockCodes) ? stockCodes : []
|
|
@@ -147,7 +147,7 @@ async function fetchTpex3insti(dateStr, stockCodes, opt = {}) {
|
|
|
147
147
|
let rocDateStr = toRocDate(dateStr)
|
|
148
148
|
let url = `${baseUrl}/web/stock/3insti/daily_trade/3itrade_hedge_result.php?l=zh-tw&t=D&d=${rocDateStr}&o=json`
|
|
149
149
|
|
|
150
|
-
if (
|
|
150
|
+
if (useShowLog) {
|
|
151
151
|
console.log(`Fetching from: ${url}`)
|
|
152
152
|
console.log(`Target: ${targetCodes.length === 0 ? 'All Market' : targetCodes.join(', ')}`)
|
|
153
153
|
}
|
|
@@ -194,7 +194,7 @@ async function fetchTpex3insti(dateStr, stockCodes, opt = {}) {
|
|
|
194
194
|
})
|
|
195
195
|
.filter((item) => targetCodes.length === 0 || targetCodes.includes(item['代號']))
|
|
196
196
|
|
|
197
|
-
if (
|
|
197
|
+
if (useShowLog) {
|
|
198
198
|
console.log(`Fetched ${processedData.length} records.`)
|
|
199
199
|
}
|
|
200
200
|
|
package/src/fetchTpexMargin.mjs
CHANGED
|
@@ -37,7 +37,7 @@ let DFLT = {
|
|
|
37
37
|
* @param {Integer} [opt.maxRetries=10] 輸入最大重試次數整數,含初始共執行maxRetries+1次,預設10
|
|
38
38
|
* @param {Integer} [opt.baseDelayMs=5000] 輸入重試之線性退避基礎毫秒整數,預設5000
|
|
39
39
|
* @param {Integer} [opt.maxDelayMs=30000] 輸入重試之線性退避上限毫秒整數,預設30000
|
|
40
|
-
* @param {Boolean} [opt.
|
|
40
|
+
* @param {Boolean} [opt.useShowLog=true] 輸入是否顯示過程訊息布林值,預設true
|
|
41
41
|
* @returns {Promise} 回傳Promise,resolve回傳結果物件{source,date,count,data},日期無效、無資料、欄位佈局不符或指定個股查無資料時reject回傳錯誤物件
|
|
42
42
|
* @example
|
|
43
43
|
*
|
|
@@ -75,7 +75,7 @@ async function fetchTpexMargin(dateStr, stockCodes, opt = {}) {
|
|
|
75
75
|
|
|
76
76
|
//optFetch
|
|
77
77
|
let optFetch = getOptFetch(opt, DFLT)
|
|
78
|
-
let
|
|
78
|
+
let useShowLog = optFetch.useShowLog
|
|
79
79
|
|
|
80
80
|
//targetCodes
|
|
81
81
|
let targetCodes = isearr(stockCodes) ? stockCodes.filter(Boolean) : []
|
|
@@ -84,7 +84,7 @@ async function fetchTpexMargin(dateStr, stockCodes, opt = {}) {
|
|
|
84
84
|
let rocDate = toRocDate(dateStr)
|
|
85
85
|
let url = `${baseUrl}/web/stock/margin_trading/margin_balance/margin_bal_result.php?l=zh-tw&d=${rocDate}&o=json`
|
|
86
86
|
|
|
87
|
-
if (
|
|
87
|
+
if (useShowLog) {
|
|
88
88
|
console.log(`Fetching TPEX margin data: ${dateStr} (${rocDate})`)
|
|
89
89
|
console.log(`Target: ${targetCodes.length > 0 ? targetCodes.join(', ') : 'All Market'}`)
|
|
90
90
|
console.log(`URL: ${url}`)
|
|
@@ -160,7 +160,7 @@ async function fetchTpexMargin(dateStr, stockCodes, opt = {}) {
|
|
|
160
160
|
}
|
|
161
161
|
})
|
|
162
162
|
|
|
163
|
-
if (
|
|
163
|
+
if (useShowLog) {
|
|
164
164
|
console.log(`Fetched ${parsedData.length} records.`)
|
|
165
165
|
}
|
|
166
166
|
|
package/src/fetchTpexStock.mjs
CHANGED
|
@@ -40,7 +40,7 @@ let MIN_COLUMNS = 8
|
|
|
40
40
|
* @param {Integer} [opt.maxRetries=10] 輸入最大重試次數整數,含初始共執行maxRetries+1次,預設10
|
|
41
41
|
* @param {Integer} [opt.baseDelayMs=5000] 輸入重試之線性退避基礎毫秒整數,預設5000
|
|
42
42
|
* @param {Integer} [opt.maxDelayMs=30000] 輸入重試之線性退避上限毫秒整數,預設30000
|
|
43
|
-
* @param {Boolean} [opt.
|
|
43
|
+
* @param {Boolean} [opt.useShowLog=true] 輸入是否顯示過程訊息布林值,預設true
|
|
44
44
|
* @returns {Promise} 回傳Promise,resolve回傳結果物件{source,date,count,data},日期無效、無資料或指定個股查無資料時reject回傳錯誤物件
|
|
45
45
|
* @example
|
|
46
46
|
*
|
|
@@ -75,7 +75,7 @@ async function fetchTpexStock(dateStr, stockCodes, opt = {}) {
|
|
|
75
75
|
|
|
76
76
|
//optFetch
|
|
77
77
|
let optFetch = getOptFetch(opt, DFLT)
|
|
78
|
-
let
|
|
78
|
+
let useShowLog = optFetch.useShowLog
|
|
79
79
|
|
|
80
80
|
//targetCodes
|
|
81
81
|
let targetCodes = isearr(stockCodes) ? stockCodes : []
|
|
@@ -84,7 +84,7 @@ async function fetchTpexStock(dateStr, stockCodes, opt = {}) {
|
|
|
84
84
|
let rocDate = toRocDate(dateStr)
|
|
85
85
|
let url = `${baseUrl}/web/stock/aftertrading/daily_close_quotes/stk_quote_result.php?l=zh-tw&d=${rocDate}&s=0,asc,0&o=json`
|
|
86
86
|
|
|
87
|
-
if (
|
|
87
|
+
if (useShowLog) {
|
|
88
88
|
console.log(`Fetching TPEX data: ${dateStr} (${rocDate})`)
|
|
89
89
|
console.log(`Target: ${targetCodes.length > 0 ? targetCodes.join(', ') : 'All Market'}`)
|
|
90
90
|
console.log(`URL: ${url}`)
|
|
@@ -70,7 +70,7 @@ function isNonHolidayEntry(entry) {
|
|
|
70
70
|
* @param {Integer} [opt.maxRetries=10] 輸入最大重試次數整數,含初始共執行maxRetries+1次,預設10
|
|
71
71
|
* @param {Integer} [opt.baseDelayMs=5000] 輸入重試之線性退避基礎毫秒整數,預設5000
|
|
72
72
|
* @param {Integer} [opt.maxDelayMs=30000] 輸入重試之線性退避上限毫秒整數,預設30000
|
|
73
|
-
* @param {Boolean} [opt.
|
|
73
|
+
* @param {Boolean} [opt.useShowLog=true] 輸入是否顯示重試訊息布林值,預設true
|
|
74
74
|
* @returns {Promise} 回傳Promise,resolve回傳結果物件{dataYear,totalHolidays,holidays},有給checkDate時另含{checkDate,isHoliday,holidayName},API回傳非JSON或空陣列時reject回傳錯誤物件
|
|
75
75
|
* @example
|
|
76
76
|
*
|
package/src/fetchTwseMargin.mjs
CHANGED
|
@@ -39,7 +39,7 @@ let MIN_FIELDS = 16
|
|
|
39
39
|
* @param {Integer} [opt.maxRetries=10] 輸入最大重試次數整數,含初始共執行maxRetries+1次,預設10
|
|
40
40
|
* @param {Integer} [opt.baseDelayMs=5000] 輸入重試之線性退避基礎毫秒整數,預設5000
|
|
41
41
|
* @param {Integer} [opt.maxDelayMs=30000] 輸入重試之線性退避上限毫秒整數,預設30000
|
|
42
|
-
* @param {Boolean} [opt.
|
|
42
|
+
* @param {Boolean} [opt.useShowLog=true] 輸入是否顯示過程訊息布林值,預設true
|
|
43
43
|
* @returns {Promise} 回傳Promise,resolve回傳結果物件{source,date,count,data},日期無效、API錯誤或指定個股查無資料時reject回傳錯誤物件
|
|
44
44
|
* @example
|
|
45
45
|
*
|
|
@@ -77,7 +77,7 @@ async function fetchTwseMargin(dateStr, stockCodes, opt = {}) {
|
|
|
77
77
|
|
|
78
78
|
//optFetch
|
|
79
79
|
let optFetch = getOptFetch(opt, DFLT)
|
|
80
|
-
let
|
|
80
|
+
let useShowLog = optFetch.useShowLog
|
|
81
81
|
|
|
82
82
|
//targetCodes
|
|
83
83
|
let targetCodes = isearr(stockCodes) ? stockCodes.filter(Boolean) : []
|
|
@@ -85,7 +85,7 @@ async function fetchTwseMargin(dateStr, stockCodes, opt = {}) {
|
|
|
85
85
|
//url
|
|
86
86
|
let url = `${baseUrl}/rwd/zh/marginTrading/MI_MARGN?date=${dateStr}&selectType=ALL&response=json`
|
|
87
87
|
|
|
88
|
-
if (
|
|
88
|
+
if (useShowLog) {
|
|
89
89
|
console.log(`Fetching TWSE margin data: ${dateStr}`)
|
|
90
90
|
console.log(`Target: ${targetCodes.length > 0 ? targetCodes.join(', ') : 'All Market'}`)
|
|
91
91
|
console.log(`URL: ${url}`)
|
|
@@ -165,7 +165,7 @@ async function fetchTwseMargin(dateStr, stockCodes, opt = {}) {
|
|
|
165
165
|
}
|
|
166
166
|
})
|
|
167
167
|
|
|
168
|
-
if (
|
|
168
|
+
if (useShowLog) {
|
|
169
169
|
console.log(`Fetched ${parsedData.length} records.`)
|
|
170
170
|
}
|
|
171
171
|
|
package/src/fetchTwseStock.mjs
CHANGED
|
@@ -34,7 +34,7 @@ let DFLT = {
|
|
|
34
34
|
* @param {Integer} [opt.maxRetries=10] 輸入最大重試次數整數,含初始共執行maxRetries+1次,預設10
|
|
35
35
|
* @param {Integer} [opt.baseDelayMs=5000] 輸入重試之線性退避基礎毫秒整數,預設5000
|
|
36
36
|
* @param {Integer} [opt.maxDelayMs=30000] 輸入重試之線性退避上限毫秒整數,預設30000
|
|
37
|
-
* @param {Boolean} [opt.
|
|
37
|
+
* @param {Boolean} [opt.useShowLog=true] 輸入是否顯示過程訊息布林值,預設true
|
|
38
38
|
* @returns {Promise} 回傳Promise,resolve回傳證交所API原始資料物件,內含fields與data等欄位,日期無效、API回傳非OK或指定日無交易資料時reject回傳錯誤物件
|
|
39
39
|
* @example
|
|
40
40
|
*
|
|
@@ -69,7 +69,7 @@ async function fetchTwseStock(dateStr, stockCode, opt = {}) {
|
|
|
69
69
|
|
|
70
70
|
//optFetch
|
|
71
71
|
let optFetch = getOptFetch(opt, DFLT)
|
|
72
|
-
let
|
|
72
|
+
let useShowLog = optFetch.useShowLog
|
|
73
73
|
|
|
74
74
|
//isSingleStock
|
|
75
75
|
let isSingleStock = isestr(stockCode) && stockCode.toLowerCase() !== 'all'
|
|
@@ -84,7 +84,7 @@ async function fetchTwseStock(dateStr, stockCode, opt = {}) {
|
|
|
84
84
|
url = `${baseUrl}/exchangeReport/MI_INDEX?response=json&date=${dateStr}&type=ALLBUT0999`
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
if (
|
|
87
|
+
if (useShowLog) {
|
|
88
88
|
console.log(`Fetching TWSE data: ${dateStr}, Stock: ${stockNo}`)
|
|
89
89
|
console.log(`URL: ${url}`)
|
|
90
90
|
}
|
package/src/fetchTwseT86.mjs
CHANGED
|
@@ -33,7 +33,7 @@ let DFLT = {
|
|
|
33
33
|
* @param {Integer} [opt.maxRetries=10] 輸入最大重試次數整數,含初始共執行maxRetries+1次,預設10
|
|
34
34
|
* @param {Integer} [opt.baseDelayMs=5000] 輸入重試之線性退避基礎毫秒整數,預設5000
|
|
35
35
|
* @param {Integer} [opt.maxDelayMs=30000] 輸入重試之線性退避上限毫秒整數,預設30000
|
|
36
|
-
* @param {Boolean} [opt.
|
|
36
|
+
* @param {Boolean} [opt.useShowLog=true] 輸入是否顯示過程訊息布林值,預設true
|
|
37
37
|
* @returns {Promise} 回傳Promise,resolve回傳結果物件{source,date,data},日期無效或API錯誤時reject回傳錯誤物件
|
|
38
38
|
* @example
|
|
39
39
|
*
|
|
@@ -72,7 +72,7 @@ async function fetchTwseT86(dateStr, stockCodes, opt = {}) {
|
|
|
72
72
|
|
|
73
73
|
//optFetch
|
|
74
74
|
let optFetch = getOptFetch(opt, DFLT)
|
|
75
|
-
let
|
|
75
|
+
let useShowLog = optFetch.useShowLog
|
|
76
76
|
|
|
77
77
|
//targetCodes
|
|
78
78
|
let targetCodes = isearr(stockCodes) ? stockCodes : []
|
|
@@ -80,7 +80,7 @@ async function fetchTwseT86(dateStr, stockCodes, opt = {}) {
|
|
|
80
80
|
//url
|
|
81
81
|
let url = `${baseUrl}/rwd/zh/fund/T86?response=json&date=${dateStr}&selectType=ALL`
|
|
82
82
|
|
|
83
|
-
if (
|
|
83
|
+
if (useShowLog) {
|
|
84
84
|
console.log(`Fetching from: ${url}`)
|
|
85
85
|
console.log(`Target: ${targetCodes.length === 0 ? 'All Market' : targetCodes.join(', ')}`)
|
|
86
86
|
}
|
|
@@ -125,7 +125,7 @@ async function fetchTwseT86(dateStr, stockCodes, opt = {}) {
|
|
|
125
125
|
parsedData = parsedData.filter((item) => targetCodes.includes(item[codeField]))
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
-
if (
|
|
128
|
+
if (useShowLog) {
|
|
129
129
|
console.log(`Fetched ${parsedData.length} records.`)
|
|
130
130
|
}
|
|
131
131
|
|
package/src/fetchWithRetry.mjs
CHANGED
|
@@ -154,7 +154,7 @@ async function fetchOnce(url, cfg) {
|
|
|
154
154
|
* @param {Integer} [opt.maxDelayMs=30000] 輸入線性退避之上限毫秒整數,預設30000
|
|
155
155
|
* @param {Array} [opt.retryStatus=[]] 輸入額外視為可重試之HTTP狀態碼陣列,例如[403],預設[]
|
|
156
156
|
* @param {String} [opt.label=''] 輸入重試訊息前綴字串,預設''
|
|
157
|
-
* @param {Boolean} [opt.
|
|
157
|
+
* @param {Boolean} [opt.useShowLog=true] 輸入是否顯示重試訊息布林值,預設true
|
|
158
158
|
* @returns {Promise} 回傳Promise,resolve回傳解析後資料,responseType為'json'時回物件、'text'時回字串、'arrayBuffer'時回ArrayBuffer,重試耗盡或遇不可重試錯誤時reject回傳錯誤物件
|
|
159
159
|
* @example
|
|
160
160
|
*
|
|
@@ -261,10 +261,10 @@ async function fetchWithRetry(url, opt = {}) {
|
|
|
261
261
|
label = 'fetchWithRetry'
|
|
262
262
|
}
|
|
263
263
|
|
|
264
|
-
//
|
|
265
|
-
let
|
|
266
|
-
if (!isbol(
|
|
267
|
-
|
|
264
|
+
//useShowLog
|
|
265
|
+
let useShowLog = get(opt, 'useShowLog')
|
|
266
|
+
if (!isbol(useShowLog)) {
|
|
267
|
+
useShowLog = true
|
|
268
268
|
}
|
|
269
269
|
|
|
270
270
|
//cfg
|
|
@@ -287,7 +287,7 @@ async function fetchWithRetry(url, opt = {}) {
|
|
|
287
287
|
|
|
288
288
|
//線性退避
|
|
289
289
|
let ms = Math.min(baseDelayMs * attempt, maxDelayMs)
|
|
290
|
-
if (
|
|
290
|
+
if (useShowLog) {
|
|
291
291
|
console.warn(`[${label}][Retry ${attempt}/${maxRetries}] ${err.message} — 等待 ${ms / 1000}s 後重試...`)
|
|
292
292
|
}
|
|
293
293
|
await delay(ms)
|
package/src/getOptFetch.mjs
CHANGED
|
@@ -55,7 +55,7 @@ function pickPint(opt, dflt, key, fallback) {
|
|
|
55
55
|
* import getOptFetch from './src/getOptFetch.mjs'
|
|
56
56
|
*
|
|
57
57
|
* console.log(getOptFetch({ maxRetries: 2 }, { timeout: 15000, label: 'abc' }))
|
|
58
|
-
* // => { timeout: 15000, maxRetries: 2, baseDelayMs: 5000, maxDelayMs: 30000,
|
|
58
|
+
* // => { timeout: 15000, maxRetries: 2, baseDelayMs: 5000, maxDelayMs: 30000, useShowLog: true, label: 'abc' }
|
|
59
59
|
*
|
|
60
60
|
*/
|
|
61
61
|
function getOptFetch(opt = {}, dflt = {}) {
|
|
@@ -72,13 +72,13 @@ function getOptFetch(opt = {}, dflt = {}) {
|
|
|
72
72
|
maxRetries = cint(maxRetries)
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
//
|
|
76
|
-
let
|
|
77
|
-
if (!isbol(
|
|
78
|
-
|
|
75
|
+
//useShowLog
|
|
76
|
+
let useShowLog = get(opt, 'useShowLog')
|
|
77
|
+
if (!isbol(useShowLog)) {
|
|
78
|
+
useShowLog = get(dflt, 'useShowLog')
|
|
79
79
|
}
|
|
80
|
-
if (!isbol(
|
|
81
|
-
|
|
80
|
+
if (!isbol(useShowLog)) {
|
|
81
|
+
useShowLog = true
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
let r = {
|
|
@@ -86,7 +86,7 @@ function getOptFetch(opt = {}, dflt = {}) {
|
|
|
86
86
|
maxRetries,
|
|
87
87
|
baseDelayMs: pickPint(opt, dflt, 'baseDelayMs', DEFAULT_BASE_DELAY_MS),
|
|
88
88
|
maxDelayMs: pickPint(opt, dflt, 'maxDelayMs', DEFAULT_MAX_DELAY_MS),
|
|
89
|
-
|
|
89
|
+
useShowLog,
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
//各函數固有特性, 不開放呼叫端覆寫
|
|
@@ -18,7 +18,7 @@ describe('fetchAiNewsAggregator', function() {
|
|
|
18
18
|
})
|
|
19
19
|
|
|
20
20
|
it('轉為統一格式並去除欄位前後空白', async function() {
|
|
21
|
-
let r = await fetchAiNewsAggregator({ url: svr.url('/ai-news/latest-24h.json'),
|
|
21
|
+
let r = await fetchAiNewsAggregator({ url: svr.url('/ai-news/latest-24h.json'), useShowLog: false })
|
|
22
22
|
let rr = [
|
|
23
23
|
{
|
|
24
24
|
url: 'https://ai.example/a1',
|
|
@@ -39,7 +39,7 @@ describe('fetchAiNewsAggregator', function() {
|
|
|
39
39
|
})
|
|
40
40
|
|
|
41
41
|
it('published_at無法解析時time退回空字串', async function() {
|
|
42
|
-
let t = await fetchAiNewsAggregator({ url: svr.url('/ai-news/latest-24h.json'),
|
|
42
|
+
let t = await fetchAiNewsAggregator({ url: svr.url('/ai-news/latest-24h.json'), useShowLog: false })
|
|
43
43
|
let r = t[1].time
|
|
44
44
|
let rr = ''
|
|
45
45
|
assert.strict.deepEqual(r, rr)
|
|
@@ -47,7 +47,7 @@ describe('fetchAiNewsAggregator', function() {
|
|
|
47
47
|
|
|
48
48
|
it('回傳非JSON時reject', async function() {
|
|
49
49
|
let r = null
|
|
50
|
-
await fetchAiNewsAggregator({ url: svr.url('/json/bad'), maxRetries: 0,
|
|
50
|
+
await fetchAiNewsAggregator({ url: svr.url('/json/bad'), maxRetries: 0, useShowLog: false })
|
|
51
51
|
.then(() => {
|
|
52
52
|
r = 'resolved'
|
|
53
53
|
})
|
|
@@ -61,7 +61,7 @@ describe('fetchAiNewsAggregator', function() {
|
|
|
61
61
|
it('HTTP 404不重試並reject', async function() {
|
|
62
62
|
let c0 = svr.getCount('/status/404')
|
|
63
63
|
let e = null
|
|
64
|
-
await fetchAiNewsAggregator({ url: svr.url('/status/404'), maxRetries: 3,
|
|
64
|
+
await fetchAiNewsAggregator({ url: svr.url('/status/404'), maxRetries: 3, useShowLog: false })
|
|
65
65
|
.catch((err) => {
|
|
66
66
|
e = err
|
|
67
67
|
})
|
|
@@ -71,7 +71,7 @@ describe('fetchAiNewsAggregator', function() {
|
|
|
71
71
|
})
|
|
72
72
|
|
|
73
73
|
it('回應無items欄位時回傳空陣列', async function() {
|
|
74
|
-
let r = await fetchAiNewsAggregator({ url: svr.url('/json/ok'),
|
|
74
|
+
let r = await fetchAiNewsAggregator({ url: svr.url('/json/ok'), useShowLog: false })
|
|
75
75
|
let rr = []
|
|
76
76
|
assert.strict.deepEqual(r, rr)
|
|
77
77
|
})
|
|
@@ -22,7 +22,7 @@ describe('fetchAisixiang', function() {
|
|
|
22
22
|
|
|
23
23
|
before(async function() {
|
|
24
24
|
svr = await serverForTest()
|
|
25
|
-
optFast = { baseUrl: svr.url(''), baseDelayMs: 10, maxDelayMs: 10, pageDelayMs: 0,
|
|
25
|
+
optFast = { baseUrl: svr.url(''), baseDelayMs: 10, maxDelayMs: 10, pageDelayMs: 0, useShowLog: false }
|
|
26
26
|
})
|
|
27
27
|
|
|
28
28
|
after(async function() {
|
package/test/fetchCnyes.test.mjs
CHANGED
|
@@ -18,7 +18,7 @@ describe('fetchCnyes', function() {
|
|
|
18
18
|
})
|
|
19
19
|
|
|
20
20
|
it('轉為統一格式並組出新聞內容頁網址', async function() {
|
|
21
|
-
let r = await fetchCnyes({ url: svr.url('/cnyes/newslist'), pageDelayMs: 0,
|
|
21
|
+
let r = await fetchCnyes({ url: svr.url('/cnyes/newslist'), pageDelayMs: 0, useShowLog: false })
|
|
22
22
|
let rr = [
|
|
23
23
|
{ time: '2026-08-06 15:06:40', title: '鉅亨標題一', link: 'https://news.cnyes.com/news/id/5001' },
|
|
24
24
|
{ time: '', title: '鉅亨標題二', link: 'https://news.cnyes.com/news/id/5002' },
|
|
@@ -27,7 +27,7 @@ describe('fetchCnyes', function() {
|
|
|
27
27
|
})
|
|
28
28
|
|
|
29
29
|
it('publishAt缺漏時該筆time退回空字串, 不影響其餘新聞', async function() {
|
|
30
|
-
let t = await fetchCnyes({ url: svr.url('/cnyes/newslist'), pageDelayMs: 0,
|
|
30
|
+
let t = await fetchCnyes({ url: svr.url('/cnyes/newslist'), pageDelayMs: 0, useShowLog: false })
|
|
31
31
|
let r = [t.length, t[1].time, t[1].title]
|
|
32
32
|
let rr = [2, '', '鉅亨標題二']
|
|
33
33
|
assert.strict.deepEqual(r, rr)
|
|
@@ -35,7 +35,7 @@ describe('fetchCnyes', function() {
|
|
|
35
35
|
|
|
36
36
|
it('無更多資料時停止翻頁', async function() {
|
|
37
37
|
let c0 = svr.getCount('/cnyes/newslist')
|
|
38
|
-
await fetchCnyes({ url: svr.url('/cnyes/newslist'), pageDelayMs: 0,
|
|
38
|
+
await fetchCnyes({ url: svr.url('/cnyes/newslist'), pageDelayMs: 0, useShowLog: false })
|
|
39
39
|
let r = svr.getCount('/cnyes/newslist') - c0
|
|
40
40
|
let rr = 2
|
|
41
41
|
assert.strict.deepEqual(r, rr)
|
|
@@ -43,7 +43,7 @@ describe('fetchCnyes', function() {
|
|
|
43
43
|
|
|
44
44
|
it('targetTotal達標即停止翻頁', async function() {
|
|
45
45
|
let c0 = svr.getCount('/cnyes/newslist')
|
|
46
|
-
let t = await fetchCnyes({ url: svr.url('/cnyes/newslist'), targetTotal: 1, pageDelayMs: 0,
|
|
46
|
+
let t = await fetchCnyes({ url: svr.url('/cnyes/newslist'), targetTotal: 1, pageDelayMs: 0, useShowLog: false })
|
|
47
47
|
let r = [t.length, svr.getCount('/cnyes/newslist') - c0]
|
|
48
48
|
let rr = [1, 1]
|
|
49
49
|
assert.strict.deepEqual(r, rr)
|
|
@@ -51,7 +51,7 @@ describe('fetchCnyes', function() {
|
|
|
51
51
|
|
|
52
52
|
it('HTTP 403納入重試範圍, 含初始共執行maxRetries+1次', async function() {
|
|
53
53
|
let c0 = svr.getCount('/status/403')
|
|
54
|
-
await fetchCnyes({ url: svr.url('/status/403'), maxRetries: 2, baseDelayMs: 10, maxDelayMs: 10, pageDelayMs: 0,
|
|
54
|
+
await fetchCnyes({ url: svr.url('/status/403'), maxRetries: 2, baseDelayMs: 10, maxDelayMs: 10, pageDelayMs: 0, useShowLog: false })
|
|
55
55
|
.catch(() => {})
|
|
56
56
|
let r = svr.getCount('/status/403') - c0
|
|
57
57
|
let rr = 3
|
|
@@ -24,7 +24,7 @@ describe('fetchGuancha', function() {
|
|
|
24
24
|
|
|
25
25
|
before(async function() {
|
|
26
26
|
svr = await serverForTest()
|
|
27
|
-
optFast = { baseUrl: svr.url(''), maxRetries: 0, pageDelayMs: 0,
|
|
27
|
+
optFast = { baseUrl: svr.url(''), maxRetries: 0, pageDelayMs: 0, useShowLog: false }
|
|
28
28
|
})
|
|
29
29
|
|
|
30
30
|
after(async function() {
|
|
@@ -18,7 +18,7 @@ describe('fetchHackerNews', function() {
|
|
|
18
18
|
})
|
|
19
19
|
|
|
20
20
|
it('僅保留story, 無url者退回站內討論頁網址', async function() {
|
|
21
|
-
let r = await fetchHackerNews(30, { apiBase: svr.url('/hn'),
|
|
21
|
+
let r = await fetchHackerNews(30, { apiBase: svr.url('/hn'), useShowLog: false })
|
|
22
22
|
let rr = [
|
|
23
23
|
{
|
|
24
24
|
url: 'https://hn.example/1',
|
|
@@ -39,7 +39,7 @@ describe('fetchHackerNews', function() {
|
|
|
39
39
|
})
|
|
40
40
|
|
|
41
41
|
it('limit限制取回篇數', async function() {
|
|
42
|
-
let t = await fetchHackerNews(1, { apiBase: svr.url('/hn'),
|
|
42
|
+
let t = await fetchHackerNews(1, { apiBase: svr.url('/hn'), useShowLog: false })
|
|
43
43
|
let r = [t.length, t[0].title]
|
|
44
44
|
let rr = [1, 'HN標題一']
|
|
45
45
|
assert.strict.deepEqual(r, rr)
|
|
@@ -48,7 +48,7 @@ describe('fetchHackerNews', function() {
|
|
|
48
48
|
it('limit非正整數時改用預設值30而非回傳空陣列', async function() {
|
|
49
49
|
let r = []
|
|
50
50
|
for (let limit of [0, -1, NaN, null, undefined, 'abc']) {
|
|
51
|
-
let t = await fetchHackerNews(limit, { apiBase: svr.url('/hn'),
|
|
51
|
+
let t = await fetchHackerNews(limit, { apiBase: svr.url('/hn'), useShowLog: false })
|
|
52
52
|
r.push(t.length)
|
|
53
53
|
}
|
|
54
54
|
let rr = [2, 2, 2, 2, 2, 2]
|
|
@@ -56,7 +56,7 @@ describe('fetchHackerNews', function() {
|
|
|
56
56
|
})
|
|
57
57
|
|
|
58
58
|
it('apiBase尾端斜線會被正規化', async function() {
|
|
59
|
-
let t = await fetchHackerNews(30, { apiBase: svr.url('/hn') + '/',
|
|
59
|
+
let t = await fetchHackerNews(30, { apiBase: svr.url('/hn') + '/', useShowLog: false })
|
|
60
60
|
let r = t.length
|
|
61
61
|
let rr = 2
|
|
62
62
|
assert.strict.deepEqual(r, rr)
|
|
@@ -64,7 +64,7 @@ describe('fetchHackerNews', function() {
|
|
|
64
64
|
|
|
65
65
|
it('取ID清單失敗時reject', async function() {
|
|
66
66
|
let r = null
|
|
67
|
-
await fetchHackerNews(30, { apiBase: svr.url('/nothing'), maxRetries: 0,
|
|
67
|
+
await fetchHackerNews(30, { apiBase: svr.url('/nothing'), maxRetries: 0, useShowLog: false })
|
|
68
68
|
.then(() => {
|
|
69
69
|
r = 'resolved'
|
|
70
70
|
})
|
|
@@ -8,7 +8,7 @@ describe('fetchMoneydj', function() {
|
|
|
8
8
|
let svr = null
|
|
9
9
|
|
|
10
10
|
//optFast, 令重試與頁間等待縮至最短, 避免測試耗時
|
|
11
|
-
let optFast = { baseDelayMs: 10, maxDelayMs: 10, pageDelayMs: 0,
|
|
11
|
+
let optFast = { baseDelayMs: 10, maxDelayMs: 10, pageDelayMs: 0, useShowLog: false }
|
|
12
12
|
|
|
13
13
|
before(async function() {
|
|
14
14
|
svr = await serverForTest()
|
package/test/fetchMops.test.mjs
CHANGED
|
@@ -62,7 +62,7 @@ describe('fetchMops', function() {
|
|
|
62
62
|
pageUrl: svr.url('/mops/page'),
|
|
63
63
|
apiUrl: svr.url('/mops/api/t146sb10'),
|
|
64
64
|
maxRetries: 0,
|
|
65
|
-
|
|
65
|
+
useShowLog: false,
|
|
66
66
|
})
|
|
67
67
|
let r = [
|
|
68
68
|
t.hasError,
|
|
@@ -92,7 +92,7 @@ describe('fetchMops', function() {
|
|
|
92
92
|
pageUrl: svr.url('/mops/page'),
|
|
93
93
|
apiUrl: svr.url('/mops-error/api/t146sb10'),
|
|
94
94
|
maxRetries: 0,
|
|
95
|
-
|
|
95
|
+
useShowLog: false,
|
|
96
96
|
})
|
|
97
97
|
let r = [
|
|
98
98
|
t.hasError,
|