w-trade-solve 1.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.
Files changed (88) hide show
  1. package/.editorconfig +9 -0
  2. package/.eslintignore +3 -0
  3. package/.eslintrc.js +55 -0
  4. package/.jsdoc +25 -0
  5. package/README.md +132 -0
  6. package/SECURITY.md +5 -0
  7. package/babel.config.js +16 -0
  8. package/dist/w-trade-solve.umd.js +19 -0
  9. package/dist/w-trade-solve.umd.js.map +1 -0
  10. package/docs/calcFitness.mjs.html +205 -0
  11. package/docs/calcLevelNumTrade.mjs.html +164 -0
  12. package/docs/cont.mjs.html +94 -0
  13. package/docs/estimComps.mjs.html +190 -0
  14. package/docs/estimFinder.mjs.html +197 -0
  15. package/docs/estimKeys.mjs.html +571 -0
  16. package/docs/estimPicks.mjs.html +200 -0
  17. package/docs/fonts/Montserrat/Montserrat-Bold.eot +0 -0
  18. package/docs/fonts/Montserrat/Montserrat-Bold.ttf +0 -0
  19. package/docs/fonts/Montserrat/Montserrat-Bold.woff +0 -0
  20. package/docs/fonts/Montserrat/Montserrat-Bold.woff2 +0 -0
  21. package/docs/fonts/Montserrat/Montserrat-Regular.eot +0 -0
  22. package/docs/fonts/Montserrat/Montserrat-Regular.ttf +0 -0
  23. package/docs/fonts/Montserrat/Montserrat-Regular.woff +0 -0
  24. package/docs/fonts/Montserrat/Montserrat-Regular.woff2 +0 -0
  25. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.eot +0 -0
  26. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.svg +978 -0
  27. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.ttf +0 -0
  28. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff +0 -0
  29. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff2 +0 -0
  30. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.eot +0 -0
  31. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.svg +1049 -0
  32. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.ttf +0 -0
  33. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff +0 -0
  34. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff2 +0 -0
  35. package/docs/genSid.mjs.html +125 -0
  36. package/docs/genStrategyFileName.mjs.html +131 -0
  37. package/docs/genTid.mjs.html +113 -0
  38. package/docs/global.html +4932 -0
  39. package/docs/index.html +84 -0
  40. package/docs/ott.mjs.html +137 -0
  41. package/docs/p2r.mjs.html +111 -0
  42. package/docs/readStrategies.mjs.html +203 -0
  43. package/docs/scripts/collapse.js +39 -0
  44. package/docs/scripts/commonNav.js +28 -0
  45. package/docs/scripts/linenumber.js +25 -0
  46. package/docs/scripts/nav.js +12 -0
  47. package/docs/scripts/polyfill.js +4 -0
  48. package/docs/scripts/prettify/Apache-License-2.0.txt +202 -0
  49. package/docs/scripts/prettify/lang-css.js +2 -0
  50. package/docs/scripts/prettify/prettify.js +28 -0
  51. package/docs/scripts/search.js +99 -0
  52. package/docs/styles/jsdoc.css +776 -0
  53. package/docs/styles/prettify.css +80 -0
  54. package/g.mjs +110 -0
  55. package/package.json +31 -0
  56. package/script.txt +19 -0
  57. package/src/WTradeSolve.mjs +29 -0
  58. package/src/calcFitness.mjs +133 -0
  59. package/src/calcLevelNumTrade.mjs +92 -0
  60. package/src/cont.mjs +22 -0
  61. package/src/estimComps.mjs +118 -0
  62. package/src/estimFinder.mjs +125 -0
  63. package/src/estimKeys.mjs +499 -0
  64. package/src/estimPicks.mjs +128 -0
  65. package/src/genSid.mjs +53 -0
  66. package/src/genStrategyFileName.mjs +59 -0
  67. package/src/genTid.mjs +41 -0
  68. package/src/ott.mjs +65 -0
  69. package/src/p2r.mjs +39 -0
  70. package/src/readStrategies.mjs +131 -0
  71. package/test/unit-WTradeSolve.test.mjs +83 -0
  72. package/test/unit-calcFitness.test.mjs +121 -0
  73. package/test/unit-calcLevelNumTrade.test.mjs +80 -0
  74. package/test/unit-estimComps.test.mjs +193 -0
  75. package/test/unit-estimFinder.test.mjs +184 -0
  76. package/test/unit-estimKeys.test.mjs +260 -0
  77. package/test/unit-estimPicks.test.mjs +241 -0
  78. package/test/unit-genSid.test.mjs +75 -0
  79. package/test/unit-genStrategyFileName.test.mjs +85 -0
  80. package/test/unit-genTid.test.mjs +72 -0
  81. package/test/unit-p2r.test.mjs +51 -0
  82. package/test/unit-readStrategies.test.mjs +258 -0
  83. package/test/unit-setup.mjs +119 -0
  84. package/toolg/addVersion.mjs +4 -0
  85. package/toolg/cleanFolder.mjs +5 -0
  86. package/toolg/gDistRollup.mjs +37 -0
  87. package/toolg/gDocsExams.mjs +48 -0
  88. package/toolg/modifyReadme.mjs +4 -0
