@fugle/node-twstock 2.2.1-alpha.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.
Files changed (137) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1837 -0
  3. package/lib/enums/exchange.enum.d.ts +5 -0
  4. package/lib/enums/exchange.enum.js +9 -0
  5. package/lib/enums/futopt.enum.d.ts +34 -0
  6. package/lib/enums/futopt.enum.js +38 -0
  7. package/lib/enums/index.d.ts +6 -0
  8. package/lib/enums/index.enum.d.ts +67 -0
  9. package/lib/enums/index.enum.js +71 -0
  10. package/lib/enums/index.js +22 -0
  11. package/lib/enums/industry.enum.d.ts +40 -0
  12. package/lib/enums/industry.enum.js +44 -0
  13. package/lib/enums/market.enum.d.ts +5 -0
  14. package/lib/enums/market.enum.js +9 -0
  15. package/lib/enums/scraper.enum.d.ts +10 -0
  16. package/lib/enums/scraper.enum.js +14 -0
  17. package/lib/index.d.ts +2 -0
  18. package/lib/index.js +18 -0
  19. package/lib/interfaces/futopt-exchange-rates.interface.d.ts +13 -0
  20. package/lib/interfaces/futopt-exchange-rates.interface.js +2 -0
  21. package/lib/interfaces/futopt-historical.interface.d.ts +23 -0
  22. package/lib/interfaces/futopt-historical.interface.js +2 -0
  23. package/lib/interfaces/futopt-institutional.interface.d.ts +21 -0
  24. package/lib/interfaces/futopt-institutional.interface.js +2 -0
  25. package/lib/interfaces/futopt-large-traders.interface.d.ts +16 -0
  26. package/lib/interfaces/futopt-large-traders.interface.js +2 -0
  27. package/lib/interfaces/futopt-mxf-retail-position.interface.d.ts +7 -0
  28. package/lib/interfaces/futopt-mxf-retail-position.interface.js +2 -0
  29. package/lib/interfaces/futopt-quote.interface.d.ts +31 -0
  30. package/lib/interfaces/futopt-quote.interface.js +2 -0
  31. package/lib/interfaces/futopt-tmf-retail-position.interface.d.ts +7 -0
  32. package/lib/interfaces/futopt-tmf-retail-position.interface.js +2 -0
  33. package/lib/interfaces/futopt-txo-put-call-ratio.interface.d.ts +9 -0
  34. package/lib/interfaces/futopt-txo-put-call-ratio.interface.js +2 -0
  35. package/lib/interfaces/futopt.interface.d.ts +7 -0
  36. package/lib/interfaces/futopt.interface.js +2 -0
  37. package/lib/interfaces/index-historical.interface.d.ts +11 -0
  38. package/lib/interfaces/index-historical.interface.js +2 -0
  39. package/lib/interfaces/index-quote.interface.d.ts +13 -0
  40. package/lib/interfaces/index-quote.interface.js +2 -0
  41. package/lib/interfaces/index-trades.interface.d.ts +9 -0
  42. package/lib/interfaces/index-trades.interface.js +2 -0
  43. package/lib/interfaces/index.d.ts +37 -0
  44. package/lib/interfaces/index.js +53 -0
  45. package/lib/interfaces/market-breadth.interface.d.ts +11 -0
  46. package/lib/interfaces/market-breadth.interface.js +2 -0
  47. package/lib/interfaces/market-institutional.interface.d.ts +10 -0
  48. package/lib/interfaces/market-institutional.interface.js +2 -0
  49. package/lib/interfaces/market-margin-trades.interface.d.ts +19 -0
  50. package/lib/interfaces/market-margin-trades.interface.js +2 -0
  51. package/lib/interfaces/market-trades.interface.d.ts +7 -0
  52. package/lib/interfaces/market-trades.interface.js +2 -0
  53. package/lib/interfaces/rate-limit-options.interface.d.ts +4 -0
  54. package/lib/interfaces/rate-limit-options.interface.js +2 -0
  55. package/lib/interfaces/stock-capital-reductions.interface.d.ts +23 -0
  56. package/lib/interfaces/stock-capital-reductions.interface.js +2 -0
  57. package/lib/interfaces/stock-dividends.interface.d.ts +27 -0
  58. package/lib/interfaces/stock-dividends.interface.js +2 -0
  59. package/lib/interfaces/stock-eps.interface.d.ts +8 -0
  60. package/lib/interfaces/stock-eps.interface.js +2 -0
  61. package/lib/interfaces/stock-fini-holdings.interface.d.ts +12 -0
  62. package/lib/interfaces/stock-fini-holdings.interface.js +2 -0
  63. package/lib/interfaces/stock-futopt.interface.d.ts +8 -0
  64. package/lib/interfaces/stock-futopt.interface.js +2 -0
  65. package/lib/interfaces/stock-historical.interface.d.ts +14 -0
  66. package/lib/interfaces/stock-historical.interface.js +2 -0
  67. package/lib/interfaces/stock-institutional.interface.d.ts +12 -0
  68. package/lib/interfaces/stock-institutional.interface.js +2 -0
  69. package/lib/interfaces/stock-margin-trades.interface.d.ts +20 -0
  70. package/lib/interfaces/stock-margin-trades.interface.js +2 -0
  71. package/lib/interfaces/stock-quote.interface.d.ts +19 -0
  72. package/lib/interfaces/stock-quote.interface.js +2 -0
  73. package/lib/interfaces/stock-revenue.interface.d.ts +8 -0
  74. package/lib/interfaces/stock-revenue.interface.js +2 -0
  75. package/lib/interfaces/stock-shareholders.interface.d.ts +11 -0
  76. package/lib/interfaces/stock-shareholders.interface.js +2 -0
  77. package/lib/interfaces/stock-short-sales.interface.d.ts +19 -0
  78. package/lib/interfaces/stock-short-sales.interface.js +2 -0
  79. package/lib/interfaces/stock-splits.interface.d.ts +12 -0
  80. package/lib/interfaces/stock-splits.interface.js +2 -0
  81. package/lib/interfaces/stock-values.interface.d.ts +10 -0
  82. package/lib/interfaces/stock-values.interface.js +2 -0
  83. package/lib/interfaces/stock.interface.d.ts +8 -0
  84. package/lib/interfaces/stock.interface.js +2 -0
  85. package/lib/interfaces/stocks-capital-reduction-announcement.interface.d.ts +19 -0
  86. package/lib/interfaces/stocks-capital-reduction-announcement.interface.js +2 -0
  87. package/lib/interfaces/stocks-dividends-announcement.interface.d.ts +21 -0
  88. package/lib/interfaces/stocks-dividends-announcement.interface.js +2 -0
  89. package/lib/interfaces/stocks-etf-split-announcement.interface.d.ts +11 -0
  90. package/lib/interfaces/stocks-etf-split-announcement.interface.js +2 -0
  91. package/lib/interfaces/stocks-listing-application.interface.d.ts +16 -0
  92. package/lib/interfaces/stocks-listing-application.interface.js +2 -0
  93. package/lib/interfaces/stocks-split-announcement.interface.d.ts +11 -0
  94. package/lib/interfaces/stocks-split-announcement.interface.js +2 -0
  95. package/lib/interfaces/ticker.interface.d.ts +10 -0
  96. package/lib/interfaces/ticker.interface.js +2 -0
  97. package/lib/scrapers/index.d.ts +10 -0
  98. package/lib/scrapers/index.js +26 -0
  99. package/lib/scrapers/isin-scraper.d.ts +13 -0
  100. package/lib/scrapers/isin-scraper.js +76 -0
  101. package/lib/scrapers/mis-taifex-scraper.d.ts +17 -0
  102. package/lib/scrapers/mis-taifex-scraper.js +126 -0
  103. package/lib/scrapers/mis-twse-scraper.d.ts +15 -0
  104. package/lib/scrapers/mis-twse-scraper.js +97 -0
  105. package/lib/scrapers/mops-scraper.d.ts +17 -0
  106. package/lib/scrapers/mops-scraper.js +64 -0
  107. package/lib/scrapers/scraper-factory.d.ts +25 -0
  108. package/lib/scrapers/scraper-factory.js +62 -0
  109. package/lib/scrapers/scraper.d.ts +6 -0
  110. package/lib/scrapers/scraper.js +20 -0
  111. package/lib/scrapers/taifex-scraper.d.ts +44 -0
  112. package/lib/scrapers/taifex-scraper.js +406 -0
  113. package/lib/scrapers/tdcc-scraper.d.ts +11 -0
  114. package/lib/scrapers/tdcc-scraper.js +82 -0
  115. package/lib/scrapers/tpex-scraper.d.ts +95 -0
  116. package/lib/scrapers/tpex-scraper.js +786 -0
  117. package/lib/scrapers/twse-scraper.d.ts +110 -0
  118. package/lib/scrapers/twse-scraper.js +967 -0
  119. package/lib/twstock.d.ts +229 -0
  120. package/lib/twstock.js +507 -0
  121. package/lib/utils/as-exchange.util.d.ts +2 -0
  122. package/lib/utils/as-exchange.util.js +15 -0
  123. package/lib/utils/as-index.util.d.ts +1 -0
  124. package/lib/utils/as-index.util.js +178 -0
  125. package/lib/utils/as-industry.util.d.ts +2 -0
  126. package/lib/utils/as-industry.util.js +49 -0
  127. package/lib/utils/date-converter.util.d.ts +47 -0
  128. package/lib/utils/date-converter.util.js +113 -0
  129. package/lib/utils/html-cleaner.util.d.ts +29 -0
  130. package/lib/utils/html-cleaner.util.js +45 -0
  131. package/lib/utils/index.d.ts +7 -0
  132. package/lib/utils/index.js +23 -0
  133. package/lib/utils/is-warrant.util.d.ts +1 -0
  134. package/lib/utils/is-warrant.util.js +25 -0
  135. package/lib/utils/parse-numeric.util.d.ts +21 -0
  136. package/lib/utils/parse-numeric.util.js +58 -0
  137. package/package.json +70 -0
