import get from 'lodash-es/get.js'
import sample from 'lodash-es/sample.js'
import isestr from 'wsemi/src/isestr.mjs'
import estimComps from './estimComps.mjs'
/**
* 以隨機之交易方向與指標組成求解最佳策略
*
* 供批次探索用之進入點,未指定時mode由'long'與'short'隨機取一,comps由18種預設組成隨機取一,故重複呼叫即可隨機探索各種策略
* 未指定時timeStart預設為'2022-07-01T00:00:00',timeEnd則依mode給予不同值: 做多為'2025-04-08T00:00:00'(須涵蓋暴跌數據),做空為'2025-10-06T00:00:00'(須涵蓋暴漲數據),藉此提高率定難度
* name、symbol、interval、fdOhlc、fdParam與fdData無效時throw
*
* Unit Test: {@link https://github.com/yuda-lyu/w-trade-solve/blob/master/test/unit-estimFinder.test.mjs Github}
* @function
* @param {Function} ott 輸入時區時間函數,傳入時間字串回傳dayjs時間物件,可用src/ott.mjs
* @param {String} name 輸入幣種名稱字串,例如'btc'
* @param {String} symbol 輸入交易對名稱字串,例如'BTCUSDT'
* @param {String} interval 輸入K線週期字串,例如'4hr'
* @param {String} fdOhlc 輸入儲存K線(ohlc)序列資料夾字串
* @param {String} fdParam 輸入儲存指標參數序列資料夾字串
* @param {String} fdData 輸入儲存策略資料夾字串,不存在時自動建立
* @param {Object} [opt={}] 輸入設定物件,預設{},全部欄位皆傳遞至estimComps
* @param {String} [opt.mode=''] 輸入交易方向字串,可選'long'或'short',未給予時隨機取一,預設''
* @param {String} [opt.timeStart='2022-07-01T00:00:00'] 輸入回測起始時間字串,預設'2022-07-01T00:00:00'
* @param {String} [opt.timeEnd=''] 輸入回測結束時間字串,未給予時做多為'2025-04-08T00:00:00'、做空為'2025-10-06T00:00:00',預設''
* @param {String} [opt.comps=''] 輸入指標組成字串,例如'2,1,0',未給予時由18種預設組成隨機取一,預設''
* @returns {Promise} 回傳Promise,resolve為undefined
* @example
*
* import ott from './src/ott.mjs'
*
* //mode與comps皆隨機, 適合以迴圈重複呼叫批次探索
* await estimFinder(ott, 'btc', 'BTCUSDT', '4hr', './data/ohlc', './data/param', './data/strategy', {})
*
*/
let estimFinder = async (ott, name, symbol, interval, fdOhlc, fdParam, fdData, opt = {}) => {
if (!isestr(name)) {
throw new Error(`invalid name[${name}]`)
}
if (!isestr(symbol)) {
throw new Error(`invalid symbol[${symbol}]`)
}
if (!isestr(interval)) {
throw new Error(`invalid interval[${interval}]`)
}
if (!isestr(fdOhlc)) {
throw new Error(`invalid fdOhlc[${fdOhlc}]`)
}
if (!isestr(fdParam)) {
throw new Error(`invalid fdParam[${fdParam}]`)
}
if (!isestr(fdData)) {
throw new Error(`invalid fdData[${fdData}]`)
}
let useMode = get(opt, 'mode', null)
let useTimeStart = get(opt, 'timeStart', null)
let useTimeEnd = get(opt, 'timeEnd', null)
let uesComps = get(opt, 'comps', null)
//mode
let mode = ''
if (isestr(useMode)) {
mode = useMode
}
else {
mode = Math.random() < 0.5 ? `long` : `short`
}
//timeStart
let timeStart = '2022-07-01T00:00:00'
if (isestr(useTimeStart)) {
timeStart = useTimeStart
}
//timeEnd
let timeEnd = ''
if (isestr(useTimeEnd)) {
timeEnd = useTimeEnd
}
else {
if (mode === 'long') {
timeEnd = '2025-04-08T00:00:00' //做多策略須使用至4/8暴跌數據, 提高難度 [tag:提高率定難度]
}
else {
timeEnd = '2025-10-06T00:00:00' //做空策略須使用至10/6暴漲數據, 增加難度 [tag:提高率定難度]
}
}
//comps
let compsAll = [
'2,0,0',
'3,0,0',
'4,0,0',
'2,1,0',
'3,1,0',
'4,1,0',
'2,2,0',
'3,2,0',
'4,2,0',
'2,0,1',
'3,0,1',
'4,0,1',
'2,1,1',
'3,1,1',
'4,1,1',
'2,2,1',
'3,2,1',
'4,2,1',
]
let comps = ''
if (isestr(uesComps)) {
comps = uesComps
}
else {
comps = sample(compsAll) //例如comps='2,1,0', 此為挑2種norm, 1種index, 0種volumn
}
//estimComps
await estimComps(ott, name, symbol, interval, fdOhlc, fdParam, timeStart, timeEnd, mode, comps, fdData, opt)
}
export default estimFinder