w-trade-solve 1.0.2 → 1.0.4

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.
@@ -2,11 +2,11 @@ import assert from 'assert'
2
2
  import fs from 'fs'
3
3
  import estimFinder from '../src/estimFinder.mjs'
4
4
  import ott from '../src/ott.mjs'
5
- import { buildFdTmp, buildFdData, buildSt, keySettings } from './unit-setup.mjs'
5
+ import { buildFdTmp, buildFdData, name, symbol, interval } from './unit-setup.mjs'
6
6
 
7
7
 
8
8
  //規格來源: src/estimFinder.mjs
9
- // estimFinder(ott, st, fdOhlc, fdParam, fdData, opt):
9
+ // estimFinder(ott, name, symbol, interval, fdOhlc, fdParam, fdData, opt):
10
10
  // mode未給時由'long'與'short'隨機取一
11
11
  // timeStart未給時為'2022-07-01T00:00:00'
12
12
  // timeEnd未給時依mode給予: long為'2025-04-08T00:00:00', short為'2025-10-06T00:00:00'
@@ -19,9 +19,6 @@ import { buildFdTmp, buildFdData, buildSt, keySettings } from './unit-setup.mjs'
19
19
  let fdTmp = buildFdTmp('estimFinder')
20
20
 
21
21
 
22
- let st = buildSt()
23
-
24
-
25
22
  //keysParam: norm 4個、index 2個、volumn 1個, 使18種預設comps皆可取樣
26
23
  let keysNorm = ['btc_4hr_ma_1day', 'btc_4hr_ma_2day', 'btc_4hr_rsi', 'btc_4hr_kdj']
27
24
  let keysIndex = ['index_fear_greed', 'index_dxy']
@@ -39,13 +36,16 @@ let d = null
39
36
  //call: 以合法引數為底, 依ov.opt覆寫opt後呼叫
40
37
  let call = (ov = {}) => {
41
38
  let a = {
39
+ name,
40
+ symbol,
41
+ interval,
42
42
  fdOhlc: d.fdOhlc,
43
43
  fdParam: d.fdParam,
44
44
  fdData: d.fdData,
45
- opt: { keySettings, comps: '1,0,0' },
45
+ opt: { comps: '1,0,0' },
46
46
  ...ov,
47
47
  }
48
- return estimFinder(ott, st, a.fdOhlc, a.fdParam, a.fdData, a.opt)
48
+ return estimFinder(ott, a.name, a.symbol, a.interval, a.fdOhlc, a.fdParam, a.fdData, a.opt)
49
49
  }
50
50
 
51
51
 
