w-trade-backtest 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/.editorconfig +9 -0
  2. package/.eslintignore +3 -0
  3. package/.eslintrc.js +55 -0
  4. package/.jsdoc +25 -0
  5. package/README.md +167 -0
  6. package/SECURITY.md +5 -0
  7. package/babel.config.js +16 -0
  8. package/dist/renderOrdersTable.js +217 -0
  9. package/dist/renderOrdersTimeline.js +159 -0
  10. package/dist/renderProfitPlot.js +198 -0
  11. package/dist/renderSummary.js +194 -0
  12. package/dist/tmp.html +232 -0
  13. package/dist/w-trade-backtest.umd.js +32 -0
  14. package/dist/w-trade-backtest.umd.js.map +1 -0
  15. package/docs/calcOrders.mjs.html +278 -0
  16. package/docs/calcOrdersRatio.mjs.html +146 -0
  17. package/docs/calcOrdersSummary.mjs.html +506 -0
  18. package/docs/calcOrdersSummarySimple.mjs.html +207 -0
  19. package/docs/calcSummary.mjs.html +201 -0
  20. package/docs/closeAndSummaryOrders.mjs.html +265 -0
  21. package/docs/fonts/Montserrat/Montserrat-Bold.eot +0 -0
  22. package/docs/fonts/Montserrat/Montserrat-Bold.ttf +0 -0
  23. package/docs/fonts/Montserrat/Montserrat-Bold.woff +0 -0
  24. package/docs/fonts/Montserrat/Montserrat-Bold.woff2 +0 -0
  25. package/docs/fonts/Montserrat/Montserrat-Regular.eot +0 -0
  26. package/docs/fonts/Montserrat/Montserrat-Regular.ttf +0 -0
  27. package/docs/fonts/Montserrat/Montserrat-Regular.woff +0 -0
  28. package/docs/fonts/Montserrat/Montserrat-Regular.woff2 +0 -0
  29. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.eot +0 -0
  30. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.svg +978 -0
  31. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.ttf +0 -0
  32. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff +0 -0
  33. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff2 +0 -0
  34. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.eot +0 -0
  35. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.svg +1049 -0
  36. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.ttf +0 -0
  37. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff +0 -0
  38. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff2 +0 -0
  39. package/docs/genReport.mjs.html +196 -0
  40. package/docs/genReportCore.mjs.html +128 -0
  41. package/docs/global.html +4231 -0
  42. package/docs/index.html +84 -0
  43. package/docs/runStrategies.mjs.html +244 -0
  44. package/docs/runStrategy.mjs.html +455 -0
  45. package/docs/scripts/collapse.js +39 -0
  46. package/docs/scripts/commonNav.js +28 -0
  47. package/docs/scripts/linenumber.js +25 -0
  48. package/docs/scripts/nav.js +12 -0
  49. package/docs/scripts/polyfill.js +4 -0
  50. package/docs/scripts/prettify/Apache-License-2.0.txt +202 -0
  51. package/docs/scripts/prettify/lang-css.js +2 -0
  52. package/docs/scripts/prettify/prettify.js +28 -0
  53. package/docs/scripts/search.js +99 -0
  54. package/docs/styles/jsdoc.css +776 -0
  55. package/docs/styles/prettify.css +80 -0
  56. package/g.mjs +146 -0
  57. package/package.json +30 -0
  58. package/script.txt +19 -0
  59. package/src/WTradeBacktest.mjs +27 -0
  60. package/src/calcOrders.mjs +206 -0
  61. package/src/calcOrdersRatio.mjs +74 -0
  62. package/src/calcOrdersSummary.mjs +434 -0
  63. package/src/calcOrdersSummarySimple.mjs +135 -0
  64. package/src/calcSummary.mjs +129 -0
  65. package/src/closeAndSummaryOrders.mjs +193 -0
  66. package/src/genReport.mjs +124 -0
  67. package/src/genReportCore.mjs +56 -0
  68. package/src/ott.mjs +28 -0
  69. package/src/readJson.mjs +26 -0
  70. package/src/renderOrdersTable.js +217 -0
  71. package/src/renderOrdersTimeline.js +159 -0
  72. package/src/renderProfitPlot.js +198 -0
  73. package/src/renderSummary.js +194 -0
  74. package/src/runStrategies.mjs +172 -0
  75. package/src/runStrategy.mjs +383 -0
  76. package/src/tmp.html +232 -0
  77. package/src/writeJson.mjs +31 -0
  78. package/src/writeTxt.mjs +19 -0
  79. package/test/unit-WTradeBacktest.test.mjs +541 -0
  80. package/toolg/addVersion.mjs +4 -0
  81. package/toolg/cleanFolder.mjs +5 -0
  82. package/toolg/gDistRollup.mjs +51 -0
  83. package/toolg/gDocsExams.mjs +48 -0
  84. package/toolg/modifyReadme.mjs +4 -0