@@ -0,0 +1,406 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TaifexScraper = void 0;
4
+ const cheerio = require("cheerio");
5
+ const csvtojson = require("csvtojson");
6
+ const iconv = require("iconv-lite");
7
+ const numeral = require("numeral");
8
+ const luxon_1 = require("luxon");
9
+ const enums_1 = require("../enums");
10
+ const scraper_1 = require("./scraper");
11
+ class TaifexScraper extends scraper_1.Scraper {
12
+ async fetchListedStockFutOpt() {
13
+ const url = 'https://www.taifex.com.tw/cht/2/stockLists';
14
+ const response = await this.httpService.get(url);
15
+ const $ = cheerio.load(response.data);
16
+ const list = $('#myTable tbody tr').map((_, el) => {
17
+ const td = $(el).find('td');
18
+ return {
19
+ symbol: td.eq(0).text().trim(),
20
+ underlyingStock: td.eq(1).text().trim(),
21
+ underlyingSymbol: td.eq(2).text().trim(),
22
+ underlyingName: td.eq(3).text().trim(),
23
+ hasFutures: td.eq(4).text().includes('是股票期貨標的'),
24
+ hasOptions: td.eq(5).text().includes('是股票選擇權標的'),
25
+ isTwseStock: td.eq(6).text().includes('是上市普通股標的證券'),
26
+ isTpexStock: td.eq(7).text().includes('是上櫃普通股標的證券'),
27
+ isTwseETF: td.eq(8).text().includes('是上市ETF標的證券'),
28
+ isTpexETF: td.eq(9).text().includes('是上櫃ETF標的證券'),
29
+ shares: numeral(td.eq(10).text()).value(),
30
+ };
31
+ }).toArray();
32
+ const data = list.reduce((data, row) => {
33
+ const isMicro = (row.shares === 100);
34
+ const futures = {
35
+ symbol: `${row.symbol}F`,
36
+ name: `${isMicro ? '小型' : ''}${row.underlyingName}期貨`,
37
+ exchange: enums_1.Exchange.TAIFEX,
38
+ type: '股票期貨',
39
+ underlyingSymbol: row.underlyingSymbol,
40
+ underlyingName: row.underlyingName,
41
+ };
42
+ const options = {
43
+ symbol: `${row.symbol}O`,
44
+ name: `${isMicro ? '小型' : ''}${row.underlyingName}選擇權`,
45
+ exchange: enums_1.Exchange.TAIFEX,
46
+ type: '股票選擇權',
47
+ underlyingName: row.underlyingName,
48
+ underlying: row.underlyingSymbol,
49
+ };
50
+ if (row.hasFutures)
51
+ data.push(futures);
52
+ if (row.hasOptions)
53
+ data.push(options);
54
+ return data;
55
+ }, []);
56
+ return data;
57
+ }
58
+ async fetchFuturesHistorical(options) {
59
+ const { date, symbol, afterhours } = options;
60
+ const alias = {
61
+ 'TX': 'TXF',
62
+ 'TE': 'EXF',
63
+ 'TF': 'FXF',
64
+ 'MTX': 'MXF', // 小型臺指期貨
65
+ };
66
+ const queryDate = luxon_1.DateTime.fromISO(date).toFormat('yyyy/MM/dd');
67
+ const form = new URLSearchParams({
68
+ down_type: '1',
69
+ queryStartDate: queryDate,
70
+ queryEndDate: queryDate,
71
+ commodity_id: 'all',
72
+ });
73
+ const url = 'https://www.taifex.com.tw/cht/3/futDataDown';
74
+ const response = await this.httpService.post(url, form, { responseType: 'arraybuffer' });
75
+ const csv = iconv.decode(response.data, 'big5');
76
+ const json = await csvtojson({ noheader: true, output: 'csv' }).fromString(csv);
77
+ const [_, ...rows] = json;
78
+ if (!rows.length)
79
+ return null;
80
+ const data = rows.map(row => {
81
+ var _a;
82
+ const [date, contract, contractMonth, ...values] = row;
83
+ const data = {};
84
+ data.date = luxon_1.DateTime.fromFormat(date, 'yyyy/MM/dd').toISODate();
85
+ data.exchange = enums_1.Exchange.TAIFEX;
86
+ data.symbol = (_a = alias[contract]) !== null && _a !== void 0 ? _a : contract;
87
+ data.contractMonth = contractMonth;
88
+ data.open = numeral(values[0]).value();
89
+ data.high = numeral(values[1]).value();
90
+ data.low = numeral(values[2]).value();
91
+ data.close = numeral(values[3]).value();
92
+ data.change = numeral(values[4]).value();
93
+ data.changePercent = numeral(values[5].replace('%', '')).value();
94
+ data.volume = numeral(values[6]).value();
95
+ data.settlementPrice = numeral(values[7]).value();
96
+ data.openInterest = numeral(values[8]).value();
97
+ data.bestBid = numeral(values[9]).value();
98
+ data.bestAsk = numeral(values[10]).value();
99
+ 8;
100
+ data.historicalHigh = numeral(values[11]).value();
101
+ data.historicalLow = numeral(values[12]).value();
102
+ data.session = values[14];
103
+ data.volumeSpread = numeral(values[15]).value();
104
+ return data;
105
+ }).filter(row => afterhours ? row.session === '盤後' : row.session === '一般');
106
+ return symbol ? data.filter(data => data.symbol === symbol) : data;
107
+ }
108
+ async fetchOptionsHistorical(options) {
109
+ const { date, symbol, afterhours } = options;
110
+ const queryDate = luxon_1.DateTime.fromISO(date).toFormat('yyyy/MM/dd');
111
+ const form = new URLSearchParams({
112
+ down_type: '1',
113
+ queryStartDate: queryDate,
114
+ queryEndDate: queryDate,
115
+ commodity_id: 'all',
116
+ });
117
+ const url = 'https://www.taifex.com.tw/cht/3/optDataDown';
118
+ const response = await this.httpService.post(url, form, { responseType: 'arraybuffer' });
119
+ const csv = iconv.decode(response.data, 'big5');
120
+ const json = await csvtojson({ noheader: true, output: 'csv' }).fromString(csv);
121
+ const [_, ...rows] = json;
122
+ if (!rows.length)
123
+ return null;
124
+ const data = rows.map(row => {
125
+ const [date, contract, contractMonth, strikePrice, type, ...values] = row;
126
+ const data = {};
127
+ data.date = luxon_1.DateTime.fromFormat(date, 'yyyy/MM/dd').toISODate();
128
+ data.exchange = enums_1.Exchange.TAIFEX;
129
+ data.symbol = contract;
130
+ data.contractMonth = contractMonth;
131
+ data.strikePrice = numeral(strikePrice).value();
132
+ data.type = type;
133
+ data.open = numeral(values[0]).value();
134
+ data.high = numeral(values[1]).value();
135
+ data.low = numeral(values[2]).value();
136
+ data.close = numeral(values[3]).value();
137
+ data.volume = numeral(values[4]).value();
138
+ data.settlementPrice = numeral(values[5]).value();
139
+ data.openInterest = numeral(values[6]).value();
140
+ data.bestBid = numeral(values[7]).value();
141
+ data.bestAsk = numeral(values[8]).value();
142
+ data.historicalHigh = numeral(values[9]).value();
143
+ data.historicalLow = numeral(values[10]).value();
144
+ data.session = values[12];
145
+ data.change = numeral(values[13]).value();
146
+ data.changePercent = numeral(values[14].replace('%', '')).value();
147
+ return data;
148
+ }).filter(row => afterhours ? row.session === '盤後' : row.session === '一般');
149
+ return symbol ? data.filter(data => data.symbol === symbol) : data;
150
+ }
151
+ async fetchFuturesInstitutional(options) {
152
+ const { date, symbol } = options;
153
+ const queryDate = luxon_1.DateTime.fromISO(date).toFormat('yyyy/MM/dd');
154
+ const form = new URLSearchParams({
155
+ queryStartDate: queryDate,
156
+ queryEndDate: queryDate,
157
+ commodityId: symbol,
158
+ });
159
+ const url = 'https://www.taifex.com.tw/cht/3/futContractsDateDown';
160
+ const response = await this.httpService.post(url, form, { responseType: 'arraybuffer' });
161
+ if (response.data.toString().includes('查無資料'))
162
+ return null;
163
+ if (response.data.toString().includes('日期時間錯誤'))
164
+ return null;
165
+ const csv = iconv.decode(response.data, 'big5');
166
+ const json = await csvtojson({ noheader: true, output: 'csv' }).fromString(csv);
167
+ const [_, ...rows] = json;
168
+ const data = {};
169
+ data.date = date,
170
+ data.exchange = enums_1.Exchange.TAIFEX;
171
+ data.symbol = symbol;
172
+ data.name = rows[0][1];
173
+ data.institutional = rows.map(row => ({
174
+ investor: row[2],
175
+ longTradeVolume: numeral(row[3]).value(),
176
+ longTradeValue: numeral(row[4]).value(),
177
+ shortTradeVolume: numeral(row[5]).value(),
178
+ shortTradeValue: numeral(row[6]).value(),
179
+ netTradeVolume: numeral(row[7]).value(),
180
+ netTradeValue: numeral(row[8]).value(),
181
+ longOiVolume: numeral(row[9]).value(),
182
+ longOiValue: numeral(row[10]).value(),
183
+ shortOiVolume: numeral(row[11]).value(),
184
+ shortOiValue: numeral(row[12]).value(),
185
+ netOiVolume: numeral(row[13]).value(),
186
+ netOiValue: numeral(row[14]).value(),
187
+ }));
188
+ return data;
189
+ }
190
+ async fetchOptionsInstitutional(options) {
191
+ const { date, symbol } = options;
192
+ const queryDate = luxon_1.DateTime.fromISO(date).toFormat('yyyy/MM/dd');
193
+ const form = new URLSearchParams({
194
+ queryStartDate: queryDate,
195
+ queryEndDate: queryDate,
196
+ commodityId: symbol,
197
+ });
198
+ const url = 'https://www.taifex.com.tw/cht/3/callsAndPutsDateDown';
199
+ const response = await this.httpService.post(url, form, { responseType: 'arraybuffer' });
200
+ if (response.data.toString().includes('查無資料'))
201
+ return null;
202
+ if (response.data.toString().includes('日期時間錯誤'))
203
+ return null;
204
+ const csv = iconv.decode(response.data, 'big5');
205
+ const json = await csvtojson({ noheader: true, output: 'csv' }).fromString(csv);
206
+ const [_, ...rows] = json;
207
+ const data = {};
208
+ data.date = date;
209
+ data.exchange = enums_1.Exchange.TAIFEX;
210
+ data.symbol = symbol;
211
+ data.name = rows[0][1];
212
+ data.institutional = rows.map(row => ({
213
+ type: row[2],
214
+ investor: row[3],
215
+ longTradeVolume: numeral(row[4]).value(),
216
+ longTradeValue: numeral(row[5]).value(),
217
+ shortTradeVolume: numeral(row[6]).value(),
218
+ shortTradeValue: numeral(row[7]).value(),
219
+ netTradeVolume: numeral(row[8]).value(),
220
+ netTradeValue: numeral(row[9]).value(),
221
+ longOiVolume: numeral(row[10]).value(),
222
+ longOiValue: numeral(row[11]).value(),
223
+ shortOiVolume: numeral(row[12]).value(),
224
+ shortOiValue: numeral(row[13]).value(),
225
+ netOiVolume: numeral(row[14]).value(),
226
+ netOiValue: numeral(row[15]).value(),
227
+ }));
228
+ return data;
229
+ }
230
+ async fetchFuturesLargeTraders(options) {
231
+ const { date, symbol } = options;
232
+ const alias = {
233
+ 'TXF': 'TX',
234
+ 'EXF': 'TE',
235
+ 'FXF': 'TF', // 金融期貨
236
+ };
237
+ const queryDate = luxon_1.DateTime.fromISO(date).toFormat('yyyy/MM/dd');
238
+ const form = new URLSearchParams({
239
+ queryStartDate: queryDate,
240
+ queryEndDate: queryDate,
241
+ });
242
+ const url = 'https://www.taifex.com.tw/cht/3/largeTraderFutDown';
243
+ const response = await this.httpService.post(url, form, { responseType: 'arraybuffer' });
244
+ if (response.data.toString().includes('查無資料'))
245
+ return null;
246
+ const csv = iconv.decode(response.data, 'big5');
247
+ const json = await csvtojson({ noheader: true, output: 'csv' }).fromString(csv);
248
+ const [_, ...rows] = json;
249
+ const targetRows = rows.filter(row => {
250
+ var _a;
251
+ return row[1] === ((_a = alias[symbol]) !== null && _a !== void 0 ? _a : symbol) || (row[1] === symbol.substring(0, 2));
252
+ });
253
+ const data = {};
254
+ data.date = date;
255
+ data.exchange = enums_1.Exchange.TAIFEX;
256
+ data.symbol = symbol;
257
+ data.name = targetRows[0][2];
258
+ data.largeTraders = targetRows
259
+ .map(row => ({
260
+ contractMonth: row[3],
261
+ traderType: row[4],
262
+ topFiveLongOi: numeral(row[5]).value(),
263
+ topFiveShortOi: numeral(row[6]).value(),
264
+ topTenLongOi: numeral(row[7]).value(),
265
+ topTenShortOi: numeral(row[8]).value(),
266
+ marketOi: numeral(row[9]).value(),
267
+ }));
268
+ return data;
269
+ }
270
+ async fetchOptionsLargeTraders(options) {
271
+ const { date, symbol } = options;
272
+ const queryDate = luxon_1.DateTime.fromISO(date).toFormat('yyyy/MM/dd');
273
+ const form = new URLSearchParams({
274
+ queryStartDate: queryDate,
275
+ queryEndDate: queryDate,
276
+ });
277
+ const url = 'https://www.taifex.com.tw/cht/3/largeTraderOptDown';
278
+ const response = await this.httpService.post(url, form, { responseType: 'arraybuffer' });
279
+ if (response.data.toString().includes('查無資料'))
280
+ return null;
281
+ const csv = iconv.decode(response.data, 'big5');
282
+ const json = await csvtojson({ noheader: true, output: 'csv' }).fromString(csv);
283
+ const [_, ...rows] = json;
284
+ const targetRows = rows.filter(row => {
285
+ return row[1] === symbol || (row[1] === symbol.substring(0, 2));
286
+ });
287
+ const data = {};
288
+ data.date = date;
289
+ data.exchange = enums_1.Exchange.TAIFEX;
290
+ data.symbol = symbol;
291
+ data.name = targetRows[0][2];
292
+ data.largeTraders = targetRows
293
+ .map(row => ({
294
+ type: row[3],
295
+ contractMonth: row[4],
296
+ traderType: row[5],
297
+ topFiveLongOi: numeral(row[6]).value(),
298
+ topFiveShortOi: numeral(row[7]).value(),
299
+ topTenLongOi: numeral(row[8]).value(),
300
+ topTenShortOi: numeral(row[9]).value(),
301
+ marketOi: numeral(row[10]).value(),
302
+ }));
303
+ return data;
304
+ }
305
+ async fetchMxfRetailPosition(options) {
306
+ const date = options.date;
307
+ const [fetchedMxfHistorical, fetchedMxfInstitutional] = await Promise.all([
308
+ this.fetchFuturesHistorical({ date, symbol: 'MXF' }),
309
+ this.fetchFuturesInstitutional({ date, symbol: 'MXF' }),
310
+ ]);
311
+ if (!fetchedMxfHistorical || !fetchedMxfInstitutional)
312
+ return null;
313
+ const mxfMarketOi = fetchedMxfHistorical
314
+ .filter(row => row.session === '一般' && !row.volumeSpread)
315
+ .reduce((oi, row) => oi + numeral(row.openInterest).value(), 0);
316
+ const { mxfInstitutionalLongOi, mxfInstitutionalShortOi } = fetchedMxfInstitutional.institutional
317
+ .reduce((institutional, row) => ({
318
+ mxfInstitutionalLongOi: institutional.mxfInstitutionalLongOi + row.longOiVolume,
319
+ mxfInstitutionalShortOi: institutional.mxfInstitutionalShortOi + row.shortOiVolume,
320
+ }), { mxfInstitutionalLongOi: 0, mxfInstitutionalShortOi: 0 });
321
+ const data = {};
322
+ data.date = date;
323
+ data.mxfRetailLongOi = mxfMarketOi - mxfInstitutionalLongOi;
324
+ data.mxfRetailShortOi = mxfMarketOi - mxfInstitutionalShortOi;
325
+ data.mxfRetailNetOi = data.mxfRetailLongOi - data.mxfRetailShortOi;
326
+ data.mxfRetailLongShortRatio = Math.round(data.mxfRetailNetOi / mxfMarketOi * 10000) / 10000;
327
+ return data;
328
+ }
329
+ async fetchTmfRetailPosition(options) {
330
+ const date = options.date;
331
+ const [fetchedTmfHistorical, fetchedTmfInstitutional] = await Promise.all([
332
+ this.fetchFuturesHistorical({ date, symbol: 'TMF' }),
333
+ this.fetchFuturesInstitutional({ date, symbol: 'TMF' }),
334
+ ]);
335
+ if (!fetchedTmfHistorical || !fetchedTmfInstitutional)
336
+ return null;
337
+ const tmfMarketOi = fetchedTmfHistorical
338
+ .filter(row => row.session === '一般' && !row.volumeSpread)
339
+ .reduce((oi, row) => oi + numeral(row.openInterest).value(), 0);
340
+ const { tmfInstitutionalLongOi, tmfInstitutionalShortOi } = fetchedTmfInstitutional.institutional
341
+ .reduce((institutional, row) => ({
342
+ tmfInstitutionalLongOi: institutional.tmfInstitutionalLongOi + row.longOiVolume,
343
+ tmfInstitutionalShortOi: institutional.tmfInstitutionalShortOi + row.shortOiVolume,
344
+ }), { tmfInstitutionalLongOi: 0, tmfInstitutionalShortOi: 0 });
345
+ const data = {};
346
+ data.date = date;
347
+ data.tmfRetailLongOi = tmfMarketOi - tmfInstitutionalLongOi;
348
+ data.tmfRetailShortOi = tmfMarketOi - tmfInstitutionalShortOi;
349
+ data.tmfRetailNetOi = data.tmfRetailLongOi - data.tmfRetailShortOi;
350
+ data.tmfRetailLongShortRatio = Math.round(data.tmfRetailNetOi / tmfMarketOi * 10000) / 10000;
351
+ return data;
352
+ }
353
+ async fetchTxoPutCallRatio(options) {
354
+ const { date } = options;
355
+ const queryDate = luxon_1.DateTime.fromISO(date).toFormat('yyyy/MM/dd');
356
+ const form = new URLSearchParams({
357
+ queryStartDate: queryDate,
358
+ queryEndDate: queryDate,
359
+ });
360
+ const url = 'https://www.taifex.com.tw/cht/3/pcRatioDown';
361
+ const response = await this.httpService.post(url, form, { responseType: 'arraybuffer' });
362
+ const csv = iconv.decode(response.data, 'big5');
363
+ const json = await csvtojson({ noheader: true, output: 'csv' }).fromString(csv);
364
+ const [_, row] = json;
365
+ if (!row)
366
+ return null;
367
+ const data = {};
368
+ data.date = date;
369
+ data.txoPutVolume = numeral(row[1]).value();
370
+ data.txoCallVolume = numeral(row[2]).value();
371
+ data.txoPutCallVolumeRatio = numeral(row[3]).divide(100).value();
372
+ data.txoPutOi = numeral(row[4]).value();
373
+ data.txoCallOi = numeral(row[5]).value();
374
+ data.txoPutCallOiRatio = numeral(row[6]).divide(100).value();
375
+ return data;
376
+ }
377
+ async fetchExchangeRates(options) {
378
+ const date = options.date;
379
+ const queryDate = luxon_1.DateTime.fromISO(date).toFormat('yyyy/MM/dd');
380
+ const form = new URLSearchParams({
381
+ queryStartDate: queryDate,
382
+ queryEndDate: queryDate,
383
+ });
384
+ const url = 'https://www.taifex.com.tw/cht/3/dailyFXRateDown';
385
+ const response = await this.httpService.post(url, form, { responseType: 'arraybuffer' });
386
+ const csv = iconv.decode(response.data, 'big5');
387
+ const json = await csvtojson({ noheader: true, output: 'csv' }).fromString(csv);
388
+ const [_, row] = json;
389
+ if (!row.length)
390
+ return null;
391
+ const data = {};
392
+ data.date = luxon_1.DateTime.fromFormat(row[0], 'yyyy/MM/dd').toISODate();
393
+ data.usdtwd = numeral(row[1]).value();
394
+ data.cnytwd = numeral(row[2]).value();
395
+ data.eurusd = numeral(row[3]).value();
396
+ data.usdjpy = numeral(row[4]).value();
397
+ data.gbpusd = numeral(row[5]).value();
398
+ data.audusd = numeral(row[6]).value();
399
+ data.usdhkd = numeral(row[7]).value();
400
+ data.usdcny = numeral(row[8]).value();
401
+ data.usdzar = numeral(row[9]).value();
402
+ data.nzdusd = numeral(row[10]).value();
403
+ return data;
404
+ }
405
+ }
406
+ exports.TaifexScraper = TaifexScraper;
@@ -0,0 +1,11 @@
1
+ import { Scraper } from './scraper';
2
+ import { StockShareholders } from '../interfaces';
3
+ export declare class TdccScraper extends Scraper {
4
+ fetchStocksShareholders(options: {
5
+ date: string;
6
+ symbol: string;
7
+ }): Promise<StockShareholders | null>;
8
+ fetchStocksShareholdersRecentWeek(options?: {
9
+ symbol: string;
10
+ }): Promise<StockShareholders | StockShareholders[] | undefined>;
11
+ }
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TdccScraper = void 0;
4
+ const _ = require("lodash");
5
+ const cheerio = require("cheerio");
6
+ const numeral = require("numeral");
7
+ const csvtojson = require("csvtojson");
8
+ const luxon_1 = require("luxon");
9
+ const scraper_1 = require("./scraper");
10
+ class TdccScraper extends scraper_1.Scraper {
11
+ async fetchStocksShareholders(options) {
12
+ const { date, symbol } = options;
13
+ const url = 'https://www.tdcc.com.tw/portal/zh/smWeb/qryStock';
14
+ const request = await this.httpService.get(url);
15
+ const $request = cheerio.load(request.data);
16
+ const token = $request('#SYNCHRONIZER_TOKEN').attr('value');
17
+ const uri = $request('#SYNCHRONIZER_URI').attr('value');
18
+ const method = $request('#method').attr('value');
19
+ const firDate = $request('#firDate').attr('value');
20
+ const scaDate = $request('#scaDate').find('option').map((_, el) => {
21
+ return $request(el).attr('value');
22
+ }).toArray();
23
+ const cookie = request.headers['set-cookie'];
24
+ const form = new URLSearchParams({
25
+ SYNCHRONIZER_TOKEN: token,
26
+ SYNCHRONIZER_URI: uri,
27
+ method,
28
+ firDate,
29
+ scaDate: luxon_1.DateTime.fromISO(date).toFormat('yyyyMMdd'),
30
+ sqlMethod: 'StockNo',
31
+ stockNo: symbol,
32
+ stockName: '',
33
+ });
34
+ const response = await this.httpService.post(url, form, { headers: { 'Cookie': cookie } });
35
+ const $response = cheerio.load(response.data);
36
+ const message = $response('.table tr').find('td').eq(0).text();
37
+ if (message === '查無此資料')
38
+ return null;
39
+ const data = {};
40
+ data.date = date;
41
+ data.symbol = symbol;
42
+ data.shareholders = $response('.table tr').slice(1).map((_, el) => {
43
+ const td = $response(el).find('td');
44
+ return {
45
+ level: numeral(td.eq(0).text()).value(),
46
+ holders: numeral(td.eq(2).text()).value(),
47
+ shares: numeral(td.eq(3).text()).value(),
48
+ proportion: numeral(td.eq(4).text()).value(),
49
+ };
50
+ }).toArray();
51
+ if (data.shareholders.length === 16) {
52
+ data.shareholders = [
53
+ ...data.shareholders.slice(0, -1),
54
+ { level: 16, holders: null, shares: 0, proportion: 0 },
55
+ Object.assign(Object.assign({}, data.shareholders[data.shareholders.length - 1]), { level: 17 })
56
+ ];
57
+ }
58
+ return data;
59
+ }
60
+ async fetchStocksShareholdersRecentWeek(options) {
61
+ const url = 'https://smart.tdcc.com.tw/opendata/getOD.ashx?id=1-5';
62
+ const response = await this.httpService.get(url);
63
+ const json = await csvtojson({ noheader: true, output: 'csv' }).fromString(response.data);
64
+ const [fields, ...rows] = json;
65
+ const distributions = rows.map(row => ({
66
+ date: luxon_1.DateTime.fromFormat(row[0], 'yyyyMMdd').toISODate(),
67
+ symbol: row[1],
68
+ level: numeral(row[2]).value(),
69
+ holders: row[2] === '16' ? null : numeral(row[3]).value(),
70
+ shares: row[2] === '16' ? numeral(row[4]).multiply(-1).value() : numeral(row[4]).value(),
71
+ proportion: row[2] === '16' ? numeral(row[5]).multiply(-1).value() : numeral(row[5]).value(),
72
+ }));
73
+ const data = _(distributions).groupBy('symbol')
74
+ .map(rows => {
75
+ const { date, symbol } = rows[0];
76
+ const shareholders = rows.map(row => _.omit(row, ['date', 'symbol']));
77
+ return { date, symbol, shareholders };
78
+ }).value();
79
+ return (options === null || options === void 0 ? void 0 : options.symbol) ? data.find(data => data.symbol === options.symbol) : data;
80
+ }
81
+ }
82
+ exports.TdccScraper = TdccScraper;
@@ -0,0 +1,95 @@
1
+ import { Scraper } from './scraper';
2
+ import { IndexHistorical, IndexTrades, MarketBreadth, MarketInstitutional, MarketMarginTrades, MarketTrades, StockCapitalReductions, StockDividends, StockFiniHoldings, StockHistorical, StockInstitutional, StocksListingApplication, StockMarginTrades, StockShortSales, StockSplits, StockValues, StocksDividendAnnouncement, StocksCapitalReductionAnnouncement, StocksSplitAnnouncement, StocksEtfSplitAnnouncement } from '../interfaces';
3
+ export declare class TpexScraper extends Scraper {
4
+ fetchStocksHistorical(options: {
5
+ date: string;
6
+ symbol?: string;
7
+ }): Promise<StockHistorical | StockHistorical[] | null | undefined>;
8
+ fetchStocksInstitutional(options: {
9
+ date: string;
10
+ symbol?: string;
11
+ }): Promise<StockInstitutional | StockInstitutional[] | null | undefined>;
12
+ fetchStocksFiniHoldings(options: {
13
+ date: string;
14
+ symbol?: string;
15
+ }): Promise<StockFiniHoldings | StockFiniHoldings[] | null | undefined>;
16
+ fetchStocksMarginTrades(options: {
17
+ date: string;
18
+ symbol?: string;
19
+ }): Promise<StockMarginTrades | StockMarginTrades[] | null | undefined>;
20
+ fetchStocksShortSales(options: {
21
+ date: string;
22
+ symbol?: string;
23
+ }): Promise<StockShortSales | StockShortSales[] | null | undefined>;
24
+ fetchStocksValues(options: {
25
+ date: string;
26
+ symbol?: string;
27
+ }): Promise<StockValues | StockValues[] | null | undefined>;
28
+ fetchStocksDividends(options: {
29
+ startDate: string;
30
+ endDate: string;
31
+ symbol?: string;
32
+ includeDetail?: boolean;
33
+ }): Promise<StockDividends[]>;
34
+ fetchStocksCapitalReductions(options: {
35
+ startDate: string;
36
+ endDate: string;
37
+ symbol?: string;
38
+ includeDetail?: boolean;
39
+ }): Promise<StockCapitalReductions[]>;
40
+ fetchStocksSplits(options: {
41
+ startDate: string;
42
+ endDate: string;
43
+ symbol?: string;
44
+ }): Promise<StockSplits[]>;
45
+ fetchStocksEtfSplits(options: {
46
+ startDate: string;
47
+ endDate: string;
48
+ symbol?: string;
49
+ }): Promise<StockSplits[]>;
50
+ fetchStocksEtfReverseSplits(options: {
51
+ startDate: string;
52
+ endDate: string;
53
+ symbol?: string;
54
+ }): Promise<StockSplits[]>;
55
+ fetchIndicesHistorical(options: {
56
+ date: string;
57
+ symbol?: string;
58
+ }): Promise<IndexHistorical | IndexHistorical[] | null | undefined>;
59
+ fetchIndicesTrades(options: {
60
+ date: string;
61
+ symbol?: string;
62
+ }): Promise<IndexTrades | IndexTrades[] | null | undefined>;
63
+ fetchMarketTrades(options: {
64
+ date: string;
65
+ }): Promise<MarketTrades | null>;
66
+ fetchMarketBreadth(options: {
67
+ date: string;
68
+ }): Promise<MarketBreadth | null>;
69
+ fetchMarketInstitutional(options: {
70
+ date: string;
71
+ }): Promise<MarketInstitutional | null>;
72
+ fetchMarketMarginTrades(options: {
73
+ date: string;
74
+ }): Promise<MarketMarginTrades | null>;
75
+ fetchStocksDividendsAnnouncement(options?: {
76
+ symbol?: string;
77
+ }): Promise<StocksDividendAnnouncement[]>;
78
+ fetchStocksCapitalReductionAnnouncement(options?: {
79
+ symbol?: string;
80
+ }): Promise<StocksCapitalReductionAnnouncement[]>;
81
+ fetchStocksSplitAnnouncement(options?: {
82
+ symbol?: string;
83
+ }): Promise<StocksSplitAnnouncement[]>;
84
+ fetchStocksEtfSplitAnnouncement(options?: {
85
+ symbol?: string;
86
+ }): Promise<StocksEtfSplitAnnouncement[]>;
87
+ fetchStocksEtfReverseSplitAnnouncement(options?: {
88
+ symbol?: string;
89
+ }): Promise<StocksEtfSplitAnnouncement[]>;
90
+ private fetchEtfSplitAnnouncementBase;
91
+ fetchStocksListingApplicants(options?: {
92
+ symbol?: string;
93
+ year?: number | 'ALL';
94
+ }): Promise<StocksListingApplication[]>;
95
+ }