@@ -75,19 +75,19 @@ describe('estimFinder', function() {
75
75
 
76
76
  it('未給opt.timeStart時使用2022-07-01T00:00:00', async function() {
77
77
  //測試資料恰自2022-07-01起算, 故預設timeStart通過而停在timeEnd檢核
78
- let msg = await catchMsg(call({ opt: { keySettings, comps: '1,0,0', mode: 'long' } }))
78
+ let msg = await catchMsg(call({ opt: { comps: '1,0,0', mode: 'long' } }))
79
79
  assert.match(msg, /timeEnd/, `實得: ${msg}`)
80
80
  assert.doesNotMatch(msg, /timeStartData/, `實得: ${msg}`)
81
81
  })
82
82
 
83
83
  it('資料起始晚於預設timeStart時, 錯誤訊息指出預設之timeStart', async function() {
84
84
  let dd = buildFdData(fdTmp, 'ts-late', keysNorm, { n: 10, base: Date.UTC(2023, 0, 1) })
85
- let msg = await catchMsg(estimFinder(ott, st, dd.fdOhlc, dd.fdParam, dd.fdData, { keySettings, comps: '1,0,0', mode: 'long' }))
85
+ let msg = await catchMsg(estimFinder(ott, name, symbol, interval, dd.fdOhlc, dd.fdParam, dd.fdData, { comps: '1,0,0', mode: 'long' }))
86
86
  assert.match(msg, /timeStart\[2022-07-01T00:00:00\]/, `實得: ${msg}`)
87
87
  })
88
88
 
89
89
  it('給予opt.timeStart時以其覆寫預設值', async function() {
90
- let msg = await catchMsg(call({ opt: { keySettings, comps: '1,0,0', mode: 'long', timeStart: '1990-01-01T00:00:00' } }))
90
+ let msg = await catchMsg(call({ opt: { comps: '1,0,0', mode: 'long', timeStart: '1990-01-01T00:00:00' } }))
91
91
  assert.match(msg, /timeStart\[1990-01-01T00:00:00\]/, `實得: ${msg}`)
92
92
  })
93
93
 
@@ -96,18 +96,18 @@ describe('estimFinder', function() {
96
96
  describe('timeEnd預設與覆寫', function() {
97
97
 
98
98
  it('mode為long時預設timeEnd為2025-04-08T00:00:00', async function() {
99
- let msg = await catchMsg(call({ opt: { keySettings, comps: '1,0,0', mode: 'long' } }))
99
+ let msg = await catchMsg(call({ opt: { comps: '1,0,0', mode: 'long' } }))
100
100
  assert.match(msg, /timeEnd\[2025-04-08T00:00:00\]/, `實得: ${msg}`)
101
101
  })
102
102
 
103
103
  it('mode為short時預設timeEnd為2025-10-06T00:00:00', async function() {
104
- let msg = await catchMsg(call({ opt: { keySettings, comps: '1,0,0', mode: 'short' } }))
104
+ let msg = await catchMsg(call({ opt: { comps: '1,0,0', mode: 'short' } }))
105
105
  assert.match(msg, /timeEnd\[2025-10-06T00:00:00\]/, `實得: ${msg}`)
106
106
  })
107
107
 
108
108
  it('給予opt.timeEnd時以其覆寫預設值, 不再依mode區分', async function() {
109
- let msgLong = await catchMsg(call({ opt: { keySettings, comps: '1,0,0', mode: 'long', timeEnd: '2099-01-01T00:00:00' } }))
110
- let msgShort = await catchMsg(call({ opt: { keySettings, comps: '1,0,0', mode: 'short', timeEnd: '2099-01-01T00:00:00' } }))
109
+ let msgLong = await catchMsg(call({ opt: { comps: '1,0,0', mode: 'long', timeEnd: '2099-01-01T00:00:00' } }))
110
+ let msgShort = await catchMsg(call({ opt: { comps: '1,0,0', mode: 'short', timeEnd: '2099-01-01T00:00:00' } }))
111
111
  assert.match(msgLong, /timeEnd\[2099-01-01T00:00:00\]/, `實得: ${msgLong}`)
112
112
  assert.match(msgShort, /timeEnd\[2099-01-01T00:00:00\]/, `實得: ${msgShort}`)
113
113
  })
@@ -122,7 +122,7 @@ describe('estimFinder', function() {
122
122
  //以預設timeEnd反推所用之mode: long對應2025-04-08, short對應2025-10-06
123
123
  let got = new Set()
124
124
  for (let i = 0; i < 40; i++) {
125
- let msg = await catchMsg(call({ opt: { keySettings, comps: '1,0,0' } }))
125
+ let msg = await catchMsg(call({ opt: { comps: '1,0,0' } }))
126
126
  if (msg.indexOf('timeEnd[2025-04-08T00:00:00]') >= 0) {
127
127
  got.add('long')
128
128
  }
@@ -138,7 +138,7 @@ describe('estimFinder', function() {
138
138
 
139
139
  it('給予opt.mode時不再隨機', async function() {
140
140
  for (let i = 0; i < 10; i++) {
141
- let msg = await catchMsg(call({ opt: { keySettings, comps: '1,0,0', mode: 'short' } }))
141
+ let msg = await catchMsg(call({ opt: { comps: '1,0,0', mode: 'short' } }))
142
142
  assert.match(msg, /timeEnd\[2025-10-06T00:00:00\]/, `實得: ${msg}`)
143
143
  }
144
144
  })
@@ -148,24 +148,37 @@ describe('estimFinder', function() {
148
148
  describe('comps預設與覆寫', function() {
149
149
 
150
150
  it('未給opt.comps時由預設組成隨機取一, 挑出之keys非空', async function() {
151
- //opt未給keySettings, keys非空時停在estimKeys之opt.keySettings檢核
151
+ //keys非空時通過keys檢核, 續於預設timeEnd超出資料範圍處reject
152
152
  for (let i = 0; i < 20; i++) {
153
- await assert.rejects(call({ opt: {} }), /invalid opt.keySettings/)
153
+ await assert.rejects(call({ opt: {} }), /timeEnd\[/)
154
154
  }
155
155
  })
156
156
 
157
157
  it('給予opt.comps時以其覆寫, 三段皆為0則未挑任何key', async function() {
158
- await assert.rejects(call({ opt: { keySettings, comps: '0,0,0' } }), /invalid keys/)
158
+ await assert.rejects(call({ opt: { comps: '0,0,0' } }), /invalid keys/)
159
159
  })
160
160
 
161
161
  it('給予非3段之opt.comps時throw', async function() {
162
- await assert.rejects(call({ opt: { keySettings, comps: '1,2' } }), /非預期/)
162
+ await assert.rejects(call({ opt: { comps: '1,2' } }), /非預期/)
163
163
  })
164
164
 
165
165
  })
166
166
 
167
167
  describe('輸入檢核', function() {
168
168
 
169
+ it('name非有效字串時reject', async function() {
170
+ await assert.rejects(call({ name: '' }), /invalid name/)
171
+ await assert.rejects(call({ name: null }), /invalid name/)
172
+ })
173
+
174
+ it('symbol非有效字串時reject', async function() {
175
+ await assert.rejects(call({ symbol: '' }), /invalid symbol/)
176
+ })
177
+
178
+ it('interval非有效字串時reject', async function() {
179
+ await assert.rejects(call({ interval: '' }), /invalid interval/)
180
+ })
181
+
169
182
  it('fdOhlc非有效字串時reject', async function() {
170
183
  await assert.rejects(call({ fdOhlc: '' }), /invalid fdOhlc/)
171
184
  await assert.rejects(call({ fdOhlc: null }), /invalid fdOhlc/)
@@ -6,14 +6,14 @@ import readStrategies from '../src/readStrategies.mjs'
6
6
  import genTid from '../src/genTid.mjs'
7
7
  import cont from '../src/cont.mjs'
8
8
  import ott from '../src/ott.mjs'
9
- import { buildFdTmp, buildFdData, buildSt, keySettings } from './unit-setup.mjs'
9
+ import { buildFdTmp, buildFdData, name, symbol, interval } from './unit-setup.mjs'
10
10
 
11
11
 
12
12
  //規格來源: src/estimKeys.mjs
13
- // estimKeys(ott, st, fdOhlc, fdParam, timeStart, timeEnd, mode, keys, fdData, opt):
13
+ // estimKeys(ott, name, symbol, interval, fdOhlc, fdParam, timeStart, timeEnd, mode, keys, fdData, opt):
14
14
  // 設計變數為[止損, 止盈, ...各key門檻], 止損止盈由opt.thsSl與opt.thsTp之百分比清單除以100離散取值
15
15
  // 各key門檻由rang(-2,2,40)之41點各自平移+10與-10為82點, 解值>0代表'>'條件、<0代表'<'條件, 還原時扣除平移值
16
- // 以omlPSO求解, 各次以runStrategy回測並經calcFitness計算適應值
16
+ // 以opt.methodOml指定之演算法求解(預設PSO), 各次以runStrategy回測並經calcFitness計算適應值
17
17
  // 滿足thNumTrade、thRWin與thREquivalentCumuProfitOrLossFinalNormYear三門檻者, 以genStrategyFileName存入fdData
18
18
  // 同tkid(tid+級距)已有策略時, 僅於等效年化盈虧更佳時抽換並刪除較差者, 故同tkid恆僅一檔
19
19
  // fdData不存在時自動建立
@@ -22,12 +22,8 @@ import { buildFdTmp, buildFdData, buildSt, keySettings } from './unit-setup.mjs'
22
22
  let fdTmp = buildFdTmp('estimKeys')
23
23
 
24
24
 
25
- let st = buildSt()
26
-
27
-
28
25
  //optBase: 三門檻皆放寬, 使求解過程之可用參數組必然寫出策略檔
29
26
  let optBase = {
30
- keySettings,
31
27
  thsTp: [3],
32
28
  thsSl: [2],
33
29
  thNumTrade: 1,
@@ -51,7 +47,7 @@ describe('estimKeys', function() {
51
47
  before(async function() {
52
48
  this.timeout(120000)
53
49
  d = buildFdData(fdTmp, 'solve', keys, { n: 20 })
54
- m = await estimKeys(ott, st, d.fdOhlc, d.fdParam, d.timeStart, d.timeEnd, 'long', keys, d.fdData, optBase)
50
+ m = await estimKeys(ott, name, symbol, interval, d.fdOhlc, d.fdParam, d.timeStart, d.timeEnd, 'long', keys, d.fdData, optBase)
55
51
  })
56
52
 
57
53
  it('回傳omlPSO之求解結果, 含bestSolution與停止資訊', function() {
@@ -167,7 +163,7 @@ describe('estimKeys', function() {
167
163
  let keys = ['btc_4hr_ma_1day']
168
164
  let d = buildFdData(fdTmp, 'nosave', keys, { n: 20 })
169
165
 
170
- await estimKeys(ott, st, d.fdOhlc, d.fdParam, d.timeStart, d.timeEnd, 'long', keys, d.fdData, {
166
+ await estimKeys(ott, name, symbol, interval, d.fdOhlc, d.fdParam, d.timeStart, d.timeEnd, 'long', keys, d.fdData, {
171
167
  ...optBase,
172
168
  thNumTrade: 1e9, //資料僅20根K線, 無從達成
173
169
  })
@@ -200,7 +196,7 @@ describe('estimKeys', function() {
200
196
  //call: 以獨立之策略資料夾執行求解
201
197
  let call = (tag, extra = {}) => {
202
198
  let fdData = path.resolve(fdTmp, 'pso', `s-${tag}`)
203
- return estimKeys(ott, st, d.fdOhlc, d.fdParam, d.timeStart, d.timeEnd, 'long', keys, fdData, {
199
+ return estimKeys(ott, name, symbol, interval, d.fdOhlc, d.fdParam, d.timeStart, d.timeEnd, 'long', keys, fdData, {
204
200
  ...optBase,
205
201
  ...optPso,
206
202
  ...extra,
@@ -295,7 +291,7 @@ describe('estimKeys', function() {
295
291
 
296
292
  it('PSO設定與estimKeys自身設定可同時給予且互不干擾', async function() {
297
293
  let fdData = path.resolve(fdTmp, 'pso', 's-mix')
298
- let m = await estimKeys(ott, st, d.fdOhlc, d.fdParam, d.timeStart, d.timeEnd, 'long', keys, fdData, {
294
+ let m = await estimKeys(ott, name, symbol, interval, d.fdOhlc, d.fdParam, d.timeStart, d.timeEnd, 'long', keys, fdData, {
299
295
  ...optBase,
300
296
  ...optPso,
301
297
  NContiguous: 5,
@@ -319,6 +315,97 @@ describe('estimKeys', function() {
319
315
 
320
316
  })
321
317
 
318
+ describe('methodOml演算法選擇', function() {
319
+
320
+ //各演算法之超參數集合不同, 以funGenerationBefore所收到之params鍵集合判別實際採用者
321
+ // 迴圈數之欄位名亦不同: RGA與DE為Ng、HS為Ni、PSO與ACO為Nl, 故一次給齊各欄位後,
322
+ // 由stopMode所標明之欄位名即可看出實際採用之演算法
323
+
324
+ let keys = ['btc_4hr_ma_1day']
325
+ let d = null
326
+
327
+ before(function() {
328
+ d = buildFdData(fdTmp, 'method', keys, { n: 20 })
329
+ })
330
+
331
+ //kpParams: 各演算法於funGenerationBefore所提供之超參數鍵(已排序)
332
+ let kpParams = {
333
+ RGA: ['LocalSearchMethod', 'ModeOutLimit', 'rgaCrossover', 'rgaElitism', 'rgaMutation', 'rgaMutationRate', 'rgaSelection'],
334
+ DE: ['LocalSearchMethod', 'ModeOutLimit', 'deCrossoverFactor', 'deF', 'deLanda', 'deMutation'],
335
+ HS: ['LocalSearchMethod', 'ModeOutLimit', 'hsHMC', 'hsHMCR', 'hsPA', 'hsPAR'],
336
+ PSO: ['LocalSearchMethod', 'ModeOutLimit', 'psoBeta', 'psoC1', 'psoC2', 'psoGamma', 'psoInertiaMin'],
337
+ ACO: ['LocalSearchMethod', 'ModeOutLimit', 'acoAlpha', 'acoExploitationRate', 'acoRo'],
338
+ }
339
+
340
+ //kpKeyIteration: 各演算法之迴圈數欄位名
341
+ let kpKeyIteration = {
342
+ RGA: 'Ng',
343
+ DE: 'Ng',
344
+ HS: 'Ni',
345
+ PSO: 'Nl',
346
+ ACO: 'Nl',
347
+ }
348
+
349
+ //optStop: 一次給齊各演算法之迴圈數與族群數欄位, 各演算法僅取用自身認得者
350
+ let optStop = {
351
+ Ng: 2,
352
+ Ni: 2,
353
+ Nl: 2,
354
+ Np: 6,
355
+ Ns: 6,
356
+ Na: 6,
357
+ UseRepeat: false,
358
+ UseImmigration: false,
359
+ }
360
+
361
+ //run: 執行求解並回傳{m, params}
362
+ let run = async (tag, extra = {}) => {
363
+ let fdData = path.resolve(fdTmp, 'method', `s-${tag}`)
364
+ let params = null
365
+ let m = await estimKeys(ott, name, symbol, interval, d.fdOhlc, d.fdParam, d.timeStart, d.timeEnd, 'long', keys, fdData, {
366
+ ...optBase,
367
+ ...optStop,
368
+ ...extra,
369
+ funGenerationBefore: (o) => {
370
+ if (params === null) {
371
+ params = o.params
372
+ }
373
+ },
374
+ })
375
+ return { m, params }
376
+ }
377
+
378
+ Object.keys(kpParams).forEach((methodOml) => {
379
+ it(`methodOml為${methodOml}時採用該演算法`, async function() {
380
+ let { m, params } = await run(methodOml, { methodOml })
381
+ assert.deepStrictEqual(Object.keys(params).sort(), kpParams[methodOml], `${methodOml}之超參數集合非預期`)
382
+ assert.match(m.stopMode, new RegExp(`^stop by ${kpKeyIteration[methodOml]}\\[2\\]$`), `${methodOml}實得: ${m.stopMode}`)
383
+ assert.strictEqual(m.stopIteration, 2)
384
+ })
385
+ })
386
+
387
+ it('未給methodOml時採PSO', async function() {
388
+ let { m, params } = await run('default')
389
+ assert.deepStrictEqual(Object.keys(params).sort(), kpParams.PSO)
390
+ assert.match(m.stopMode, /^stop by Nl\[2\]$/, `實得: ${m.stopMode}`)
391
+ })
392
+
393
+ it('methodOml非五者之一時亦採PSO', async function() {
394
+ let { m, params } = await run('unknown', { methodOml: 'XXX' })
395
+ assert.deepStrictEqual(Object.keys(params).sort(), kpParams.PSO)
396
+ assert.match(m.stopMode, /^stop by Nl\[2\]$/, `實得: ${m.stopMode}`)
397
+ })
398
+
399
+ it('各演算法之求解結果皆含bestSolution與停止資訊', async function() {
400
+ let { m } = await run('shape', { methodOml: 'DE' })
401
+ assert.ok(Number.isFinite(m.bestSolution.fitness))
402
+ assert.strictEqual(m.bestSolution.ps.length, 2 + keys.length)
403
+ assert.ok(typeof m.stopMode === 'string' && m.stopMode.length > 0)
404
+ assert.ok(m.stopExecutions > 0)
405
+ })
406
+
407
+ })
408
+
322
409
  describe('輸入檢核', function() {
323
410
 
324
411
  let keys = ['btc_4hr_ma_1day']
@@ -331,6 +418,9 @@ describe('estimKeys', function() {
331
418
  //call: 以合法引數為底, 依opt覆寫指定引數後呼叫
332
419
  let call = (ov = {}) => {
333
420
  let a = {
421
+ name,
422
+ symbol,
423
+ interval,
334
424
  fdOhlc: d.fdOhlc,
335
425
  fdParam: d.fdParam,
336
426
  timeStart: d.timeStart,
@@ -341,9 +431,22 @@ describe('estimKeys', function() {
341
431
  opt: optBase,
342
432
  ...ov,
343
433
  }
344
- return estimKeys(ott, st, a.fdOhlc, a.fdParam, a.timeStart, a.timeEnd, a.mode, a.keys, a.fdData, a.opt)
434
+ return estimKeys(ott, a.name, a.symbol, a.interval, a.fdOhlc, a.fdParam, a.timeStart, a.timeEnd, a.mode, a.keys, a.fdData, a.opt)
345
435
  }
346
436
 
437
+ it('name非有效字串時reject', async function() {
438
+ await assert.rejects(call({ name: '' }), /invalid name/)
439
+ await assert.rejects(call({ name: null }), /invalid name/)
440
+ })
441
+
442
+ it('symbol非有效字串時reject', async function() {
443
+ await assert.rejects(call({ symbol: '' }), /invalid symbol/)
444
+ })
445
+
446
+ it('interval非有效字串時reject', async function() {
447
+ await assert.rejects(call({ interval: '' }), /invalid interval/)
448
+ })
449
+
347
450
  it('fdOhlc非有效字串時reject', async function() {
348
451
  await assert.rejects(call({ fdOhlc: '' }), /invalid fdOhlc/)
349
452
  await assert.rejects(call({ fdOhlc: null }), /invalid fdOhlc/)
@@ -375,11 +478,6 @@ describe('estimKeys', function() {
375
478
  await assert.rejects(call({ fdData: '' }), /invalid fdData/)
376
479
  })
377
480
 
378
- it('opt.keySettings非有效字串時reject', async function() {
379
- await assert.rejects(call({ opt: {} }), /invalid opt.keySettings/)
380
- await assert.rejects(call({ opt: { keySettings: 123 } }), /invalid opt.keySettings/)
381
- })
382
-
383
481
  it('回測時間範圍超出資料範圍時reject', async function() {
384
482
  //w-data-tdprovide之getTimeSeriesFullByTimeRange檢核起訖時間
385
483
  await assert.rejects(call({ timeStart: '1990-01-01T00:00:00' }), /timeStartData/)
@@ -2,11 +2,11 @@ import assert from 'assert'
2
2
  import fs from 'fs'
3
3
  import estimPicks from '../src/estimPicks.mjs'
4
4
  import ott from '../src/ott.mjs'
5
- import { buildFdTmp, buildFdData, buildSt, keySettings } from './unit-setup.mjs'
5
+ import { buildFdTmp, buildFdData, name, symbol, interval } from './unit-setup.mjs'
6
6
 
7
7
 
8
8
  //規格來源: src/estimPicks.mjs
9
- // estimPicks(ott, st, fdOhlc, fdParam, timeStart, timeEnd, mode, funPickKeys, fdData, opt):
9
+ // estimPicks(ott, name, symbol, interval, fdOhlc, fdParam, timeStart, timeEnd, mode, funPickKeys, fdData, opt):
10
10
  // 由fdParam列舉全部指標key後分3類: 開頭為'index_'者為keysIndex, 含'_vbs_'者為keysVolumn, 其餘為keysNorm
11
11
  // 分類後各類依序經opt.funFilterKeysNorm、opt.funFilterKeysIndex、opt.funFilterKeysVolumn過濾(皆await)
12
12
  // 再呼叫funPickKeys({keysNorm,keysIndex,keysVolumn})取得keys, 最後交由estimKeys求解
@@ -16,9 +16,6 @@ import { buildFdTmp, buildFdData, buildSt, keySettings } from './unit-setup.mjs'
16
16
  let fdTmp = buildFdTmp('estimPicks')
17
17
 
18
18
 
19
- let st = buildSt()
20
-
21
-
22
19
  //keysParam: 涵蓋3類, index_開頭為指數類, 含_vbs_為成交量類, 其餘為一般類
23
20
  let keyNormA = 'btc_4hr_ma_1day'
24
21
  let keyNormB = 'btc_4hr_rsi'
@@ -33,6 +30,9 @@ let d = null
33
30
  //call: 以合法引數為底, 依ov覆寫指定引數後呼叫
34
31
  let call = (ov = {}) => {
35
32
  let a = {
33
+ name,
34
+ symbol,
35
+ interval,
36
36
  fdOhlc: d.fdOhlc,
37
37
  fdParam: d.fdParam,
38
38
  timeStart: d.timeStart,
@@ -42,10 +42,10 @@ let call = (ov = {}) => {
42
42
  return []
43
43
  },
44
44
  fdData: d.fdData,
45
- opt: { keySettings },
45
+ opt: {},
46
46
  ...ov,
47
47
  }
48
- return estimPicks(ott, st, a.fdOhlc, a.fdParam, a.timeStart, a.timeEnd, a.mode, a.funPickKeys, a.fdData, a.opt)
48
+ return estimPicks(ott, a.name, a.symbol, a.interval, a.fdOhlc, a.fdParam, a.timeStart, a.timeEnd, a.mode, a.funPickKeys, a.fdData, a.opt)
49
49
  }
50
50
 
51
51
 
@@ -92,10 +92,10 @@ describe('estimPicks', function() {
92
92
  it('僅開頭為index_者歸為指數類, 名稱中段含index_者仍為一般類', async function() {
93
93
  let dd = buildFdData(fdTmp, 'cls-index', ['btc_index_rsi', 'index_rsi'], { n: 10 })
94
94
  let got = null
95
- await assert.rejects(estimPicks(ott, st, dd.fdOhlc, dd.fdParam, dd.timeStart, dd.timeEnd, 'long', (o) => {
95
+ await assert.rejects(estimPicks(ott, name, symbol, interval, dd.fdOhlc, dd.fdParam, dd.timeStart, dd.timeEnd, 'long', (o) => {
96
96
  got = o
97
97
  return []
98
- }, dd.fdData, { keySettings }), /invalid keys/)
98
+ }, dd.fdData, {}), /invalid keys/)
99
99
 
100
100
  assert.deepStrictEqual(got.keysIndex, ['index_rsi'])
101
101
  assert.deepStrictEqual(got.keysNorm, ['btc_index_rsi'])
@@ -105,10 +105,10 @@ describe('estimPicks', function() {
105
105
  //判斷順序為index_開頭 → 含_vbs_ → 其餘
106
106
  let dd = buildFdData(fdTmp, 'cls-vbs', ['index_a_vbs_b', 'btc_vbs_c'], { n: 10 })
107
107
  let got = null
108
- await assert.rejects(estimPicks(ott, st, dd.fdOhlc, dd.fdParam, dd.timeStart, dd.timeEnd, 'long', (o) => {
108
+ await assert.rejects(estimPicks(ott, name, symbol, interval, dd.fdOhlc, dd.fdParam, dd.timeStart, dd.timeEnd, 'long', (o) => {
109
109
  got = o
110
110
  return []
111
- }, dd.fdData, { keySettings }), /invalid keys/)
111
+ }, dd.fdData, {}), /invalid keys/)
112
112
 
113
113
  assert.deepStrictEqual(got.keysIndex, ['index_a_vbs_b'])
114
114
  assert.deepStrictEqual(got.keysVolumn, ['btc_vbs_c'])
@@ -123,7 +123,6 @@ describe('estimPicks', function() {
123
123
  let got = null
124
124
  await assert.rejects(call({
125
125
  opt: {
126
- keySettings,
127
126
  funFilterKeysNorm: (ks) => {
128
127
  return ks.filter((v) => {
129
128
  return v === keyNormA
@@ -143,7 +142,6 @@ describe('estimPicks', function() {
143
142
  let got = null
144
143
  await assert.rejects(call({
145
144
  opt: {
146
- keySettings,
147
145
  funFilterKeysIndex: () => {
148
146
  return []
149
147
  },
@@ -166,7 +164,6 @@ describe('estimPicks', function() {
166
164
  let got = null
167
165
  await assert.rejects(call({
168
166
  opt: {
169
- keySettings,
170
167
  funFilterKeysNorm: async (ks) => {
171
168
  return ks.slice(0, 1)
172
169
  },
@@ -194,19 +191,32 @@ describe('estimPicks', function() {
194
191
  })
195
192
 
196
193
  it('funPickKeys回傳非空陣列時通過keys檢核, 續往後續檢核', async function() {
197
- //opt未給keySettings, 故停在estimKeys之opt.keySettings檢核, 代表keys已通過
194
+ //以超出資料範圍之timeEnd呼叫, 停在w-data-tdprovide之時間範圍檢核, 代表keys已通過
198
195
  await assert.rejects(call({
199
196
  funPickKeys: () => {
200
197
  return [keyNormA]
201
198
  },
202
- opt: {},
203
- }), /invalid opt.keySettings/)
199
+ timeEnd: '2099-01-01T00:00:00',
200
+ }), /timeEndData/)
204
201
  })
205
202
 
206
203
  })
207
204
 
208
205
  describe('輸入檢核', function() {
209
206
 
207
+ it('name非有效字串時reject', async function() {
208
+ await assert.rejects(call({ name: '' }), /invalid name/)
209
+ await assert.rejects(call({ name: null }), /invalid name/)
210
+ })
211
+
212
+ it('symbol非有效字串時reject', async function() {
213
+ await assert.rejects(call({ symbol: '' }), /invalid symbol/)
214
+ })
215
+
216
+ it('interval非有效字串時reject', async function() {
217
+ await assert.rejects(call({ interval: '' }), /invalid interval/)
218
+ })
219
+
210
220
  it('fdOhlc非有效字串時reject', async function() {
211
221
  await assert.rejects(call({ fdOhlc: '' }), /invalid fdOhlc/)
212
222
  })
@@ -11,21 +11,14 @@ import path from 'path'
11
11
  let buildFdTmp = (name) => path.resolve(`./test/tmp-unit-${name}`)
12
12
 
13
13
 
14
- //st: estim系列所需之設定物件, 以keySettings為鍵取用
15
- let keySettings = 'btc4hr'
16
- let buildSt = () => {
17
- return {
18
- [keySettings]: {
19
- name: 'btc',
20
- symbol: 'BTCUSDT',
21
- interval: '4hr',
22
- },
23
- }
24
- }
14
+ //name, symbol, interval: estim系列前置之幣種設定
15
+ let name = 'btc'
16
+ let symbol = 'BTCUSDT'
17
+ let interval = '4hr'
25
18
 
26
19
 
27
20
  //keyOhlc: 依estimKeys內`${name}_price_${interval}`規則組出之K線序列key
28
- let keyOhlc = 'btc_price_4hr'
21
+ let keyOhlc = `${name}_price_${interval}`
29
22
 
30
23
 
31
24
  //tBase, tStep: 基準起始時間2020-01-01T00:00:00, 每根K線間隔4小時
@@ -109,8 +102,9 @@ let buildFdData = (fdTmp, fd, keysParam, opt = {}) => {
109
102
 
110
103
  export {
111
104
  buildFdTmp,
112
- keySettings,
113
- buildSt,
105
+ name,
106
+ symbol,
107
+ interval,
114
108
  keyOhlc,
115
109
  genTimes,
116
110
  genArrOhlc,