calcKlr.mjs

import isNumber from 'lodash-es/isNumber.js'
import size from 'lodash-es/size.js'
import get from 'lodash-es/get.js'
import each from 'lodash-es/each.js'
import diviProt from './diviProt.mjs'


/**
 * 計算各週期之K線OHLC比值(K-Line OHLC Ratio)
 *
 * 僅一期'4hr'(len固定為1),逐根獨立計算同根Open/High/Low/Close間之六種比值,不需回看歷史根數,
 * 輸出自輸入索引0起,逐根對齊至最後一根,資料筆數不足len(即輸入為空陣列)時,該期vs為空陣列
 * ho=H/O,lo=L/O,co=C/O,hc=H/C,lc=L/C,hl=H/L,六比值皆先將O/H/L/C同步加上opt.plusClose後才經diviProt計算(分母保護:|分母|<0.00001時clamp至±0.00001,保留符號)
 *
 * Unit Test: {@link https://github.com/yuda-lyu/w-data-trade/blob/master/test/unit-calcKlr.test.mjs Github}
 * @function
 * @param {Array} arr 輸入K線陣列,各元素需含time、Open、High、Low、Close欄位
 * @param {String} key 輸入計算所用數值欄位名稱字串,例如'Close'(僅供介面一致性,本函式實際計算取用Open/High/Low/Close)
 * @param {Object} [opt={}] 輸入設定物件,預設{}
 * @param {Number} [opt.plusClose=0] 輸入對Open/High/Low/Close同步加上之偏移量,加完後才計算六比值,預設0
 * @returns {Promise} 回傳Promise,resolve為各期結果陣列,各元素為{period,len,vs},vs內各元素為{time,ho,lo,co,hc,lc,hl}
 * @example
 *
 * let arr = [
 *     { time: '2020-01-01T00:00:00', Open: 101, High: 101.5, Low: 100.5, Close: 101 },
 * ]
 *
 * calcKlr(arr, 'Close')
 *     .then((rs) => {
 *         console.log(rs[0].vs)
 *         // => [
 *         //   {
 *         //     time: '2020-01-01T00:00:00',
 *         //     ho: 1.004950495049505,
 *         //     lo: 0.995049504950495,
 *         //     co: 1,
 *         //     hc: 1.004950495049505,
 *         //     lc: 0.995049504950495,
 *         //     hl: 1.0099502487562189
 *         //   }
 *         // ]
 *     })
 *
 */
let calcKlr = (() => {

    let kp = {
        '4hr': 1,
    }

    let caKlr = (arr, len, opt = {}) => {

        //check
        if (!isNumber(len)) {
            throw new Error(`len is not a number`)
        }

        //n
        let n = size(arr)

        //check
        if (n < len) {
            return []
        }

        //plusClose
        let plusClose = get(opt, 'plusClose', 0)

        let kTime = 'time'
        let kOpen = 'Open'
        let kHigh = 'High'
        let kLow = 'Low'
        let kClose = 'Close'

        //rs
        let rs = []
        for (let i = 0; i < n; i++) {

            //o, h, l, c
            let o = arr[i][kOpen] + plusClose
            let h = arr[i][kHigh] + plusClose
            let l = arr[i][kLow] + plusClose
            let c = arr[i][kClose] + plusClose
            // console.log(`arr[${i}]`, arr[i])

            //ho, h/o
            let ho = diviProt(h, o)

            //lo, l/o
            let lo = diviProt(l, o)

            //co, c/o
            let co = diviProt(c, o)

            //hc, h/c
            let hc = diviProt(h, c)

            //lc, l/c
            let lc = diviProt(l, c)

            //hl, h/l
            let hl = diviProt(h, l)

            //push
            rs.push({
                time: arr[i][kTime],
                ho,
                lo,
                co,
                hc,
                lc,
                hl,
            })

        }
        // console.log('rs', rs)

        return rs
    }

    let caKlrs = (arr, opt = {}) => {

        //rrs
        let rrs = []
        each(kp, (len, period) => {

            //caKlr
            let rs = caKlr(arr, len, opt)

            //push
            rrs.push({
                period,
                len,
                vs: rs,
            })

        })

        return rrs
    }

    let calcKlr = async(arr, key, opt = {}) => {
        // arr = [
        //   {"time":"2020-01-01T00:00:00","Open":7195,"High":7225.62,"Low":7145.01,"Close":7173.32,"Volumn":4657.972543,"CloseTime":"2020-01-01T03:59:59","QuoteAssetVolume":33441599.81960844,"NumberOfTrades":46046,"TakerBuyBaseAssetVolume":2161.591103,"TakerBuyQuoteAssetVolume":15520451.66291961},
        //   {"time":"2020-01-01T04:00:00","Open":7173.75,"High":7208.41,"Low":7165.1,"Close":7195.23,"Volumn":2091.720176,"CloseTime":"2020-01-01T07:59:59","QuoteAssetVolume":15032938.06298061,"NumberOfTrades":29738,"TakerBuyBaseAssetVolume":1010.628114,"TakerBuyQuoteAssetVolume":7263338.98247078},
        //   {"time":"2020-01-01T08:00:00","Open":7195.24,"High":7245,"Low":7175.46,"Close":7225.01,"Volumn":2833.74918,"CloseTime":"2020-01-01T11:59:59","QuoteAssetVolume":20445895.8017956,"NumberOfTrades":32476,"TakerBuyBaseAssetVolume":1548.865619,"TakerBuyQuoteAssetVolume":11176594.41972043},
        //   {"time":"2020-01-01T12:00:00","Open":7225,"High":7236.27,"Low":7199.11,"Close":7209.83,"Volumn":2061.295051,"CloseTime":"2020-01-01T15:59:59","QuoteAssetVolume":14890182.27509305,"NumberOfTrades":29991,"TakerBuyBaseAssetVolume":1049.711236,"TakerBuyQuoteAssetVolume":7582850.37593562},
        //   ...
        // ]

        // //pickValue
        // let vs = pickValue(arr, key) //轉為 { time, value }

        //caKlrs
        let rs = caKlrs(arr, opt)

        return rs
    }

    return calcKlr
})()


export default calcKlr