@@ -0,0 +1,571 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+
5
+ <meta charset="utf-8">
6
+ <title>estimKeys.mjs - Documentation</title>
7
+
8
+
9
+ <script src="scripts/prettify/prettify.js"></script>
10
+ <script src="scripts/prettify/lang-css.js"></script>
11
+ <!--[if lt IE 9]>
12
+ <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
13
+ <![endif]-->
14
+ <link type="text/css" rel="stylesheet" href="styles/prettify.css">
15
+ <link type="text/css" rel="stylesheet" href="styles/jsdoc.css">
16
+ <script src="scripts/nav.js" defer></script>
17
+
18
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
19
+ </head>
20
+ <body>
21
+
22
+ <input type="checkbox" id="nav-trigger" class="nav-trigger" />
23
+ <label for="nav-trigger" class="navicon-button x">
24
+ <div class="navicon"></div>
25
+ </label>
26
+
27
+ <label for="nav-trigger" class="overlay"></label>
28
+
29
+ <nav >
30
+
31
+
32
+ <h2><a href="index.html">Home</a></h2><h3>Global</h3><ul><li><a href="global.html#calcFitness">calcFitness</a></li><li><a href="global.html#calcLevelNumTrade">calcLevelNumTrade</a></li><li><a href="global.html#cont">cont</a></li><li><a href="global.html#estimComps">estimComps</a></li><li><a href="global.html#estimFinder">estimFinder</a></li><li><a href="global.html#estimKeys">estimKeys</a></li><li><a href="global.html#estimPicks">estimPicks</a></li><li><a href="global.html#genSid">genSid</a></li><li><a href="global.html#genStrategyFileName">genStrategyFileName</a></li><li><a href="global.html#genTid">genTid</a></li><li><a href="global.html#nowTpeStr">nowTpeStr</a></li><li><a href="global.html#nowTpeStrp">nowTpeStrp</a></li><li><a href="global.html#ott">ott</a></li><li><a href="global.html#p2r">p2r</a></li><li><a href="global.html#readStrategies">readStrategies</a></li></ul>
33
+
34
+ </nav>
35
+
36
+ <div id="main">
37
+
38
+ <h1 class="page-title">estimKeys.mjs</h1>
39
+
40
+
41
+
42
+
43
+
44
+
45
+
46
+ <section>
47
+ <article>
48
+ <pre class="prettyprint source linenums"><code>import path from 'path'
49
+ import each from 'lodash-es/each.js'
50
+ import get from 'lodash-es/get.js'
51
+ import map from 'lodash-es/map.js'
52
+ import round from 'lodash-es/round.js'
53
+ import size from 'lodash-es/size.js'
54
+ import cint from 'wsemi/src/cint.mjs'
55
+ import dig from 'wsemi/src/dig.mjs'
56
+ import fsCreateFolder from 'wsemi/src/fsCreateFolder.mjs'
57
+ import fsDeleteFile from 'wsemi/src/fsDeleteFile.mjs'
58
+ import fsIsFolder from 'wsemi/src/fsIsFolder.mjs'
59
+ import fsWriteJson from 'wsemi/src/fsWriteJson.mjs'
60
+ import haskey from 'wsemi/src/haskey.mjs'
61
+ import isearr from 'wsemi/src/isearr.mjs'
62
+ import isestr from 'wsemi/src/isestr.mjs'
63
+ import isnum from 'wsemi/src/isnum.mjs'
64
+ import rang from 'wsemi/src/rang.mjs'
65
+ import wround from 'wsemi/src/round.mjs'
66
+ import omlPSO from 'w-optimization/src/omlPSO.mjs'
67
+ import dataProvide from 'w-data-tdprovide'
68
+ import runStrategy from 'w-trade-backtest/src/runStrategy.mjs'
69
+ import readStrategies from './readStrategies.mjs'
70
+ import genTid from './genTid.mjs'
71
+ import genSid from './genSid.mjs'
72
+ import p2r from './p2r.mjs'
73
+ import calcLevelNumTrade from './calcLevelNumTrade.mjs'
74
+ import calcFitness from './calcFitness.mjs'
75
+ import genStrategyFileName from './genStrategyFileName.mjs'
76
+
77
+
78
+ /**
79
+ * 針對指定keys率定各key之門檻與止盈止損,求解最佳策略
80
+ *
81
+ * 設計變數為[止損比例, 止盈比例, ...各key之門檻],止損與止盈由opt.thsSl與opt.thsTp給予之百分比清單離散取值
82
+ * 各key門檻取值範圍為[-thLim,thLim]切為thLim*20段,並各自平移+vdir與-vdir(vdir=thLim*5)為兩組,使解值恆大於0代表'>'條件、恆小於0代表'&lt;'條件,還原時再扣除平移值,故單一設計變數即同時涵蓋大於與小於兩種條件
83
+ * 以omlPSO粒子群最佳化求解,各次計算皆以w-trade-backtest之runStrategy回測並經calcFitness計算適應值
84
+ * 各次計算結果若同時滿足交易次數、勝率與最終等效年化盈虧三門檻,則以genStrategyFileName產生檔名存入fdData;同tid且同交易次數級距(tkid)已有策略時,僅於最終等效年化盈虧更佳時抽換,並刪除較差者
85
+ * fdOhlc、fdParam、timeStart、timeEnd、keys、fdData無效或mode非'long'或'short'時throw
86
+ *
87
+ * Unit Test: {@link https://github.com/yuda-lyu/w-trade-solve/blob/master/test/unit-estimKeys.test.mjs Github}
88
+ * @function
89
+ * @param {Function} ott 輸入時區時間函數,傳入時間字串回傳dayjs時間物件,可用src/ott.mjs
90
+ * @param {Object} st 輸入設定物件,需可由opt.keySettings取得name、symbol與interval欄位
91
+ * @param {String} fdOhlc 輸入儲存K線(ohlc)序列資料夾字串
92
+ * @param {String} fdParam 輸入儲存指標參數序列資料夾字串
93
+ * @param {String} timeStart 輸入回測起始時間字串,格式'YYYY-MM-DDTHH:mm:ss'
94
+ * @param {String} timeEnd 輸入回測結束時間字串,格式'YYYY-MM-DDTHH:mm:ss'
95
+ * @param {String} mode 輸入交易方向字串,可選'long'或'short'
96
+ * @param {Array} keys 輸入待率定門檻之指標key字串陣列
97
+ * @param {String} fdData 輸入儲存策略資料夾字串,不存在時自動建立
98
+ * @param {Object} [opt={}] 輸入設定物件,預設{}
99
+ * @param {String} opt.keySettings 輸入取用st內設定之鍵字串,例如'btc4hr'代表取用st.btc4hr.name、st.btc4hr.symbol與st.btc4hr.interval
100
+ * @param {Array} [opt.thsTp=[1,2,3,4,5,6,7,8,9,10]] 輸入止盈門檻百分比數值陣列,預設[1,2,3,4,5,6,7,8,9,10]
101
+ * @param {Array} [opt.thsSl=[1,2,3,4,5,6,7,8,9,10]] 輸入止損門檻百分比數值陣列,預設[1,2,3,4,5,6,7,8,9,10]
102
+ * @param {Number} [opt.thNumTrade=1] 輸入策略須儲存之最少交易次數數值,預設1
103
+ * @param {Number} [opt.thRWin=0.5] 輸入策略須儲存之最低勝率比例數值,預設0.5
104
+ * @param {Number} [opt.thREquivalentCumuProfitOrLossFinalNormYear=0.15] 輸入策略須儲存之最低最終等效年化盈虧比例數值,預設0.15
105
+ * @param {Boolean} [opt.useShowLog=false] 輸入是否顯示求解過程log布林值,預設false
106
+ * @returns {Promise} 回傳Promise,resolve為omlPSO之求解結果物件,含bestSolution、stopMode、stopIteration與stopExecutions等欄位
107
+ * @example
108
+ *
109
+ * import ott from './src/ott.mjs'
110
+ *
111
+ * //st, 各設定以keySettings為鍵儲存
112
+ * let st = {
113
+ * btc4hr: {
114
+ * name: 'btc',
115
+ * symbol: 'BTCUSDT',
116
+ * interval: '4hr',
117
+ * },
118
+ * }
119
+ *
120
+ * //keys, 待率定門檻之指標key, 各key於fdParam內須有對應之`${key}.json`
121
+ * let keys = ['btc_price_4hr_ma_1day']
122
+ *
123
+ * let m = await estimKeys(ott, st, './data/ohlc', './data/param', '2022-07-01T00:00:00', '2025-04-08T00:00:00', 'long', keys, './data/strategy', {
124
+ * keySettings: 'btc4hr',
125
+ * thsTp: [1, 2, 3],
126
+ * thsSl: [1, 2, 3],
127
+ * useShowLog: true,
128
+ * })
129
+ * console.log(m.bestSolution.fitness)
130
+ * // => 1.0325
131
+ *
132
+ */
133
+ let estimKeys = async (ott, st, fdOhlc, fdParam, timeStart, timeEnd, mode, keys, fdData, opt = {}) => {
134
+
135
+ if (!isestr(fdOhlc)) {
136
+ throw new Error(`invalid fdOhlc[${fdOhlc}]`)
137
+ }
138
+ if (!isestr(fdParam)) {
139
+ throw new Error(`invalid fdParam[${fdParam}]`)
140
+ }
141
+ if (!isestr(timeStart)) {
142
+ throw new Error(`invalid timeStart[${timeStart}]`)
143
+ }
144
+ if (!isestr(timeEnd)) {
145
+ throw new Error(`invalid timeEnd[${timeEnd}]`)
146
+ }
147
+ if (mode !== 'long' &amp;&amp; mode !== 'short') {
148
+ throw new Error(`invalid mode[${mode}]`)
149
+ }
150
+ if (!isearr(keys)) {
151
+ throw new Error(`invalid keys[${keys}]`)
152
+ }
153
+ if (!isestr(fdData)) {
154
+ throw new Error(`invalid fdData[${fdData}]`)
155
+ }
156
+
157
+ let keySettings = get(opt, 'keySettings', '')
158
+ if (!isestr(keySettings)) {
159
+ throw new Error(`invalid opt.keySettings[${keySettings}]`)
160
+ }
161
+ let thsTp = get(opt, 'thsTp', null)
162
+ if (!isearr(thsTp)) {
163
+ thsTp = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
164
+ }
165
+ let thsSl = get(opt, 'thsSl', null)
166
+ if (!isearr(thsSl)) {
167
+ thsSl = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
168
+ }
169
+ let thNumTrade = get(opt, 'thNumTrade', null)
170
+ if (!isnum(thNumTrade)) {
171
+ thNumTrade = 1
172
+ }
173
+ let thRWin = get(opt, 'thRWin', null)
174
+ if (!isnum(thRWin)) {
175
+ thRWin = 0.5
176
+ }
177
+ let thREquivalentCumuProfitOrLossFinalNormYear = get(opt, 'thREquivalentCumuProfitOrLossFinalNormYear', null)
178
+ if (!isnum(thREquivalentCumuProfitOrLossFinalNormYear)) {
179
+ thREquivalentCumuProfitOrLossFinalNormYear = 0.15
180
+ }
181
+ let useShowLog = get(opt, 'useShowLog', null)
182
+
183
+ //provideData
184
+ let provideData = dataProvide(fdOhlc, fdParam)
185
+
186
+ //st
187
+ let name = get(st, `${keySettings}.name`, '')
188
+ let symbol = get(st, `${keySettings}.symbol`, '')
189
+ let interval = get(st, `${keySettings}.interval`, '')
190
+
191
+ //fdData, 儲存策略資料夾, 由呼叫端指定, 不存在則建立
192
+ if (!fsIsFolder(fdData)) {
193
+ fsCreateFolder(fdData)
194
+ }
195
+
196
+ //funGetSeries
197
+ let funGetSeries = await provideData.buildGetTimeSeriesByTimeRange(timeStart, timeEnd)
198
+
199
+ //tid
200
+ let tid = genTid(name, interval, mode, keys)
201
+ // console.log('tid', tid)
202
+
203
+ //vdir, 添加平移值方便後續判識方向
204
+ let thLim = 2
205
+ let vdir = thLim * 5
206
+
207
+ //dps
208
+ let dps = []
209
+ let idx = 0 //額外(非conds)設計變數之個數
210
+ if (true) {
211
+
212
+ //dps, 止損門檻與增量
213
+ if (true) {
214
+ let values
215
+
216
+ //掛單止損門檻(%), 對應 xs[0] = rStopLoss
217
+ values = thsSl
218
+ values = map(values, (v) => {
219
+ return v / 100
220
+ })
221
+ // console.log('values', values)
222
+ dps.push({
223
+ values,
224
+ n: size(values),
225
+ })
226
+ idx++
227
+
228
+ //掛單止盈門檻(%), 對應 xs[1] = rTakeProfit
229
+ values = thsTp
230
+ values = map(values, (v) => {
231
+ return v / 100
232
+ })
233
+ // console.log('values', values)
234
+ dps.push({
235
+ values,
236
+ n: size(values),
237
+ })
238
+ idx++
239
+
240
+ // //每次下單金額(USDT), 因等效年化報酬都是會基於最大持倉去計算, 故下單金額與最大持倉成正比, 故不再須視為參數之一
241
+ // values = [1]
242
+ // // console.log('values', values)
243
+ // dps.push({
244
+ // values,
245
+ // n: size(values),
246
+ // })
247
+ // idx++
248
+
249
+ }
250
+
251
+ //dps, 基於keys
252
+ each(keys, () => {
253
+
254
+ //大於條件
255
+ // let valuesU_fine = rang(0.01, 0.10, 9) //細粒度小尺度區: [0.01, 0.10] step 0.01 (10 點, 給 absP99 &lt; 0.1 的 115 個小尺度 keyOut)
256
+ // let valuesU_main = rang(0.15, 2.0, 37) //主區: [0.15, 2.0] step 0.05 (38 點)
257
+ // let valuesU = [...valuesU_fine, ...valuesU_main] //合計 48 點
258
+ let valuesU = rang(-thLim, thLim, thLim * 20)
259
+ valuesU = map(valuesU, (v) => {
260
+ return v + vdir //添加vdir使全部解皆>0, 方便後續判識轉換
261
+ })
262
+
263
+ //小於條件
264
+ // let valuesL_main = rang(-2.0, -0.15, 37) //主區: [-2.0, -0.15] step 0.05 (38 點)
265
+ // let valuesL_fine = rang(-0.10, -0.01, 9) //細粒度小尺度區: [-0.10, -0.01] step 0.01 (10 點, 給 absP99 &lt; 0.1 的 115 個小尺度 keyOut)
266
+ // let valuesL = [...valuesL_main, ...valuesL_fine] //合計 48 點
267
+ let valuesL = rang(-thLim, thLim, thLim * 20)
268
+ valuesL = map(valuesL, (v) => {
269
+ return v - vdir //添加-vdir使全部解皆&lt;0, 方便後續判識轉換
270
+ })
271
+
272
+ //values
273
+ let values = [
274
+ ...valuesU,
275
+ ...valuesL,
276
+ ]
277
+
278
+ //push
279
+ dps.push({
280
+ values,
281
+ n: size(values),
282
+ })
283
+
284
+ })
285
+
286
+ }
287
+
288
+ //ifun, fitnessMin
289
+ let ifun = 0
290
+ let fitnessMin = 1e20
291
+
292
+ //fun
293
+ let fun = async (xs) => {
294
+ //xs: 各元素為整數指標, 代表key所使用的conds
295
+
296
+ ifun++
297
+
298
+ //rStopLoss, rTakeProfit
299
+ let rStopLoss = xs[0] //止損
300
+ rStopLoss = round(rStopLoss, 3)
301
+ let rTakeProfit = xs[1] //止盈
302
+ rTakeProfit = round(rTakeProfit, 3)
303
+ let uTrade = 1 //原本使用xs[2]
304
+
305
+ //conds
306
+ let conds = map(keys, (key, i) => {
307
+
308
+ //ix
309
+ let ix = i + idx
310
+
311
+ //x
312
+ let x = get(xs, ix, null)
313
+
314
+ //check
315
+ if (x === null) {
316
+ console.log('xs', xs)
317
+ console.log('ix', ix)
318
+ console.log('key', key)
319
+ console.log('i', i)
320
+ throw new Error(`invalid x`)
321
+ }
322
+
323
+ //sym, th
324
+ let sym = ''
325
+ let th = null
326
+ if (x &lt; 0) {
327
+ sym = '&lt;'
328
+ th = x + vdir
329
+ th = wround(th, 3) //門檻小數位要夠多, 否則切細會被抹去
330
+ }
331
+ else {
332
+ sym = '>'
333
+ th = x - vdir
334
+ th = wround(th, 3) //門檻小數位要夠多, 否則切細會被抹去
335
+ }
336
+
337
+ return {
338
+ key,
339
+ sym,
340
+ th,
341
+ }
342
+ })
343
+ // console.log(ifun, 'conds', conds)
344
+
345
+ //sid
346
+ let sid = genSid(name, interval, mode, conds)
347
+ // console.log('sid', sid)
348
+
349
+ //strategy
350
+ let strategy = {
351
+
352
+ tid,
353
+ sid,
354
+
355
+ name,
356
+ symbol,
357
+ interval,
358
+
359
+ keyOhlc: `${name}_price_${interval}`, //OHLC數據
360
+
361
+ mode,
362
+
363
+ conds,
364
+
365
+ settings: {
366
+ uIni: 1000, //初始資金(USDT)
367
+ uTrade, //每次下單金額(USDT)
368
+ rTakeProfit, //止盈
369
+ rStopLoss, //止損
370
+ rFee: 0.0005, //手續費
371
+ },
372
+
373
+ }
374
+
375
+ //runStrategy
376
+ let r = await runStrategy(ott, strategy, funGetSeries)
377
+ // console.log(ifun, 'summary', r.summary)
378
+
379
+ //numTrade
380
+ let numTrade = cint(r.summary.numTrade)
381
+
382
+ //rWin
383
+ let rWin = p2r(r.summary.rWin)
384
+
385
+ // //rCumuProfitOrLossFinal
386
+ // let rCumuProfitOrLossFinal = p2r(r.summary.rCumuProfitOrLossFinal)
387
+
388
+ // //rTradeAllMax
389
+ // let rTradeAllMax = p2r(r.summary.rTradeAllMax)
390
+
391
+ // //rEquivalentCumuProfitOrLossFinal
392
+ // let rEquivalentCumuProfitOrLossFinal = p2r(r.summary.rEquivalentCumuProfitOrLossFinal)
393
+
394
+ //rEquivalentCumuProfitOrLossFinalNormYear
395
+ let rEquivalentCumuProfitOrLossFinalNormYear = p2r(r.summary.rEquivalentCumuProfitOrLossFinalNormYear)
396
+
397
+ //fitness
398
+ let fitness = calcFitness(strategy.settings, r.summary)
399
+
400
+ //res
401
+ let res = {
402
+
403
+ ...strategy,
404
+
405
+ summary: r.summary,
406
+
407
+ fitness,
408
+
409
+ }
410
+ // console.log(ifun, res)
411
+ let resBrief = {
412
+
413
+ '方向': res.mode,
414
+ '每次下單金額': res.settings.uTrade,
415
+ '止盈': dig(res.settings.rTakeProfit * 100, 1) + '%',
416
+ '止損': dig(res.settings.rStopLoss * 100, 1) + '%',
417
+ // '最大持倉金額': res.summary.uTradeAllMax,
418
+ '最大持倉比例': res.summary.rTradeAllMax,
419
+ '交易次數': res.summary.numTrade,
420
+ '最大回撤率': res.summary.rDrawdownMax,
421
+ '勝率': res.summary.rWin,
422
+ '夏普值': dig(res.summary.rSharpe, 4),
423
+ '最終盈虧': res.summary.rCumuProfitOrLossFinal,
424
+ '最終等效盈虧': res.summary.rEquivalentCumuProfitOrLossFinal,
425
+ '最終等效年化盈虧': res.summary.rEquivalentCumuProfitOrLossFinalNormYear,
426
+
427
+ 'fitness': res.fitness,
428
+
429
+ }
430
+ // console.log(ifun, resBrief)
431
+
432
+ //update fitnessMin
433
+ if (fitnessMin > fitness) {
434
+
435
+ if (useShowLog) {
436
+ console.log(ifun, '更新最佳解', resBrief)
437
+ }
438
+
439
+ //update
440
+ fitnessMin = fitness
441
+
442
+ }
443
+
444
+ //儲存可用參數組
445
+ if (
446
+ numTrade >= thNumTrade &amp;&amp;
447
+ rWin >= thRWin &amp;&amp;
448
+ // rCumuProfitOrLossFinal > 0 &amp;&amp;
449
+ // rTradeAllMax &lt;= 0.8 &amp;&amp;
450
+ // rEquivalentCumuProfitOrLossFinal >= 0.3 &amp;&amp;
451
+ rEquivalentCumuProfitOrLossFinalNormYear >= thREquivalentCumuProfitOrLossFinalNormYear &amp;&amp; //單策略等效年化報酬率之限制, 不能給太高
452
+ true
453
+ ) {
454
+
455
+ if (useShowLog) {
456
+ console.log(ifun, '可用參數組', resBrief)
457
+ }
458
+
459
+ //kpTid
460
+ let kpTkid = {}
461
+ if (true) {
462
+ let ss = readStrategies(fdData, { readContent: false })
463
+ each(ss, (s) => {
464
+ kpTkid[s.tkid] = s
465
+ })
466
+ }
467
+ // console.log('kpTkid', kpTkid)
468
+
469
+ //levelNumTrade
470
+ let levelNumTrade = calcLevelNumTrade(numTrade)
471
+
472
+ //tkid
473
+ let tkid = `${tid}:${levelNumTrade}`
474
+
475
+ //b
476
+ let b = false
477
+
478
+ //rEquivalentCumuProfitOrLossFinalNormYearNew
479
+ let rEquivalentCumuProfitOrLossFinalNormYearNew = rEquivalentCumuProfitOrLossFinalNormYear
480
+
481
+ //檢查是否已有tid策略
482
+ if (haskey(kpTkid, tkid)) {
483
+
484
+ //rEquivalentCumuProfitOrLossFinalNormYearOld
485
+ let rEquivalentCumuProfitOrLossFinalNormYearOld = p2r(kpTkid[tkid].rEquivalentCumuProfitOrLossFinalNormYear)
486
+
487
+ //check
488
+ if (rEquivalentCumuProfitOrLossFinalNormYearNew > rEquivalentCumuProfitOrLossFinalNormYearOld) {
489
+
490
+ //出現更好的等效盈虧比例, 須抽換
491
+ b = true
492
+
493
+ //先刪除較差tid策略
494
+ if (useShowLog) {
495
+ console.log(`刪除較差策略...`, `${rEquivalentCumuProfitOrLossFinalNormYearNew} > ${rEquivalentCumuProfitOrLossFinalNormYearOld}`, kpTkid[tkid].path)
496
+ }
497
+ fsDeleteFile(kpTkid[tkid].path)
498
+
499
+ }
500
+
501
+ }
502
+ else {
503
+
504
+ //新出現須儲存
505
+ b = true
506
+
507
+ }
508
+
509
+ //save
510
+ if (b) {
511
+
512
+ //fnStrategy
513
+ let fnStrategy = genStrategyFileName(name, interval, mode, keys, strategy.settings, r.summary)
514
+
515
+ //fpStrategy
516
+ let fpStrategy = path.resolve(fdData, fnStrategy)
517
+
518
+ //fsWriteJson
519
+ fsWriteJson(fpStrategy, res, { useFormat: true })
520
+
521
+ }
522
+
523
+ }
524
+
525
+ return fitness
526
+ }
527
+
528
+ //m
529
+ let m = await omlPSO(dps, fun, {
530
+ // funGenerationBefore,
531
+ // funGenerationAfter,
532
+ // funGetBetter,
533
+ })
534
+ if (useShowLog) {
535
+ // console.log('m', m)
536
+ console.log('bestSolution', m.bestSolution)
537
+ console.log('stopMode', m.stopMode)
538
+ console.log('stopIteration', m.stopIteration)
539
+ console.log('stopExecutions', m.stopExecutions)
540
+ }
541
+
542
+ return m
543
+ }
544
+
545
+
546
+ export default estimKeys
547
+ </code></pre>
548
+ </article>
549
+ </section>
550
+
551
+
552
+
553
+
554
+
555
+
556
+ </div>
557
+
558
+ <br class="clear">
559
+
560
+ <footer>
561
+ Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Tue Aug 04 2026 23:21:49 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
562
+ </footer>
563
+
564
+ <script>prettyPrint();</script>
565
+ <script src="scripts/polyfill.js"></script>
566
+ <script src="scripts/linenumber.js"></script>
567
+
568
+
569
+
570
+ </body>
571
+ </html>