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.
- package/.editorconfig +9 -0
- package/.eslintignore +3 -0
- package/.eslintrc.js +55 -0
- package/.jsdoc +25 -0
- package/README.md +167 -0
- package/SECURITY.md +5 -0
- package/babel.config.js +16 -0
- package/dist/renderOrdersTable.js +217 -0
- package/dist/renderOrdersTimeline.js +159 -0
- package/dist/renderProfitPlot.js +198 -0
- package/dist/renderSummary.js +194 -0
- package/dist/tmp.html +232 -0
- package/dist/w-trade-backtest.umd.js +32 -0
- package/dist/w-trade-backtest.umd.js.map +1 -0
- package/docs/calcOrders.mjs.html +278 -0
- package/docs/calcOrdersRatio.mjs.html +146 -0
- package/docs/calcOrdersSummary.mjs.html +506 -0
- package/docs/calcOrdersSummarySimple.mjs.html +207 -0
- package/docs/calcSummary.mjs.html +201 -0
- package/docs/closeAndSummaryOrders.mjs.html +265 -0
- package/docs/fonts/Montserrat/Montserrat-Bold.eot +0 -0
- package/docs/fonts/Montserrat/Montserrat-Bold.ttf +0 -0
- package/docs/fonts/Montserrat/Montserrat-Bold.woff +0 -0
- package/docs/fonts/Montserrat/Montserrat-Bold.woff2 +0 -0
- package/docs/fonts/Montserrat/Montserrat-Regular.eot +0 -0
- package/docs/fonts/Montserrat/Montserrat-Regular.ttf +0 -0
- package/docs/fonts/Montserrat/Montserrat-Regular.woff +0 -0
- package/docs/fonts/Montserrat/Montserrat-Regular.woff2 +0 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.eot +0 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.svg +978 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.ttf +0 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff +0 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff2 +0 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.eot +0 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.svg +1049 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.ttf +0 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff +0 -0
- package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff2 +0 -0
- package/docs/genReport.mjs.html +196 -0
- package/docs/genReportCore.mjs.html +128 -0
- package/docs/global.html +4231 -0
- package/docs/index.html +84 -0
- package/docs/runStrategies.mjs.html +244 -0
- package/docs/runStrategy.mjs.html +455 -0
- package/docs/scripts/collapse.js +39 -0
- package/docs/scripts/commonNav.js +28 -0
- package/docs/scripts/linenumber.js +25 -0
- package/docs/scripts/nav.js +12 -0
- package/docs/scripts/polyfill.js +4 -0
- package/docs/scripts/prettify/Apache-License-2.0.txt +202 -0
- package/docs/scripts/prettify/lang-css.js +2 -0
- package/docs/scripts/prettify/prettify.js +28 -0
- package/docs/scripts/search.js +99 -0
- package/docs/styles/jsdoc.css +776 -0
- package/docs/styles/prettify.css +80 -0
- package/g.mjs +146 -0
- package/package.json +30 -0
- package/script.txt +19 -0
- package/src/WTradeBacktest.mjs +27 -0
- package/src/calcOrders.mjs +206 -0
- package/src/calcOrdersRatio.mjs +74 -0
- package/src/calcOrdersSummary.mjs +434 -0
- package/src/calcOrdersSummarySimple.mjs +135 -0
- package/src/calcSummary.mjs +129 -0
- package/src/closeAndSummaryOrders.mjs +193 -0
- package/src/genReport.mjs +124 -0
- package/src/genReportCore.mjs +56 -0
- package/src/ott.mjs +28 -0
- package/src/readJson.mjs +26 -0
- package/src/renderOrdersTable.js +217 -0
- package/src/renderOrdersTimeline.js +159 -0
- package/src/renderProfitPlot.js +198 -0
- package/src/renderSummary.js +194 -0
- package/src/runStrategies.mjs +172 -0
- package/src/runStrategy.mjs +383 -0
- package/src/tmp.html +232 -0
- package/src/writeJson.mjs +31 -0
- package/src/writeTxt.mjs +19 -0
- package/test/unit-WTradeBacktest.test.mjs +541 -0
- package/toolg/addVersion.mjs +4 -0
- package/toolg/cleanFolder.mjs +5 -0
- package/toolg/gDistRollup.mjs +51 -0
- package/toolg/gDocsExams.mjs +48 -0
- package/toolg/modifyReadme.mjs +4 -0
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import flatten from 'lodash-es/flatten.js'
|
|
2
|
+
import get from 'lodash-es/get.js'
|
|
3
|
+
import isNumber from 'lodash-es/isNumber.js'
|
|
4
|
+
import map from 'lodash-es/map.js'
|
|
5
|
+
import size from 'lodash-es/size.js'
|
|
6
|
+
import sortBy from 'lodash-es/sortBy.js'
|
|
7
|
+
import isestr from 'wsemi/src/isestr.mjs'
|
|
8
|
+
import pmSeries from 'wsemi/src/pmSeries.mjs'
|
|
9
|
+
import runStrategy from './runStrategy.mjs'
|
|
10
|
+
import calcSummary from './calcSummary.mjs'
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* 執行多策略回測
|
|
15
|
+
*
|
|
16
|
+
* 逐策略呼叫runStrategy(withSummary:false),各單附加策略sid後合併並依timeStart排序
|
|
17
|
+
* 以各策略settings.uIni總和為初始資金呼叫calcSummary統計合併摘要
|
|
18
|
+
* 個別策略執行失敗(如時間點無參數無法下單)時強制略過
|
|
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 {Function} ott 輸入時區時間函數,傳入時間字串回傳dayjs時間物件(可用src/ott.mjs或自行以dayjs包裝)
|
|
23
|
+
* @param {Array} strategies 輸入策略陣列,各元素需含sid與runStrategy之strategy欄位
|
|
24
|
+
* @param {Function} funGetSeries 輸入序列查詢async函數,依key回傳時間序列陣列
|
|
25
|
+
* @param {Object} [opt={}] 輸入設定物件,預設{}
|
|
26
|
+
* @param {Boolean} [opt.withCalcOrderProfitOrLoss=true] 輸入是否經calcOrders結算訂單布林值,預設true
|
|
27
|
+
* @param {Boolean} [opt.withSummary=true] 輸入是否計算統計摘要布林值,預設true
|
|
28
|
+
* @returns {Promise} 回傳Promise,resolve為{orders,summary}物件
|
|
29
|
+
* @example
|
|
30
|
+
*
|
|
31
|
+
* let arrOhlc = [
|
|
32
|
+
* { time: '2020-01-01T00:00:00', Open: 100, High: 101, Low: 99, Close: 100 },
|
|
33
|
+
* { time: '2020-01-01T04:00:00', Open: 100, High: 106, Low: 100, Close: 105 },
|
|
34
|
+
* { time: '2020-01-01T08:00:00', Open: 105, High: 107, Low: 102, Close: 103 },
|
|
35
|
+
* { time: '2020-01-01T12:00:00', Open: 103, High: 104, Low: 94, Close: 95 },
|
|
36
|
+
* { time: '2020-01-01T16:00:00', Open: 95, High: 98, Low: 92, Close: 93 },
|
|
37
|
+
* { time: '2020-01-01T20:00:00', Open: 93, High: 99, Low: 95, Close: 97 },
|
|
38
|
+
* ]
|
|
39
|
+
*
|
|
40
|
+
* let arrSig = [
|
|
41
|
+
* { time: '2020-01-01T00:00:00', param: 1 },
|
|
42
|
+
* { time: '2020-01-01T04:00:00', param: 0 },
|
|
43
|
+
* { time: '2020-01-01T08:00:00', param: 1 },
|
|
44
|
+
* { time: '2020-01-01T12:00:00', param: 0 },
|
|
45
|
+
* { time: '2020-01-01T16:00:00', param: 0 },
|
|
46
|
+
* { time: '2020-01-01T20:00:00', param: 0 },
|
|
47
|
+
* ]
|
|
48
|
+
*
|
|
49
|
+
* let funGetSeries = async (key) => {
|
|
50
|
+
* if (key === 'btc') {
|
|
51
|
+
* return arrOhlc
|
|
52
|
+
* }
|
|
53
|
+
* if (key === 'sig') {
|
|
54
|
+
* return arrSig
|
|
55
|
+
* }
|
|
56
|
+
* throw new Error(`invalid key[${key}]`)
|
|
57
|
+
* }
|
|
58
|
+
*
|
|
59
|
+
* let settings = { uIni: 1000, uTrade: 100, rTakeProfit: 0.05, rStopLoss: 0.03, rFee: 0.0005 }
|
|
60
|
+
* let strategies = [
|
|
61
|
+
* { sid: 's1', mode: 'long', keyOhlc: 'btc', conds: [{ key: 'sig', sym: '>', th: 0.5, opr: 'and' }], settings },
|
|
62
|
+
* { sid: 's2', mode: 'short', keyOhlc: 'btc', conds: [{ key: 'sig', sym: '<', th: 0.5, opr: 'and' }], settings },
|
|
63
|
+
* ]
|
|
64
|
+
*
|
|
65
|
+
* let rr = await runStrategies(ott, strategies, funGetSeries)
|
|
66
|
+
* console.log(rr.orders.map((o) => `${o.sid} ${o.timeStart} ${o.mode} ${o.modeResult || 'unsettled'}`))
|
|
67
|
+
* // => [
|
|
68
|
+
* // 's1 2020-01-01T00:00:00 long profit',
|
|
69
|
+
* // 's2 2020-01-01T04:00:00 short profit',
|
|
70
|
+
* // 's1 2020-01-01T08:00:00 long loss',
|
|
71
|
+
* // 's2 2020-01-01T12:00:00 short loss',
|
|
72
|
+
* // 's2 2020-01-01T16:00:00 short loss',
|
|
73
|
+
* // 's2 2020-01-01T20:00:00 short unsettled'
|
|
74
|
+
* // ]
|
|
75
|
+
* console.log(rr.summary.uIni, rr.summary.numTrade, rr.summary.rWin)
|
|
76
|
+
* // => 2000 6 40.00%
|
|
77
|
+
*
|
|
78
|
+
*/
|
|
79
|
+
let runStrategies = async (ott, strategies, funGetSeries, opt = {}) => {
|
|
80
|
+
|
|
81
|
+
//withCalcOrderProfitOrLoss
|
|
82
|
+
let withCalcOrderProfitOrLoss = get(opt, 'withCalcOrderProfitOrLoss', true)
|
|
83
|
+
|
|
84
|
+
//withSummary
|
|
85
|
+
let withSummary = get(opt, 'withSummary', true)
|
|
86
|
+
|
|
87
|
+
//uIniAll, ordersAll
|
|
88
|
+
let uIniAll = 0
|
|
89
|
+
let ordersAll = []
|
|
90
|
+
let r = null
|
|
91
|
+
await pmSeries(strategies, async(strategy, k) => {
|
|
92
|
+
|
|
93
|
+
//有些策略因為時間點無參數故無法計算, 代表無法下單, 故強制略過
|
|
94
|
+
try {
|
|
95
|
+
|
|
96
|
+
//runStrategy
|
|
97
|
+
r = await runStrategy(ott, strategy, funGetSeries, { withCalcOrderProfitOrLoss, withSummary: false })
|
|
98
|
+
|
|
99
|
+
//uIni
|
|
100
|
+
let uIni = get(strategy, 'settings.uIni', '')
|
|
101
|
+
if (!isNumber(uIni)) {
|
|
102
|
+
throw new Error(`uIni[${uIni}] is not a number`)
|
|
103
|
+
}
|
|
104
|
+
// console.log(' ', 'uIni =', uIni, 'numTrade =', strategy.summary.numTrade)
|
|
105
|
+
|
|
106
|
+
//累加初始金額(本金)
|
|
107
|
+
uIniAll += uIni
|
|
108
|
+
|
|
109
|
+
//add sid, push
|
|
110
|
+
if (size(r.orders) >= 1) {
|
|
111
|
+
|
|
112
|
+
//各單額外儲存策略sid
|
|
113
|
+
r.orders = map(r.orders, (o) => {
|
|
114
|
+
o.sid = strategy.sid
|
|
115
|
+
return o
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
//push
|
|
119
|
+
ordersAll.push(r.orders)
|
|
120
|
+
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
}
|
|
124
|
+
catch (err) {
|
|
125
|
+
// console.log(err)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
})
|
|
129
|
+
ordersAll = flatten(ordersAll)
|
|
130
|
+
// console.log('ordersAll', ordersAll)
|
|
131
|
+
|
|
132
|
+
//sortBy
|
|
133
|
+
if (size(ordersAll) >= 2) {
|
|
134
|
+
ordersAll = sortBy(ordersAll, 'timeStart')
|
|
135
|
+
}
|
|
136
|
+
// console.log('size(ordersAll)', size(ordersAll))
|
|
137
|
+
|
|
138
|
+
//timeOhlcStart, timeOhlcEnd
|
|
139
|
+
let timeOhlcStart = get(r, 'summary.timeOhlcStart', '') //各策略ohlc起訖時間皆相同, 取最後r內的timeOhlcStart來使用
|
|
140
|
+
let timeOhlcEnd = get(r, 'summary.timeOhlcEnd', '') //各策略ohlc起訖時間皆相同, 取最後r內的timeOhlcEnd來使用
|
|
141
|
+
|
|
142
|
+
//check
|
|
143
|
+
if (!isestr(timeOhlcStart)) {
|
|
144
|
+
throw new Error(`invalid timeOhlcStart`)
|
|
145
|
+
}
|
|
146
|
+
if (!isestr(timeOhlcEnd)) {
|
|
147
|
+
throw new Error(`invalid timeOhlcEnd`)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
//summary
|
|
151
|
+
let summary = {
|
|
152
|
+
timeTest: ott().format('YYYY-MM-DDTHH:mm:ssZ'),
|
|
153
|
+
timeOhlcStart,
|
|
154
|
+
timeOhlcEnd,
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
//withSummary
|
|
158
|
+
if (withSummary) {
|
|
159
|
+
summary = await calcSummary(ott, uIniAll, ordersAll, timeOhlcStart, timeOhlcEnd)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
//rr
|
|
163
|
+
let rr = {
|
|
164
|
+
orders: ordersAll,
|
|
165
|
+
summary,
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return rr
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export default runStrategies
|
|
172
|
+
|