@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,13 @@
1
+ import { Scraper } from './scraper';
2
+ import { FutOpt, Stock } from '../interfaces';
3
+ export declare class IsinScraper extends Scraper {
4
+ fetchListed(options: {
5
+ symbol: string;
6
+ }): Promise<Stock[]>;
7
+ fetchListedStocks(options: {
8
+ exchange: 'TWSE' | 'TPEx';
9
+ }): Promise<Stock[]>;
10
+ fetchListedFutOpt(options?: {
11
+ type?: 'F' | 'O';
12
+ }): Promise<FutOpt[]>;
13
+ }
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.IsinScraper = void 0;
4
+ const cheerio = require("cheerio");
5
+ const iconv = require("iconv-lite");
6
+ const luxon_1 = require("luxon");
7
+ const scraper_1 = require("./scraper");
8
+ const utils_1 = require("../utils");
9
+ class IsinScraper extends scraper_1.Scraper {
10
+ async fetchListed(options) {
11
+ const { symbol } = options;
12
+ const url = `https://isin.twse.com.tw/isin/single_main.jsp?owncode=${symbol}`;
13
+ const response = await this.httpService.get(url, { responseType: 'arraybuffer' });
14
+ const page = iconv.decode(response.data, 'big5');
15
+ const $ = cheerio.load(page);
16
+ const data = $('.h4 tr').slice(1).map((_, el) => {
17
+ const td = $(el).find('td');
18
+ return {
19
+ symbol: td.eq(2).text().trim(),
20
+ name: td.eq(3).text().trim(),
21
+ exchange: (0, utils_1.asExchange)(td.eq(4).text().trim()),
22
+ type: td.eq(5).text().trim(),
23
+ industry: (0, utils_1.asIndustry)(td.eq(6).text().trim()),
24
+ listedDate: luxon_1.DateTime.fromFormat(td.eq(7).text().trim(), 'yyyy/MM/dd').toISODate(),
25
+ };
26
+ }).toArray();
27
+ return data;
28
+ }
29
+ async fetchListedStocks(options) {
30
+ const { exchange } = options;
31
+ const url = {
32
+ 'TWSE': 'https://isin.twse.com.tw/isin/class_main.jsp?market=1',
33
+ 'TPEx': 'https://isin.twse.com.tw/isin/class_main.jsp?market=2',
34
+ };
35
+ const response = await this.httpService.get(url[exchange], { responseType: 'arraybuffer' });
36
+ const page = iconv.decode(response.data, 'big5');
37
+ const $ = cheerio.load(page);
38
+ const data = $('.h4 tr').slice(1).map((_, el) => {
39
+ const td = $(el).find('td');
40
+ return {
41
+ symbol: td.eq(2).text().trim(),
42
+ name: td.eq(3).text().trim(),
43
+ exchange: (0, utils_1.asExchange)(td.eq(4).text().trim()),
44
+ type: td.eq(5).text().trim(),
45
+ industry: (0, utils_1.asIndustry)(td.eq(6).text().trim()),
46
+ listedDate: luxon_1.DateTime.fromFormat(td.eq(7).text().trim(), 'yyyy/MM/dd').toISODate(),
47
+ };
48
+ }).toArray();
49
+ return data;
50
+ }
51
+ async fetchListedFutOpt(options) {
52
+ const { type } = options !== null && options !== void 0 ? options : {};
53
+ const url = 'https://isin.twse.com.tw/isin/class_main.jsp?market=7';
54
+ const response = await this.httpService.get(url, { responseType: 'arraybuffer' });
55
+ const page = iconv.decode(response.data, 'big5');
56
+ const $ = cheerio.load(page);
57
+ const data = $('.h4 tr').slice(1).map((_, el) => {
58
+ const td = $(el).find('td');
59
+ return {
60
+ symbol: td.eq(2).text().trim(),
61
+ name: td.eq(3).text().trim(),
62
+ exchange: (0, utils_1.asExchange)(td.eq(4).text().trim()),
63
+ type: td.eq(5).text().trim(),
64
+ listedDate: luxon_1.DateTime.fromFormat(td.eq(7).text().trim(), 'yyyy/MM/dd').toISODate(),
65
+ };
66
+ }).toArray();
67
+ return data.filter(row => {
68
+ if (type === 'F')
69
+ return row.type.includes('期貨');
70
+ if (type === 'O')
71
+ return row.type.includes('選擇權');
72
+ return true;
73
+ });
74
+ }
75
+ }
76
+ exports.IsinScraper = IsinScraper;
@@ -0,0 +1,17 @@
1
+ import { Scraper } from './scraper';
2
+ import { FutOptQuote, Ticker } from '../interfaces';
3
+ export declare class MisTaifexScraper extends Scraper {
4
+ fetchListedFutOpt(options?: {
5
+ type?: 'F' | 'O';
6
+ }): Promise<any>;
7
+ fetchFutOptQuoteList(options: {
8
+ ticker: Ticker;
9
+ afterhours?: boolean;
10
+ }): Promise<FutOptQuote[] | null>;
11
+ fetchFutOptQuoteDetail(options: {
12
+ ticker: Ticker;
13
+ afterhours?: boolean;
14
+ }): Promise<FutOptQuote | null | undefined>;
15
+ private extractSymbolIdFromTicker;
16
+ private extractTypeFromCmdyDDLItem;
17
+ }
@@ -0,0 +1,126 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MisTaifexScraper = void 0;
4
+ const numeral = require("numeral");
5
+ const luxon_1 = require("luxon");
6
+ const scraper_1 = require("./scraper");
7
+ const enums_1 = require("../enums");
8
+ class MisTaifexScraper extends scraper_1.Scraper {
9
+ async fetchListedFutOpt(options) {
10
+ const { type } = options !== null && options !== void 0 ? options : {};
11
+ const url = 'https://mis.taifex.com.tw/futures/api/getCmdyDDLItemByKind';
12
+ const body = JSON.stringify({
13
+ SymbolType: type,
14
+ });
15
+ const response = await this.httpService.post(url, body, {
16
+ headers: { 'Content-Type': 'application/json' },
17
+ });
18
+ const json = response.data;
19
+ const data = json.RtData.Items.map((row) => (Object.assign({ symbol: row.CID, name: row.DispCName, exchange: enums_1.Exchange.TAIFEX, type: type !== null && type !== void 0 ? type : this.extractTypeFromCmdyDDLItem(row) }, (row.SpotID && { underlying: row.SpotID }))));
20
+ return data;
21
+ }
22
+ async fetchFutOptQuoteList(options) {
23
+ const { ticker, afterhours } = options;
24
+ const body = JSON.stringify({
25
+ CID: ticker.symbol,
26
+ SymbolType: ticker.type,
27
+ MarketType: afterhours ? 1 : 0,
28
+ });
29
+ const url = `https://mis.taifex.com.tw/futures/api/getQuoteList`;
30
+ const response = await this.httpService.post(url, body, {
31
+ headers: { 'Content-Type': 'application/json' },
32
+ });
33
+ const json = (response.data.RtCode === '0') && response.data;
34
+ if (!json)
35
+ return null;
36
+ const data = json.RtData.QuoteList.map((row) => ({
37
+ symbol: row.SymbolID.split('-')[0],
38
+ name: row.DispCName,
39
+ status: row.Status,
40
+ openPrice: row.COpenPrice && numeral(row.COpenPrice).value(),
41
+ highPrice: row.CHighPrice && numeral(row.CHighPrice).value(),
42
+ lowPrice: row.CLowPrice && numeral(row.CLowPrice).value(),
43
+ lastPrice: row.CLastPrice && numeral(row.CLastPrice).value(),
44
+ referencePrice: row.CRefPrice && numeral(row.CRefPrice).value(),
45
+ limitUpPrice: row.CCeilPrice && numeral(row.CCeilPrice).value(),
46
+ limitDownPrice: row.CFloorPrice && numeral(row.CFloorPrice).value(),
47
+ settlementPrice: row.SettlementPrice && numeral(row.SettlementPrice).value(),
48
+ change: row.CDiff && numeral(row.CDiff).value(),
49
+ changePercent: row.CDiffRate && numeral(row.CDiffRate).value(),
50
+ amplitude: row.CAmpRate && numeral(row.CAmpRate).value(),
51
+ totalVoluem: row.CTotalVolume && numeral(row.CTotalVolume).value(),
52
+ openInterest: row.OpenInterest && numeral(row.OpenInterest).value(),
53
+ bestBidPrice: row.CBestBidPrice && numeral(row.CBestBidPrice).value(),
54
+ bestAskPrice: row.CBestAskPrice && numeral(row.CBestAskPrice).value(),
55
+ bestBidSize: row.CBestBidSize && numeral(row.CBestBidSize).value(),
56
+ bestAskSize: row.CBestAskSize && numeral(row.CBestAskSize).value(),
57
+ testPrice: row.CTestPrice && numeral(row.CTestPrice).value(),
58
+ testSize: row.CTestVolume && numeral(row.CTestVolume).value(),
59
+ lastUpdated: row.CTime && luxon_1.DateTime.fromFormat(`${row.CDate} ${row.CTime}`, 'yyyyMMdd hhmmss', { zone: 'Asia/Taipei' }).toMillis(),
60
+ }));
61
+ return data;
62
+ }
63
+ async fetchFutOptQuoteDetail(options) {
64
+ const { ticker, afterhours } = options;
65
+ const body = JSON.stringify({ SymbolID: [this.extractSymbolIdFromTicker(ticker, { afterhours })] });
66
+ const url = `https://mis.taifex.com.tw/futures/api/getQuoteDetail`;
67
+ const response = await this.httpService.post(url, body, {
68
+ headers: { 'Content-Type': 'application/json' },
69
+ });
70
+ const json = (response.data.RtCode === '0') && response.data;
71
+ if (!json)
72
+ return null;
73
+ const data = json.RtData.QuoteList.map((row) => ({
74
+ symbol: ticker.symbol,
75
+ name: row.DispCName,
76
+ status: row.Status,
77
+ referencePrice: row.CRefPrice && numeral(row.CRefPrice).value(),
78
+ limitUpPrice: row.CCeilPrice && numeral(row.CCeilPrice).value(),
79
+ limitDownPrice: row.CFloorPrice && numeral(row.CFloorPrice).value(),
80
+ openPrice: row.COpenPrice && numeral(row.COpenPrice).value(),
81
+ highPrice: row.CHighPrice && numeral(row.CHighPrice).value(),
82
+ lowPrice: row.CLowPrice && numeral(row.CLowPrice).value(),
83
+ lastPrice: row.CLastPrice && numeral(row.CLastPrice).value(),
84
+ lastSize: row.CSingleVolume && numeral(row.CSingleVolume).value(),
85
+ testPrice: row.CTestPrice && numeral(row.CTestPrice).value(),
86
+ testSize: row.CTestVolume && numeral(row.CTestVolume).value(),
87
+ testTime: row.CTestTime && luxon_1.DateTime.fromFormat(`${row.CDate} ${row.CTestTime}`, 'yyyyMMdd hhmmss', { zone: 'Asia/Taipei' }).toMillis(),
88
+ totalVoluem: row.CTotalVolume && numeral(row.CTotalVolume).value(),
89
+ openInterest: row.OpenInterest && numeral(row.OpenInterest).value(),
90
+ bidOrders: row.CBidCount && numeral(row.CBidCount).value(),
91
+ askOrders: row.CAskCount && numeral(row.CAskCount).value(),
92
+ bidVolume: row.CBidUnit && numeral(row.CBidUnit).value(),
93
+ askVolume: row.CAskUnit && numeral(row.CAskUnit).value(),
94
+ bidPrice: [row.CBidPrice1, row.CBidPrice2, row.CBidPrice3, row.CBidPrice4, row.CBidPrice5].map(price => numeral(price).value()),
95
+ askPrice: [row.CAskPrice1, row.CAskPrice2, row.CAskPrice3, row.CAskPrice4, row.CAskPrice5].map(price => numeral(price).value()),
96
+ bidSize: [row.CBidSize1, row.CBidSize2, row.CBidSize3, row.CBidSize4, row.CBidSize5].map(size => numeral(size).value()),
97
+ askSize: [row.CAskSize1, row.CAskSize2, row.CAskSize3, row.CAskSize4, row.CAskSize5].map(size => numeral(size).value()),
98
+ extBidPrice: row.CExtBidPrice && numeral(row.CExtBidPrice).value(),
99
+ extAskPrice: row.CExtAskPrice && numeral(row.CExtAskPrice).value(),
100
+ extBidSize: row.CExtBidSize && numeral(row.CExtBidSize).value(),
101
+ extAskSize: row.CExtAskSize && numeral(row.CExtAskSize).value(),
102
+ lastUpdated: luxon_1.DateTime.fromFormat(`${row.CDate} ${row.CTime}`, 'yyyyMMdd hhmmss', { zone: 'Asia/Taipei' }).toMillis(),
103
+ }));
104
+ return data.find(row => row.symbol.includes(ticker.symbol));
105
+ }
106
+ extractSymbolIdFromTicker(ticker, options) {
107
+ const { symbol, type } = ticker;
108
+ const { afterhours } = options;
109
+ if (type && type.includes('期貨')) {
110
+ return afterhours ? `${symbol}-M` : `${symbol}-F`;
111
+ }
112
+ if (type && type.includes('選擇權')) {
113
+ return afterhours ? `${symbol}-N` : `${symbol}-O`;
114
+ }
115
+ }
116
+ extractTypeFromCmdyDDLItem(item) {
117
+ const type = item.CID.charAt(2);
118
+ if (['F', 'O'].includes(type))
119
+ return type;
120
+ if (item.DispCName.includes('期貨'))
121
+ return 'F';
122
+ if (item.DispCName.includes('選擇權'))
123
+ return 'O';
124
+ }
125
+ }
126
+ exports.MisTaifexScraper = MisTaifexScraper;
@@ -0,0 +1,15 @@
1
+ import { Scraper } from './scraper';
2
+ import { IndexQuote, StockQuote, Ticker } from '../interfaces';
3
+ export declare class MisTwseScraper extends Scraper {
4
+ fetchListedIndices(options: {
5
+ exchange: 'TWSE' | 'TPEx';
6
+ }): Promise<any>;
7
+ fetchStocksQuote(options: {
8
+ ticker: Ticker;
9
+ odd?: boolean;
10
+ }): Promise<StockQuote | null | undefined>;
11
+ fetchIndicesQuote(options: {
12
+ ticker: Ticker;
13
+ }): Promise<IndexQuote | null | undefined>;
14
+ private extractExChFromTicker;
15
+ }
@@ -0,0 +1,97 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MisTwseScraper = void 0;
4
+ const numeral = require("numeral");
5
+ const luxon_1 = require("luxon");
6
+ const scraper_1 = require("./scraper");
7
+ const utils_1 = require("../utils");
8
+ class MisTwseScraper extends scraper_1.Scraper {
9
+ async fetchListedIndices(options) {
10
+ const { exchange } = options;
11
+ const ex = { 'TWSE': 'tse', 'TPEx': 'otc' };
12
+ const i = { 'TWSE': 'TIDX', 'TPEx': 'OIDX' };
13
+ const query = new URLSearchParams({
14
+ ex: ex[exchange],
15
+ i: i[exchange],
16
+ });
17
+ const url = `https://mis.twse.com.tw/stock/api/getCategory.jsp?${query}`;
18
+ const response = await this.httpService.get(url);
19
+ const json = (response.data.rtmessage === 'OK') && response.data;
20
+ if (!json)
21
+ return null;
22
+ const data = json.msgArray.map((row) => {
23
+ var _a;
24
+ return ({
25
+ symbol: (_a = (0, utils_1.asIndex)(row.n)) !== null && _a !== void 0 ? _a : (row.ch).replace('.tw', ''),
26
+ name: row.n,
27
+ exchange,
28
+ ex_ch: `${row.ex}_${row.ch}`,
29
+ });
30
+ });
31
+ return data;
32
+ }
33
+ async fetchStocksQuote(options) {
34
+ const { ticker, odd } = options;
35
+ const query = new URLSearchParams({
36
+ ex_ch: this.extractExChFromTicker(ticker),
37
+ });
38
+ const url = odd
39
+ ? `https://mis.twse.com.tw/stock/api/getOddInfo.jsp?${query}`
40
+ : `https://mis.twse.com.tw/stock/api/getStockInfo.jsp?${query}`;
41
+ const response = await this.httpService.get(url);
42
+ const json = (response.data.rtmessage === 'OK') && response.data;
43
+ if (!json)
44
+ return null;
45
+ const data = json.msgArray.map((row) => ({
46
+ date: luxon_1.DateTime.fromFormat(row.d, 'yyyyMMdd').toISODate(),
47
+ symbol: row.c,
48
+ name: row.n,
49
+ referencePrice: row.y && numeral(row.y).value(),
50
+ limitUpPrice: row.u && numeral(row.u).value(),
51
+ limitDownPrice: row.w && numeral(row.w).value(),
52
+ openPrice: row.o && numeral(row.o).value(),
53
+ highPrice: row.h && numeral(row.h).value(),
54
+ lowPrice: row.l && numeral(row.l).value(),
55
+ lastPrice: row.z && numeral(row.z).value(),
56
+ lastSize: row.s && numeral(row.s).value(),
57
+ totalVoluem: row.v && numeral(row.v).value(),
58
+ bidPrice: row.b && row.b.split('_').slice(0, -1).map((price) => numeral(price).value()),
59
+ askPrice: row.a && row.a.split('_').slice(0, -1).map((price) => numeral(price).value()),
60
+ bidSize: row.g && row.g.split('_').slice(0, -1).map((size) => numeral(size).value()),
61
+ askSize: row.f && row.f.split('_').slice(0, -1).map((size) => numeral(size).value()),
62
+ lastUpdated: row.tlong && numeral(row.tlong).value(),
63
+ }));
64
+ return data.find(row => row.symbol === ticker.symbol);
65
+ }
66
+ async fetchIndicesQuote(options) {
67
+ const { ticker } = options;
68
+ const query = new URLSearchParams({
69
+ ex_ch: this.extractExChFromTicker(ticker),
70
+ });
71
+ const url = `https://mis.twse.com.tw/stock/api/getStockInfo.jsp?${query}`;
72
+ const response = await this.httpService.get(url);
73
+ const json = (response.data.rtmessage === 'OK') && response.data;
74
+ if (!json)
75
+ return null;
76
+ const data = json.msgArray.map((row) => ({
77
+ date: luxon_1.DateTime.fromFormat(row.d, 'yyyyMMdd').toISODate(),
78
+ symbol: ticker.symbol,
79
+ name: row.n,
80
+ previousClose: row.y && numeral(row.y).value(),
81
+ open: row.o && numeral(row.o).value(),
82
+ high: row.h && numeral(row.h).value(),
83
+ low: row.l && numeral(row.l).value(),
84
+ close: row.z && numeral(row.z).value(),
85
+ volume: row.v && numeral(row.v).value(),
86
+ lastUpdated: row.tlong && numeral(row.tlong).value(),
87
+ }));
88
+ return data.find(row => row.symbol === ticker.symbol);
89
+ }
90
+ extractExChFromTicker(ticker) {
91
+ const { symbol, exchange, ex_ch } = ticker;
92
+ const ex = { 'TWSE': 'tse', 'TPEx': 'otc' };
93
+ const ch = `${symbol}.tw`;
94
+ return ex_ch ? ex_ch : `${ex[exchange]}_${ch}`;
95
+ }
96
+ }
97
+ exports.MisTwseScraper = MisTwseScraper;
@@ -0,0 +1,17 @@
1
+ import { Scraper } from './scraper';
2
+ import { StockEps, StockRevenue } from '../interfaces';
3
+ export declare class MopsScraper extends Scraper {
4
+ fetchStocksEps(options: {
5
+ exchange: string;
6
+ year: number;
7
+ quarter: number;
8
+ symbol?: string;
9
+ }): Promise<StockEps | StockEps[] | null | undefined>;
10
+ fetchStocksRevenue(options: {
11
+ exchange: string;
12
+ year: number;
13
+ month: number;
14
+ foreign?: boolean;
15
+ symbol?: string;
16
+ }): Promise<StockRevenue | StockRevenue[] | null | undefined>;
17
+ }
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MopsScraper = void 0;
4
+ const _ = require("lodash");
5
+ const cheerio = require("cheerio");
6
+ const iconv = require("iconv-lite");
7
+ const numeral = require("numeral");
8
+ const scraper_1 = require("./scraper");
9
+ class MopsScraper extends scraper_1.Scraper {
10
+ async fetchStocksEps(options) {
11
+ const { exchange, year, quarter, symbol } = options;
12
+ const type = { 'TWSE': 'sii', 'TPEx': 'otc' };
13
+ const form = new URLSearchParams({
14
+ encodeURIComponent: '1',
15
+ step: '1',
16
+ firstin: '1',
17
+ off: '1',
18
+ isQuery: 'Y',
19
+ TYPEK: type[exchange],
20
+ year: numeral(year).subtract(1911).format(),
21
+ season: numeral(quarter).format('00'),
22
+ });
23
+ const url = 'https://mops.twse.com.tw/mops/web/t163sb04';
24
+ const response = await this.httpService.post(url, form);
25
+ if (response.data.includes('查詢無資料!'))
26
+ return null;
27
+ const $ = cheerio.load(response.data);
28
+ const data = $('.even,.odd').map((_, el) => {
29
+ const td = $(el).find('td');
30
+ const symbol = td.eq(0).text().trim();
31
+ const name = td.eq(1).text().trim();
32
+ const eps = numeral(td.eq(td.length - 1).text().trim()).value();
33
+ return { exchange, symbol, name, eps, year, quarter };
34
+ }).toArray();
35
+ return symbol ? data.find(data => data.symbol === symbol) : _.sortBy(data, 'symbol');
36
+ }
37
+ async fetchStocksRevenue(options) {
38
+ const { exchange, year, month, foreign = false, symbol } = options;
39
+ const type = { 'TWSE': 'sii', 'TPEx': 'otc' };
40
+ const suffix = `${numeral(year).subtract(1911).value()}_${month}_${+foreign}`;
41
+ const url = `https://mops.twse.com.tw/nas/t21/${type[exchange]}/t21sc03_${suffix}.html`;
42
+ const response = await this.httpService.get(url, { responseType: 'arraybuffer' });
43
+ const page = iconv.decode(response.data, 'big5');
44
+ if (page.toString().includes('查無資料'))
45
+ return null;
46
+ const $ = cheerio.load(page);
47
+ const data = $('tr [align=right]')
48
+ .filter((_, el) => {
49
+ const th = $(el).find('th');
50
+ const td = $(el).find('td');
51
+ return (th.length === 0) && !!td.eq(0).text();
52
+ })
53
+ .map((_, el) => {
54
+ const td = $(el).find('td');
55
+ const symbol = td.eq(0).text().trim();
56
+ const name = td.eq(1).text().trim();
57
+ const revenue = numeral(td.eq(2).text().trim()).value();
58
+ return { exchange, symbol, name, revenue, year, month };
59
+ })
60
+ .toArray();
61
+ return symbol ? data.find(data => data.symbol === symbol) : _.sortBy(data, 'symbol');
62
+ }
63
+ }
64
+ exports.MopsScraper = MopsScraper;
@@ -0,0 +1,25 @@
1
+ import { TwseScraper } from './twse-scraper';
2
+ import { TpexScraper } from './tpex-scraper';
3
+ import { TaifexScraper } from './taifex-scraper';
4
+ import { TdccScraper } from './tdcc-scraper';
5
+ import { MisTwseScraper } from './mis-twse-scraper';
6
+ import { MisTaifexScraper } from './mis-taifex-scraper';
7
+ import { MopsScraper } from './mops-scraper';
8
+ import { IsinScraper } from './isin-scraper';
9
+ import { Scraper } from './scraper';
10
+ import { Scraper as ScraperType } from '../enums';
11
+ import { RateLimitOptions } from '../interfaces';
12
+ export declare class ScraperFactory {
13
+ private readonly options?;
14
+ private readonly scrapers;
15
+ constructor(options?: RateLimitOptions | undefined);
16
+ get(type: ScraperType): Scraper;
17
+ getTwseScraper(): TwseScraper;
18
+ getTpexScraper(): TpexScraper;
19
+ getTaifexScraper(): TaifexScraper;
20
+ getTdccScraper(): TdccScraper;
21
+ getMisTwseScraper(): MisTwseScraper;
22
+ getMisTaifexScraper(): MisTaifexScraper;
23
+ getMopsScraper(): MopsScraper;
24
+ getIsinScraper(): IsinScraper;
25
+ }
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ScraperFactory = void 0;
4
+ const twse_scraper_1 = require("./twse-scraper");
5
+ const tpex_scraper_1 = require("./tpex-scraper");
6
+ const taifex_scraper_1 = require("./taifex-scraper");
7
+ const tdcc_scraper_1 = require("./tdcc-scraper");
8
+ const mis_twse_scraper_1 = require("./mis-twse-scraper");
9
+ const mis_taifex_scraper_1 = require("./mis-taifex-scraper");
10
+ const mops_scraper_1 = require("./mops-scraper");
11
+ const isin_scraper_1 = require("./isin-scraper");
12
+ const enums_1 = require("../enums");
13
+ class ScraperFactory {
14
+ constructor(options) {
15
+ this.options = options;
16
+ this.scrapers = new Map();
17
+ }
18
+ get(type) {
19
+ let scraper = this.scrapers.get(type);
20
+ if (!scraper) {
21
+ const scrapers = {
22
+ [enums_1.Scraper.Twse]: twse_scraper_1.TwseScraper,
23
+ [enums_1.Scraper.Tpex]: tpex_scraper_1.TpexScraper,
24
+ [enums_1.Scraper.Taifex]: taifex_scraper_1.TaifexScraper,
25
+ [enums_1.Scraper.Tdcc]: tdcc_scraper_1.TdccScraper,
26
+ [enums_1.Scraper.MisTwse]: mis_twse_scraper_1.MisTwseScraper,
27
+ [enums_1.Scraper.MisTaifex]: mis_taifex_scraper_1.MisTaifexScraper,
28
+ [enums_1.Scraper.Mops]: mops_scraper_1.MopsScraper,
29
+ [enums_1.Scraper.Isin]: isin_scraper_1.IsinScraper,
30
+ };
31
+ const ScraperClass = scrapers[type];
32
+ scraper = new ScraperClass(this.options);
33
+ this.scrapers.set(type, scraper);
34
+ }
35
+ return scraper;
36
+ }
37
+ getTwseScraper() {
38
+ return this.get(enums_1.Scraper.Twse);
39
+ }
40
+ getTpexScraper() {
41
+ return this.get(enums_1.Scraper.Tpex);
42
+ }
43
+ getTaifexScraper() {
44
+ return this.get(enums_1.Scraper.Taifex);
45
+ }
46
+ getTdccScraper() {
47
+ return this.get(enums_1.Scraper.Tdcc);
48
+ }
49
+ getMisTwseScraper() {
50
+ return this.get(enums_1.Scraper.MisTwse);
51
+ }
52
+ getMisTaifexScraper() {
53
+ return this.get(enums_1.Scraper.MisTaifex);
54
+ }
55
+ getMopsScraper() {
56
+ return this.get(enums_1.Scraper.Mops);
57
+ }
58
+ getIsinScraper() {
59
+ return this.get(enums_1.Scraper.Isin);
60
+ }
61
+ }
62
+ exports.ScraperFactory = ScraperFactory;
@@ -0,0 +1,6 @@
1
+ import { AxiosInstance } from "axios";
2
+ import { RateLimitOptions } from "../interfaces";
3
+ export declare abstract class Scraper {
4
+ protected readonly httpService: AxiosInstance;
5
+ constructor(options?: RateLimitOptions);
6
+ }
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Scraper = void 0;
4
+ const axios_1 = require("axios");
5
+ const rateLimit = require("axios-rate-limit");
6
+ const https = require("https");
7
+ class Scraper {
8
+ constructor(options = { limit: 1, ttl: 5000 }) {
9
+ const maxRequests = options.limit;
10
+ const perMilliseconds = options.ttl;
11
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
12
+ // @ts-ignore
13
+ this.httpService = rateLimit(axios_1.default.create({
14
+ httpsAgent: new https.Agent({
15
+ rejectUnauthorized: false,
16
+ }),
17
+ }), { maxRequests, perMilliseconds });
18
+ }
19
+ }
20
+ exports.Scraper = Scraper;
@@ -0,0 +1,44 @@
1
+ import { Scraper } from './scraper';
2
+ import { FutOptHistorical, FutOptInstitutional, FutOptLargeTraders, FutOptMxfRetailPosition, FutOptTmfRetailPosition, FutOptTxoPutCallRatio, StockFutOpt } from '../interfaces';
3
+ import { FutOptExchangeRates } from '../interfaces/futopt-exchange-rates.interface';
4
+ export declare class TaifexScraper extends Scraper {
5
+ fetchListedStockFutOpt(): Promise<StockFutOpt[]>;
6
+ fetchFuturesHistorical(options: {
7
+ date: string;
8
+ symbol?: string;
9
+ afterhours?: boolean;
10
+ }): Promise<FutOptHistorical[] | null>;
11
+ fetchOptionsHistorical(options: {
12
+ date: string;
13
+ symbol?: string;
14
+ afterhours?: boolean;
15
+ }): Promise<FutOptHistorical[] | null>;
16
+ fetchFuturesInstitutional(options: {
17
+ date: string;
18
+ symbol: string;
19
+ }): Promise<FutOptInstitutional | null>;
20
+ fetchOptionsInstitutional(options: {
21
+ date: string;
22
+ symbol: string;
23
+ }): Promise<FutOptInstitutional | null>;
24
+ fetchFuturesLargeTraders(options: {
25
+ date: string;
26
+ symbol: string;
27
+ }): Promise<FutOptLargeTraders | null>;
28
+ fetchOptionsLargeTraders(options: {
29
+ date: string;
30
+ symbol: string;
31
+ }): Promise<FutOptLargeTraders | null>;
32
+ fetchMxfRetailPosition(options: {
33
+ date: string;
34
+ }): Promise<FutOptMxfRetailPosition | null>;
35
+ fetchTmfRetailPosition(options: {
36
+ date: string;
37
+ }): Promise<FutOptTmfRetailPosition | null>;
38
+ fetchTxoPutCallRatio(options: {
39
+ date: string;
40
+ }): Promise<FutOptTxoPutCallRatio | null>;
41
+ fetchExchangeRates(options: {
42
+ date: string;
43
+ }): Promise<FutOptExchangeRates | null>;
44
+ }