w-trade-solve 1.0.4 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -192,7 +192,7 @@ export default calcFitness
192
192
  <br class="clear">
193
193
 
194
194
  <footer>
195
- Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Sat Aug 08 2026 00:39:09 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
195
+ Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Mon Aug 10 2026 21:53:44 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
196
196
  </footer>
197
197
 
198
198
  <script>prettyPrint();</script>
@@ -151,7 +151,7 @@ export default calcLevelNumTrade
151
151
  <br class="clear">
152
152
 
153
153
  <footer>
154
- Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Sat Aug 08 2026 00:39:09 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
154
+ Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Mon Aug 10 2026 21:53:44 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
155
155
  </footer>
156
156
 
157
157
  <script>prettyPrint();</script>
@@ -49,7 +49,7 @@
49
49
  * 共用常數
50
50
  *
51
51
  * dlmSeps為tid與sid內各段(mode與各key或各cond)之分隔字串
52
- * dlmPkgs為策略檔名內各段(tid、levelNumTrade、rEquivalentCumuProfitOrLossFinalNormYear)之分隔字串
52
+ * dlmPkgs為舊版策略檔名內各段(tid、levelNumTrade、rEquivalentCumuProfitOrLossFinalNormYear)之分隔字串,自genStrategyFileName改採雜湊檔名後僅供readStrategies解析舊格式檔名使用
53
53
  *
54
54
  * @name cont
55
55
  * @const
@@ -81,7 +81,7 @@ export default r
81
81
  <br class="clear">
82
82
 
83
83
  <footer>
84
- Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Sat Aug 08 2026 00:39:09 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
84
+ Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Mon Aug 10 2026 21:53:44 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
85
85
  </footer>
86
86
 
87
87
  <script>prettyPrint();</script>
@@ -182,7 +182,7 @@ export default estimComps
182
182
  <br class="clear">
183
183
 
184
184
  <footer>
185
- Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Sat Aug 08 2026 00:39:09 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
185
+ Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Mon Aug 10 2026 21:53:44 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
186
186
  </footer>
187
187
 
188
188
  <script>prettyPrint();</script>
@@ -189,7 +189,7 @@ export default estimFinder
189
189
  <br class="clear">
190
190
 
191
191
  <footer>
192
- Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Sat Aug 08 2026 00:39:09 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
192
+ Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Mon Aug 10 2026 21:53:44 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
193
193
  </footer>
194
194
 
195
195
  <script>prettyPrint();</script>
@@ -54,10 +54,10 @@ import size from 'lodash-es/size.js'
54
54
  import cint from 'wsemi/src/cint.mjs'
55
55
  import dig from 'wsemi/src/dig.mjs'
56
56
  import fsCreateFolder from 'wsemi/src/fsCreateFolder.mjs'
57
- import fsDeleteFile from 'wsemi/src/fsDeleteFile.mjs'
57
+ import fsIsFile from 'wsemi/src/fsIsFile.mjs'
58
58
  import fsIsFolder from 'wsemi/src/fsIsFolder.mjs'
59
+ import fsReadJson from 'wsemi/src/fsReadJson.mjs'
59
60
  import fsWriteJson from 'wsemi/src/fsWriteJson.mjs'
60
- import haskey from 'wsemi/src/haskey.mjs'
61
61
  import isearr from 'wsemi/src/isearr.mjs'
62
62
  import isestr from 'wsemi/src/isestr.mjs'
63
63
  import isnum from 'wsemi/src/isnum.mjs'
@@ -70,7 +70,6 @@ import omlPSO from 'w-optimization/src/omlPSO.mjs'
70
70
  import omlACO from 'w-optimization/src/omlACO.mjs'
71
71
  import dataProvide from 'w-data-tdprovide'
72
72
  import runStrategy from 'w-trade-backtest/src/runStrategy.mjs'
73
- import readStrategies from './readStrategies.mjs'
74
73
  import genTid from './genTid.mjs'
75
74
  import genSid from './genSid.mjs'
76
75
  import p2r from './p2r.mjs'
@@ -85,7 +84,8 @@ import genStrategyFileName from './genStrategyFileName.mjs'
85
84
  * 設計變數為[止損比例, 止盈比例, ...各key之門檻],止損與止盈由opt.thsSl與opt.thsTp給予之百分比清單離散取值
86
85
  * 各key門檻取值範圍為[-thLim,thLim]切為thLim*20段,並各自平移+vdir與-vdir(vdir=thLim*5)為兩組,使解值恆大於0代表'>'條件、恆小於0代表'&lt;'條件,還原時再扣除平移值,故單一設計變數即同時涵蓋大於與小於兩種條件
87
86
  * 以w-optimization之最佳化函數求解,由opt.methodOml指定演算法,各次計算皆以w-trade-backtest之runStrategy回測並經calcFitness計算適應值
