w-trade-backtest 1.0.3 → 1.0.5
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.
- package/dist/w-trade-backtest.umd.js +8 -8
- package/dist/w-trade-backtest.umd.js.map +1 -1
- package/docs/buildKLinesTpSl.mjs.html +218 -0
- package/docs/calcOrders.mjs.html +2 -2
- package/docs/calcOrdersRatio.mjs.html +2 -2
- package/docs/calcOrdersSummary.mjs.html +2 -2
- package/docs/calcOrdersSummarySimple.mjs.html +2 -2
- package/docs/calcSummary.mjs.html +2 -2
- package/docs/closeAndSummaryOrders.mjs.html +2 -2
- package/docs/genKLinesTpSl.mjs.html +154 -0
- package/docs/genReport.mjs.html +2 -2
- package/docs/genReportCore.mjs.html +2 -2
- package/docs/global.html +2078 -380
- package/docs/index.html +2 -2
- package/docs/loadKLinesTpSl.mjs.html +189 -0
- package/docs/runStrategies.mjs.html +2 -2
- package/docs/runStrategiesAndBacktest.mjs.html +224 -0
- package/docs/runStrategy.mjs.html +2 -2
- package/package.json +1 -1
- package/src/WTradeBacktest.mjs +8 -0
- package/src/buildKLinesTpSl.mjs +146 -0
- package/src/genKLinesTpSl.mjs +82 -0
- package/src/loadKLinesTpSl.mjs +117 -0
- package/src/runStrategiesAndBacktest.mjs +152 -0
- package/test/unit-WTradeBacktest.test.mjs +23 -529
- package/test/unit-buildKLinesTpSl.test.mjs +98 -0
- package/test/unit-calcOrders.test.mjs +60 -0
- package/test/unit-calcOrdersRatio.test.mjs +39 -0
- package/test/unit-calcOrdersSummary.test.mjs +71 -0
- package/test/unit-calcOrdersSummarySimple.test.mjs +30 -0
- package/test/unit-calcSummary.test.mjs +30 -0
- package/test/unit-closeAndSummaryOrders.test.mjs +53 -0
- package/test/unit-genKLinesTpSl.test.mjs +46 -0
- package/test/unit-genReport.test.mjs +113 -0
- package/test/unit-genReportCore.test.mjs +39 -0
- package/test/unit-loadKLinesTpSl.test.mjs +158 -0
- package/test/unit-ott.test.mjs +20 -0
- package/test/unit-runStrategies.test.mjs +30 -0
- package/test/unit-runStrategiesAndBacktest.test.mjs +114 -0
- package/test/unit-runStrategy.test.mjs +53 -0
- package/test/unit-setup.mjs +120 -0
- package/test/unit-writeJson.test.mjs +29 -0
- package/test/unit-writeTxt.test.mjs +27 -0
package/docs/index.html
CHANGED
|
@@ -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#buildKLinesTpSl">buildKLinesTpSl</a></li><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#genKLinesTpSl">genKLinesTpSl</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#loadKLinesTpSl">loadKLinesTpSl</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
|
|
|
@@ -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
|
|
74
|
+
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Mon Aug 03 2026 14:28:31 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
|
|
75
75
|
</footer>
|
|
76
76
|
|
|
77
77
|
<script>prettyPrint();</script>
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
|
|
5
|
+
<meta charset="utf-8">
|
|
6
|
+
<title>loadKLinesTpSl.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#buildKLinesTpSl">buildKLinesTpSl</a></li><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#genKLinesTpSl">genKLinesTpSl</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#loadKLinesTpSl">loadKLinesTpSl</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">loadKLinesTpSl.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 fs from 'fs'
|
|
50
|
+
import dig from 'wsemi/src/dig.mjs'
|
|
51
|
+
import isestr from 'wsemi/src/isestr.mjs'
|
|
52
|
+
import fsIsFile from 'wsemi/src/fsIsFile.mjs'
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* 載入止盈止損先觸結果快取檔並提供訂單重建
|
|
57
|
+
*
|
|
58
|
+
* 讀取buildKLinesTpSl產出之快取json,提供ordersFromBars依指定mode與tpsl組合與進場時間陣列重建訂單陣列
|
|
59
|
+
* 重建訂單之盈虧與累計欄位公式逐欄對齊calcOrders,可直接餵入calcOrdersSummary或calcOrdersSummarySimple
|
|
60
|
+
* fpJson非有效字串或非檔案時throw
|
|
61
|
+
*
|
|
62
|
+
* Unit Test: {@link https://github.com/yuda-lyu/w-trade-backtest/blob/master/test/unit-WTradeBacktest.test.mjs Github}
|
|
63
|
+
* @function
|
|
64
|
+
* @param {String} fpJson 輸入buildKLinesTpSl產出之快取json檔案路徑字串
|
|
65
|
+
* @returns {Object} 回傳物件,內含meta(快取設定物件)、bars(以進場時間為key之結果物件)與ordersFromBars(mode, tpsl, entryTimes, opt)訂單重建函數,函數之opt可給uTrade(每單金額,預設1)、uIni(初始資金,預設1000)、rFee(手續費率,預設0.0005)、maxExitTime(出場時間大於等於此值之單視為未平倉,預設null)與skipSort(呼叫端保證entryTimes已升序時可省排序,預設false),回傳訂單陣列,快取內查無之進場時間會被濾除,未結單各盈虧欄位留空
|
|
66
|
+
* @example
|
|
67
|
+
*
|
|
68
|
+
* //fpJson為buildKLinesTpSl產出之快取檔
|
|
69
|
+
* let r = loadKLinesTpSl('./cache/kltpsl.json')
|
|
70
|
+
* console.log(r.meta.nBars)
|
|
71
|
+
* // => 6
|
|
72
|
+
*
|
|
73
|
+
* let orders = r.ordersFromBars('long', 5, ['2020-01-01T00:00:00', '2020-01-01T12:00:00'], { uTrade: 100, uIni: 1000 })
|
|
74
|
+
* console.log(orders.map((o) => `${o.timeStart} ${o.timeEnd || 'unsettled'} ${o.modeResult || '-'}`))
|
|
75
|
+
* // => [
|
|
76
|
+
* // '2020-01-01T00:00:00 2020-01-01T04:00:00 profit',
|
|
77
|
+
* // '2020-01-01T12:00:00 unsettled -'
|
|
78
|
+
* // ]
|
|
79
|
+
* console.log(orders[0].priceStart, orders[0].priceEnd, orders[0].uProfitOrLoss, orders[0].uEquity)
|
|
80
|
+
* // => 100 105 4.9 1004.9
|
|
81
|
+
*
|
|
82
|
+
*/
|
|
83
|
+
let loadKLinesTpSl = (fpJson) => {
|
|
84
|
+
|
|
85
|
+
//check
|
|
86
|
+
if (!isestr(fpJson)) {
|
|
87
|
+
throw new Error(`invalid fpJson`)
|
|
88
|
+
}
|
|
89
|
+
if (!fsIsFile(fpJson)) {
|
|
90
|
+
throw new Error(`fpJson[${fpJson}] is not a file`)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
let obj = JSON.parse(fs.readFileSync(path.resolve(fpJson), 'utf8'))
|
|
94
|
+
let meta = obj.meta
|
|
95
|
+
let bars = obj.bars
|
|
96
|
+
|
|
97
|
+
let ordersFromBars = (mode, tpsl, entryTimes, opt = {}) => {
|
|
98
|
+
let { uTrade = 1, uIni = 1000, rFee = 0.0005, maxExitTime = null, skipSort = false } = opt
|
|
99
|
+
let k = `${mode}_${tpsl}`
|
|
100
|
+
let tpr = tpsl / 100
|
|
101
|
+
let uFee = rFee * uTrade
|
|
102
|
+
|
|
103
|
+
//先建全部 order 物件(未結者欄位留空, 對齊 calcOrders 對未平倉單的處理)。
|
|
104
|
+
// skipSort: 呼叫端保證 entryTimes 已升序時省 [...].sort()(熱路徑省數千筆複製+排序); 預設 false 維持原行為。
|
|
105
|
+
let ets = skipSort ? entryTimes : [...entryTimes].sort()
|
|
106
|
+
let orders = ets.filter((t) => bars[t]).map((t) => {
|
|
107
|
+
let c0 = bars[t].close
|
|
108
|
+
return {
|
|
109
|
+
mode,
|
|
110
|
+
timeStart: t,
|
|
111
|
+
priceStart: c0,
|
|
112
|
+
uTrade,
|
|
113
|
+
rTakeProfit: tpr,
|
|
114
|
+
priceTakeProfit: mode === 'long' ? (1 + tpr) * c0 : (1 - tpr) * c0,
|
|
115
|
+
rStopLoss: tpr,
|
|
116
|
+
priceStopLoss: mode === 'long' ? (1 - tpr) * c0 : (1 + tpr) * c0,
|
|
117
|
+
timeEnd: '',
|
|
118
|
+
priceEnd: '',
|
|
119
|
+
modeResult: '',
|
|
120
|
+
rFee,
|
|
121
|
+
uFee,
|
|
122
|
+
uProfitOrLoss: '',
|
|
123
|
+
rProfitOrLoss: '',
|
|
124
|
+
uCumuProfitOrLoss: '',
|
|
125
|
+
rCumuProfitOrLoss: '',
|
|
126
|
+
uEquity: '',
|
|
127
|
+
uTradeAll: '',
|
|
128
|
+
rTradeAll: '',
|
|
129
|
+
}
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
//填已結單(公式對齊 d04 calcOrders)
|
|
133
|
+
let uEquity = uIni
|
|
134
|
+
let uCumu = 0
|
|
135
|
+
for (let o of orders) {
|
|
136
|
+
let r = bars[o.timeStart][k]
|
|
137
|
+
if (!r || r.win < 0 || !r.timeEnd) {
|
|
138
|
+
continue //未結 → 留空(計入 numTrade, 不計入已結績效)
|
|
139
|
+
}
|
|
140
|
+
if (maxExitTime && r.timeEnd >= maxExitTime) {
|
|
141
|
+
continue //區段內尚未觸 → 留未平倉
|
|
142
|
+
}
|
|
143
|
+
o.timeEnd = r.timeEnd
|
|
144
|
+
o.priceEnd = r.win === 1 ? o.priceTakeProfit : o.priceStopLoss
|
|
145
|
+
o.uProfitOrLoss = o.mode === 'long'
|
|
146
|
+
? o.uTrade * (o.priceEnd / o.priceStart) - o.uTrade - 2 * o.uFee
|
|
147
|
+
: (o.priceStart - o.priceEnd) * (o.uTrade / o.priceStart) - 2 * o.uFee
|
|
148
|
+
o.rProfitOrLoss = o.uProfitOrLoss / o.uTrade
|
|
149
|
+
uCumu += o.uProfitOrLoss
|
|
150
|
+
o.uCumuProfitOrLoss = uCumu
|
|
151
|
+
o.rCumuProfitOrLoss = dig((o.uCumuProfitOrLoss / uIni) * 100, 2) + '%'
|
|
152
|
+
uEquity += o.uProfitOrLoss
|
|
153
|
+
o.uEquity = uEquity
|
|
154
|
+
o.modeResult = o.uProfitOrLoss < 0 ? 'loss' : 'profit'
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return orders
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return { meta, bars, ordersFromBars }
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
export default loadKLinesTpSl
|
|
165
|
+
</code></pre>
|
|
166
|
+
</article>
|
|
167
|
+
</section>
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
</div>
|
|
175
|
+
|
|
176
|
+
<br class="clear">
|
|
177
|
+
|
|
178
|
+
<footer>
|
|
179
|
+
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Mon Aug 03 2026 14:28:31 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
|
|
180
|
+
</footer>
|
|
181
|
+
|
|
182
|
+
<script>prettyPrint();</script>
|
|
183
|
+
<script src="scripts/polyfill.js"></script>
|
|
184
|
+
<script src="scripts/linenumber.js"></script>
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
</body>
|
|
189
|
+
</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#buildKLinesTpSl">buildKLinesTpSl</a></li><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#genKLinesTpSl">genKLinesTpSl</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#loadKLinesTpSl">loadKLinesTpSl</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
|
|
|
@@ -231,7 +231,7 @@ export default runStrategies
|
|
|
231
231
|
<br class="clear">
|
|
232
232
|
|
|
233
233
|
<footer>
|
|
234
|
-
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on
|
|
234
|
+
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Mon Aug 03 2026 14:28:31 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
|
|
235
235
|
</footer>
|
|
236
236
|
|
|
237
237
|
<script>prettyPrint();</script>
|
|
@@ -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#buildKLinesTpSl">buildKLinesTpSl</a></li><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#genKLinesTpSl">genKLinesTpSl</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#loadKLinesTpSl">loadKLinesTpSl</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: '<', 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 14:28:31 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#buildKLinesTpSl">buildKLinesTpSl</a></li><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#genKLinesTpSl">genKLinesTpSl</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#loadKLinesTpSl">loadKLinesTpSl</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
|
|
445
|
+
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> on Mon Aug 03 2026 14:28:31 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
package/src/WTradeBacktest.mjs
CHANGED
|
@@ -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'
|
|
@@ -8,11 +9,15 @@ import calcSummary from './calcSummary.mjs'
|
|
|
8
9
|
import closeAndSummaryOrders from './closeAndSummaryOrders.mjs'
|
|
9
10
|
import genReport from './genReport.mjs'
|
|
10
11
|
import genReportCore from './genReportCore.mjs'
|
|
12
|
+
import genKLinesTpSl from './genKLinesTpSl.mjs'
|
|
13
|
+
import buildKLinesTpSl from './buildKLinesTpSl.mjs'
|
|
14
|
+
import loadKLinesTpSl from './loadKLinesTpSl.mjs'
|
|
11
15
|
|
|
12
16
|
|
|
13
17
|
let r = {
|
|
14
18
|
runStrategy,
|
|
15
19
|
runStrategies,
|
|
20
|
+
runStrategiesAndBacktest,
|
|
16
21
|
calcOrders,
|
|
17
22
|
calcOrdersRatio,
|
|
18
23
|
calcOrdersSummary,
|
|
@@ -21,6 +26,9 @@ let r = {
|
|
|
21
26
|
closeAndSummaryOrders,
|
|
22
27
|
genReport,
|
|
23
28
|
genReportCore,
|
|
29
|
+
genKLinesTpSl,
|
|
30
|
+
buildKLinesTpSl,
|
|
31
|
+
loadKLinesTpSl,
|
|
24
32
|
}
|
|
25
33
|
|
|
26
34
|
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import path from 'path'
|
|
2
|
+
import fs from 'fs'
|
|
3
|
+
import isestr from 'wsemi/src/isestr.mjs'
|
|
4
|
+
import istime from 'wsemi/src/istime.mjs'
|
|
5
|
+
import fsIsFolder from 'wsemi/src/fsIsFolder.mjs'
|
|
6
|
+
import fsCreateFolder from 'wsemi/src/fsCreateFolder.mjs'
|
|
7
|
+
import WDataTdprovide from 'w-data-tdprovide/src/WDataTdprovide.mjs'
|
|
8
|
+
import genKLinesTpSl from './genKLinesTpSl.mjs'
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* 建置各K線與各止盈止損組合之先觸結果快取檔
|
|
13
|
+
*
|
|
14
|
+
* 以w-data-tdprovide讀取fdOhlc與fdParam資料夾內keyOhlc序列,取ts至te範圍K線後,逐一組合modes與tps呼叫genKLinesTpSl
|
|
15
|
+
* 各組合以`${mode}_${tpsl}`為key存入各根K線,win為1(止盈先觸)、0(止損先觸)或-1(至區間末仍未結)
|
|
16
|
+
* timeEnd於建置時由hold位移換算為絕對時間,故下游查表即得而不需位置索引,未結者為null
|
|
17
|
+
* 僅存win、timeEnd與各根close,盈虧由下游loadKLinesTpSl之ordersFromBars以close與手續費重算
|
|
18
|
+
* fdOhlc或fdParam非資料夾、fpJson或opt.keyOhlc非有效字串、opt.ts或opt.te非時間字串時throw
|
|
19
|
+
*
|
|
20
|
+
* Unit Test: {@link https://github.com/yuda-lyu/w-trade-backtest/blob/master/test/unit-WTradeBacktest.test.mjs Github}
|
|
21
|
+
* @function
|
|
22
|
+
* @param {String} fdOhlc 輸入儲存K線(ohlc)序列資料夾字串,各序列以`${key}.json`儲存
|
|
23
|
+
* @param {String} fdParam 輸入儲存指標參數序列資料夾字串
|
|
24
|
+
* @param {String} fpJson 輸入輸出快取json檔案路徑字串,資料夾不存在時自動建立
|
|
25
|
+
* @param {Object} [opt={}] 輸入設定物件,預設{}
|
|
26
|
+
* @param {String} opt.keyOhlc 輸入K線序列key字串
|
|
27
|
+
* @param {String} opt.ts 輸入起始秒時間字串
|
|
28
|
+
* @param {String} opt.te 輸入結束秒時間字串
|
|
29
|
+
* @param {Array} [opt.tps=[4,5,6,7,8,9,10]] 輸入止盈止損百分比數值陣列,如5代表止盈與止損皆5%,預設[4,5,6,7,8,9,10]
|
|
30
|
+
* @param {Array} [opt.modes=['long','short']] 輸入交易方向字串陣列,預設['long','short']
|
|
31
|
+
* @returns {Promise} 回傳Promise,resolve為{meta,bars}物件,meta內含keyOhlc、ts、te、tps、modes與nBars(K線根數),bars以進場時間為key,各值內含close與各`${mode}_${tpsl}`之{win,timeEnd},並同步寫出至fpJson
|
|
32
|
+
* @example
|
|
33
|
+
*
|
|
34
|
+
* import fs from 'fs'
|
|
35
|
+
*
|
|
36
|
+
* let arrOhlc = [
|
|
37
|
+
* { time: '2020-01-01T00:00:00', Open: 100, High: 101, Low: 99, Close: 100 },
|
|
38
|
+
* { time: '2020-01-01T04:00:00', Open: 100, High: 106, Low: 100, Close: 105 },
|
|
39
|
+
* { time: '2020-01-01T08:00:00', Open: 105, High: 107, Low: 102, Close: 103 },
|
|
40
|
+
* { time: '2020-01-01T12:00:00', Open: 103, High: 104, Low: 94, Close: 95 },
|
|
41
|
+
* { time: '2020-01-01T16:00:00', Open: 95, High: 98, Low: 92, Close: 93 },
|
|
42
|
+
* { time: '2020-01-01T20:00:00', Open: 93, High: 99, Low: 95, Close: 97 },
|
|
43
|
+
* ]
|
|
44
|
+
*
|
|
45
|
+
* //建立數據資料夾, fdOhlc放K線序列, fdParam放指標參數序列
|
|
46
|
+
* fs.mkdirSync('./data-ohlc', { recursive: true })
|
|
47
|
+
* fs.mkdirSync('./data-param', { recursive: true })
|
|
48
|
+
* fs.writeFileSync('./data-ohlc/btc.json', JSON.stringify(arrOhlc), 'utf8')
|
|
49
|
+
*
|
|
50
|
+
* let r = await buildKLinesTpSl('./data-ohlc', './data-param', './cache/kltpsl.json', {
|
|
51
|
+
* keyOhlc: 'btc',
|
|
52
|
+
* ts: '2020-01-01T00:00:00',
|
|
53
|
+
* te: '2020-01-01T20:00:00',
|
|
54
|
+
* tps: [5],
|
|
55
|
+
* modes: ['long'],
|
|
56
|
+
* })
|
|
57
|
+
* console.log(r.meta)
|
|
58
|
+
* // => {
|
|
59
|
+
* // keyOhlc: 'btc',
|
|
60
|
+
* // ts: '2020-01-01T00:00:00',
|
|
61
|
+
* // te: '2020-01-01T20:00:00',
|
|
62
|
+
* // tps: [ 5 ],
|
|
63
|
+
* // modes: [ 'long' ],
|
|
64
|
+
* // nBars: 6
|
|
65
|
+
* // }
|
|
66
|
+
* console.log(r.bars['2020-01-01T00:00:00'])
|
|
67
|
+
* // => { close: 100, long_5: { win: 1, timeEnd: '2020-01-01T04:00:00' } }
|
|
68
|
+
* console.log(r.bars['2020-01-01T12:00:00'])
|
|
69
|
+
* // => { close: 95, long_5: { win: -1, timeEnd: null } }
|
|
70
|
+
*
|
|
71
|
+
*/
|
|
72
|
+
let buildKLinesTpSl = async (fdOhlc, fdParam, fpJson, opt = {}) => {
|
|
73
|
+
//一次性全建(訓練區間固定, 不需增量); 若日後要增量延長: 已結entry不動、未結entry用新OHLC重掃、新棒加key即可
|
|
74
|
+
|
|
75
|
+
//check
|
|
76
|
+
if (!fsIsFolder(fdOhlc)) {
|
|
77
|
+
throw new Error(`fdOhlc[${fdOhlc}] is not a folder`)
|
|
78
|
+
}
|
|
79
|
+
if (!fsIsFolder(fdParam)) {
|
|
80
|
+
throw new Error(`fdParam[${fdParam}] is not a folder`)
|
|
81
|
+
}
|
|
82
|
+
if (!isestr(fpJson)) {
|
|
83
|
+
throw new Error(`invalid fpJson`)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
let provideData = WDataTdprovide(fdOhlc, fdParam) //數據資料夾由呼叫端顯式指定
|
|
87
|
+
|
|
88
|
+
let {
|
|
89
|
+
keyOhlc,
|
|
90
|
+
ts,
|
|
91
|
+
te,
|
|
92
|
+
tps = [4, 5, 6, 7, 8, 9, 10],
|
|
93
|
+
modes = ['long', 'short'],
|
|
94
|
+
} = opt
|
|
95
|
+
|
|
96
|
+
//check
|
|
97
|
+
if (!isestr(keyOhlc)) {
|
|
98
|
+
throw new Error(`invalid opt.keyOhlc`)
|
|
99
|
+
}
|
|
100
|
+
if (!istime(ts)) {
|
|
101
|
+
throw new Error(`invalid opt.ts`)
|
|
102
|
+
}
|
|
103
|
+
if (!istime(te)) {
|
|
104
|
+
throw new Error(`invalid opt.te`)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
//OHLC(訓練區間)
|
|
108
|
+
let arrOhlc = await provideData.getTimeSeriesByTimeRange(keyOhlc, ts, te)
|
|
109
|
+
let n = arrOhlc.length
|
|
110
|
+
let time = arrOhlc.map((r) => r.time)
|
|
111
|
+
|
|
112
|
+
//每根 close(各 mode_tpsl 共用)
|
|
113
|
+
let bars = {}
|
|
114
|
+
for (let i = 0; i < n; i++) {
|
|
115
|
+
bars[time[i]] = { close: arrOhlc[i].Close }
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
//各 (mode, tpsl) 跑 first-touch → 填 { win, timeEnd }
|
|
119
|
+
for (let mode of modes) {
|
|
120
|
+
for (let tpsl of tps) {
|
|
121
|
+
let { win, hold } = genKLinesTpSl(arrOhlc, { tp: tpsl / 100, sl: tpsl / 100, mode })
|
|
122
|
+
let k = `${mode}_${tpsl}`
|
|
123
|
+
for (let i = 0; i < n; i++) {
|
|
124
|
+
let wn = win[i]
|
|
125
|
+
let timeEnd = (wn >= 0 && Number.isFinite(hold[i])) ? time[i + hold[i]] : null
|
|
126
|
+
bars[time[i]][k] = { win: wn, timeEnd }
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
//out
|
|
132
|
+
let out = { meta: { keyOhlc, ts, te, tps, modes, nBars: n }, bars }
|
|
133
|
+
|
|
134
|
+
//寫檔
|
|
135
|
+
let jp = path.resolve(fpJson)
|
|
136
|
+
let fd = path.dirname(jp)
|
|
137
|
+
if (!fsIsFolder(fd)) {
|
|
138
|
+
fsCreateFolder(fd)
|
|
139
|
+
}
|
|
140
|
+
fs.writeFileSync(jp, JSON.stringify(out))
|
|
141
|
+
|
|
142
|
+
return out
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
export default buildKLinesTpSl
|