UNPKG

node-twstock

Version:

A client library for scraping Taiwan stock market data

127 lines (126 loc) 7.33 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.MisTaifexScraper = void 0; const numeral = require("numeral"); const luxon_1 = require("luxon"); const scraper_1 = require("./scraper"); const enums_1 = require("../enums"); class MisTaifexScraper extends scraper_1.Scraper { async fetchListedFutOpt(options) { const { type } = options !== null && options !== void 0 ? options : {}; const url = 'https://mis.taifex.com.tw/futures/api/getCmdyDDLItemByKind'; const body = JSON.stringify({ SymbolType: type, }); const response = await this.httpService.post(url, body, { headers: { 'Content-Type': 'application/json' }, }); const json = response.data; 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 })))); return data; } async fetchFutOptQuoteList(options) { const { ticker, afterhours } = options; const body = JSON.stringify({ CID: ticker.symbol, SymbolType: ticker.type, MarketType: afterhours ? 1 : 0, }); const url = `https://mis.taifex.com.tw/futures/api/getQuoteList`; const response = await this.httpService.post(url, body, { headers: { 'Content-Type': 'application/json' }, }); const json = (response.data.RtCode === '0') && response.data; if (!json) return null; const data = json.RtData.QuoteList.map((row) => ({ symbol: row.SymbolID.split('-')[0], name: row.DispCName, status: row.Status, openPrice: row.COpenPrice && numeral(row.COpenPrice).value(), highPrice: row.CHighPrice && numeral(row.CHighPrice).value(), lowPrice: row.CLowPrice && numeral(row.CLowPrice).value(), lastPrice: row.CLastPrice && numeral(row.CLastPrice).value(), referencePrice: row.CRefPrice && numeral(row.CRefPrice).value(), limitUpPrice: row.CCeilPrice && numeral(row.CCeilPrice).value(), limitDownPrice: row.CFloorPrice && numeral(row.CFloorPrice).value(), settlementPrice: row.SettlementPrice && numeral(row.SettlementPrice).value(), change: row.CDiff && numeral(row.CDiff).value(), changePercent: row.CDiffRate && numeral(row.CDiffRate).value(), amplitude: row.CAmpRate && numeral(row.CAmpRate).value(), totalVoluem: row.CTotalVolume && numeral(row.CTotalVolume).value(), openInterest: row.OpenInterest && numeral(row.OpenInterest).value(), bestBidPrice: row.CBestBidPrice && numeral(row.CBestBidPrice).value(), bestAskPrice: row.CBestAskPrice && numeral(row.CBestAskPrice).value(), bestBidSize: row.CBestBidSize && numeral(row.CBestBidSize).value(), bestAskSize: row.CBestAskSize && numeral(row.CBestAskSize).value(), testPrice: row.CTestPrice && numeral(row.CTestPrice).value(), testSize: row.CTestVolume && numeral(row.CTestVolume).value(), lastUpdated: row.CTime && luxon_1.DateTime.fromFormat(`${row.CDate} ${row.CTime}`, 'yyyyMMdd hhmmss', { zone: 'Asia/Taipei' }).toMillis(), })); return data; } async fetchFutOptQuoteDetail(options) { const { ticker, afterhours } = options; const body = JSON.stringify({ SymbolID: [this.extractSymbolIdFromTicker(ticker, { afterhours })] }); const url = `https://mis.taifex.com.tw/futures/api/getQuoteDetail`; const response = await this.httpService.post(url, body, { headers: { 'Content-Type': 'application/json' }, }); const json = (response.data.RtCode === '0') && response.data; if (!json) return null; const data = json.RtData.QuoteList.map((row) => ({ symbol: ticker.symbol, name: row.DispCName, status: row.Status, referencePrice: row.CRefPrice && numeral(row.CRefPrice).value(), limitUpPrice: row.CCeilPrice && numeral(row.CCeilPrice).value(), limitDownPrice: row.CFloorPrice && numeral(row.CFloorPrice).value(), openPrice: row.COpenPrice && numeral(row.COpenPrice).value(), highPrice: row.CHighPrice && numeral(row.CHighPrice).value(), lowPrice: row.CLowPrice && numeral(row.CLowPrice).value(), lastPrice: row.CLastPrice && numeral(row.CLastPrice).value(), lastSize: row.CSingleVolume && numeral(row.CSingleVolume).value(), testPrice: row.CTestPrice && numeral(row.CTestPrice).value(), testSize: row.CTestVolume && numeral(row.CTestVolume).value(), testTime: row.CTestTime && luxon_1.DateTime.fromFormat(`${row.CDate} ${row.CTestTime}`, 'yyyyMMdd hhmmss', { zone: 'Asia/Taipei' }).toMillis(), totalVoluem: row.CTotalVolume && numeral(row.CTotalVolume).value(), openInterest: row.OpenInterest && numeral(row.OpenInterest).value(), bidOrders: row.CBidCount && numeral(row.CBidCount).value(), askOrders: row.CAskCount && numeral(row.CAskCount).value(), bidVolume: row.CBidUnit && numeral(row.CBidUnit).value(), askVolume: row.CAskUnit && numeral(row.CAskUnit).value(), bidPrice: [row.CBidPrice1, row.CBidPrice2, row.CBidPrice3, row.CBidPrice4, row.CBidPrice5].map(price => numeral(price).value()), askPrice: [row.CAskPrice1, row.CAskPrice2, row.CAskPrice3, row.CAskPrice4, row.CAskPrice5].map(price => numeral(price).value()), bidSize: [row.CBidSize1, row.CBidSize2, row.CBidSize3, row.CBidSize4, row.CBidSize5].map(size => numeral(size).value()), askSize: [row.CAskSize1, row.CAskSize2, row.CAskSize3, row.CAskSize4, row.CAskSize5].map(size => numeral(size).value()), extBidPrice: row.CExtBidPrice && numeral(row.CExtBidPrice).value(), extAskPrice: row.CExtAskPrice && numeral(row.CExtAskPrice).value(), extBidSize: row.CExtBidSize && numeral(row.CExtBidSize).value(), extAskSize: row.CExtAskSize && numeral(row.CExtAskSize).value(), lastUpdated: luxon_1.DateTime.fromFormat(`${row.CDate} ${row.CTime}`, 'yyyyMMdd hhmmss', { zone: 'Asia/Taipei' }).toMillis(), })); return data.find(row => row.symbol.includes(ticker.symbol)); } extractSymbolIdFromTicker(ticker, options) { const { symbol, type } = ticker; const { afterhours } = options; if (type && type.includes('期貨')) { return afterhours ? `${symbol}-M` : `${symbol}-F`; } if (type && type.includes('選擇權')) { return afterhours ? `${symbol}-N` : `${symbol}-O`; } } extractTypeFromCmdyDDLItem(item) { const type = item.CID.charAt(2); if (['F', 'O'].includes(type)) return type; if (item.DispCName.includes('期貨')) return 'F'; if (item.DispCName.includes('選擇權')) return 'O'; } } exports.MisTaifexScraper = MisTaifexScraper;