w-trade-backtest 1.0.3 → 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.
@@ -0,0 +1,224 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+
5
+ <meta charset="utf-8">
6
+ <title>runStrategiesAndBacktest.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#calcOrders">calcOrders</a></li><li><a href="global.html#calcOrdersRatio">calcOrdersRatio</a></li><li><a href="global.html#calcOrdersSummary">calcOrdersSummary</a></li><li><a href="global.html#calcOrdersSummarySimple">calcOrdersSummarySimple</a></li><li><a href="global.html#calcSummary">calcSummary</a></li><li><a href="global.html#closeAndSummaryOrders">closeAndSummaryOrders</a></li><li><a href="global.html#genReport">genReport</a></li><li><a href="global.html#genReportCore">genReportCore</a></li><li><a href="global.html#runStrategies">runStrategies</a></li><li><a href="global.html#runStrategiesAndBacktest">runStrategiesAndBacktest</a></li><li><a href="global.html#runStrategy">runStrategy</a></li></ul>
33
+
34
+ </nav>
35
+
36
+ <div id="main">
37
+
38
+ <h1 class="page-title">runStrategiesAndBacktest.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 get from 'lodash-es/get.js'
50
+ import size from 'lodash-es/size.js'
51
+ import isearr from 'wsemi/src/isearr.mjs'
52
+ import fsIsFolder from 'wsemi/src/fsIsFolder.mjs'
53
+ import WDataTdprovide from 'w-data-tdprovide/src/WDataTdprovide.mjs'
54
+ import runStrategies from './runStrategies.mjs'
55
+ import genReportCore from './genReportCore.mjs'
56
+ import writeJson from './writeJson.mjs'
57
+
58
+
59
+ /**
60
+ * 執行多策略回測並輸出訂單、摘要與報告檔案
61
+ *
62
+ * 以w-data-tdprovide讀取fdOhlc與fdParam資料夾數據,建立timeStart至timeEnd範圍之序列查詢函數後執行runStrategies
63
+ * 合併結果命名為'全部策略',並輸出orders.json、summary.json、report.html至fdTest資料夾
64
+ * fdOhlc或fdParam非資料夾、strategies非有效陣列時throw
65
+ *
66
+ * Unit Test: {@link https://github.com/yuda-lyu/w-trade-backtest/blob/master/test/unit-WTradeBacktest.test.mjs Github}
67
+ * @function
68
+ * @param {Function} ott 輸入時區時間函數,傳入時間字串回傳dayjs時間物件(可用src/ott.mjs或自行以dayjs包裝)
69
+ * @param {String} fdOhlc 輸入儲存K線(ohlc)序列資料夾字串,各序列以`${key}.json`儲存
70
+ * @param {String} fdParam 輸入儲存指標參數序列資料夾字串
71
+ * @param {Array} strategies 輸入策略陣列,各元素需含sid與runStrategy之strategy欄位
72
+ * @param {String} timeStart 輸入回測起始秒時間字串
73
+ * @param {String} timeEnd 輸入回測結束秒時間字串
74
+ * @param {String} fdTest 輸入輸出結果資料夾字串
75
+ * @param {Object} [opt={}] 輸入設定物件,預設{}
76
+ * @param {Boolean} [opt.writeOrders=true] 輸入是否輸出orders.json布林值,預設true
77
+ * @param {Boolean} [opt.writeSummary=true] 輸入是否輸出summary.json布林值,預設true
78
+ * @param {Boolean} [opt.writeReport=true] 輸入是否輸出report.html布林值,預設true
79
+ * @returns {Promise} 回傳Promise,resolve為{name,orders,summary}物件,另於fdTest輸出結果檔案
80
+ * @example
81
+ *
82
+ * import fs from 'fs'
83
+ *
84
+ * let arrOhlc = [
85
+ * { time: '2020-01-01T00:00:00', Open: 100, High: 101, Low: 99, Close: 100 },
86
+ * { time: '2020-01-01T04:00:00', Open: 100, High: 106, Low: 100, Close: 105 },
87
+ * { time: '2020-01-01T08:00:00', Open: 105, High: 107, Low: 102, Close: 103 },
88
+ * { time: '2020-01-01T12:00:00', Open: 103, High: 104, Low: 94, Close: 95 },
89
+ * { time: '2020-01-01T16:00:00', Open: 95, High: 98, Low: 92, Close: 93 },
90
+ * { time: '2020-01-01T20:00:00', Open: 93, High: 99, Low: 95, Close: 97 },
91
+ * ]
92
+ *
93
+ * let arrSig = [
94
+ * { time: '2020-01-01T00:00:00', param: 1 },
95
+ * { time: '2020-01-01T04:00:00', param: 0 },
96
+ * { time: '2020-01-01T08:00:00', param: 1 },
97
+ * { time: '2020-01-01T12:00:00', param: 0 },
98
+ * { time: '2020-01-01T16:00:00', param: 0 },
99
+ * { time: '2020-01-01T20:00:00', param: 0 },
100
+ * ]
101
+ *
102
+ * //建立數據資料夾, fdOhlc放K線序列, fdParam放指標參數序列
103
+ * fs.mkdirSync('./data-ohlc', { recursive: true })
104
+ * fs.mkdirSync('./data-param', { recursive: true })
105
+ * fs.writeFileSync('./data-ohlc/btc.json', JSON.stringify(arrOhlc), 'utf8')
106
+ * fs.writeFileSync('./data-param/sig.json', JSON.stringify(arrSig), 'utf8')
107
+ *
108
+ * let settings = { uIni: 1000, uTrade: 100, rTakeProfit: 0.05, rStopLoss: 0.03, rFee: 0.0005 }
109
+ * let strategies = [
110
+ * { sid: 's1', mode: 'long', keyOhlc: 'btc', conds: [{ key: 'sig', sym: '>', th: 0.5, opr: 'and' }], settings },
111
+ * { sid: 's2', mode: 'short', keyOhlc: 'btc', conds: [{ key: 'sig', sym: '&lt;', th: 0.5, opr: 'and' }], settings },
112
+ * ]
113
+ *
114
+ * let r = await runStrategiesAndBacktest(ott, './data-ohlc', './data-param', strategies, '2020-01-01T00:00:00', '2020-01-01T20:00:00', './result')
115
+ * console.log(r.name, r.summary.uIni, r.summary.numTrade, r.summary.rWin)
116
+ * // => 全部策略 2000 6 40.00%
117
+ * console.log(fs.readdirSync('./result'))
118
+ * // => [ 'orders.json', 'report.html', 'summary.json' ]
119
+ *
120
+ */
121
+ let runStrategiesAndBacktest = async (ott, fdOhlc, fdParam, strategies, timeStart, timeEnd, fdTest, opt = {}) => {
122
+
123
+ if (!fsIsFolder(fdOhlc)) {
124
+ throw new Error(`fdOhlc[${fdOhlc}] is not a forder`)
125
+ }
126
+ if (!fsIsFolder(fdParam)) {
127
+ throw new Error(`fdParam[${fdParam}] is not a forder`)
128
+ }
129
+ //check
130
+ if (!isearr(strategies)) {
131
+ throw new Error(`invalid strategies`)
132
+ }
133
+
134
+ let writeOrders = get(opt, 'writeOrders', true)
135
+ let writeSummary = get(opt, 'writeSummary', true)
136
+ let writeReport = get(opt, 'writeReport', true)
137
+
138
+ //wdp
139
+ let wdp = WDataTdprovide(fdOhlc, fdParam)
140
+
141
+ //funGetSeries
142
+ let funGetSeries = await wdp.buildGetTimeSeriesByTimeRange(timeStart, timeEnd)
143
+
144
+ //runStrategies
145
+ let r = await runStrategies(ott, strategies, funGetSeries, {})
146
+ r.name = `全部策略`
147
+
148
+ if (writeOrders) {
149
+
150
+ //fnOrders
151
+ let fnOrders = `orders.json`
152
+
153
+ //fpOrders
154
+ let fpOrders = path.resolve(fdTest, fnOrders)
155
+
156
+ //writeJson
157
+ writeJson(fpOrders, r.orders, { structured: true })
158
+
159
+ }
160
+
161
+ if (writeSummary) {
162
+
163
+ //fnSummary
164
+ let fnSummary = `summary.json`
165
+
166
+ //fpSummary
167
+ let fpSummary = path.resolve(fdTest, fnSummary)
168
+
169
+ //stsm
170
+ let stsm = {
171
+ name: r.name,
172
+ summary: r.summary,
173
+ }
174
+ // console.log('stsm', stsm)
175
+
176
+ //writeJson
177
+ writeJson(fpSummary, stsm, { structured: true })
178
+
179
+ console.log('summary', r.summary, size(r.orders))
180
+ }
181
+
182
+ if (writeReport) {
183
+
184
+ //fnHtml
185
+ let fnHtml = `report.html`
186
+
187
+ //fpHtml
188
+ let fpHtml = path.resolve(fdTest, fnHtml)
189
+
190
+ //genReport
191
+ genReportCore(r, fpHtml)
192
+
193
+ }
194
+
195
+ return r
196
+ }
197
+
198
+
199
+ export default runStrategiesAndBacktest
200
+ </code></pre>
201
+ </article>
202
+ </section>
203
+
204
+
205
+
206
+
207
+
208
+
209
+ </div>
210
+
211
+ <br class="clear">
212
+
213
+ <footer>
214
+ Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Mon Aug 03 2026 10:26:54 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
215
+ </footer>
216
+
217
+ <script>prettyPrint();</script>
218
+ <script src="scripts/polyfill.js"></script>
219
+ <script src="scripts/linenumber.js"></script>
220
+
221
+
222
+
223
+ </body>
224
+ </html>
@@ -29,7 +29,7 @@
29
29
  <nav >
