w-trade-solve 1.0.5 → 2.0.2

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.
@@ -4,6 +4,7 @@ import path from 'path'
4
4
  import readStrategies from '../src/readStrategies.mjs'
5
5
  import genStrategyFileName from '../src/genStrategyFileName.mjs'
6
6
  import genTid from '../src/genTid.mjs'
7
+ import calcLevelNumTrade from '../src/calcLevelNumTrade.mjs'
7
8
  import cont from '../src/cont.mjs'
8
9
  import { buildFdTmp } from './unit-setup.mjs'
9
10
 
@@ -12,12 +13,15 @@ import { buildFdTmp } from './unit-setup.mjs'
12
13
  // readStrategies(fd, opt): 列舉fd第1層之檔案並解析策略資訊
13
14
  // fd非資料夾時回傳[], 資料夾內無檔案時回傳[]
14
15
  // 僅列檔案, 資料夾不列入(fsTreeFolder預設levelLimit=1, 故子資料夾內檔案亦不列入)
15
- // 主檔名以wsemi replace剔除全部'.json'後, 依cont.dlmPkgs切段, 依序為tid、levelNumTrade與
16
- // rEquivalentCumuProfitOrLossFinalNormYear, 三者皆為字串, 缺段者為空字串
17
- // 切不出tid(非wsemi isestr判定之有效字串)者略過
16
+ // opt.fromContent決定解析來源, 可選'filename'|'content'|'auto'(預設):
17
+ // 'filename': 主檔名以wsemi replace剔除全部'.json'後依cont.dlmPkgs切段, 依序為tid、levelNumTrade與
18
+ // rEquivalentCumuProfitOrLossFinalNormYear, 三者皆為字串, 缺段者為空字串, 切不出tid者略過
19
+ // 'content': 讀檔內json之tid、tkid、levelNumTrade與summary.rEquivalentCumuProfitOrLossFinalNormYear,
20
+ // levelNumTrade轉為字串, 無tkid欄位時由tid與levelNumTrade組出, 非合法json或無tid者略過
21
+ // 'auto': 檔名符合`^(long|short)_\d+_[0-9a-f]{16}\.json$`者走'content', 其餘走'filename'
18
22
  // tid再依cont.dlmSeps切段, 首段為mode, 其餘為ps
19
23
  // tkid為`${tid}:${levelNumTrade}`
20
- // opt.readContent為true時以data提供檔案內容, 內容非合法json時data為null
24
+ // opt.readContent為true時以data提供檔案內容
21
25
 
22
26
 
23
27
  let fdTmp = buildFdTmp('readStrategies')
@@ -34,14 +38,35 @@ let buildFd = (fd) => {
34
38
  }
35
39
 
36
40
 
