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,198 @@
1
+
2
+
3
+ let renderProfitPlot = (ele, orders, opt = {}) => {
4
+
5
+ let _ = window._
6
+ let w = window.wsemi
7
+ let ot = window.dayjs
8
+ let echarts = window.echarts
9
+
10
+ if (!w.isEle(ele)) {
11
+ throw new Error(`invalid ele`)
12
+ }
13
+ if (!w.isearr(orders)) {
14
+ throw new Error(`invalid orders`)
15
+ }
16
+
17
+ let p2r = (p) => {
18
+ p = w.cstr(p)
19
+ p = p.replace('%', '')
20
+ p = w.cdbl(p)
21
+ // p /= 100
22
+ return p
23
+ }
24
+
25
+ //以target為中心, 抓最大距離*1.2
26
+ let mkMin = (target) => {
27
+ let f = (value) => {
28
+ // console.log(value.min, value.max)
29
+ let d = _.max([target - value.min, value.max - target])
30
+ return target - w.cdbl(d * 1.2)
31
+ }
32
+ return f
33
+ }
34
+ let mkMax = (target) => {
35
+ let f = (value) => {
36
+ // console.log(value.min, value.max)
37
+ let d = _.max([target - value.min, value.max - target])
38
+ return target + w.cdbl(d * 1.2)
39
+ }
40
+ return f
41
+ }
42
+
43
+ let ordersDedup = _
44
+ .chain(orders)
45
+ .groupBy(o => o.timeStart)
46
+ .map(group => _.last(group)) //同一時間取最後一筆
47
+ .sortBy(o => o.timeStart) //重新依時間排序
48
+ .value()
49
+
50
+ ordersDedup = _.filter(ordersDedup, (o) => {
51
+ return w.isnum(o.uCumuProfitOrLoss) && w.isestr(o.rCumuProfitOrLoss)
52
+ })
53
+
54
+ //usEquity
55
+ let usEquity = _.map(ordersDedup, (o) => {
56
+ let t = ot(o.timeStart).toDate()
57
+ return [t, w.cdbl(o.uCumuProfitOrLoss)]
58
+ })
59
+
60
+ //rsEquity
61
+ let rsEquity = _.map(ordersDedup, (o) => {
62
+ let t = ot(o.timeStart).toDate()
63
+ return [t, p2r(o.rCumuProfitOrLoss)]
64
+ })
65
+
66
+ // 基本 optChart
67
+ let optChart = {
68
+ tooltip: {
69
+ trigger: 'axis',
70
+ axisPointer: {
71
+ type: 'cross',
72
+ crossStyle: { color: '#999' },
73
+ },
74
+ },
75
+ grid: {
76
+ left: 80,
77
+ right: 80,
78
+ top: 60,
79
+ bottom: 60,
80
+ containLabel: false,
81
+ },
82
+ legend: {
83
+ data: ['累計盈虧(USDT)', '累計盈虧比例(%)'],
84
+ right: 100,
85
+ bottom: 90,
86
+ orient: 'vertical',
87
+ icon: 'circle',
88
+ itemWidth: 10,
89
+ itemHeight: 10,
90
+ textStyle: {
91
+ fontSize: 12,
92
+ },
93
+ },
94
+ xAxis: [{
95
+ type: 'time',
96
+ boundaryGap: false,
97
+ // min: usEquity[0][0], // 起點時間
98
+ // max: _.last(usEquity)[0], // 終點時間
99
+ axisLabel: {
100
+ formatter: (value) => {
101
+ return ot(value).format('YYYY-MM-DD')
102
+ },
103
+ hideOverlap: false,
104
+ showMinLabel: true, //強制顯示最左
105
+ showMaxLabel: true, //強制顯示最右
106
+ },
107
+ axisLine: {
108
+ onZero: false,
109
+ // lineStyle: { color: '#999' },
110
+ },
111
+ axisTick: {
112
+ show: true,
113
+ inside: false,
114
+ length: 6,
115
+ },
116
+ }],
117
+ yAxis: [
118
+ {
119
+ type: 'value',
120
+ name: '累計盈虧(USDT)',
121
+ position: 'left',
122
+ min: mkMin(0),
123
+ max: mkMax(0),
124
+ axisLine: {
125
+ show: true,
126
+ lineStyle: { color: '#4561b5' },
127
+ },
128
+ axisLabel: {
129
+ hideOverlap: false,
130
+ showMinLabel: false,
131
+ showMaxLabel: false,
132
+ },
133
+ axisTick: {
134
+ show: true,
135
+ inside: true,
136
+ length: 6,
137
+ },
138
+ },
139
+ {
140
+ type: 'value',
141
+ name: '累計盈虧比例(%)',
142
+ position: 'right',
143
+ min: mkMin(0),
144
+ max: mkMax(0),
145
+ axisLine: {
146
+ show: true,
147
+ lineStyle: { color: '#559538' },
148
+ },
149
+ splitLine: { show: false },
150
+ axisLabel: {
151
+ // hideOverlap: false,
152
+ showMinLabel: false,
153
+ showMaxLabel: false,
154
+ },
155
+ axisTick: {
156
+ show: true,
157
+ inside: true,
158
+ length: 6,
159
+ },
160
+ axisPointer: {
161
+ label: {
162
+ formatter: (params) => {
163
+ return `${params.value.toFixed(2)}%`
164
+ }
165
+ }
166
+ },
167
+ },
168
+ ],
169
+ series: [
170
+ {
171
+ name: '累計盈虧(USDT)',
172
+ type: 'line',
173
+ smooth: true,
174
+ yAxisIndex: 0,
175
+ itemStyle: { color: '#7893e3' },
176
+ showSymbol: false,
177
+ data: usEquity,
178
+ },
179
+ {
180
+ name: '累計盈虧比例(%)',
181
+ type: 'line',
182
+ smooth: true,
183
+ yAxisIndex: 1,
184
+ itemStyle: { color: 'transparent' },
185
+ showSymbol: false,
186
+ data: rsEquity,
187
+ },
188
+ ],
189
+ }
190
+
191
+ let chart = echarts.init(ele)
192
+ chart.setOption(optChart)
193
+
194
+ window.addEventListener('resize', () => chart.resize())
195
+
196
+ return chart
197
+ }
198
+
@@ -0,0 +1,194 @@
1
+
2
+
3
+ let genCard = (opt = {}) => {
4
+
5
+ let {
6
+ icon = 'fa-question-circle',
7
+ iconColor = 'text-gray-600',
8
+ iconBg = 'bg-gray-100',
9
+ title = '',
10
+ main = '',
11
+ mainColor = 'text-gray-900',
12
+ sub = '',
13
+ } = opt
14
+
15
+ let h = `
16
+ <div class="stat-card flex items-start gap-4">
17
+ <div class="p-3 rounded-lg ${iconBg} ${iconColor}">
18
+ <i class="fas ${icon} fa-lg"></i>
19
+ </div>
20
+ <div>
21
+ <p class="text-sm font-medium text-gray-500">${title}</p>
22
+ <p class="text-2xl font-bold ${mainColor}">${main}</p>
23
+ <p class="text-md font-semibold text-gray-500">${sub}</p>
24
+ </div>
25
+ </div>
26
+ `
27
+
28
+ return h
29
+ }
30
+
31
+
32
+ let renderSummary = (ele, summary) => {
33
+
34
+ let _ = window._
35
+ let w = window.wsemi
36
+ let ot = window.dayjs
37
+
38
+ if (!w.isEle(ele)) {
39
+ throw new Error(`invalid ele`)
40
+ }
41
+
42
+ let cards = []
43
+
44
+ let isProfit = summary.uEquityFinal >= summary.uIni
45
+
46
+ let profitColor = isProfit ? 'text-green-600' : 'text-red-600'
47
+ let profitBg = isProfit ? 'bg-green-100' : 'bg-red-100'
48
+
49
+ let tStart = ot(summary.timeOhlcStart)
50
+ let tEnd = ot(summary.timeOhlcEnd)
51
+ // console.log('summary.timeOhlcStart', summary.timeOhlcStart, tStart)
52
+ // console.log('summary.timeOhlcEnd', summary.timeOhlcEnd, tEnd)
53
+
54
+ let sStart = tStart.format('YYYY-MM-DD')
55
+ let sEnd = tEnd.format('YYYY-MM-DD')
56
+
57
+ //總報酬率
58
+ cards.push(genCard({
59
+ icon: 'fa-arrow-trend-up',
60
+ iconColor: profitColor,
61
+ iconBg: profitBg,
62
+ title: '總報酬率',
63
+ main: `${summary.rCumuProfitOrLossFinal}`,
64
+ mainColor: profitColor,
65
+ sub: `年化報酬率: ${summary.rCumuProfitOrLossFinalNormYear}`,
66
+ }))
67
+
68
+ //總等效報酬率
69
+ cards.push(genCard({
70
+ icon: 'fa-solid fa-shuffle',
71
+ iconColor: profitColor,
72
+ iconBg: profitBg,
73
+ title: '總等效報酬率',
74
+ main: `${summary.rEquivalentCumuProfitOrLossFinal}`,
75
+ mainColor: profitColor,
76
+ sub: `等效年化報酬率: ${summary.rEquivalentCumuProfitOrLossFinalNormYear}`,
77
+ }))
78
+
79
+ //最終資金
80
+ cards.push(genCard({
81
+ icon: 'fa-arrow-up-right-dots',
82
+ iconColor: profitColor,
83
+ iconBg: profitBg,
84
+ title: '最終資金',
85
+ main: `${w.dig(summary.uEquityFinal, 2)} USDT`,
86
+ mainColor: profitColor,
87
+ sub: `淨盈虧: ${w.dig(summary.uEquityFinal - summary.uIni, 2)} USDT`,
88
+ }))
89
+
90
+ //初始資金
91
+ cards.push(genCard({
92
+ icon: 'fa-coins',
93
+ iconColor: 'text-yellow-600',
94
+ iconBg: 'bg-yellow-100',
95
+ title: '初始資金',
96
+ main: `${w.dig(summary.uIni, 2)} USDT`,
97
+ mainColor: 'text-gray-900',
98
+ sub: '',
99
+ }))
100
+
101
+ //最大回撤
102
+ cards.push(genCard({
103
+ icon: 'fa-arrow-trend-down',
104
+ iconColor: 'text-red-600',
105
+ iconBg: 'bg-red-100',
106
+ title: '最大回撤',
107
+ main: `${w.dig(summary.uDrawdownMax, 2)} USDT`,
108
+ mainColor: 'text-red-500',
109
+ sub: '',
110
+ }))
111
+
112
+ //最大回撤率
113
+ cards.push(genCard({
114
+ icon: 'fa-arrow-trend-down',
115
+ iconColor: 'text-red-600',
116
+ iconBg: 'bg-red-100',
117
+ title: '最大回撤率',
118
+ main: `${summary.rDrawdownMax}`,
119
+ mainColor: 'text-red-500',
120
+ sub: `等效最大回徹率: ${summary.rEquivalentDrawdownMax}`,
121
+ }))
122
+
123
+ //最大持倉筆數
124
+ cards.push(genCard({
125
+ icon: 'fa-layer-group',
126
+ iconColor: 'text-orange-600',
127
+ iconBg: 'bg-orange-100',
128
+ title: '最大持倉筆數',
129
+ main: `${summary.numTradeAllMax} 筆`,
130
+ mainColor: 'text-gray-900',
131
+ sub: `同時出場單數為 ${summary.numTradeAllMax * 2} 筆`,
132
+ }))
133
+
134
+ //最大持倉
135
+ cards.push(genCard({
136
+ icon: 'fa-wallet',
137
+ iconColor: 'text-orange-600',
138
+ iconBg: 'bg-orange-100',
139
+ title: '最大持倉',
140
+ main: `${w.dig(summary.uTradeAllMax, 2)} USDT`,
141
+ mainColor: 'text-gray-900',
142
+ sub: `佔本金: ${summary.rTradeAllMax}`,
143
+ }))
144
+
145
+ //總交易次數
146
+ cards.push(genCard({
147
+ icon: 'fa-solid fa-arrows-rotate',
148
+ iconColor: 'text-blue-600',
149
+ iconBg: 'bg-blue-100',
150
+ title: '總交易次數',
151
+ main: `${summary.numTrade} 次`,
152
+ mainColor: 'text-gray-900',
153
+ sub: '',
154
+ }))
155
+
156
+ //勝率
157
+ cards.push(genCard({
158
+ icon: 'fa-percent',
159
+ iconColor: 'text-indigo-600',
160
+ iconBg: 'bg-indigo-100',
161
+ title: '勝率',
162
+ main: `${summary.rWin}`,
163
+ mainColor: 'text-gray-900',
164
+ sub: '',
165
+ }))
166
+
167
+ //夏普值
168
+ cards.push(genCard({
169
+ icon: 'fa-chart-line',
170
+ iconColor: 'text-emerald-600',
171
+ iconBg: 'bg-emerald-100',
172
+ title: '夏普值',
173
+ main: w.dig(summary.rSharpe, 2),
174
+ mainColor: 'text-emerald-700',
175
+ sub: '風險調整後報酬',
176
+ }))
177
+
178
+ //回測區間
179
+ cards.push(genCard({
180
+ icon: 'fa-calendar-alt',
181
+ iconColor: 'text-sky-600',
182
+ iconBg: 'bg-sky-100',
183
+ title: '回測區間',
184
+ main: `${summary.btDays}日(${summary.btYears}年)`,
185
+ mainColor: 'text-gray-900',
186
+ sub: `${sStart} 至 ${sEnd}`,
187
+ }))
188
+
189
+ let h = cards.join('')
190
+
191
+ ele.innerHTML = h
192
+
193
+ return null
194
+ }
package/dist/tmp.html ADDED
@@ -0,0 +1,232 @@
1
+ <!DOCTYPE html>
2
+ <html lang="zh-Hant">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>交易策略報告</title>
7
+
8
+ <!-- Tailwind CSS -->
9
+ <script src="https://cdn.tailwindcss.com"></script>
10
+
11
+ <!-- Font Awesome -->
12
+ <link
13
+ rel="stylesheet"
14
+ href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css"
15
+ />
16
+
17
+ <!-- lodash -->
18
+ <script src="https://cdn.jsdelivr.net/npm/lodash@4.18.1/lodash.min.js"></script>
19
+
20
+ <!-- wsemi -->
21
+ <script src="https://cdn.jsdelivr.net/npm/wsemi@1.8.67/dist/wsemi.umd.min.js"></script>
22
+ <script>
23
+ let w=wsemi
24
+ </script>
25
+
26
+ <!-- dayjs -->
27
+ <script src="https://cdn.jsdelivr.net/npm/dayjs@1.11.19/dayjs.min.js"></script>
28
+ <script>
29
+ let ot=dayjs
30
+ </script>
31
+
32
+ <!-- ECharts -->
33
+ <script src="https://cdn.jsdelivr.net/npm/echarts@5.5.0/dist/echarts.min.js"></script>
34
+
35
+ <!-- vis-timeline standalone (CSS + JS) -->
36
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/vis-timeline@latest/styles/vis-timeline-graph2d.min.css">
37
+ <script src="https://cdn.jsdelivr.net/npm/vis-timeline@latest/standalone/umd/vis-timeline-graph2d.min.js"></script>
38
+
39
+ <!-- render -->
40
+ <script>
41
+ {renderProfitPlot}
42
+ </script>
43
+ <script>
44
+ {renderOrdersTable}
45
+ </script>
46
+ <script>
47
+ {renderOrdersTimeline}
48
+ </script>
49
+ <script>
50
+ {renderSummary}
51
+ </script>
52
+
53
+ <style>
54
+
55
+ body {
56
+ font-family: "微軟正黑體";
57
+ background-color: #f8fafc; /* 使用更柔和的淺灰色背景 */
58
+ }
59
+
60
+ .stat-card {
61
+ background-color: white;
62
+ border-radius: 0.75rem;
63
+ padding: 1.5rem;
64
+ box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
65
+ transition: all 0.3s ease-in-out;
66
+ border: 1px solid #e5e7eb;
67
+ }
68
+
69
+ .trade-table th {
70
+ background-color: #f1f5f9;
71
+ position: sticky;
72
+ top: 0;
73
+ font-weight: bold;
74
+ text-align: center;
75
+ }
76
+ .trade-table tr:nth-child(even) {
77
+ background-color: #f8fafc;
78
+ }
79
+ .trade-table tr:hover {
80
+ background-color: #e2e8f0;
81
+ }
82
+
83
+ .trade-table td {
84
+ text-align: center;
85
+ }
86
+
87
+ .vis-group {
88
+
89
+ }
90
+
91
+ .vis-label {
92
+ font-size: 12px;
93
+ line-height: 28px;
94
+ }
95
+
96
+ .vis-item {
97
+
98
+ }
99
+
100
+ .vis-range {
101
+
102
+ }
103
+
104
+ .vis-item-content {
105
+ display: block !important;
106
+ font-size: 12px;
107
+ padding: 0px 2px !important;
108
+ }
109
+
110
+ .vis-item.bar-long {
111
+ background-color: #2eb65a;
112
+ border-color: #16A34A;
113
+ color: #fff;
114
+ }
115
+
116
+ .vis-item.bar-short {
117
+ background-color: #d85c5c;
118
+ border-color: #DC2626;
119
+ color: #fff;
120
+ }
121
+
122
+ </style>
123
+ </head>
124
+ <body>
125
+
126
+ <div class=" mx-auto p-4 sm:p-6 lg:p-8">
127
+
128
+ <!-- 標題 -->
129
+ <header class="mb-10">
130
+ <p class="text-md font-semibold text-gray-800">交易策略報告</p>
131
+ <h1 class="text-3xl text-gray-900 mt-2">
132
+ {name}
133
+ </h1>
134
+ </header>
135
+
136
+ <!-- 摘要資訊區 -->
137
+ <section class="mb-8">
138
+
139
+ <h2 class="text-md font-semibold text-gray-800 mb-4">
140
+ 績效總覽
141
+ </h2>
142
+
143
+ <div
144
+ id="ref-summary"
145
+ class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
146
+ </div>
147
+
148
+ </section>
149
+
150
+ <!-- 累計收益圖 -->
151
+ <section class="mb-8">
152
+
153
+ <h2 class="text-md font-semibold text-gray-800 mb-4">
154
+ 收益曲線
155
+ </h2>
156
+
157
+ <div
158
+ class="bg-white rounded-lg shadow-md border border-gray-200"
159
+ >
160
+ <div
161
+ id="ref-chart"
162
+ style="width: 100%; height: 450px;"
163
+ ></div>
164
+ </div>
165
+
166
+ </section>
167
+
168
+ <!-- 交易資訊列表 -->
169
+ <section class="mb-8">
170
+
171
+ <h2 class="text-md font-semibold text-gray-800 mb-4">
172
+ 交易紀錄
173
+ </h2>
174
+
175
+ <div
176
+ class="bg-white rounded-lg shadow-md border border-gray-200 overflow-x-auto max-h-[600px]"
177
+ >
178
+ <div
179
+ id="ref-table"
180
+ ></div>
181
+ </div>
182
+
183
+ </section>
184
+
185
+ <!-- 交易甘特圖 -->
186
+ <section class="mb-8">
187
+
188
+ <h2 class="text-md font-semibold text-gray-800 mb-4">
189
+ 交易時序
190
+ </h2>
191
+
192
+ <div
193
+ class="bg-white rounded-lg shadow-md border border-gray-200 overflow-x-auto max-h-[600px] overflow-hidden"
194
+ >
195
+ <div
196
+ id="ref-timeline"
197
+ ></div>
198
+ </div>
199
+
200
+ </section>
201
+
202
+ </div>
203
+
204
+ <script>
205
+
206
+ let orders = {orders}
207
+ let summary = {summary}
208
+
209
+ document.addEventListener("DOMContentLoaded", () => {
210
+
211
+ // Summary卡區
212
+ let eleSummary = document.getElementById("ref-summary")
213
+ renderSummary(eleSummary, summary)
214
+
215
+ // 累計盈虧圖
216
+ let eleChart = document.getElementById("ref-chart");
217
+ renderProfitPlot(eleChart, orders, { uIni: summary.uIni })
218
+
219
+ // 交易表格
220
+ let eleTable = document.getElementById("ref-table")
221
+ renderOrdersTable(eleTable, orders)
222
+
223
+ // 交易甘特圖
224
+ let eleTimeline = document.getElementById("ref-timeline")
225
+ renderOrdersTimeline(eleTimeline, orders)
226
+
227
+ });
228
+
229
+ </script>
230
+
231
+ </body>
232
+ </html>