30
30
 
31
31
 
32
- <h2><a href="index.html">Home</a></h2><h3>Global</h3><ul><li><a href="global.html#calcOrders">calcOrders</a></li><li><a href="global.html#calcOrdersRatio">calcOrdersRatio</a></li><li><a href="global.html#calcOrdersSummary">calcOrdersSummary</a></li><li><a href="global.html#calcOrdersSummarySimple">calcOrdersSummarySimple</a></li><li><a href="global.html#calcSummary">calcSummary</a></li><li><a href="global.html#closeAndSummaryOrders">closeAndSummaryOrders</a></li><li><a href="global.html#genReport">genReport</a></li><li><a href="global.html#genReportCore">genReportCore</a></li><li><a href="global.html#runStrategies">runStrategies</a></li><li><a href="global.html#runStrategy">runStrategy</a></li></ul>
32
+ <h2><a href="index.html">Home</a></h2><h3>Global</h3><ul><li><a href="global.html#calcOrders">calcOrders</a></li><li><a href="global.html#calcOrdersRatio">calcOrdersRatio</a></li><li><a href="global.html#calcOrdersSummary">calcOrdersSummary</a></li><li><a href="global.html#calcOrdersSummarySimple">calcOrdersSummarySimple</a></li><li><a href="global.html#calcSummary">calcSummary</a></li><li><a href="global.html#closeAndSummaryOrders">closeAndSummaryOrders</a></li><li><a href="global.html#genReport">genReport</a></li><li><a href="global.html#genReportCore">genReportCore</a></li><li><a href="global.html#runStrategies">runStrategies</a></li><li><a href="global.html#runStrategiesAndBacktest">runStrategiesAndBacktest</a></li><li><a href="global.html#runStrategy">runStrategy</a></li></ul>
33
33
 