88
- * 各次計算結果若同時滿足交易次數、勝率與最終等效年化盈虧三門檻,則以genStrategyFileName產生檔名存入fdData;同tid且同交易次數級距(tkid)已有策略時,僅於最終等效年化盈虧更佳時抽換,並刪除較差者
87
+ * 各次計算結果若同時滿足交易次數、勝率與最終等效年化盈虧三門檻,則以genStrategyFileName產生檔名存入fdData;檔名為tkid(tid+交易次數級距)之純函數,故直接組出檔名查詢既有策略(O(1),不列舉全庫),同tkid已有策略時僅於最終等效年化盈虧更佳時原地覆寫,故同tkid恆僅一檔;覆寫前驗證舊檔內tkid、name與interval與本次一致,不一致代表雜湊碰撞,直接throw以避免無聲覆寫
88
+ * 策略檔內容除策略本體(tid、sid、name、symbol、interval、keyOhlc、mode、conds、settings)與summary、fitness外,另落地tkid、levelNumTrade、keys與solve區塊(timeStart、timeEnd、methodOml、thNumTrade、thRWin、thREquivalentCumuProfitOrLossFinalNormYear、thsTp、thsSl、timeSolved),使識別與求解條件欄位自足,不依賴檔名
89
89
  * opt除下列各欄位外,其餘欄位皆原樣傳遞至所選之最佳化函數,兩者欄位名稱無重疊,故可一併給予求解設定
90
90
  * 各演算法共通之設定為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等
91
91
  * 預設之求解量甚大(單一key約需1萬7千次回測),僅需快速試算時可調小族群數與NContiguous
@@ -93,7 +93,7 @@ import genStrategyFileName from './genStrategyFileName.mjs'
93
93
  *
