downloadData.mjs

import axios from 'axios'
import ot from 'dayjs'
import get from 'lodash-es/get.js'
import map from 'lodash-es/map.js'
import size from 'lodash-es/size.js'
import join from 'lodash-es/join.js'
import iseobj from 'wsemi/src/iseobj.mjs'
import isbol from 'wsemi/src/isbol.mjs'
import delay from 'wsemi/src/delay.mjs'


/**
 * 下載指定時間區間內的 K 線數據(Kline),並可選擇轉為 CSV 格式輸出。
 *
 * @async
 * @function downloadData
 * @param {string} endpoint - API 端點 URL,例如 'https://api.binance.com/api/v3/klines'
 * @param {string} symbol - 幣種對,例如 'BTCUSDT'
 * @param {string} timeStart - 開始時間,格式為 'YYYY-MM-DDTHH:mm:ss'
 * @param {string} timeEnd - 結束時間,格式為 'YYYY-MM-DDTHH:mm:ss'
 * @param {string} interval - K 線資料間隔時間,例如 '1m', '5m', '1h' 等
 * @param {Object} [opt={}] - 可選參數設定
 * @param {Object} [opt.proxy] - 代理伺服器設定,符合 axios proxy 格式 `{ protocol, host, port }`
 * @param {boolean} [opt.useConvertToCsv=true] - 是否將結果轉換為 CSV 字串格式
 * @returns {Promise} 回傳Promise,resolve回傳解析後的 K 線資料陣列,或轉為 CSV 字串(依 useConvertToCsv 而定),reject回傳錯誤訊息
 * @example
 *
 * import WDwdataBinace from './src/WDwdataBinace.mjs'
 *
 * function test() {
 *
 *     //wdd
 *     let wdd = WDwdataBinace()
 *
 *     //BTC
 *     let name = 'BTC'
 *
 *     //price
 *     let type = 'price'
 *
 *     //幣安API的klines
 *     let endpoint = 'https://api.binance.com/api/v3/klines'
 *
 *     //BTCUSDT交易對
 *     let symbol = 'BTCUSDT'
 *
 *     //p
 *     let p = wdd.syncData(name, type, endpoint, symbol, { useShowLog: true })
 *
 *     //run
 *     p.run()
 *
 * }
 * test()
 * //get BTC price ts[2020-01-02T11:00:00] -> te[2020-01-02T11:59:59] downloading...
 * //get BTC price ts[2020-01-02T11:00:00] -> te[2020-01-02T11:59:59] finish
 * //get BTC price ts[2020-01-02T12:00:00] -> te[2020-01-02T12:59:59] downloading...
 * //get BTC price ts[2020-01-02T12:00:00] -> te[2020-01-02T12:59:59] finish
 * //get BTC price ts[2020-01-02T13:00:00] -> te[2020-01-02T13:59:59] downloading...
 * //continuing...
 *
 */
let downloadData = async (endpoint, symbol, timeStart, timeEnd, interval, opt = {}) => {

    //proxy
    let proxy = get(opt, 'proxy')
    if (!iseobj(proxy)) {
        proxy = {}
    }

    //useConvertToCsv
    let useConvertToCsv = get(opt, 'useConvertToCsv')
    if (!isbol(useConvertToCsv)) {
        useConvertToCsv = true
    }

    //limit, 0.5d=0.5*24*60=720筆分鐘數據/每半天
    let limit = 1000 //每次請求的資料點數量, 最高1000

    //startTime
    let startTime = ot(timeStart, 'YYYY-MM-DDTHH:mm:ss').valueOf()
    // console.log('timeStart', timeStart)
    // console.log('startTime', startTime)

    //endTime
    let endTime = ot(timeEnd, 'YYYY-MM-DDTHH:mm:ss').valueOf()
    // console.log('timeEnd', timeEnd)
    // console.log('endTime', endTime)

    // //url
    // let url = `${endpoint}?symbol=${symbol}&interval=${interval}&startTime=${startTime}&endTime=${endTime}&limit=${limit}`
    // // console.log('url', url)

    //rs
    let url = ''
    let rs = []
    try {

        //get
        let response = await axios.get(endpoint, {
            params: {
                symbol,
                startTime,
                endTime,
                interval,
                limit,
            },
            // proxy,
        })
        let res = response.data

        //url
        url = get(response, 'request.res.responseUrl', '')
        // console.log('url',url)

        //delay
        await delay(100) //幣安limit: 2400 requests per minute

        //timeDataMax
        let timeDataMax = ot().format('YYYY-MM-DDTHH:mm:ss')

        //parse
        rs = map(res, (v) => {

            //timeDataStart
            let timeDataStart = ot(v[0]).format('YYYY-MM-DDTHH:mm:ss')
            // if (timeDataStart > timeDataMax) {
            //     timeDataStart = timeDataMax
            // }

            //timeDataEnd
            let timeDataEnd = ot(v[6]).format('YYYY-MM-DDTHH:mm:ss')
            if (timeDataEnd > timeDataMax) {
                timeDataEnd = timeDataMax
            }

            return [
                timeDataStart, //開始時間
                v[1], // 開盤價
                v[2], // 最高價
                v[3], // 最低價
                v[4], // 收盤價
                v[5], // 成交量, index無此數據
                timeDataEnd, //結束時間
                v[7], // 成交金額, index無此數據
                v[8], // 成交筆數, index無此數據
                v[9], // 主動買入成交量, index無此數據
                v[10], // 主動買入成交金額, index無此數據
                // v[11] // 忽略此欄位, index無此數據
            ]
        })

    }
    catch (err) {
        console.log(`can not get k-data for symbol[${symbol}]`, err)
    }

    //check
    if (size(rs) === 0) {
        console.log('url', url)
        console.log(`invalid k-data for symbol[${symbol}]`)
        return
    }

    //useConvertToCsv
    if (useConvertToCsv) {
        let cd = map(rs, (v) => {
            return join(v, ', ')
        })
        let cs = join(cd, '\n')
        // console.log('cs', cs)
        rs = cs
    }

    return rs
}


export default downloadData