37
- //writeStrategy: 以genStrategyFileName之規則寫出策略檔
38
- let writeStrategy = (fdData, keys, numTrade, rEq, content = {}) => {
39
- let fn = genStrategyFileName('btc', '4hr', 'long', keys, settings, {
41
+ //writeStrategyNew: 以genStrategyFileName之雜湊檔名規則寫出策略檔, 內容含識別欄位(同estimKeys所寫出者)
42
+ let writeStrategyNew = (fdData, keys, numTrade, rEq, extra = {}) => {
43
+ let summary = {
40
44
  numTrade,
41
45
  rEquivalentCumuProfitOrLossFinalNormYear: rEq,
42
- })
46
+ }
47
+ let fn = genStrategyFileName('btc', '4hr', 'long', keys, settings, summary)
48
+ let tid = genTid('btc', '4hr', 'long', keys)
49
+ let levelNumTrade = calcLevelNumTrade(numTrade)
50
+ let content = {
51
+ tid,
52
+ tkid: `${tid}:${levelNumTrade}`,
53
+ levelNumTrade,
54
+ mode: 'long',
55
+ summary,
56
+ ...extra,
57
+ }
58
+ fs.writeFileSync(path.resolve(fdData, fn), JSON.stringify(content), 'utf8')
59
+ return { fn, tid, levelNumTrade, content }
60
+ }
61
+
62
+
63
+ //writeStrategyLegacy: 以舊式`${tid} ⊙ ${levelNumTrade} ⊙ ${rEq}.json`檔名規則寫出策略檔
64
+ let writeStrategyLegacy = (fdData, keys, numTrade, rEq, content = {}) => {
65
+ let tid = genTid('btc', '4hr', 'long', keys)
66
+ let levelNumTrade = calcLevelNumTrade(numTrade)
67
+ let fn = `${tid}${cont.dlmPkgs}${levelNumTrade}${cont.dlmPkgs}${rEq}.json`
43
68
  fs.writeFileSync(path.resolve(fdData, fn), JSON.stringify(content), 'utf8')
44
- return fn
69
+ return { fn, tid, levelNumTrade }
45
70
  }
46
71
 
47
72
 
@@ -51,17 +76,15 @@ describe('readStrategies', function() {
51
76
  fs.rmSync(fdTmp, { recursive: true, force: true })
52
77
  })
53
78
 
54
- describe('檔名解析', function() {
79
+ describe('新格式(雜湊檔名, auto走content)', function() {
55
80
 
56
- it('讀出genStrategyFileName所寫之策略檔, 各欄位可由檔名反解', function() {
57
- let fd = buildFd('basic')
81
+ it('讀出genStrategyFileName所寫之策略檔, 各欄位取自檔內json', function() {
82
+ let fd = buildFd('new-basic')
58
83
  let keys = ['btc_price_4hr_ma_1day']
59
- let fn = writeStrategy(fd, keys, 30, '31.25%')
84
+ let { fn, tid } = writeStrategyNew(fd, keys, 30, '31.25%')
60
85
 
61
86
  let ss = readStrategies(fd)
62
87
  assert.strictEqual(ss.length, 1)
63
-
64
- let tid = genTid('btc', '4hr', 'long', keys)
65
88
  assert.strictEqual(ss[0].name, fn)
66
89
  assert.strictEqual(ss[0].tid, tid)
67
90
  assert.strictEqual(ss[0].levelNumTrade, '12')
@@ -69,44 +92,99 @@ describe('readStrategies', function() {
69
92
  assert.strictEqual(ss[0].rEquivalentCumuProfitOrLossFinalNormYear, '31.25%')
70
93
  })
71
94
 
72
- it('levelNumTrade與等效年化盈虧皆為字串, 未轉型', function() {
73
- let fd = buildFd('str')
74
- writeStrategy(fd, ['aa'], 30, '31.25%')
95
+ it('levelNumTrade與等效年化盈虧皆轉為與檔名反解一致之字串', function() {
96
+ let fd = buildFd('new-str')
97
+ writeStrategyNew(fd, ['aa'], 30, '31.25%')
75
98
 
76
99
  let ss = readStrategies(fd)
77
100
  assert.strictEqual(typeof ss[0].levelNumTrade, 'string')
78
101
  assert.strictEqual(typeof ss[0].rEquivalentCumuProfitOrLossFinalNormYear, 'string')
79
102
  })
80
103
 
81
- it('tid再依dlmSeps拆為mode與ps', function() {
82
- let fd = buildFd('modeps')
104
+ it('tid依dlmSeps拆為mode與ps', function() {
105
+ let fd = buildFd('new-modeps')
83
106
  //keys經genTid縮寫後為p_ma_1day與i_rsi_4hr
84
- writeStrategy(fd, ['btc_price_4hr_ma_1day', 'btc_index_rsi_4hr'], 30, '10%')
107
+ writeStrategyNew(fd, ['btc_price_4hr_ma_1day', 'btc_index_rsi_4hr'], 30, '10%')
85
108
 
86
109
  let ss = readStrategies(fd)
87
110
  assert.strictEqual(ss[0].mode, 'long')
88
111
  assert.deepStrictEqual(ss[0].ps, ['p_ma_1day', 'i_rsi_4hr'])
89
112
  })
90
113
 
91
- it('單一key時ps僅一元素', function() {
92
- let fd = buildFd('ps1')
93
- writeStrategy(fd, ['btc_price_4hr_ma_1day'], 30, '10%')
114
+ it('內容無tkid欄位時由tid與levelNumTrade組出', function() {
115
+ let fd = buildFd('new-notkid')
116
+ let keys = ['aa']
117
+ let summary = { numTrade: 30, rEquivalentCumuProfitOrLossFinalNormYear: '10%' }
118
+ let fn = genStrategyFileName('btc', '4hr', 'long', keys, settings, summary)
119
+ let tid = genTid('btc', '4hr', 'long', keys)
120
+ fs.writeFileSync(path.resolve(fd, fn), JSON.stringify({ tid, levelNumTrade: 12, summary }), 'utf8')
121
+
122
+ let ss = readStrategies(fd)
123
+ assert.strictEqual(ss[0].tkid, `${tid}:12`)
124
+ })
125
+
126
+ it('同tid不同級距之策略檔各自為一筆, tkid不同', function() {
127
+ let fd = buildFd('new-levels')
128
+ let keys = ['aa']
129
+ writeStrategyNew(fd, keys, 20, '10%') //級距11
130
+ writeStrategyNew(fd, keys, 30, '20%') //級距12
131
+
132
+ let ss = readStrategies(fd)
133
+ assert.strictEqual(ss.length, 2)
134
+
135
+ let tid = genTid('btc', '4hr', 'long', keys)
136
+ let tkids = ss.map((v) => {
137
+ return v.tkid
138
+ }).sort()
139
+ assert.deepStrictEqual(tkids, [`${tid}:11`, `${tid}:12`].sort())
140
+ })
141
+
142
+ it('雜湊檔名但內容非合法json者略過', function() {
143
+ let fd = buildFd('new-badjson')
144
+ writeStrategyNew(fd, ['aa'], 30, '10%')
145
+ fs.writeFileSync(path.resolve(fd, 'long_12_0123456789abcdef.json'), 'not-a-json', 'utf8')
146
+
147
+ let ss = readStrategies(fd)
148
+ assert.strictEqual(ss.length, 1)
149
+ })
150
+
151
+ it('雜湊檔名但內容無tid欄位者略過', function() {
152
+ let fd = buildFd('new-notid')
153
+ fs.writeFileSync(path.resolve(fd, 'long_12_0123456789abcdef.json'), '{}', 'utf8')
94
154
 
95
155
  let ss = readStrategies(fd)
96
- assert.deepStrictEqual(ss[0].ps, ['p_ma_1day'])
156
+ assert.deepStrictEqual(ss, [])
157
+ })
158
+
159
+ })
160
+
161
+ describe('舊格式(dlmPkgs檔名, auto走filename)', function() {
162
+
163
+ it('讀出舊式檔名之策略檔, 各欄位可由檔名反解', function() {
164
+ let fd = buildFd('legacy-basic')
165
+ let keys = ['btc_price_4hr_ma_1day']
166
+ let { fn, tid } = writeStrategyLegacy(fd, keys, 30, '31.25%')
167
+
168
+ let ss = readStrategies(fd)
169
+ assert.strictEqual(ss.length, 1)
170
+ assert.strictEqual(ss[0].name, fn)
171
+ assert.strictEqual(ss[0].tid, tid)
172
+ assert.strictEqual(ss[0].levelNumTrade, '12')
173
+ assert.strictEqual(ss[0].tkid, `${tid}:12`)
174
+ assert.strictEqual(ss[0].rEquivalentCumuProfitOrLossFinalNormYear, '31.25%')
97
175
  })
98
176
 
99
177
  it('等效年化盈虧為負值時亦可正確反解', function() {
100
- let fd = buildFd('neg')
101
- writeStrategy(fd, ['aa'], 30, '-71.17%')
178
+ let fd = buildFd('legacy-neg')
179
+ writeStrategyLegacy(fd, ['aa'], 30, '-71.17%')
102
180
 
103
181
  let ss = readStrategies(fd)
104
182
  assert.strictEqual(ss[0].rEquivalentCumuProfitOrLossFinalNormYear, '-71.17%')
105
183
  })
106
184
 
107
185
  it('path為絕對路徑, 即使fd以相對路徑給予', function() {
108
- let fd = buildFd('abs')
109
- let fn = writeStrategy(fd, ['aa'], 30, '10%')
186
+ let fd = buildFd('legacy-abs')
187
+ let { fn } = writeStrategyLegacy(fd, ['aa'], 30, '10%')
110
188
 
111
189
  let fdRel = path.relative(process.cwd(), fd)
112
190
  let ss = readStrategies(fdRel)
@@ -114,32 +192,82 @@ describe('readStrategies', function() {
114
192
  assert.ok(path.isAbsolute(ss[0].path))
115
193
  })
116
194
 
117
- it('同tid不同級距之策略檔各自為一筆, tkid不同', function() {
118
- let fd = buildFd('levels')
119
- let keys = ['aa']
120
- writeStrategy(fd, keys, 20, '10%') //級距11
121
- writeStrategy(fd, keys, 30, '20%') //級距12
195
+ it('與舊檔名規則可往返: 由讀回之欄位可重組原檔名', function() {
196
+ let fd = buildFd('legacy-roundtrip')
197
+ let keys = ['btc_price_4hr_ma_1day', 'btc_index_rsi_4hr']
198
+ let { fn } = writeStrategyLegacy(fd, keys, 300, '8.5%')
199
+
200
+ let ss = readStrategies(fd)
201
+ let s = ss[0]
202
+ let fnRebuild = `${s.tid}${cont.dlmPkgs}${s.levelNumTrade}${cont.dlmPkgs}${s.rEquivalentCumuProfitOrLossFinalNormYear}.json`
203
+ assert.strictEqual(fnRebuild, fn)
204
+ })
205
+
206
+ })
207
+
208
+ describe('新舊格式混存', function() {
209
+
210
+ it('auto模式下新舊格式皆正確列舉', function() {
211
+ let fd = buildFd('mixed')
212
+ let { tid: tidNew } = writeStrategyNew(fd, ['btc_price_4hr_ma_1day'], 30, '10%')
213
+ let { tid: tidLegacy } = writeStrategyLegacy(fd, ['btc_index_rsi_4hr'], 20, '20%')
122
214
 
123
215
  let ss = readStrategies(fd)
124
216
  assert.strictEqual(ss.length, 2)
125
217
 
126
- let tid = genTid('btc', '4hr', 'long', keys)
127
218
  let tkids = ss.map((v) => {
128
219
  return v.tkid
129
220
  }).sort()
130
- assert.deepStrictEqual(tkids, [`${tid}:11`, `${tid}:12`].sort())
131
- assert.strictEqual(ss[0].tid, ss[1].tid)
221
+ assert.deepStrictEqual(tkids, [`${tidNew}:12`, `${tidLegacy}:11`].sort())
132
222
  })
133
223
 
134
- it('與genStrategyFileName可往返: 由讀回之欄位可重組原檔名', function() {
135
- let fd = buildFd('roundtrip')
136
- let keys = ['btc_price_4hr_ma_1day', 'btc_index_rsi_4hr']
137
- let fn = writeStrategy(fd, keys, 300, '8.5%')
224
+ })
138
225
 
139
- let ss = readStrategies(fd)
140
- let s = ss[0]
141
- let fnRebuild = `${s.tid}${cont.dlmPkgs}${s.levelNumTrade}${cont.dlmPkgs}${s.rEquivalentCumuProfitOrLossFinalNormYear}.json`
142
- assert.strictEqual(fnRebuild, fn)
226
+ describe('fromContent選項', function() {
227
+
228
+ it("fromContent為'filename'時雜湊檔名不讀內容, 整個主檔名成為tid", function() {
229
+ let fd = buildFd('fc-filename')
230
+ let { fn } = writeStrategyNew(fd, ['aa'], 30, '10%')
231
+
232
+ let ss = readStrategies(fd, { fromContent: 'filename' })
233
+ assert.strictEqual(ss.length, 1)
234
+ //雜湊檔名內無dlmPkgs, 故主檔名整段為tid, 其餘欄位為空字串
235
+ assert.strictEqual(ss[0].tid, fn.slice(0, -5))
236
+ assert.strictEqual(ss[0].levelNumTrade, '')
237
+ })
238
+
239
+ it("fromContent為'content'時舊式檔名亦讀內容, 內容具tid欄位者以內容為準", function() {
240
+ let fd = buildFd('fc-content')
241
+ let keys = ['btc_price_4hr_ma_1day']
242
+ let tid = genTid('btc', '4hr', 'long', keys)
243
+ writeStrategyLegacy(fd, keys, 30, '10%', {
244
+ tid,
245
+ tkid: `${tid}:12`,
246
+ levelNumTrade: 12,
247
+ summary: { numTrade: 30, rEquivalentCumuProfitOrLossFinalNormYear: '10%' },
248
+ })
249
+
250
+ let ss = readStrategies(fd, { fromContent: 'content' })
251
+ assert.strictEqual(ss.length, 1)
252
+ assert.strictEqual(ss[0].tid, tid)
253
+ assert.strictEqual(ss[0].tkid, `${tid}:12`)
254
+ })
255
+
256
+ it("fromContent為'content'時內容無tid欄位者略過", function() {
257
+ let fd = buildFd('fc-content-skip')
258
+ writeStrategyLegacy(fd, ['aa'], 30, '10%', {}) //舊檔內容無識別欄位
259
+
260
+ let ss = readStrategies(fd, { fromContent: 'content' })
261
+ assert.deepStrictEqual(ss, [])
262
+ })
263
+
264
+ it("fromContent非三值之一時採'auto'", function() {
265
+ let fd = buildFd('fc-fallback')
266
+ let { tid } = writeStrategyNew(fd, ['aa'], 30, '10%')
267
+
268
+ let ss = readStrategies(fd, { fromContent: 'xxx' })
269
+ assert.strictEqual(ss.length, 1)
270
+ assert.strictEqual(ss[0].tid, tid)
143
271
  })
144
272
 
145
273
  })
@@ -179,7 +307,7 @@ describe('readStrategies', function() {
179
307
 
180
308
  it('主檔名剔除.json後為空字串者被略過', function() {
181
309
  let fd = buildFd('emptyname')
182
- writeStrategy(fd, ['aa'], 30, '10%')
310
+ writeStrategyNew(fd, ['aa'], 30, '10%')
183
311
  fs.writeFileSync(path.resolve(fd, '.json'), '{}', 'utf8')
184
312
 
185
313
  let ss = readStrategies(fd)
@@ -193,10 +321,10 @@ describe('readStrategies', function() {
193
321
 
194
322
  it('僅列第1層, 子資料夾與其內檔案皆不列入', function() {
195
323
  let fd = buildFd('nested')
196
- writeStrategy(fd, ['aa'], 30, '10%')
324
+ writeStrategyNew(fd, ['aa'], 30, '10%')
197
325
  let fdSub = path.resolve(fd, 'sub')
198
326
  fs.mkdirSync(fdSub, { recursive: true })
199
- writeStrategy(fdSub, ['bb'], 30, '10%')
327
+ writeStrategyNew(fdSub, ['bb'], 30, '10%')
200
328
 
201
329
  let ss = readStrategies(fd)
202
330
  assert.strictEqual(ss.length, 1)
@@ -230,22 +358,30 @@ describe('readStrategies', function() {
230
358
 
231
359
  it('預設不讀取內容, 無data欄位', function() {
232
360
  let fd = buildFd('nodata')
233
- writeStrategy(fd, ['aa'], 30, '10%', { tid: 'x', fitness: 1.5 })
361
+ writeStrategyNew(fd, ['aa'], 30, '10%', { fitness: 1.5 })
234
362
 
235
363
  let ss = readStrategies(fd)
236
364
  assert.strictEqual(ss[0].data, undefined)
237
365
  })
238
366
 
239
- it('opt.readContent為true時以data提供檔案內容', function() {
240
- let fd = buildFd('data')
367
+ it('opt.readContent為true時以data提供檔案內容(新格式)', function() {
368
+ let fd = buildFd('data-new')
369
+ let { content } = writeStrategyNew(fd, ['aa'], 30, '10%', { fitness: 1.5 })
370
+
371
+ let ss = readStrategies(fd, { readContent: true })
372
+ assert.deepStrictEqual(ss[0].data, content)
373
+ })
374
+
375
+ it('opt.readContent為true時以data提供檔案內容(舊格式)', function() {
376
+ let fd = buildFd('data-legacy')
241
377
  let content = { tid: 'x', fitness: 1.5, summary: { numTrade: 30 } }
242
- writeStrategy(fd, ['aa'], 30, '10%', content)
378
+ writeStrategyLegacy(fd, ['aa'], 30, '10%', content)
243
379
 
244
380
  let ss = readStrategies(fd, { readContent: true })
245
381
  assert.deepStrictEqual(ss[0].data, content)
246
382
  })
247
383
 
248
- it('內容非合法json時data為null', function() {
384
+ it('舊式檔名內容非合法json時data為null', function() {
249
385
  let fd = buildFd('badjson')
250
386
  fs.writeFileSync(path.resolve(fd, 'bad.json'), 'not-a-json', 'utf8')
251
387