@@ -0,0 +1,196 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+
5
+ <meta charset="utf-8">
6
+ <title>genReport.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#runStrategy">runStrategy</a></li></ul>
33
+
34
+ </nav>
35
+
36
+ <div id="main">
37
+
38
+ <h1 class="page-title">genReport.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 _ from 'lodash-es'
50
+ import w from 'wsemi'
51
+ import calcSummary from './calcSummary.mjs'
52
+ import genReportCore from './genReportCore.mjs'
53
+ import readJson from './readJson.mjs'
54
+ import writeJson from './writeJson.mjs'
55
+
56
+
57
+ /**
58
+ * 由orders.json產製績效html報表
59
+ *
60
+ * 讀取orders.json(已結算訂單陣列)後,經calcSummary統計摘要,再以genReportCore產出html報表
61
+ * 訂單須為已結算形(含uProfitOrLoss、uEquity等欄位),未結算單(timeEnd為空)仍可計算,計入summary.numTradeUnsettled
62
+ * fpOrders非有效字串或非檔案、內容非有效陣列時throw
63
+ *
64
+ * Unit Test: {@link https://github.com/yuda-lyu/w-trade-backtest/blob/master/test/unit-WTradeBacktest.test.mjs Github}
65
+ * @function
66
+ * @param {Function} ott 輸入時區時間函數,傳入時間字串回傳dayjs時間物件(可用src/ott.mjs或自行以dayjs包裝)
67
+ * @param {String} fpOrders 輸入orders.json檔案路徑字串
68
+ * @param {Object} [opt={}] 輸入設定物件,預設{}
69
+ * @param {String} [opt.fpOut=''] 輸入輸出html檔案路徑字串,預設為與orders.json同資料夾之report.html
70
+ * @param {String} [opt.name=''] 輸入報表標題字串,預設為orders.json所在資料夾名
71
+ * @param {Number} [opt.uIni=null] 輸入初始資金數值,預設由首筆已結算單推回(uEquity - uCumuProfitOrLoss),推不出則為1000
72
+ * @param {String} [opt.timeOhlcStart=''] 輸入摘要統計起始秒時間字串,影響btDays與年化,預設由訂單最早timeStart推得,注意訂單起訖僅為「有單的範圍」,若實際回測窗較寬(頭尾無單)年化會被高估,建議明給
73
+ * @param {String} [opt.timeOhlcEnd=''] 輸入摘要統計結束秒時間字串,預設由訂單最晚timeEnd(未平倉單取timeStart)推得
74
+ * @param {Boolean} [opt.withWriteSummary=false] 輸入是否同時輸出summary.json(與fpOut同資料夾)布林值,預設false
75
+ * @returns {Promise} 回傳Promise,resolve為物件,內含orders(訂單陣列)、summary(摘要物件)、fpOut(輸出html路徑)欄位
76
+ * @example
77
+ *
78
+ * import fs from 'fs'
79
+ *
80
+ * //orders為已結算訂單陣列(如runStrategy、runStrategies或closeAndSummaryOrders產出)
81
+ * let orders = [{
82
+ * mode: 'long',
83
+ * timeStart: '2020-01-01T00:00:00',
84
+ * priceStart: 100,
85
+ * uTrade: 100,
86
+ * priceTakeProfit: 105,
87
+ * priceStopLoss: 97,
88
+ * timeEnd: '2020-01-01T04:00:00',
89
+ * priceEnd: 105,
90
+ * modeResult: 'profit',
91
+ * uFee: 0.05,
92
+ * uProfitOrLoss: 4.9,
93
+ * rProfitOrLoss: 0.049,
94
+ * uCumuProfitOrLoss: 4.9,
95
+ * uEquity: 1004.9,
96
+ * }]
97
+ *
98
+ * fs.mkdirSync('./result', { recursive: true })
99
+ * fs.writeFileSync('./result/orders.json', JSON.stringify(orders), 'utf8')
100
+ *
101
+ * let r = await genReport(ott, './result/orders.json', { name: '示範策略', uIni: 1000 })
102
+ * console.log(r.summary.numTrade, r.summary.rWin, r.summary.uEquityFinal)
103
+ * // => 1 100.00% 1004.9
104
+ * console.log(fs.readdirSync('./result'))
105
+ * // => [ 'orders.json', 'report.html' ]
106
+ *
107
+ */
108
+ let genReport = async (ott, fpOrders, opt = {}) => {
109
+
110
+ //check
111
+ if (!w.isestr(fpOrders)) {
112
+ throw new Error(`invalid fpOrders`)
113
+ }
114
+ if (!w.fsIsFile(fpOrders)) {
115
+ throw new Error(`fpOrders[${fpOrders}] is not a file`)
116
+ }
117
+
118
+ //orders
119
+ let orders = readJson(fpOrders)
120
+ if (!w.isearr(orders)) {
121
+ throw new Error(`orders in fpOrders[${fpOrders}] is not an effective array`)
122
+ }
123
+
124
+ //fpOut
125
+ let fpOut = _.get(opt, 'fpOut', '')
126
+ if (!w.isestr(fpOut)) {
127
+ fpOut = path.resolve(path.dirname(fpOrders), 'report.html')
128
+ }
129
+
130
+ //name
131
+ let name = _.get(opt, 'name', '')
132
+ if (!w.isestr(name)) {
133
+ name = path.basename(path.dirname(path.resolve(fpOrders)))
134
+ }
135
+
136
+ //uIni, 未給則由首筆已結算單推回(calcOrders 定義 uEquity = uIni + uCumuProfitOrLoss)
137
+ let uIni = _.get(opt, 'uIni', null)
138
+ if (!w.isnum(uIni)) {
139
+ let o = _.find(orders, (o) => w.isnum(o.uEquity) &amp;&amp; w.isnum(o.uCumuProfitOrLoss))
140
+ uIni = o ? o.uEquity - o.uCumuProfitOrLoss : 1000
141
+ }
142
+
143
+ //timeOhlcStart, timeOhlcEnd, 未給則由訂單起訖推得
144
+ let timeOhlcStart = _.get(opt, 'timeOhlcStart', '')
145
+ if (!w.isestr(timeOhlcStart)) {
146
+ timeOhlcStart = _.min(_.map(orders, 'timeStart'))
147
+ }
148
+ let timeOhlcEnd = _.get(opt, 'timeOhlcEnd', '')
149
+ if (!w.isestr(timeOhlcEnd)) {
150
+ timeOhlcEnd = _.max(_.map(orders, (o) => w.isestr(o.timeEnd) ? o.timeEnd : o.timeStart))
151
+ }
152
+
153
+ //withWriteSummary
154
+ let withWriteSummary = _.get(opt, 'withWriteSummary', false)
155
+
156
+ //calcSummary
157
+ let summary = await calcSummary(ott, uIni, orders, timeOhlcStart, timeOhlcEnd)
158
+
159
+ //writeJson
160
+ if (withWriteSummary) {
161
+ writeJson(path.resolve(path.dirname(fpOut), 'summary.json'), { name, summary }, { structured: true })
162
+ }
163
+
164
+ //genReportCore
165
+ genReportCore({ name, orders, summary }, fpOut)
166
+
167
+ return { orders, summary, fpOut }
168
+ }
169
+
170
+
171
+ export default genReport
172
+ </code></pre>
173
+ </article>
174
+ </section>
175
+
176
+
177
+
178
+
179
+
180
+
181
+ </div>
182
+
183
+ <br class="clear">
184
+
185
+ <footer>
186
+ 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.
187
+ </footer>
188
+
189
+ <script>prettyPrint();</script>
190
+ <script src="scripts/polyfill.js"></script>
191
+ <script src="scripts/linenumber.js"></script>
192
+
193
+
194
+
195
+ </body>
196
+ </html>
@@ -0,0 +1,128 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+
5
+ <meta charset="utf-8">
6
+ <title>genReportCore.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#runStrategy">runStrategy</a></li></ul>
33
+
34
+ </nav>
35
+
36
+ <div id="main">
37
+
38
+ <h1 class="page-title">genReportCore.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 { fileURLToPath } from 'url'
51
+ import writeTxt from './writeTxt.mjs'
52
+
53
+
54
+ //模板資產(tmp.html, render*.js)與本模組同資料夾, 以模組位置解析, 不依賴 cwd
55
+ let fd = path.dirname(fileURLToPath(import.meta.url))
56
+
57
+ /**
58
+ * 產出html回測報告
59
+ *
60
+ * 讀取與本模組同資料夾之模板資產(tmp.html與render*.js),置換{name}、{orders}、{summary}後寫出html
61
+ * 報告內含權益曲線、訂單表格、時間軸與摘要
62
+ *
63
+ * Unit Test: {@link https://github.com/yuda-lyu/w-trade-backtest/blob/master/test/unit-WTradeBacktest.test.mjs Github}
64
+ * @function
65
+ * @param {Object} r 輸入報告物件,需含name(報告名稱字串)、orders(訂單陣列)、summary(摘要物件)欄位
66
+ * @param {String} fpOut 輸入輸出html檔案路徑字串
67
+ * @returns {undefined} 無回傳,於fpOut寫出html報告
68
+ * @example
69
+ *
70
+ * //rr為runStrategies範例之回傳{orders,summary}
71
+ * genReportCore({ name: '示範策略', orders: rr.orders, summary: rr.summary }, './report.html')
72
+ * // => 於'./report.html'寫出html報告
73
+ *
74
+ */
75
+ let genReportCore = (r, fpOut) => {
76
+
77
+ let rd = (fn) => {
78
+ let fp = path.resolve(fd, fn)
79
+ let h = fs.readFileSync(fp, 'utf8')
80
+ return h
81
+ }
82
+
83
+ let h = rd('tmp.html')
84
+ let hPlot = rd('renderProfitPlot.js')
85
+ let hTable = rd('renderOrdersTable.js')
86
+ let hTimeline = rd('renderOrdersTimeline.js')
87
+ let hSummary = rd('renderSummary.js')
88
+
89
+ h = h.replace(`{renderProfitPlot}`, hPlot)
90
+ h = h.replace(`{renderOrdersTable}`, hTable)
91
+ h = h.replace(`{renderOrdersTimeline}`, hTimeline)
92
+ h = h.replace(`{renderSummary}`, hSummary)
93
+ h = h.replace(`{name}`, r.name)
94
+ h = h.replace(`{orders}`, JSON.stringify(r.orders))
95
+ h = h.replace(`{summary}`, JSON.stringify(r.summary))
96
+
97
+ //writeTxt
98
+ writeTxt(fpOut, h)
99
+
100
+ }
101
+
102
+
103
+ export default genReportCore
104
+ </code></pre>
105
+ </article>
106
+ </section>
107
+
108
+
109
+
110
+
111
+
112
+
113
+ </div>
114
+
115
+ <br class="clear">
116
+
117
+ <footer>
118
+ 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.
119
+ </footer>
120
+
121
+ <script>prettyPrint();</script>
122
+ <script src="scripts/polyfill.js"></script>
123
+ <script src="scripts/linenumber.js"></script>
124
+
125
+
126
+
127
+ </body>
128
+ </html>