94
94
  * Unit Test: {@link https://github.com/yuda-lyu/w-trade-solve/blob/master/test/unit-estimKeys.test.mjs Github}
95
95
  * @function
96
- * @param {Function} ott 輸入時區時間函數,傳入時間字串回傳dayjs時間物件,可用src/ott.mjs
96
+ * @param {Function} ott 輸入時區時間函數,傳入時間字串回傳dayjs時間物件,未傳入時回傳當下時間(供solve.timeSolved記錄),可用src/ott.mjs
97
97
  * @param {String} name 輸入幣種名稱字串,例如'btc',用於組出K線序列key(`${name}_price_${interval}`)與縮寫tid
98
98
  * @param {String} symbol 輸入交易對名稱字串,例如'BTCUSDT',僅儲存至策略內供辨識
99
99
  * @param {String} interval 輸入K線週期字串,例如'4hr'
@@ -176,8 +176,8 @@ let estimKeys = async (ott, name, symbol, interval, fdOhlc, fdParam, timeStart,
176
176
  }
177
177
 
178
178
  let methodOml = get(opt, 'methodOml', '')
179
- if (!isestr(methodOml)) {
180
- methodOml = 'PSO'
179
+ if (methodOml !== 'RGA' &amp;&amp; methodOml !== 'DE' &amp;&amp; methodOml !== 'HS' &amp;&amp; methodOml !== 'PSO' &amp;&amp; methodOml !== 'ACO') {
180
+ methodOml = 'PSO' //非五者之一時採PSO, 使solve.methodOml記錄實際採用之演算法
181
181
  }
182
182
  let thsTp = get(opt, 'thsTp', null)
183
183
  if (!isearr(thsTp)) {
@@ -413,15 +413,37 @@ let estimKeys = async (ott, name, symbol, interval, fdOhlc, fdParam, timeStart,
413
413
  //fitness
414
414
  let fitness = calcFitness(strategy.settings, r.summary)
415
415
 
416
- //res
416
+ //levelNumTrade
417
+ let levelNumTrade = calcLevelNumTrade(numTrade)
418
+
419
+ //tkid
420
+ let tkid = `${tid}:${levelNumTrade}`
421
+
422
+ //res, 除策略本體與summary、fitness外, 落地tkid、levelNumTrade、keys與solve使欄位自足, 識別與求解條件不依賴檔名
417
423
  let res = {
418
424
 
419
425
  ...strategy,
420
426
 
427
+ tkid,
428
+ levelNumTrade,
429
+ keys,
430
+
421
431
  summary: r.summary,
422
432
 
423
433
  fitness,
424
434
 
435
+ solve: {
436
+ timeStart,
437
+ timeEnd,
438
+ methodOml,
439
+ thNumTrade,
440
+ thRWin,
441
+ thREquivalentCumuProfitOrLossFinalNormYear,
442
+ thsTp,
443
+ thsSl,
444
+ timeSolved: ott().format('YYYY-MM-DDTHH:mm:ss'),
445
+ },
446
+
425
447
  }
426
448
  // console.log(ifun, res)
427
449
  let resBrief = {
@@ -472,21 +494,11 @@ let estimKeys = async (ott, name, symbol, interval, fdOhlc, fdParam, timeStart,
472
494
  console.log(ifun, '可用參數組', resBrief)
473
495
  }
474
496
 
475
- //kpTid
476
- let kpTkid = {}
477
- if (true) {
478
- let ss = readStrategies(fdData, { readContent: false })
479
- each(ss, (s) => {
480
- kpTkid[s.tkid] = s
481
- })
482
- }
483
- // console.log('kpTkid', kpTkid)
484
-
485
- //levelNumTrade
486
- let levelNumTrade = calcLevelNumTrade(numTrade)
497
+ //fnStrategy, 檔名為tkid之純函數, 故可直接組出檔名查詢既有策略, 不需列舉全庫
498
+ let fnStrategy = genStrategyFileName(name, interval, mode, keys, strategy.settings, r.summary)
487
499
 
488
- //tkid
489
- let tkid = `${tid}:${levelNumTrade}`
500
+ //fpStrategy
501
+ let fpStrategy = path.resolve(fdData, fnStrategy)
490
502
 
491
503
  //b
492
504
  let b = false
@@ -494,23 +506,41 @@ let estimKeys = async (ott, name, symbol, interval, fdOhlc, fdParam, timeStart,
494
506
  //rEquivalentCumuProfitOrLossFinalNormYearNew
495
507
  let rEquivalentCumuProfitOrLossFinalNormYearNew = rEquivalentCumuProfitOrLossFinalNormYear
496
508
 
497
- //檢查是否已有tid策略
498
- if (haskey(kpTkid, tkid)) {
509
+ //檢查是否已有同tkid策略
510
+ if (fsIsFile(fpStrategy)) {
499
511
 
500
- //rEquivalentCumuProfitOrLossFinalNormYearOld
501
- let rEquivalentCumuProfitOrLossFinalNormYearOld = p2r(kpTkid[tkid].rEquivalentCumuProfitOrLossFinalNormYear)
512
+ //dataOld
513
+ let dOld = fsReadJson(fpStrategy) //回傳{success}或{error}, 讀取或解析失敗時為null
514
+ let dataOld = get(dOld, 'success', null)
502
515
 
503
- //check
504
- if (rEquivalentCumuProfitOrLossFinalNormYearNew > rEquivalentCumuProfitOrLossFinalNormYearOld) {
516
+ if (dataOld === null) {
505
517
 
506
- //出現更好的等效盈虧比例, 須抽換
518
+ //舊檔非合法json, 直接覆寫
507
519
  b = true
508
520
 
509
- //先刪除較差tid策略
510
- if (useShowLog) {
511
- console.log(`刪除較差策略...`, `${rEquivalentCumuProfitOrLossFinalNormYearNew} > ${rEquivalentCumuProfitOrLossFinalNormYearOld}`, kpTkid[tkid].path)
521
+ }
522
+ else {
523
+
524
+ //覆寫前驗證舊檔身分與本次一致, 不一致代表雜湊碰撞, 直接throw以避免無聲覆寫
525
+ let tkidOld = get(dataOld, 'tkid', '')
526
+ let nameOld = get(dataOld, 'name', '')
527
+ let intervalOld = get(dataOld, 'interval', '')
528
+ if ((isestr(tkidOld) &amp;&amp; tkidOld !== tkid) || (isestr(nameOld) &amp;&amp; nameOld !== name) || (isestr(intervalOld) &amp;&amp; intervalOld !== interval)) {
529
+ throw new Error(`hash collision: fnStrategy[${fnStrategy}], tkidOld[${tkidOld}] vs tkid[${tkid}], nameOld[${nameOld}] vs name[${name}], intervalOld[${intervalOld}] vs interval[${interval}]`)
530
+ }
531
+
532
+ //rEquivalentCumuProfitOrLossFinalNormYearOld
533
+ let rEquivalentCumuProfitOrLossFinalNormYearOld = p2r(get(dataOld, 'summary.rEquivalentCumuProfitOrLossFinalNormYear', ''))
534
+
535
+ //check, 出現更好的等效盈虧比例, 原地覆寫
536
+ if (rEquivalentCumuProfitOrLossFinalNormYearNew > rEquivalentCumuProfitOrLossFinalNormYearOld) {
537
+
538
+ if (useShowLog) {
539
+ console.log(`覆寫較差策略...`, `${rEquivalentCumuProfitOrLossFinalNormYearNew} > ${rEquivalentCumuProfitOrLossFinalNormYearOld}`, fpStrategy)
540
+ }
541
+ b = true
542
+
512
543
  }
513
- fsDeleteFile(kpTkid[tkid].path)
514
544
 
515
545
  }
516
546
 
@@ -525,12 +555,6 @@ let estimKeys = async (ott, name, symbol, interval, fdOhlc, fdParam, timeStart,
525
555
  //save
526
556
  if (b) {
527
557
 
528
- //fnStrategy
529
- let fnStrategy = genStrategyFileName(name, interval, mode, keys, strategy.settings, r.summary)
530
-
531
- //fpStrategy
532
- let fpStrategy = path.resolve(fdData, fnStrategy)
533
-
534
558
  //fsWriteJson
535
559
  fsWriteJson(fpStrategy, res, { useFormat: true })
536
560
 
@@ -593,7 +617,7 @@ export default estimKeys
593
617
  <br class="clear">
594
618
 
595
619
  <footer>
596
- Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Sat Aug 08 2026 00:39:09 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
620
+ Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Mon Aug 10 2026 21:53:44 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
597
621
  </footer>
598
622
 
599
623
  <script>prettyPrint();</script>
@@ -192,7 +192,7 @@ export default estimPicks
192
192
  <br class="clear">
193
193
 
194
194
  <footer>
195
- Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Sat Aug 08 2026 00:39:09 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
195
+ Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Mon Aug 10 2026 21:53:44 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
196
196
  </footer>
197
197
 
198
198
  <script>prettyPrint();</script>
@@ -112,7 +112,7 @@ export default genSid
112
112
  <br class="clear">
113
113
 
114
114
  <footer>
115
- Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Sat Aug 08 2026 00:39:09 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
115
+ Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Mon Aug 10 2026 21:53:44 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
116
116
  </footer>
117
117
 
118
118
  <script>prettyPrint();</script>
@@ -45,8 +45,8 @@
45
45
 
46
46
  <section>
47
47
  <article>
48
- <pre class="prettyprint source linenums"><code>import cint from 'wsemi/src/cint.mjs'
49
- import cont from './cont.mjs'
48
+ <pre class="prettyprint source linenums"><code>import crypto from 'crypto'
49
+ import cint from 'wsemi/src/cint.mjs'
50
50
  import genTid from './genTid.mjs'
51
51
  import calcLevelNumTrade from './calcLevelNumTrade.mjs'
52
52
 
@@ -54,9 +54,12 @@ import calcLevelNumTrade from './calcLevelNumTrade.mjs'
54
54
  /**
55
55
  * 產生策略儲存檔名
56
56
  *
57
- * 檔名為`${tid}${cont.dlmPkgs}${levelNumTrade}${cont.dlmPkgs}${rEquivalentCumuProfitOrLossFinalNormYear}.json`
57
+ * 檔名為`${mode}_${levelNumTrade}_${hash}.json`,hash為md5(`${coin}:${interval}:${tid}:${levelNumTrade}`)取前16碼(64bits)
58
58
  * tid由genTid以coin、interval、mode與keys產生,levelNumTrade由calcLevelNumTrade以summary.numTrade產生
59
- * 因tid與levelNumTrade皆入檔名,故可由檔名反解出同組keys與同交易次數級距之既有策略,供readStrategies列舉比較
59
+ * 檔名為(coin,interval,tid,levelNumTrade)之純函數且與求解結果(等效年化盈虧)無關,故同tkid恆對應同一檔名,
60
+ * 存檔端可直接組出檔名以fsIsFile查詢既有策略(O(1)),不需列舉全庫,且更新為原地覆寫,結構上不會出現同tkid多檔
61
+ * 因genTid會剔除`${coin}_`與`${interval}_`樣式,不同幣種或週期之同組keys會得到相同tid,故hash輸入需含coin與interval以避免跨幣種/跨週期碰撞
62
+ * 採定長雜湊係因NTFS單一檔名段上限為255字元(UTF-16計,LongPathsEnabled無法放寬),keys個數多時舊式`${tid} ⊙ ...`檔名會超限
60
63
  * settings目前未參與檔名組成
61
64
  *
62
65
  * Unit Test: {@link https://github.com/yuda-lyu/w-trade-solve/blob/master/test/unit-genStrategyFileName.test.mjs Github}
@@ -66,7 +69,7 @@ import calcLevelNumTrade from './calcLevelNumTrade.mjs'
66
69
  * @param {String} mode 輸入交易方向字串,可選'long'或'short'
67
70
  * @param {Array} keys 輸入指標key字串陣列
68
71
  * @param {Object} settings 輸入策略設定物件
69
- * @param {Object} summary 輸入回測統計摘要物件,需含numTrade(交易次數)與rEquivalentCumuProfitOrLossFinalNormYear(最終等效年化盈虧百分比字串)欄位
72
+ * @param {Object} summary 輸入回測統計摘要物件,需含numTrade(交易次數)欄位
70
73
  * @returns {String} 回傳策略儲存檔名字串
71
74
  * @example
72
75
  *
@@ -78,7 +81,8 @@ import calcLevelNumTrade from './calcLevelNumTrade.mjs'
78
81
  *
79
82
  * let fn = genStrategyFileName('btc', '4hr', 'long', keys, settings, summary)
80
83
  * console.log(fn)
81
- * // => 'long ║ p_ma_1day ⊙ 12 ⊙ 31.25%.json'
84
+ * // => 'long_12_9fce6961e8f56493.json'
85
+ * //tid為'long ║ p_ma_1day', 級距為12, hash為md5('btc:4hr:long ║ p_ma_1day:12')前16碼
82
86
  *
83
87
  */
84
88
  let genStrategyFileName = (coin, interval, mode, keys, settings, summary) => {
@@ -93,11 +97,11 @@ let genStrategyFileName = (coin, interval, mode, keys, settings, summary) => {
93
97
  //levelNumTrade
94
98
  let levelNumTrade = calcLevelNumTrade(numTrade)
95
99
 
96
- //rEquivalentCumuProfitOrLossFinalNormYear
97
- let rEquivalentCumuProfitOrLossFinalNormYear = summary.rEquivalentCumuProfitOrLossFinalNormYear
100
+ //hash, 含coin與interval避免跨幣種/跨週期之同名tid碰撞
101
+ let hash = crypto.createHash('md5').update(`${coin}:${interval}:${tid}:${levelNumTrade}`, 'utf8').digest('hex').slice(0, 16)
98
102
 
99
103
  //fnStrategy
100
- let fnStrategy = `${tid}${cont.dlmPkgs}${levelNumTrade}${cont.dlmPkgs}${rEquivalentCumuProfitOrLossFinalNormYear}.json`
104
+ let fnStrategy = `${mode}_${levelNumTrade}_${hash}.json`
101
105
 
102
106
  return fnStrategy
103
107
  }
@@ -118,7 +122,7 @@ export default genStrategyFileName
118
122
  <br class="clear">
119
123
 
120
124
  <footer>
121
- Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Sat Aug 08 2026 00:39:09 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
125
+ Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Mon Aug 10 2026 21:53:44 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
122
126
  </footer>
123
127
 
124
128
  <script>prettyPrint();</script>
@@ -100,7 +100,7 @@ export default genTid
100
100
  <br class="clear">
101
101
 
102
102
  <footer>
103
- Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Sat Aug 08 2026 00:39:09 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
103
+ Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Mon Aug 10 2026 21:53:44 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
104
104
  </footer>
105
105
 
106
106
  <script>prettyPrint();</script>
package/docs/global.html CHANGED
@@ -136,7 +136,7 @@
136
136
  <dt class="tag-description">Description:</dt>
137
137
  <dd class="tag-description"><ul class="dummy"><li><p>共用常數</p>
138
138
  <p>dlmSeps為tid與sid內各段(mode與各key或各cond)之分隔字串
139
- dlmPkgs為策略檔名內各段(tid、levelNumTrade、rEquivalentCumuProfitOrLossFinalNormYear)之分隔字串</p></li></ul></dd>
139
+ dlmPkgs為舊版策略檔名內各段(tid、levelNumTrade、rEquivalentCumuProfitOrLossFinalNormYear)之分隔字串,自genStrategyFileName改採雜湊檔名後僅供readStrategies解析舊格式檔名使用</p></li></ul></dd>
140
140
 
141
141
 
142
142
 
@@ -184,7 +184,7 @@ dlmPkgs為策略檔名內各段(tid、levelNumTrade、rEquivalentCumuProfitOrLos
184
184
  <div class="description usertext">
185
185
  <p>共用常數</p>
186
186
  <p>dlmSeps為tid與sid內各段(mode與各key或各cond)之分隔字串
187
- dlmPkgs為策略檔名內各段(tid、levelNumTrade、rEquivalentCumuProfitOrLossFinalNormYear)之分隔字串</p>
187
+ dlmPkgs為舊版策略檔名內各段(tid、levelNumTrade、rEquivalentCumuProfitOrLossFinalNormYear)之分隔字串,自genStrategyFileName改採雜湊檔名後僅供readStrategies解析舊格式檔名使用</p>
188
188
  </div>
189
189
 
190
190
 
@@ -1842,7 +1842,8 @@ await estimFinder(ott, 'btc', 'BTCUSDT', '4hr', './data/ohlc', './data/param', '
1842
1842
  <p>設計變數為[止損比例, 止盈比例, ...各key之門檻],止損與止盈由opt.thsSl與opt.thsTp給予之百分比清單離散取值
1843
1843
  各key門檻取值範圍為[-thLim,thLim]切為thLim<em>20段,並各自平移+vdir與-vdir(vdir=thLim</em>5)為兩組,使解值恆大於0代表'&gt;'條件、恆小於0代表'&lt;'條件,還原時再扣除平移值,故單一設計變數即同時涵蓋大於與小於兩種條件
1844
1844
  以w-optimization之最佳化函數求解,由opt.methodOml指定演算法,各次計算皆以w-trade-backtest之runStrategy回測並經calcFitness計算適應值
1845
- 各次計算結果若同時滿足交易次數、勝率與最終等效年化盈虧三門檻,則以genStrategyFileName產生檔名存入fdData;同tid且同交易次數級距(tkid)已有策略時,僅於最終等效年化盈虧更佳時抽換,並刪除較差者
1845
+ 各次計算結果若同時滿足交易次數、勝率與最終等效年化盈虧三門檻,則以genStrategyFileName產生檔名存入fdData;檔名為tkid(tid+交易次數級距)之純函數,故直接組出檔名查詢既有策略(O(1),不列舉全庫),同tkid已有策略時僅於最終等效年化盈虧更佳時原地覆寫,故同tkid恆僅一檔;覆寫前驗證舊檔內tkid、name與interval與本次一致,不一致代表雜湊碰撞,直接throw以避免無聲覆寫
1846
+ 策略檔內容除策略本體(tid、sid、name、symbol、interval、keyOhlc、mode、conds、settings)與summary、fitness外,另落地tkid、levelNumTrade、keys與solve區塊(timeStart、timeEnd、methodOml、thNumTrade、thRWin、thREquivalentCumuProfitOrLossFinalNormYear、thsTp、thsSl、timeSolved),使識別與求解條件欄位自足,不依賴檔名
1846
1847
  opt除下列各欄位外,其餘欄位皆原樣傳遞至所選之最佳化函數,兩者欄位名稱無重疊,故可一併給予求解設定
1847
1848
  各演算法共通之設定為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等
1848
1849
  預設之求解量甚大(單一key約需1萬7千次回測),僅需快速試算時可調小族群數與NContiguous
@@ -1987,7 +1988,7 @@ console.log(mQuick.stopExecutions) //核心分析次數, 較預設設定大幅
1987
1988
  </td>
1988
1989
 
1989
1990
 
1990
- <td class="description last"><p>輸入時區時間函數,傳入時間字串回傳dayjs時間物件,可用src/ott.mjs</p></td>
1991
+ <td class="description last"><p>輸入時區時間函數,傳入時間字串回傳dayjs時間物件,未傳入時回傳當下時間(供solve.timeSolved記錄),可用src/ott.mjs</p></td>
1991
1992
  </tr>
1992
1993
 
1993
1994
 
@@ -3748,9 +3749,12 @@ console.log(sid)
3748
3749
 
3749
3750
  <dt class="tag-description">Description:</dt>
3750
3751
  <dd class="tag-description"><ul class="dummy"><li><p>產生策略儲存檔名</p>
3751
- <p>檔名為<code>${tid}${cont.dlmPkgs}${levelNumTrade}${cont.dlmPkgs}${rEquivalentCumuProfitOrLossFinalNormYear}.json</code>
3752
+ <p>檔名為<code>${mode}_${levelNumTrade}_${hash}.json</code>,hash為md5(<code>${coin}:${interval}:${tid}:${levelNumTrade}</code>)取前16碼(64bits)
3752
3753
  tid由genTid以coin、interval、mode與keys產生,levelNumTrade由calcLevelNumTrade以summary.numTrade產生
3753
- 因tid與levelNumTrade皆入檔名,故可由檔名反解出同組keys與同交易次數級距之既有策略,供readStrategies列舉比較
3754
+ 檔名為(coin,interval,tid,levelNumTrade)之純函數且與求解結果(等效年化盈虧)無關,故同tkid恆對應同一檔名,
3755
+ 存檔端可直接組出檔名以fsIsFile查詢既有策略(O(1)),不需列舉全庫,且更新為原地覆寫,結構上不會出現同tkid多檔
3756
+ 因genTid會剔除<code>${coin}_</code>與<code>${interval}_</code>樣式,不同幣種或週期之同組keys會得到相同tid,故hash輸入需含coin與interval以避免跨幣種/跨週期碰撞
3757
+ 採定長雜湊係因NTFS單一檔名段上限為255字元(UTF-16計,LongPathsEnabled無法放寬),keys個數多時舊式<code>${tid} ⊙ ...</code>檔名會超限
3754
3758
  settings目前未參與檔名組成</p>
3755
3759
  <p>Unit Test: <a href="https://github.com/yuda-lyu/w-trade-solve/blob/master/test/unit-genStrategyFileName.test.mjs">Github</a></p></li></ul></dd>
3756
3760
 
@@ -3758,7 +3762,7 @@ settings目前未參與檔名組成</p>
3758
3762
 
3759
3763
  <dt class="tag-source">Source:</dt>
3760
3764
  <dd class="tag-source"><ul class="dummy"><li>
3761
- <a href="genStrategyFileName.mjs.html">genStrategyFileName.mjs</a>, <a href="genStrategyFileName.mjs.html#line37">line 37</a>
3765
+ <a href="genStrategyFileName.mjs.html">genStrategyFileName.mjs</a>, <a href="genStrategyFileName.mjs.html#line41">line 41</a>
3762
3766
  </li></ul></dd>
3763
3767
 
3764
3768
 
@@ -3813,7 +3817,8 @@ let summary = { numTrade: 30, rEquivalentCumuProfitOrLossFinalNormYear: '31.25%'
3813
3817
 
3814
3818
  let fn = genStrategyFileName('btc', '4hr', 'long', keys, settings, summary)
3815
3819
  console.log(fn)
3816
- // => 'long ║ p_ma_1day ⊙ 12 ⊙ 31.25%.json'</code></pre>
3820
+ // => 'long_12_9fce6961e8f56493.json'
3821
+ //tid為'long ║ p_ma_1day', 級距為12, hash為md5('btc:4hr:long ║ p_ma_1day:12')前16碼</code></pre>
3817
3822
 
3818
3823
 
3819
3824
 
@@ -3980,7 +3985,7 @@ console.log(fn)
3980
3985
 
3981
3986
 
3982
3987
 
3983
- <td class="description last"><p>輸入回測統計摘要物件,需含numTrade(交易次數)與rEquivalentCumuProfitOrLossFinalNormYear(最終等效年化盈虧百分比字串)欄位</p></td>
3988
+ <td class="description last"><p>輸入回測統計摘要物件,需含numTrade(交易次數)欄位</p></td>
3984
3989
  </tr>
3985
3990
 
3986
3991
 
@@ -4880,8 +4885,11 @@ console.log(p2r(''))
4880
4885
  <dt class="tag-description">Description:</dt>
4881
4886
  <dd class="tag-description"><ul class="dummy"><li><p>讀取儲存策略資料夾內各策略檔資訊</p>
4882
4887
  <p>僅列舉fd第1層之檔案,資料夾不列入,fd非資料夾或無任何檔案時回傳空陣列
4883
- 各檔以主檔名(剔除'.json')依cont.dlmPkgs切段,依序為tid、levelNumTrade與rEquivalentCumuProfitOrLossFinalNormYear,即genStrategyFileName所產生之檔名,切不出tid者予以略過
4884
- tid再依cont.dlmSeps切段,首段為mode,其餘為各key所組成之ps
4888
+ 各檔依opt.fromContent決定解析來源:
4889
+ 'filename'為舊式檔名反解: 以主檔名(剔除'.json')依cont.dlmPkgs切段,依序為tid、levelNumTrade與rEquivalentCumuProfitOrLossFinalNormYear,切不出tid者予以略過
4890
+ 'content'為讀取檔內json之tid、tkid、levelNumTrade與summary等欄位(即estimKeys所寫出之策略內容),非合法json或無tid欄位者予以略過,因需讀檔故較'filename'慢
4891
+ 'auto'(預設)為逐檔判別: 檔名符合genStrategyFileName雜湊格式(<code>${mode}_${levelNumTrade}_${hash16}.json</code>)者走'content',其餘走'filename',故新舊格式混存時皆可列舉
4892
+ tid依cont.dlmSeps切段,首段為mode,其餘為各key所組成之ps
4885
4893
  tkid為<code>${tid}:${levelNumTrade}</code>,與estimKeys內用於判斷同組keys與同交易次數級距之組合方式一致</p>
4886
4894
  <p>Unit Test: <a href="https://github.com/yuda-lyu/w-trade-solve/blob/master/test/unit-readStrategies.test.mjs">Github</a></p></li></ul></dd>
4887
4895
 
@@ -4889,7 +4897,7 @@ tkid為<code>${tid}:${levelNumTrade}</code>,與estimKeys內用於判斷同組k
4889
4897
 
4890
4898
  <dt class="tag-source">Source:</dt>
4891
4899
  <dd class="tag-source"><ul class="dummy"><li>
4892
- <a href="readStrategies.mjs.html">readStrategies.mjs</a>, <a href="readStrategies.mjs.html#line56">line 56</a>
4900
+ <a href="readStrategies.mjs.html">readStrategies.mjs</a>, <a href="readStrategies.mjs.html#line67">line 67</a>
4893
4901
  </li></ul></dd>
4894
4902
 
4895
4903
 
@@ -4937,20 +4945,22 @@ tkid為<code>${tid}:${levelNumTrade}</code>,與estimKeys內用於判斷同組k
4937
4945
  <h5 class="h5-examples">Example</h5>
4938
4946
 
4939
4947
  <pre class="prettyprint"><code>import fs from 'fs'
4940
- import cont from './src/cont.mjs'
4948
+ import genStrategyFileName from './src/genStrategyFileName.mjs'
4941
4949
 
4942
4950
  let fd = './tmp/data-strategy'
4943
4951
  fs.mkdirSync(fd, { recursive: true })
4944
4952
 
4945
- //以genStrategyFileName之檔名規則儲存策略檔
4946
- let fn = `long${cont.dlmSeps}ma_1day${cont.dlmPkgs}12${cont.dlmPkgs}31.25%.json`
4947
- fs.writeFileSync(`${fd}/${fn}`, JSON.stringify({ mode: 'long' }), 'utf8')
4953
+ //以genStrategyFileName之檔名規則儲存策略檔, 檔名為雜湊格式, 識別資訊由檔內json承載
4954
+ let summary = { numTrade: 30, rEquivalentCumuProfitOrLossFinalNormYear: '31.25%' }
4955
+ let fn = genStrategyFileName('btc', '4hr', 'long', ['btc_4hr_ma_1day'], {}, summary)
4956
+ let data = { tid: 'long ║ ma_1day', tkid: 'long ║ ma_1day:12', levelNumTrade: 12, mode: 'long', summary }
4957
+ fs.writeFileSync(`${fd}/${fn}`, JSON.stringify(data), 'utf8')
4948
4958
 
4949
4959
  console.log(readStrategies(fd))
4950
4960
  // => [
4951
4961
  // {
4952
- // path: './tmp/data-strategy/long ║ ma_1day ⊙ 12 ⊙ 31.25%.json',
4953
- // name: 'long ║ ma_1day ⊙ 12 ⊙ 31.25%.json',
4962
+ // path: './tmp/data-strategy/long_12_a7578526a1fc57d8.json',
4963
+ // name: 'long_12_a7578526a1fc57d8.json',
4954
4964
  // tkid: 'long ║ ma_1day:12',
4955
4965
  // tid: 'long ║ ma_1day',
4956
4966
  // levelNumTrade: '12',
@@ -5124,7 +5134,47 @@ console.log(readStrategies(fd))
5124
5134
  </td>
5125
5135
 
5126
5136
 
5127
- <td class="description last"><p>輸入是否讀取策略檔內容布林值,為true時各元素額外提供data欄位,內容非合法json時data為null,預設false</p></td>
5137
+ <td class="description last"><p>輸入是否額外提供策略檔內容布林值,為true時各元素額外提供data欄位,內容非合法json時data為null,預設false</p></td>
5138
+ </tr>
5139
+
5140
+
5141
+
5142
+ <tr>
5143
+
5144
+ <td class="name"><code>fromContent</code></td>
5145
+
5146
+
5147
+ <td class="type">
5148
+
5149
+
5150
+ <span class="param-type">String</span>
5151
+
5152
+
5153
+
5154
+
5155
+ </td>
5156
+
5157
+
5158
+ <td class="attributes">
5159
+
5160
+ &lt;optional><br>
5161
+
5162
+
5163
+
5164
+
5165
+
5166
+ </td>
5167
+
5168
+
5169
+
5170
+ <td class="default">
5171
+
5172
+ <code>'auto'</code>
5173
+
5174
+ </td>
5175
+
5176
+
5177
+ <td class="description last"><p>輸入解析來源字串,可選'filename'(由檔名反解)、'content'(讀檔內json欄位)與'auto'(依檔名格式逐檔判別),給予其他值時亦採'auto',預設'auto'</p></td>
5128
5178
  </tr>
5129
5179
 
5130
5180
 
@@ -5199,7 +5249,7 @@ console.log(readStrategies(fd))
5199
5249
  <br class="clear">
5200
5250
 
5201
5251
  <footer>
5202
- Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Sat Aug 08 2026 00:39:09 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
5252
+ Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Mon Aug 10 2026 21:53:44 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
5203
5253
  </footer>
5204
5254
 
5205
5255
  <script>prettyPrint();</script>
package/docs/index.html CHANGED
@@ -71,7 +71,7 @@
71
71
  <br class="clear">
72
72
 
73
73
  <footer>
74
- Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Sat Aug 08 2026 00:39:09 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
74
+ Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Mon Aug 10 2026 21:53:44 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
75
75
  </footer>
76
76
 
77
77
  <script>prettyPrint();</script>
package/docs/ott.mjs.html CHANGED
@@ -124,7 +124,7 @@ export { ott, nowTpeStr, nowTpeStrp }
124
124
  <br class="clear">
125
125
 
126
126
  <footer>
127
- Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Sat Aug 08 2026 00:39:09 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
127
+ Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Mon Aug 10 2026 21:53:44 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
128
128
  </footer>
129
129
 
130
130
  <script>prettyPrint();</script>
package/docs/p2r.mjs.html CHANGED
@@ -98,7 +98,7 @@ export default p2r
98
98
  <br class="clear">
99
99
 
100
100
  <footer>
101
- Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Sat Aug 08 2026 00:39:09 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
101
+ Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Mon Aug 10 2026 21:53:44 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
102
102
  </footer>
103
103
 
104
104
  <script>prettyPrint();</script>