import path from 'path'
import each from 'lodash-es/each.js'
import get from 'lodash-es/get.js'
import map from 'lodash-es/map.js'
import round from 'lodash-es/round.js'
import size from 'lodash-es/size.js'
import cint from 'wsemi/src/cint.mjs'
import dig from 'wsemi/src/dig.mjs'
import fsCreateFolder from 'wsemi/src/fsCreateFolder.mjs'
import fsIsFile from 'wsemi/src/fsIsFile.mjs'
import fsIsFolder from 'wsemi/src/fsIsFolder.mjs'
import fsReadJson from 'wsemi/src/fsReadJson.mjs'
import fsWriteJson from 'wsemi/src/fsWriteJson.mjs'
import isearr from 'wsemi/src/isearr.mjs'
import isestr from 'wsemi/src/isestr.mjs'
import isnum from 'wsemi/src/isnum.mjs'
import isbol from 'wsemi/src/isbol.mjs'
import isfun from 'wsemi/src/isfun.mjs'
import rang from 'wsemi/src/rang.mjs'
import wround from 'wsemi/src/round.mjs'
import omlRGA from 'w-optimization/src/omlRGA.mjs'
import omlDE from 'w-optimization/src/omlDE.mjs'
import omlHS from 'w-optimization/src/omlHS.mjs'
import omlPSO from 'w-optimization/src/omlPSO.mjs'
import omlACO from 'w-optimization/src/omlACO.mjs'
import dataProvide from 'w-data-tdprovide'
import runStrategy from 'w-trade-backtest/src/runStrategy.mjs'
import genTid from './genTid.mjs'
import genSid from './genSid.mjs'
import p2r from './p2r.mjs'
import calcLevelNumTrade from './calcLevelNumTrade.mjs'
import calcFitness from './calcFitness.mjs'
import genStrategyFileName from './genStrategyFileName.mjs'
/**
* 針對指定keys率定各key之門檻與止盈止損,求解最佳策略
*
* 設計變數為[止損比例, 止盈比例, ...各key之門檻],止損與止盈由opt.thsSl與opt.thsTp給予之百分比清單離散取值
* 各key門檻取值範圍為[-thLim,thLim]切為thLim*20段,並各自平移+vdir與-vdir(vdir=thLim*5)為兩組,使解值恆大於0代表'>'條件、恆小於0代表'<'條件,還原時再扣除平移值,故單一設計變數即同時涵蓋大於與小於兩種條件
* 以w-optimization之最佳化函數求解,由opt.methodOml指定演算法,各次計算皆以w-trade-backtest之runStrategy回測並經calcFitness計算適應值;亦可由opt.funRunStrategyCustom注入自訂評估核心加速求解(如以w-trade-backtest之buildStrategyFastSession預算first-touch表與因子對位序列),注入僅回傳精簡欄位之核心時可配合opt.useRunStrategyBeforeSave於存檔前重跑完整回測,使落地summary維持完整欄位口徑;存檔前重跑後檔內numTrade、levelNumTrade、tkid、fitness與檔名皆由重跑之summary重新衍生,確保檔名與檔案內容同源一致;重算檔名與原檔名不同(評估核心與重跑結果跨級距)時,對新檔名重做存在檢查、身分驗證與優劣比較,新檔名已有更佳者則放棄本次寫入
* 各次計算結果若同時滿足交易次數、勝率與最終等效年化盈虧三門檻,則以genStrategyFileName產生檔名存入fdData;檔名為tkid(tid+交易次數級距)之純函數,故直接組出檔名查詢既有策略(O(1),不列舉全庫),同tkid已有策略時僅於最終等效年化盈虧更佳時原地覆寫,故同tkid恆僅一檔;覆寫前驗證舊檔內tkid、name與interval與本次一致,不一致代表雜湊碰撞,直接throw以避免無聲覆寫
* 策略檔內容除策略本體(tid、sid、name、symbol、interval、keyOhlc、mode、conds、settings)與summary、fitness外,另落地tkid、numTrade、levelNumTrade、keys與solve區塊(timeStart、timeEnd、methodOml、thNumTrade、thRWin、thREquivalentCumuProfitOrLossFinalNormYear、thsTp、thsSl、timeSolved),使識別與求解條件欄位自足,不依賴檔名
* opt除下列各欄位外,其餘欄位皆原樣傳遞至所選之最佳化函數,兩者欄位名稱無重疊,故可一併給予求解設定
* 各演算法共通之設定為NContiguous(最佳解連續未更新次數上限,預設100)、NRepeat、NCore、ModeOutLimit、UseRepeat、UseImmigration、LocalSearchMethod與funGetBetter、funGenerationBefore、funGenerationAfter三接口;族群數與迴圈數之欄位名稱各演算法不同,RGA與DE為Np與Ng、PSO為Np與Nl、HS為Ns與Ni、ACO為Na與Nl;另各演算法有其專屬超參數,如PSO之psoC1Start、RGA之rgaCrossover、DE之deF、HS之hsHMCR與ACO之acoAlphaStart等
* 預設之求解量甚大(單一key約需1萬7千次回測),僅需快速試算時可調小族群數與NContiguous
* name、symbol、interval、fdOhlc、fdParam、timeStart、timeEnd、keys、fdData無效或mode非'long'或'short'時throw
*
* Unit Test: {@link https://github.com/yuda-lyu/w-trade-solve/blob/master/test/unit-estimKeys.test.mjs Github}
* @function
* @param {Function} ott 輸入時區時間函數,傳入時間字串回傳dayjs時間物件,未傳入時回傳當下時間(供solve.timeSolved記錄),可用src/ott.mjs
* @param {String} name 輸入幣種名稱字串,例如'btc',用於組出K線序列key(`${name}_price_${interval}`)與縮寫tid
* @param {String} symbol 輸入交易對名稱字串,例如'BTCUSDT',僅儲存至策略內供辨識
* @param {String} interval 輸入K線週期字串,例如'4hr'
* @param {String} fdOhlc 輸入儲存K線(ohlc)序列資料夾字串
* @param {String} fdParam 輸入儲存指標參數序列資料夾字串
* @param {String} timeStart 輸入回測起始時間字串,格式'YYYY-MM-DDTHH:mm:ss'
* @param {String} timeEnd 輸入回測結束時間字串,格式'YYYY-MM-DDTHH:mm:ss'
* @param {String} mode 輸入交易方向字串,可選'long'或'short'
* @param {Array} keys 輸入待率定門檻之指標key字串陣列
* @param {String} fdData 輸入儲存策略資料夾字串,不存在時自動建立
* @param {Object} [opt={}] 輸入設定物件,預設{},除下列欄位外皆傳遞至所選之最佳化函數,故可另給NContiguous等求解設定
* @param {String} [opt.methodOml='PSO'] 輸入最佳化演算法字串,可選'RGA'(實數型基因演算法)、'DE'(差分進化法)、'HS'(和聲搜尋法)、'PSO'(粒子群最佳化)與'ACO'(蟻群最佳化),給予其他值時亦採'PSO',預設'PSO'
* @param {Array} [opt.thsTp=[1,2,3,4,5,6,7,8,9,10]] 輸入止盈門檻百分比數值陣列,預設[1,2,3,4,5,6,7,8,9,10]
* @param {Array} [opt.thsSl=[1,2,3,4,5,6,7,8,9,10]] 輸入止損門檻百分比數值陣列,預設[1,2,3,4,5,6,7,8,9,10]
* @param {Number} [opt.thNumTrade=1] 輸入策略須儲存之最少交易次數數值,預設1
* @param {Number} [opt.thRWin=0.5] 輸入策略須儲存之最低勝率比例數值,預設0.5
* @param {Number} [opt.thREquivalentCumuProfitOrLossFinalNormYear=0.15] 輸入策略須儲存之最低最終等效年化盈虧比例數值,預設0.15
* @param {Function} [opt.funRunStrategyCustom=null] 輸入自訂回測評估async函數,傳入{ott,strategy,funGetSeries}物件,須回傳{summary}且summary至少含numTrade、rWin、uEquityFinal與rEquivalentCumuProfitOrLossFinalNormYear四欄,各欄語義須與runStrategy等值,回傳之準確性由注入端負責;未給時採runStrategy,預設null
* @param {Boolean} [opt.useRunStrategyBeforeSave=false] 輸入存檔前是否重跑一次回測並以其summary重新衍生檔案內容與檔名布林值,注入僅回傳精簡欄位之評估核心時應設true以維持策略檔欄位口徑,求解搜索仍全程使用自訂函數不受影響,預設false
* @param {Function} [opt.funRunStrategyCustomBeforeSave=null] 輸入存檔前重跑用之自訂回測async函數,介面同opt.funRunStrategyCustom,僅於opt.useRunStrategyBeforeSave為true時生效,未給時採runStrategy,預設null
* @param {Boolean} [opt.useShowLog=false] 輸入是否顯示求解過程log布林值,預設false
* @returns {Promise} 回傳Promise,resolve為求解結果物件,含bestSolution(最佳解,內有ps設計變數陣列與fitness適應值)、stopMode(觸發停止之機制字串)、stopIteration(停止時之迴圈數)與stopExecutions(停止時之核心分析次數)等欄位
* @example
*
* import ott from './src/ott.mjs'
*
* //keys, 待率定門檻之指標key, 各key於fdParam內須有對應之`${key}.json`
* let keys = ['btc_price_4hr_ma_1day']
*
* let m = await estimKeys(ott, 'btc', 'BTCUSDT', '4hr', './data/ohlc', './data/param', '2022-07-01T00:00:00', '2025-04-08T00:00:00', 'long', keys, './data/strategy', {
* thsTp: [1, 2, 3],
* thsSl: [1, 2, 3],
* useShowLog: true,
* })
* console.log(m.bestSolution.fitness)
* // => 1.0325
*
* //可指定演算法並一併給予其求解設定, 縮小族群數與NContiguous即可快速試算
* let mQuick = await estimKeys(ott, 'btc', 'BTCUSDT', '4hr', './data/ohlc', './data/param', '2022-07-01T00:00:00', '2025-04-08T00:00:00', 'long', keys, './data/strategy', {
* thsTp: [1, 2, 3],
* thsSl: [1, 2, 3],
* methodOml: 'PSO',
* Np: 8, //PSO之粒子數, 預設40
* NContiguous: 5, //最佳解連續未更新次數上限, 預設100
* })
* console.log(mQuick.stopMode)
* // => 'stop by iContinue[5] >= NContiguous[5]'
* console.log(mQuick.stopExecutions) //核心分析次數, 較預設設定大幅減少, 因求解為隨機故每次不同
* // => 357
*
*/
let estimKeys = async (ott, name, symbol, interval, fdOhlc, fdParam, timeStart, timeEnd, mode, keys, 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(timeStart)) {
throw new Error(`invalid timeStart[${timeStart}]`)
}
if (!isestr(timeEnd)) {
throw new Error(`invalid timeEnd[${timeEnd}]`)
}
if (mode !== 'long' && mode !== 'short') {
throw new Error(`invalid mode[${mode}]`)
}
if (!isearr(keys)) {
throw new Error(`invalid keys[${keys}]`)
}
if (!isestr(fdData)) {
throw new Error(`invalid fdData[${fdData}]`)
}
let methodOml = get(opt, 'methodOml', '')
if (methodOml !== 'RGA' && methodOml !== 'DE' && methodOml !== 'HS' && methodOml !== 'PSO' && methodOml !== 'ACO') {
methodOml = 'PSO' //非五者之一時採PSO, 使solve.methodOml記錄實際採用之演算法
}
let thsTp = get(opt, 'thsTp', null)
if (!isearr(thsTp)) {
thsTp = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
}
let thsSl = get(opt, 'thsSl', null)
if (!isearr(thsSl)) {
thsSl = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
}
let thNumTrade = get(opt, 'thNumTrade', null)
if (!isnum(thNumTrade)) {
thNumTrade = 1
}
let thRWin = get(opt, 'thRWin', null)
if (!isnum(thRWin)) {
thRWin = 0.5
}
let thREquivalentCumuProfitOrLossFinalNormYear = get(opt, 'thREquivalentCumuProfitOrLossFinalNormYear', null)
if (!isnum(thREquivalentCumuProfitOrLossFinalNormYear)) {
thREquivalentCumuProfitOrLossFinalNormYear = 0.15
}
let funRunStrategyCustom = get(opt, 'funRunStrategyCustom', null)
let useRunStrategyBeforeSave = get(opt, 'useRunStrategyBeforeSave', null)
if (!isbol(useRunStrategyBeforeSave)) {
useRunStrategyBeforeSave = false
}
let funRunStrategyCustomBeforeSave = get(opt, 'funRunStrategyCustomBeforeSave', null)
let useShowLog = get(opt, 'useShowLog', null)
//provideData
let provideData = dataProvide(fdOhlc, fdParam)
//fdData, 儲存策略資料夾, 由呼叫端指定, 不存在則建立
if (!fsIsFolder(fdData)) {
fsCreateFolder(fdData)
}
//funGetSeries
let funGetSeries = await provideData.buildGetTimeSeriesByTimeRange(timeStart, timeEnd)
//tid
let tid = genTid(name, interval, mode, keys)
// console.log('tid', tid)
//vdir, 添加平移值方便後續判識方向
let thLim = 2
let vdir = thLim * 5
//dps
let dps = []
let idx = 0 //額外(非conds)設計變數之個數
if (true) {
//dps, 止損門檻與增量
if (true) {
let values
//掛單止損門檻(%), 對應 xs[0] = rStopLoss
values = thsSl
values = map(values, (v) => {
return v / 100
})
// console.log('values', values)
dps.push({
values,
n: size(values),
})
idx++
//掛單止盈門檻(%), 對應 xs[1] = rTakeProfit
values = thsTp
values = map(values, (v) => {
return v / 100
})
// console.log('values', values)
dps.push({
values,
n: size(values),
})
idx++
// //每次下單金額(USDT), 因等效年化報酬都是會基於最大持倉去計算, 故下單金額與最大持倉成正比, 故不再須視為參數之一
// values = [1]
// // console.log('values', values)
// dps.push({
// values,
// n: size(values),
// })
// idx++
}
//dps, 基於keys
each(keys, () => {
//大於條件
// let valuesU_fine = rang(0.01, 0.10, 9) //細粒度小尺度區: [0.01, 0.10] step 0.01 (10 點, 給 absP99 < 0.1 的 115 個小尺度 keyOut)
// let valuesU_main = rang(0.15, 2.0, 37) //主區: [0.15, 2.0] step 0.05 (38 點)
// let valuesU = [...valuesU_fine, ...valuesU_main] //合計 48 點
let valuesU = rang(-thLim, thLim, thLim * 20)
valuesU = map(valuesU, (v) => {
return v + vdir //添加vdir使全部解皆>0, 方便後續判識轉換
})
//小於條件
// let valuesL_main = rang(-2.0, -0.15, 37) //主區: [-2.0, -0.15] step 0.05 (38 點)
// let valuesL_fine = rang(-0.10, -0.01, 9) //細粒度小尺度區: [-0.10, -0.01] step 0.01 (10 點, 給 absP99 < 0.1 的 115 個小尺度 keyOut)
// let valuesL = [...valuesL_main, ...valuesL_fine] //合計 48 點
let valuesL = rang(-thLim, thLim, thLim * 20)
valuesL = map(valuesL, (v) => {
return v - vdir //添加-vdir使全部解皆<0, 方便後續判識轉換
})
//values
let values = [
...valuesU,
...valuesL,
]
//push
dps.push({
values,
n: size(values),
})
})
}
//checkSavable, 檢查指定檔名處是否可寫入策略: 無檔案或舊檔非合法json回傳true(直接寫入);
// 舊檔身分(tkid/name/interval)與本次不一致代表雜湊碰撞, 直接throw以避免無聲覆寫;
// 新等效年化盈虧較佳時回傳true(原地覆寫), 否則回傳false(保留既有較佳者)
let checkSavable = (fpStrategy, fnStrategy, tkid, rEquivalentCumuProfitOrLossFinalNormYearNew) => {
//check, 無同檔名策略, 新出現須儲存
if (!fsIsFile(fpStrategy)) {
return true
}
//dataOld
let dOld = fsReadJson(fpStrategy) //回傳{success}或{error}, 讀取或解析失敗時為null
let dataOld = get(dOld, 'success', null)
//check, 舊檔非合法json, 直接覆寫
if (dataOld === null) {
return true
}
//覆寫前驗證舊檔身分與本次一致, 不一致代表雜湊碰撞, 直接throw以避免無聲覆寫
let tkidOld = get(dataOld, 'tkid', '')
let nameOld = get(dataOld, 'name', '')
let intervalOld = get(dataOld, 'interval', '')
if ((isestr(tkidOld) && tkidOld !== tkid) || (isestr(nameOld) && nameOld !== name) || (isestr(intervalOld) && intervalOld !== interval)) {
throw new Error(`hash collision: fnStrategy[${fnStrategy}], tkidOld[${tkidOld}] vs tkid[${tkid}], nameOld[${nameOld}] vs name[${name}], intervalOld[${intervalOld}] vs interval[${interval}]`)
}
//rEquivalentCumuProfitOrLossFinalNormYearOld
let rEquivalentCumuProfitOrLossFinalNormYearOld = p2r(get(dataOld, 'summary.rEquivalentCumuProfitOrLossFinalNormYear', ''))
//check, 出現更好的等效盈虧比例, 原地覆寫
if (rEquivalentCumuProfitOrLossFinalNormYearNew > rEquivalentCumuProfitOrLossFinalNormYearOld) {
if (useShowLog) {
console.log(`覆寫較差策略...`, `${rEquivalentCumuProfitOrLossFinalNormYearNew} > ${rEquivalentCumuProfitOrLossFinalNormYearOld}`, fpStrategy)
}
return true
}
//新等效盈虧未較佳, 不寫入
return false
}
//ifun, fitnessMin
let ifun = 0
let fitnessMin = 1e20
//fun
let fun = async (xs) => {
//xs: 各元素為整數指標, 代表key所使用的conds
ifun++
//rStopLoss, rTakeProfit
let rStopLoss = xs[0] //止損
rStopLoss = round(rStopLoss, 3)
let rTakeProfit = xs[1] //止盈
rTakeProfit = round(rTakeProfit, 3)
let uTrade = 1 //原本使用xs[2]
//conds
let conds = map(keys, (key, i) => {
//ix
let ix = i + idx
//x
let x = get(xs, ix, null)
//check
if (x === null) {
console.log('xs', xs)
console.log('ix', ix)
console.log('key', key)
console.log('i', i)
throw new Error(`invalid x`)
}
//sym, th
let sym = ''
let th = null
if (x < 0) {
sym = '<'
th = x + vdir
th = wround(th, 3) //門檻小數位要夠多, 否則切細會被抹去
}
else {
sym = '>'
th = x - vdir
th = wround(th, 3) //門檻小數位要夠多, 否則切細會被抹去
}
return {
key,
sym,
th,
}
})
// console.log(ifun, 'conds', conds)
//sid
let sid = genSid(name, interval, mode, conds)
// console.log('sid', sid)
//strategy
let strategy = {
tid,
sid,
name,
symbol,
interval,
keyOhlc: `${name}_price_${interval}`, //OHLC數據
mode,
conds,
settings: {
uIni: 1000, //初始資金(USDT)
uTrade, //每次下單金額(USDT)
rTakeProfit, //止盈
rStopLoss, //止損
rFee: 0.0005, //手續費
},
}
//runStrategy
let r = null
if (isfun(funRunStrategyCustom)) {
r = await funRunStrategyCustom({ ott, strategy, funGetSeries })
}
else {
r = await runStrategy(ott, strategy, funGetSeries)
}
// console.log(ifun, 'summary', r.summary)
//rEquivalentCumuProfitOrLossFinalNormYear
let rEquivalentCumuProfitOrLossFinalNormYear = p2r(r.summary.rEquivalentCumuProfitOrLossFinalNormYear)
//rWin
let rWin = p2r(r.summary.rWin)
//genRes
let genRes = (r) => {
//fitness
let fitness = calcFitness(strategy.settings, r.summary)
//numTrade
let numTrade = cint(r.summary.numTrade)
//levelNumTrade
let levelNumTrade = calcLevelNumTrade(numTrade)
//tkid
let tkid = `${tid}:${levelNumTrade}`
return {
...strategy,
tkid,
numTrade,
levelNumTrade,
keys,
summary: r.summary,
fitness,
solve: {
timeStart,
timeEnd,
methodOml,
thNumTrade,
thRWin,
thREquivalentCumuProfitOrLossFinalNormYear,
thsTp,
thsSl,
timeSolved: ott().format('YYYY-MM-DDTHH:mm:ss'),
},
}
}
//res, 除策略本體與summary、fitness外, 落地tkid、levelNumTrade、keys與solve使欄位自足, 識別與求解條件不依賴檔名
let res = genRes(r)
// console.log(ifun, res)
//resBrief
let resBrief = {
'方向': res.mode,
'每次下單金額': res.settings.uTrade,
'止盈': dig(res.settings.rTakeProfit * 100, 1) + '%',
'止損': dig(res.settings.rStopLoss * 100, 1) + '%',
// '最大持倉金額': res.summary.uTradeAllMax,
'最大持倉比例': res.summary.rTradeAllMax,
'交易次數': res.summary.numTrade,
'最大回撤率': res.summary.rDrawdownMax,
'勝率': res.summary.rWin,
'夏普值': dig(res.summary.rSharpe, 4),
'最終盈虧': res.summary.rCumuProfitOrLossFinal,
'最終等效盈虧': res.summary.rEquivalentCumuProfitOrLossFinal,
'最終等效年化盈虧': res.summary.rEquivalentCumuProfitOrLossFinalNormYear,
'fitness': res.fitness,
}
// console.log(ifun, resBrief)
//update fitnessMin
if (fitnessMin > res.fitness) {
if (useShowLog) {
console.log(ifun, '更新最佳解', resBrief)
}
//update
fitnessMin = res.fitness
}
//儲存可用參數組
if (
res.numTrade >= thNumTrade &&
rWin >= thRWin &&
// rCumuProfitOrLossFinal > 0 &&
// rTradeAllMax <= 0.8 &&
// rEquivalentCumuProfitOrLossFinal >= 0.3 &&
rEquivalentCumuProfitOrLossFinalNormYear >= thREquivalentCumuProfitOrLossFinalNormYear && //單策略等效年化報酬率之限制, 不能給太高
true
) {
if (useShowLog) {
console.log(ifun, '可用參數組', resBrief)
}
//fnStrategy, 檔名為tkid之純函數, 故可直接組出檔名查詢既有策略, 不需列舉全庫
let fnStrategy = genStrategyFileName(name, interval, mode, keys, strategy.settings, r.summary)
//fpStrategy
let fpStrategy = path.resolve(fdData, fnStrategy)
//b, 檢查是否已有同tkid策略, 無檔案或舊檔非合法json即寫入, 已有者僅於新等效年化盈虧更佳時覆寫
let b = checkSavable(fpStrategy, fnStrategy, res.tkid, rEquivalentCumuProfitOrLossFinalNormYear)
//save
if (b) {
//useRunStrategyBeforeSave
if (useRunStrategyBeforeSave) {
if (isfun(funRunStrategyCustomBeforeSave)) {
r = await funRunStrategyCustomBeforeSave({ ott, strategy, funGetSeries })
}
else {
r = await runStrategy(ott, strategy, funGetSeries)
}
//res, 檔內全部衍生欄位由重跑之summary重新衍生, 確保檔案內容同源一致
res = genRes(r)
//fnStrategyRe, 以重跑之summary重算檔名, 使檔名級距與檔內同源
let fnStrategyRe = genStrategyFileName(name, interval, mode, keys, strategy.settings, r.summary)
//check, 檔名有變代表評估核心與重跑結果跨級距, 新檔名須重做存在檢查/身分驗證/優劣比較, 不可沿用原檔名之通過結果
if (fnStrategyRe !== fnStrategy) {
//fnStrategy, fpStrategy
fnStrategy = fnStrategyRe
fpStrategy = path.resolve(fdData, fnStrategy)
//b, 以重跑之等效年化盈虧對新檔名重檢
b = checkSavable(fpStrategy, fnStrategy, res.tkid, p2r(get(r, 'summary.rEquivalentCumuProfitOrLossFinalNormYear', '')))
}
}
//fsWriteJson
if (b) {
fsWriteJson(fpStrategy, res, { useFormat: true })
}
}
}
return res.fitness
}
//oml
let oml = omlPSO
if (methodOml === 'RGA') {
oml = omlRGA
}
else if (methodOml === 'DE') {
oml = omlDE
}
else if (methodOml === 'HS') {
oml = omlHS
}
else if (methodOml === 'PSO') {
oml = omlPSO
}
else if (methodOml === 'ACO') {
oml = omlACO
}
//m
let m = await oml(dps, fun, {
// funGenerationBefore,
// funGenerationAfter,
// funGetBetter,
...opt,
})
if (useShowLog) {
// console.log('m', m)
console.log('bestSolution', m.bestSolution)
console.log('stopMode', m.stopMode)
console.log('stopIteration', m.stopIteration)
console.log('stopExecutions', m.stopExecutions)
}
return m
}
export default estimKeys