import drop from 'lodash-es/drop.js'
import each from 'lodash-es/each.js'
import filter from 'lodash-es/filter.js'
import get from 'lodash-es/get.js'
import size from 'lodash-es/size.js'
import cstr from 'wsemi/src/cstr.mjs'
import fsIsFolder from 'wsemi/src/fsIsFolder.mjs'
import fsReadJson from 'wsemi/src/fsReadJson.mjs'
import fsTreeFolder from 'wsemi/src/fsTreeFolder.mjs'
import isestr from 'wsemi/src/isestr.mjs'
import replace from 'wsemi/src/replace.mjs'
import sep from 'wsemi/src/sep.mjs'
import cont from './cont.mjs'
//ptnFnHash, genStrategyFileName所產生之雜湊檔名樣式`${mode}_${levelNumTrade}_${hash16}.json`
let ptnFnHash = /^(long|short)_\d+_[0-9a-f]{16}\.json$/
/**
* 讀取儲存策略資料夾內各策略檔資訊
*
* 僅列舉fd第1層之檔案,資料夾不列入,fd非資料夾或無任何檔案時回傳空陣列
* 各檔依opt.fromContent決定解析來源:
* 'filename'為舊式檔名反解: 以主檔名(剔除'.json')依cont.dlmPkgs切段,依序為tid、levelNumTrade與rEquivalentCumuProfitOrLossFinalNormYear,切不出tid者予以略過
* 'content'為讀取檔內json之tid、tkid、levelNumTrade與summary等欄位(即estimKeys所寫出之策略內容),非合法json或無tid欄位者予以略過,因需讀檔故較'filename'慢
* 'auto'(預設)為逐檔判別: 檔名符合genStrategyFileName雜湊格式(`${mode}_${levelNumTrade}_${hash16}.json`)者走'content',其餘走'filename',故新舊格式混存時皆可列舉
* tid依cont.dlmSeps切段,首段為mode,其餘為各key所組成之ps
* tkid為`${tid}:${levelNumTrade}`,與estimKeys內用於判斷同組keys與同交易次數級距之組合方式一致
*
* Unit Test: {@link https://github.com/yuda-lyu/w-trade-solve/blob/master/test/unit-readStrategies.test.mjs Github}
* @function
* @param {String} fd 輸入儲存策略資料夾字串
* @param {Object} [opt={}] 輸入設定物件,預設{}
* @param {Boolean} [opt.readContent=false] 輸入是否額外提供策略檔內容布林值,為true時各元素額外提供data欄位,內容非合法json時data為null,預設false
* @param {String} [opt.fromContent='auto'] 輸入解析來源字串,可選'filename'(由檔名反解)、'content'(讀檔內json欄位)與'auto'(依檔名格式逐檔判別),給予其他值時亦採'auto',預設'auto'
* @returns {Array} 回傳策略資訊陣列,各元素為{path,name,tkid,tid,levelNumTrade,rEquivalentCumuProfitOrLossFinalNormYear,mode,ps},opt.readContent為true時額外提供data
* @example
*
* import fs from 'fs'
* import genStrategyFileName from './src/genStrategyFileName.mjs'
*
* let fd = './tmp/data-strategy'
* fs.mkdirSync(fd, { recursive: true })
*
* //以genStrategyFileName之檔名規則儲存策略檔, 檔名為雜湊格式, 識別資訊由檔內json承載
* let summary = { numTrade: 30, rEquivalentCumuProfitOrLossFinalNormYear: '31.25%' }
* let fn = genStrategyFileName('btc', '4hr', 'long', ['btc_4hr_ma_1day'], {}, summary)
* let data = { tid: 'long ║ ma_1day', tkid: 'long ║ ma_1day:12', levelNumTrade: 12, mode: 'long', summary }
* fs.writeFileSync(`${fd}/${fn}`, JSON.stringify(data), 'utf8')
*
* console.log(readStrategies(fd))
* // => [
* // {
* // path: './tmp/data-strategy/long_12_a7578526a1fc57d8.json',
* // name: 'long_12_a7578526a1fc57d8.json',
* // tkid: 'long ║ ma_1day:12',
* // tid: 'long ║ ma_1day',
* // levelNumTrade: '12',
* // rEquivalentCumuProfitOrLossFinalNormYear: '31.25%',
* // mode: 'long',
* // ps: [ 'ma_1day' ]
* // }
* // ]
*
*/
let readStrategies = (fd, opt = {}) => {
//readContent
let readContent = get(opt, 'readContent', false)
//fromContent
let fromContent = get(opt, 'fromContent', '')
if (fromContent !== 'filename' && fromContent !== 'content') {
fromContent = 'auto'
}
//check
if (!fsIsFolder(fd)) {
console.log(`invalid fd[${fd}]`)
return []
}
//vfps
let vfps = fsTreeFolder(fd)
vfps = filter(vfps, { isFolder: false })
// console.log('vfps', vfps)
//check
if (size(vfps) === 0) {
console.log(`fd[${fd}] no files`)
return []
}
//ss
let ss = []
each(vfps, (v) => {
//useContent, 'auto'依檔名是否符合雜湊格式逐檔判別
let useContent = fromContent === 'content'
if (fromContent === 'auto') {
useContent = ptnFnHash.test(v.name)
}
//s
let s = null
if (useContent) {
//data, 由檔內json取各欄位
let d = fsReadJson(v.path) //回傳{success}或{error}, 讀取或解析失敗時為null
let data = get(d, 'success', null)
//check
if (data === null) {
return true //跳出換下一個
}
//tid
let tid = get(data, 'tid', '')
//check
if (!isestr(tid)) {
return true //跳出換下一個
}
//levelNumTrade, 為與檔名反解之回傳格式一致, 轉為字串
let levelNumTrade = cstr(get(data, 'levelNumTrade', ''))
//rEquivalentCumuProfitOrLossFinalNormYear
let rEquivalentCumuProfitOrLossFinalNormYear = get(data, 'summary.rEquivalentCumuProfitOrLossFinalNormYear', '')
//ps, mode
let ps = sep(tid, cont.dlmSeps)
let mode = get(ps, 0, '')
ps = drop(ps)
//tkid, 舊內容無tkid欄位時由tid與levelNumTrade組出
let tkid = get(data, 'tkid', '')
if (!isestr(tkid)) {
tkid = `${tid}:${levelNumTrade}`
}
//s
s = {
path: v.path,
name: v.name,
tkid,
tid,
levelNumTrade,
rEquivalentCumuProfitOrLossFinalNormYear,
mode,
ps,
}
//readContent
if (readContent) {
s.data = data
}
}
else {
//qs
let name = replace(v.name, '.json', '')
let qs = sep(name, cont.dlmPkgs)
//tid
let tid = get(qs, 0, '')
//check
if (!isestr(tid)) {
return true //跳出換下一個
}
//levelNumTrade, rEquivalentCumuProfitOrLossFinalNormYear, ps, mode
let levelNumTrade = get(qs, 1, '')
let rEquivalentCumuProfitOrLossFinalNormYear = get(qs, 2, '')
let ps = sep(tid, cont.dlmSeps)
let mode = get(ps, 0, '')
ps = drop(ps)
//tkid
let tkid = `${tid}:${levelNumTrade}`
//s
s = {
path: v.path,
name: v.name,
tkid,
tid,
levelNumTrade,
rEquivalentCumuProfitOrLossFinalNormYear,
mode,
ps,
}
//readContent
if (readContent) {
let d = fsReadJson(v.path) //回傳{success}或{error}, 讀取或解析失敗時data為null
s.data = get(d, 'success', null)
}
}
//push
ss.push(s)
})
return ss
}
export default readStrategies