34
34
  </nav>
35
35
 
@@ -442,7 +442,7 @@ export default runStrategy
442
442
  <br class="clear">
443
443
 
444
444
  <footer>
445
- Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Sun Aug 02 2026 20:09:43 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
445
+ Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Mon Aug 03 2026 10:26:54 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
446
446
  </footer>
447
447
 
448
448
  <script>prettyPrint();</script>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "w-trade-backtest",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "main": "dist/w-trade-backtest.umd.js",
5
5
  "dependencies": {
6
6
  "dayjs": "^1.11.21",
@@ -1,5 +1,6 @@
1
1
  import runStrategy from './runStrategy.mjs'
2
2
  import runStrategies from './runStrategies.mjs'
3
+ import runStrategiesAndBacktest from './runStrategiesAndBacktest.mjs'
3
4
  import calcOrders from './calcOrders.mjs'
4
5
  import calcOrdersRatio from './calcOrdersRatio.mjs'
5
6
  import calcOrdersSummary from './calcOrdersSummary.mjs'
@@ -13,6 +14,7 @@ import genReportCore from './genReportCore.mjs'
13
14
  let r = {
14
15
  runStrategy,
15
16
  runStrategies,
17
+ runStrategiesAndBacktest,
16
18
  calcOrders,
17
19
  calcOrdersRatio,
18
20
  calcOrdersSummary,
@@ -0,0 +1,152 @@
1
+ import path from 'path'
2
+ import get from 'lodash-es/get.js'
3
+ import size from 'lodash-es/size.js'
4
+ import isearr from 'wsemi/src/isearr.mjs'
5
+ import fsIsFolder from 'wsemi/src/fsIsFolder.mjs'
6
+ import WDataTdprovide from 'w-data-tdprovide/src/WDataTdprovide.mjs'
7
+ import runStrategies from './runStrategies.mjs'
8
+ import genReportCore from './genReportCore.mjs'
9
+ import writeJson from './writeJson.mjs'
10
+
11
+
12
+ /**
13
+ * 執行多策略回測並輸出訂單、摘要與報告檔案
14
+ *
15
+ * 以w-data-tdprovide讀取fdOhlc與fdParam資料夾數據,建立timeStart至timeEnd範圍之序列查詢函數後執行runStrategies
16
+ * 合併結果命名為'全部策略',並輸出orders.json、summary.json、report.html至fdTest資料夾
17
+ * fdOhlc或fdParam非資料夾、strategies非有效陣列時throw
18
+ *
19
+ * Unit Test: {@link https://github.com/yuda-lyu/w-trade-backtest/blob/master/test/unit-WTradeBacktest.test.mjs Github}
20
+ * @function
21
+ * @param {Function} ott 輸入時區時間函數,傳入時間字串回傳dayjs時間物件(可用src/ott.mjs或自行以dayjs包裝)
22
+ * @param {String} fdOhlc 輸入儲存K線(ohlc)序列資料夾字串,各序列以`${key}.json`儲存
23
+ * @param {String} fdParam 輸入儲存指標參數序列資料夾字串
24
+ * @param {Array} strategies 輸入策略陣列,各元素需含sid與runStrategy之strategy欄位
25
+ * @param {String} timeStart 輸入回測起始秒時間字串
26
+ * @param {String} timeEnd 輸入回測結束秒時間字串
27
+ * @param {String} fdTest 輸入輸出結果資料夾字串
28
+ * @param {Object} [opt={}] 輸入設定物件,預設{}
29
+ * @param {Boolean} [opt.writeOrders=true] 輸入是否輸出orders.json布林值,預設true
30
+ * @param {Boolean} [opt.writeSummary=true] 輸入是否輸出summary.json布林值,預設true
31
+ * @param {Boolean} [opt.writeReport=true] 輸入是否輸出report.html布林值,預設true
32
+ * @returns {Promise} 回傳Promise,resolve為{name,orders,summary}物件,另於fdTest輸出結果檔案
33
+ * @example
34
+ *
35
+ * import fs from 'fs'
36
+ *
37
+ * let arrOhlc = [
38
+ * { time: '2020-01-01T00:00:00', Open: 100, High: 101, Low: 99, Close: 100 },
39
+ * { time: '2020-01-01T04:00:00', Open: 100, High: 106, Low: 100, Close: 105 },
40
+ * { time: '2020-01-01T08:00:00', Open: 105, High: 107, Low: 102, Close: 103 },
41
+ * { time: '2020-01-01T12:00:00', Open: 103, High: 104, Low: 94, Close: 95 },
42
+ * { time: '2020-01-01T16:00:00', Open: 95, High: 98, Low: 92, Close: 93 },
43
+ * { time: '2020-01-01T20:00:00', Open: 93, High: 99, Low: 95, Close: 97 },
44
+ * ]
45
+ *
46
+ * let arrSig = [
47
+ * { time: '2020-01-01T00:00:00', param: 1 },
48
+ * { time: '2020-01-01T04:00:00', param: 0 },
49
+ * { time: '2020-01-01T08:00:00', param: 1 },
50
+ * { time: '2020-01-01T12:00:00', param: 0 },
51
+ * { time: '2020-01-01T16:00:00', param: 0 },
52
+ * { time: '2020-01-01T20:00:00', param: 0 },
53
+ * ]
54
+ *
55
+ * //建立數據資料夾, fdOhlc放K線序列, fdParam放指標參數序列
56
+ * fs.mkdirSync('./data-ohlc', { recursive: true })
57
+ * fs.mkdirSync('./data-param', { recursive: true })
58
+ * fs.writeFileSync('./data-ohlc/btc.json', JSON.stringify(arrOhlc), 'utf8')
59
+ * fs.writeFileSync('./data-param/sig.json', JSON.stringify(arrSig), 'utf8')
60
+ *
61
+ * let settings = { uIni: 1000, uTrade: 100, rTakeProfit: 0.05, rStopLoss: 0.03, rFee: 0.0005 }
62
+ * let strategies = [
63
+ * { sid: 's1', mode: 'long', keyOhlc: 'btc', conds: [{ key: 'sig', sym: '>', th: 0.5, opr: 'and' }], settings },
64
+ * { sid: 's2', mode: 'short', keyOhlc: 'btc', conds: [{ key: 'sig', sym: '<', th: 0.5, opr: 'and' }], settings },
65
+ * ]
66
+ *
67
+ * let r = await runStrategiesAndBacktest(ott, './data-ohlc', './data-param', strategies, '2020-01-01T00:00:00', '2020-01-01T20:00:00', './result')
68
+ * console.log(r.name, r.summary.uIni, r.summary.numTrade, r.summary.rWin)
69
+ * // => 全部策略 2000 6 40.00%
70
+ * console.log(fs.readdirSync('./result'))
71
+ * // => [ 'orders.json', 'report.html', 'summary.json' ]
72
+ *
73
+ */
74
+ let runStrategiesAndBacktest = async (ott, fdOhlc, fdParam, strategies, timeStart, timeEnd, fdTest, opt = {}) => {
75
+
76
+ if (!fsIsFolder(fdOhlc)) {
77
+ throw new Error(`fdOhlc[${fdOhlc}] is not a forder`)
78
+ }
79
+ if (!fsIsFolder(fdParam)) {
80
+ throw new Error(`fdParam[${fdParam}] is not a forder`)
81
+ }
82
+ //check
83
+ if (!isearr(strategies)) {
84
+ throw new Error(`invalid strategies`)
85
+ }
86
+
87
+ let writeOrders = get(opt, 'writeOrders', true)
88
+ let writeSummary = get(opt, 'writeSummary', true)
89
+ let writeReport = get(opt, 'writeReport', true)
90
+
91
+ //wdp
92
+ let wdp = WDataTdprovide(fdOhlc, fdParam)
93
+
94
+ //funGetSeries
95
+ let funGetSeries = await wdp.buildGetTimeSeriesByTimeRange(timeStart, timeEnd)
96
+
97
+ //runStrategies
98
+ let r = await runStrategies(ott, strategies, funGetSeries, {})
99
+ r.name = `全部策略`
100
+
101
+ if (writeOrders) {
102
+
103
+ //fnOrders
104
+ let fnOrders = `orders.json`
105
+
106
+ //fpOrders
107
+ let fpOrders = path.resolve(fdTest, fnOrders)
108
+
109
+ //writeJson
110
+ writeJson(fpOrders, r.orders, { structured: true })
111
+
112
+ }
113
+
114
+ if (writeSummary) {
115
+
116
+ //fnSummary
117
+ let fnSummary = `summary.json`
118
+
119
+ //fpSummary
120
+ let fpSummary = path.resolve(fdTest, fnSummary)
121
+
122
+ //stsm
123
+ let stsm = {
124
+ name: r.name,
125
+ summary: r.summary,
126
+ }
127
+ // console.log('stsm', stsm)
128
+
129
+ //writeJson
130
+ writeJson(fpSummary, stsm, { structured: true })
131
+
132
+ console.log('summary', r.summary, size(r.orders))
133
+ }
134
+
135
+ if (writeReport) {
136
+
137
+ //fnHtml
138
+ let fnHtml = `report.html`
139
+
140
+ //fpHtml
141
+ let fpHtml = path.resolve(fdTest, fnHtml)
142
+
143
+ //genReport
144
+ genReportCore(r, fpHtml)
145
+
146
+ }
147
+
148
+ return r
149
+ }
150
+
151
+
152
+ export default runStrategiesAndBacktest
@@ -11,6 +11,7 @@ import genReport from '../src/genReport.mjs'
11
11
  import genReportCore from '../src/genReportCore.mjs'
12
12
  import ott, { nowTpeStr } from '../src/ott.mjs'
13
13
  import runStrategies from '../src/runStrategies.mjs'
14
+ import runStrategiesAndBacktest from '../src/runStrategiesAndBacktest.mjs'
14
15
  import runStrategy from '../src/runStrategy.mjs'
15
16
  import writeJson from '../src/writeJson.mjs'
16
17
  import writeTxt from '../src/writeTxt.mjs'
@@ -34,6 +35,9 @@ import writeTxt from '../src/writeTxt.mjs'
34
35
  // opt.timeOhlcStart/timeOhlcEnd預設由訂單起訖推得, opt.withWriteSummary:true另輸出summary.json
35
36
  // closeAndSummaryOrders(ott, fdOhlc, fdParam, uIni, timeOhlcStart, timeOhlcEnd, keyOhlc, ordersSubmit, fdTest, opt):
36
37
  // 以w-data-tdprovide讀取fdOhlc/fdParam數據, 結算ordersSubmit並輸出orders.json/summary.json/report.html至fdTest
38
+ // runStrategiesAndBacktest(ott, fdOhlc, fdParam, strategies, timeStart, timeEnd, fdTest, opt):
39
+ // 以w-data-tdprovide讀取fdOhlc/fdParam數據建立funGetSeries後跑runStrategies, 結果name為'全部策略',
40
+ // 並依opt.writeOrders/writeSummary/writeReport(皆預設true)輸出orders.json/summary.json/report.html至fdTest
37
41
 
38
42
 
39
43
  //approx, 浮點容差比較
@@ -538,4 +542,114 @@ describe('WTradeBacktest', function() {
538
542
 
539
543
  })
540
544
 
545
+ describe('runStrategiesAndBacktest', function() {
546
+
547
+ //sig訊號: t00與t08為1(>th觸發), 其餘為0, 存於fdParam之sig.json
548
+ let arrSig = [
549
+ { time: t00, param: 1 },
550
+ { time: t04, param: 0 },
551
+ { time: t08, param: 1 },
552
+ { time: t12, param: 0 },
553
+ { time: t16, param: 0 },
554
+ { time: t20, param: 0 },
555
+ ]
556
+ let settings = {
557
+ uIni: 1000,
558
+ uTrade: 100,
559
+ rTakeProfit: 0.05,
560
+ rStopLoss: 0.03,
561
+ rFee: 0.0005,
562
+ }
563
+ let strategies = [
564
+ { sid: 's1', mode: 'long', keyOhlc: 'btc', conds: [{ key: 'sig', sym: '>', th: 0.5, opr: 'and' }], settings },
565
+ { sid: 's2', mode: 'short', keyOhlc: 'btc', conds: [{ key: 'sig', sym: '<', th: 0.5, opr: 'and' }], settings },
566
+ ]
567
+
568
+ //buildFdData: 於`${fdTmp}/${fd}`建立data-ohlc(btc.json)與data-param(sig.json), 回傳兩資料夾路徑
569
+ let buildFdData = (fd) => {
570
+ let fdOhlc = path.resolve(fdTmp, fd, 'data-ohlc')
571
+ let fdParam = path.resolve(fdTmp, fd, 'data-param')
572
+ fs.mkdirSync(fdOhlc, { recursive: true })
573
+ fs.mkdirSync(fdParam, { recursive: true })
574
+ fs.writeFileSync(path.resolve(fdOhlc, 'btc.json'), JSON.stringify(buildArrOhlc()), 'utf8')
575
+ fs.writeFileSync(path.resolve(fdParam, 'sig.json'), JSON.stringify(arrSig), 'utf8')
576
+ return { fdOhlc, fdParam }
577
+ }
578
+
579
+ it('讀取數據執行多策略, 回傳name為全部策略之合併結果並輸出orders.json/summary.json/report.html', async function() {
580
+ let { fdOhlc, fdParam } = buildFdData('rsab-all')
581
+ let fdTest = path.resolve(fdTmp, 'rsab-all', 'result')
582
+
583
+ let r = await runStrategiesAndBacktest(ott, fdOhlc, fdParam, strategies, t00, t20, fdTest)
584
+
585
+ //r, 同runStrategies之合併結果(s1兩單, s2四單, 依timeStart排序)另附name
586
+ assert.strictEqual(r.name, '全部策略')
587
+ assert.strictEqual(r.orders.length, 6)
588
+ assert.deepStrictEqual(r.orders.map((o) => o.sid), ['s1', 's2', 's1', 's2', 's2', 's2'])
589
+ assert.strictEqual(r.summary.uIni, 2000)
590
+ assert.strictEqual(r.summary.timeOhlcStart, t00)
591
+ assert.strictEqual(r.summary.timeOhlcEnd, t20)
592
+ assert.strictEqual(r.summary.numTrade, 6)
593
+ assert.strictEqual(r.summary.numTradeUnsettled, 1)
594
+ assert.strictEqual(r.summary.rWin, '40.00%')
595
+
596
+ //orders.json
597
+ let orders = JSON.parse(fs.readFileSync(path.resolve(fdTest, 'orders.json'), 'utf8'))
598
+ assert.strictEqual(orders.length, 6)
599
+ assert.strictEqual(orders[0].sid, 's1')
600
+ assert.strictEqual(orders[0].timeStart, t00)
601
+
602
+ //summary.json
603
+ let stsm = JSON.parse(fs.readFileSync(path.resolve(fdTest, 'summary.json'), 'utf8'))
604
+ assert.strictEqual(stsm.name, '全部策略')
605
+ assert.strictEqual(stsm.summary.numTrade, 6)
606
+ assert.strictEqual(stsm.summary.rWin, '40.00%')
607
+
608
+ //report.html, 已置換name與orders
609
+ let fpHtml = path.resolve(fdTest, 'report.html')
610
+ assert.ok(fs.existsSync(fpHtml))
611
+ let h = fs.readFileSync(fpHtml, 'utf8')
612
+ assert.ok(h.includes('全部策略'))
613
+ assert.ok(!h.includes('{orders}'))
614
+ assert.ok(!h.includes('{summary}'))
615
+ })
616
+
617
+ it('writeOrders/writeSummary/writeReport皆false時不輸出檔案, 仍回傳結果', async function() {
618
+ let { fdOhlc, fdParam } = buildFdData('rsab-nowrite')
619
+ let fdTest = path.resolve(fdTmp, 'rsab-nowrite', 'result')
620
+
621
+ let r = await runStrategiesAndBacktest(ott, fdOhlc, fdParam, strategies, t00, t20, fdTest, {
622
+ writeOrders: false,
623
+ writeSummary: false,
624
+ writeReport: false,
625
+ })
626
+
627
+ assert.strictEqual(r.orders.length, 6)
628
+ assert.ok(!fs.existsSync(path.resolve(fdTest, 'orders.json')))
629
+ assert.ok(!fs.existsSync(path.resolve(fdTest, 'summary.json')))
630
+ assert.ok(!fs.existsSync(path.resolve(fdTest, 'report.html')))
631
+ })
632
+
633
+ it('fdOhlc非資料夾時reject', async function() {
634
+ let { fdParam } = buildFdData('rsab-invalid')
635
+ let fdOhlc = path.resolve(fdTmp, 'rsab-invalid', 'none')
636
+ let fdTest = path.resolve(fdTmp, 'rsab-invalid', 'result')
637
+ await assert.rejects(runStrategiesAndBacktest(ott, fdOhlc, fdParam, strategies, t00, t20, fdTest), { message: `fdOhlc[${fdOhlc}] is not a forder` })
638
+ })
639
+
640
+ it('fdParam非資料夾時reject', async function() {
641
+ let { fdOhlc } = buildFdData('rsab-invalid2')
642
+ let fdParam = path.resolve(fdTmp, 'rsab-invalid2', 'none')
643
+ let fdTest = path.resolve(fdTmp, 'rsab-invalid2', 'result')
644
+ await assert.rejects(runStrategiesAndBacktest(ott, fdOhlc, fdParam, strategies, t00, t20, fdTest), { message: `fdParam[${fdParam}] is not a forder` })
645
+ })
646
+
647
+ it('strategies非有效陣列時reject', async function() {
648
+ let { fdOhlc, fdParam } = buildFdData('rsab-nostrategies')
649
+ let fdTest = path.resolve(fdTmp, 'rsab-nostrategies', 'result')
650
+ await assert.rejects(runStrategiesAndBacktest(ott, fdOhlc, fdParam, [], t00, t20, fdTest), { message: `invalid strategies` })
651
+ })
652
+
653
+ })
654
+
541
655
  })