Methods
(async) buildKLinesTpSl(fdOhlc, fdParam, fpJson, optopt) → {Promise}
- Description:
建置各K線與各止盈止損組合之先觸結果快取檔
以w-data-tdprovide讀取fdOhlc與fdParam資料夾內keyOhlc序列,取ts至te範圍K線後,逐一組合modes與tps呼叫genKLinesTpSl 各組合以
${mode}_${tpsl}為key存入各根K線,win為1(止盈先觸)、0(止損先觸)或-1(至區間末仍未結) timeEnd於建置時由hold位移換算為絕對時間,故下游查表即得而不需位置索引,未結者為null 僅存win、timeEnd與各根close,盈虧由下游loadKLinesTpSl之ordersFromBars以close與手續費重算 fdOhlc或fdParam非資料夾、fpJson或opt.keyOhlc非有效字串、opt.ts或opt.te非時間字串時throwUnit Test: Github
- Source:
Example
import fs from 'fs'
let arrOhlc = [
{ time: '2020-01-01T00:00:00', Open: 100, High: 101, Low: 99, Close: 100 },
{ time: '2020-01-01T04:00:00', Open: 100, High: 106, Low: 100, Close: 105 },
{ time: '2020-01-01T08:00:00', Open: 105, High: 107, Low: 102, Close: 103 },
{ time: '2020-01-01T12:00:00', Open: 103, High: 104, Low: 94, Close: 95 },
{ time: '2020-01-01T16:00:00', Open: 95, High: 98, Low: 92, Close: 93 },
{ time: '2020-01-01T20:00:00', Open: 93, High: 99, Low: 95, Close: 97 },
]
//建立數據資料夾, fdOhlc放K線序列, fdParam放指標參數序列
fs.mkdirSync('./data-ohlc', { recursive: true })
fs.mkdirSync('./data-param', { recursive: true })
fs.writeFileSync('./data-ohlc/btc.json', JSON.stringify(arrOhlc), 'utf8')
let r = await buildKLinesTpSl('./data-ohlc', './data-param', './cache/kltpsl.json', {
keyOhlc: 'btc',
ts: '2020-01-01T00:00:00',
te: '2020-01-01T20:00:00',
tps: [5],
modes: ['long'],
})
console.log(r.meta)
// => {
// keyOhlc: 'btc',
// ts: '2020-01-01T00:00:00',
// te: '2020-01-01T20:00:00',
// tps: [ 5 ],
// modes: [ 'long' ],
// nBars: 6
// }
console.log(r.bars['2020-01-01T00:00:00'])
// => { close: 100, long_5: { win: 1, timeEnd: '2020-01-01T04:00:00' } }
console.log(r.bars['2020-01-01T12:00:00'])
// => { close: 95, long_5: { win: -1, timeEnd: null } }
Parameters:
| Name | Type | Attributes | Default | Description | ||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
fdOhlc |
String | 輸入儲存K線(ohlc)序列資料夾字串,各序列以 |
||||||||||||||||||||||||||||||||
fdParam |
String | 輸入儲存指標參數序列資料夾字串 |
||||||||||||||||||||||||||||||||
fpJson |
String | 輸入輸出快取json檔案路徑字串,資料夾不存在時自動建立 |
||||||||||||||||||||||||||||||||
opt |
Object |
<optional> |
{}
|
輸入設定物件,預設{} Properties
|
Returns:
回傳Promise,resolve為{meta,bars}物件,meta內含keyOhlc、ts、te、tps、modes與nBars(K線根數),bars以進場時間為key,各值內含close與各${mode}_${tpsl}之{win,timeEnd},並同步寫出至fpJson
- Type
- Promise
buildStrategyFastSession(ott, opt) → {Object}
- Description:
建立策略快速評估session,供求解熱路徑(如RGA/PSO目標函數)重複評估同一K線窗之不同條件組合
建置期一次攤提runStrategy每次呼叫都要重建的不變量: 各因子{time,param}序列轉為K棒對位Float64Array(缺值填NaN)、各(tp,sl)組合之first-touch預算表(genKLinesTpSl,惰性建置+Map快取) 評估期單迴圈逐棒判斷conds觸發並沿calcOrders同式結算(settleOrderPnl),摘要直接以calcOrdersSummarySimple計算,與官方全鏈(runStrategy→calcOrders→calcOrdersSummary)對應欄位嚴格相等 觸發語義與runStrategy等價: 任一cond序列於該棒缺值(NaN)即整棒跳過(等同僅於全部cond序列皆有值之共同時間點判斷)、'and'組須全真且'or'組至少一真、sym為'>'比大於否則比小於 序列param須為有限數值,缺值以該時間點不存在表示; opt.mode非'long'或'short'、arrOhlc或serieses無效時throw; evaluate之conds非有效陣列(含空陣列, 同runStrategy)或cond.key不在serieses時throw
Unit Test: Github
- Source:
Example
let arrOhlc = [
{ time: '2020-01-01T00:00:00', Open: 100, High: 101, Low: 99, Close: 100 },
{ time: '2020-01-01T04:00:00', Open: 100, High: 106, Low: 100, Close: 105 },
{ time: '2020-01-01T08:00:00', Open: 105, High: 107, Low: 102, Close: 103 },
{ time: '2020-01-01T12:00:00', Open: 103, High: 104, Low: 94, Close: 95 },
{ time: '2020-01-01T16:00:00', Open: 95, High: 98, Low: 92, Close: 93 },
{ time: '2020-01-01T20:00:00', Open: 93, High: 99, Low: 95, Close: 97 },
]
let arrSig = [
{ time: '2020-01-01T00:00:00', param: 1 },
{ time: '2020-01-01T04:00:00', param: 0 },
{ time: '2020-01-01T08:00:00', param: 1 },
{ time: '2020-01-01T12:00:00', param: 0 },
{ time: '2020-01-01T16:00:00', param: 0 },
{ time: '2020-01-01T20:00:00', param: 0 },
]
let session = buildStrategyFastSession(ott, {
arrOhlc,
mode: 'long',
serieses: { sig: arrSig },
})
let sm = session.evaluate({
conds: [{ key: 'sig', sym: '>', th: 0.5, opr: 'and' }],
tp: 0.05,
sl: 0.03,
settings: { uIni: 1000, uTrade: 100, rFee: 0.0005 },
})
console.log(sm.numTrade, sm.rWin, sm.uEquityFinal)
// => 2 50.00% 1001.8
Parameters:
| Name | Type | Description | |||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
ott |
function | 輸入時區時間函數,傳入時間字串回傳dayjs時間物件(可用src/ott.mjs或自行以dayjs包裝) |
|||||||||||||||||||||||||
opt |
Object | 輸入設定物件 Properties
|
Returns:
回傳session物件,內含evaluate({conds,tp,sl,settings},opt)評估函數,conds各元素為{key,sym,th,opr}(與runStrategy同構),settings可給uIni(預設1000)、uTrade(預設1)、rFee(預設0.0005),evaluate預設回傳calcOrdersSummarySimple同構摘要物件,evaluate之opt.withOrders為true時回傳{orders,summary}(orders與loadKLinesTpSl之ordersFromBars同構,供除錯對照之慢速路徑)
- Type
- Object
(async) calcOrders(arrOhlc, orders, optopt) → {Promise}
- Description:
結算訂單,依K線序列判斷各下單之止盈止損觸發並計算盈虧
各單僅檢查time大於timeStart之K棒,long以Low<=priceStopLoss先判止損、High>=priceTakeProfit判止盈,short以High>=priceStopLoss先判止損、Low<=priceTakeProfit判止盈 long盈虧為uTrade*(priceEnd/priceStart)-uTrade-2uFee,short盈虧為(priceStart-priceEnd)(uTrade/priceStart)-2*uFee 依timeStart排序逐單累計uCumuProfitOrLoss與uEquity(自uIni起算),modeResult為盈虧<0時'loss'否則'profit',未觸發止盈止損者維持原單(未平倉)
Unit Test: Github
- Source:
Example
let arrOhlc = [
{ time: '2020-01-01T00:00:00', Open: 100, High: 101, Low: 99, Close: 100 },
{ time: '2020-01-01T04:00:00', Open: 100, High: 106, Low: 100, Close: 105 },
{ time: '2020-01-01T08:00:00', Open: 105, High: 107, Low: 102, Close: 103 },
{ time: '2020-01-01T12:00:00', Open: 103, High: 104, Low: 94, Close: 95 },
{ time: '2020-01-01T16:00:00', Open: 95, High: 98, Low: 92, Close: 93 },
{ time: '2020-01-01T20:00:00', Open: 93, High: 99, Low: 95, Close: 97 },
]
let orders = [{
mode: 'long',
timeStart: '2020-01-01T00:00:00',
priceStart: 100,
uTrade: 100,
priceTakeProfit: 105,
priceStopLoss: 97,
timeEnd: '',
priceEnd: '',
modeResult: '',
uFee: 0.05,
}]
calcOrders(arrOhlc, orders, { uIni: 1000 })
.then((rs) => {
console.log(rs[0])
// => {
// mode: 'long',
// timeStart: '2020-01-01T00:00:00',
// priceStart: 100,
// uTrade: 100,
// priceTakeProfit: 105,
// priceStopLoss: 97,
// timeEnd: '2020-01-01T04:00:00',
// priceEnd: 105,
// modeResult: 'profit',
// uFee: 0.05,
// uProfitOrLoss: 4.9,
// rProfitOrLoss: 0.049,
// uCumuProfitOrLoss: 4.9,
// rCumuProfitOrLoss: '0.49%',
// uEquity: 1004.9
// }
})
Parameters:
| Name | Type | Attributes | Default | Description | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
arrOhlc |
Array | 輸入K線陣列,各元素需含time、High、Low欄位 |
||||||||||||
orders |
Array | 輸入下單陣列,各元素需含mode('long'或'short')、timeStart、priceStart、uTrade、priceTakeProfit、priceStopLoss、uFee欄位 |
||||||||||||
opt |
Object |
<optional> |
{}
|
輸入設定物件,預設{} Properties
|
Returns:
回傳Promise,resolve為結算後訂單陣列
- Type
- Promise
calcOrdersRatio(ott, orders) → {Array}
- Description:
計算各已平倉訂單之持倉天數與等效日盈虧比例
dayHold以timeStart與timeEnd之日期差+1計算,rProfitOrLossDay為(1+rProfitOrLoss)^(1/dayHold)-1 modeResult非有效字串(未平倉)之訂單不計算
Unit Test: Github
- Source:
Example
let orders = calcOrdersRatio(ott, [{
modeResult: 'profit',
timeStart: '2020-01-01T20:00:00',
timeEnd: '2020-01-03T04:00:00',
rProfitOrLoss: 0.331,
}])
console.log(orders[0])
// => {
// modeResult: 'profit',
// timeStart: '2020-01-01T20:00:00',
// timeEnd: '2020-01-03T04:00:00',
// rProfitOrLoss: 0.331,
// dayHold: 3,
// rProfitOrLossDay: 0.10000000000000009
// }
Parameters:
| Name | Type | Description |
|---|---|---|
ott |
function | 輸入時區時間函數,傳入時間字串回傳dayjs時間物件(可用src/ott.mjs或自行以dayjs包裝) |
orders |
Array | 輸入已結算訂單陣列,各元素需含modeResult、timeStart、timeEnd、rProfitOrLoss欄位 |
Returns:
回傳附加dayHold與rProfitOrLossDay之訂單陣列
- Type
- Array
calcOrdersSummary(ott, uIni, ordersAll, timeOhlcStart, timeOhlcEnd) → {Object}
- Description:
統計全部訂單之回測摘要
統計交易次數、勝率、最大回撤、最大持倉、夏普值、最終權益與各盈虧比例等欄位 訂單需先經calcOrders結算(含uEquity)與calcOrdersRatio計算(含dayHold與rProfitOrLossDay,夏普值計算所需) uIni非正數或timeOhlcStart、timeOhlcEnd非有效字串時throw
Unit Test: Github
- Source:
Example
let arrOhlc = [
{ time: '2020-01-01T00:00:00', Open: 100, High: 101, Low: 99, Close: 100 },
{ time: '2020-01-01T04:00:00', Open: 100, High: 106, Low: 100, Close: 105 },
{ time: '2020-01-01T08:00:00', Open: 105, High: 107, Low: 102, Close: 103 },
{ time: '2020-01-01T12:00:00', Open: 103, High: 104, Low: 94, Close: 95 },
{ time: '2020-01-01T16:00:00', Open: 95, High: 98, Low: 92, Close: 93 },
{ time: '2020-01-01T20:00:00', Open: 93, High: 99, Low: 95, Close: 97 },
]
//ordersSubmit, 4張下單: 前3張分別於後續K棒觸發止盈或止損, 第4張未觸發(未平倉)
let kpDef = { uTrade: 100, timeEnd: '', priceEnd: '', modeResult: '', uFee: 0.05 }
let ordersSubmit = [
{ mode: 'long', timeStart: '2020-01-01T00:00:00', priceStart: 100, priceTakeProfit: 105, priceStopLoss: 97, ...kpDef },
{ mode: 'long', timeStart: '2020-01-01T04:00:00', priceStart: 105, priceTakeProfit: 110.25, priceStopLoss: 101.85, ...kpDef },
{ mode: 'short', timeStart: '2020-01-01T08:00:00', priceStart: 103, priceTakeProfit: 97.85, priceStopLoss: 106.09, ...kpDef },
{ mode: 'long', timeStart: '2020-01-01T16:00:00', priceStart: 93, priceTakeProfit: 105, priceStopLoss: 85, ...kpDef },
]
let orders = await calcOrders(arrOhlc, ordersSubmit, { uIni: 1000 })
orders = calcOrdersRatio(ott, orders)
let summary = calcOrdersSummary(ott, 1000, orders, '2020-01-01T00:00:00', '2020-01-01T20:00:00')
console.log(summary)
// => {
// btDays: 0,
// btYears: '0.0',
// numTrade: 4,
// numTradeFin: 3,
// numTradeUnsettled: 1,
// uTradeAllMax: 200,
// rTradeAllMax: '20.00%',
// numTradeAllMax: 2,
// uDrawdownMax: 3.1000000000000227,
// rDrawdownMax: '0.31%',
// rEquivalentDrawdownMax: '1.51%',
// rSharpe: 7.675814289051032,
// rWin: '66.67%',
// uEquityFinal: 1006.6999999999999,
// uCumuProfitOrLossFinal: 6.700000000000006,
// rCumuProfitOrLossFinal: '0.67%',
// rCumuProfitOrLossFinalNormYear: '0.00%',
// rEquivalentCumuProfitOrLossFinal: '3.35%',
// rEquivalentCumuProfitOrLossFinalNormYear: '0.00%'
// }
Parameters:
| Name | Type | Description |
|---|---|---|
ott |
function | 輸入時區時間函數,傳入時間字串回傳dayjs時間物件(可用src/ott.mjs或自行以dayjs包裝) |
uIni |
Number | 輸入初始資金正數 |
ordersAll |
Array | 輸入已結算訂單陣列 |
timeOhlcStart |
String | 輸入回測起始秒時間字串 |
timeOhlcEnd |
String | 輸入回測結束秒時間字串 |
Returns:
回傳回測摘要物件
- Type
- Object
calcOrdersSummarySimple(ott, uIni, ordersAll, timeOhlcStart, timeOhlcEnd, optopt) → {Object}
- Description:
統計全部訂單之精簡版回測摘要
為calcOrdersSummary之精簡版,只算選股熱路徑所需欄位,純迴圈實作且不需先跑calcOrdersRatio 對應欄位與完整版bit-exact: numTrade、numTradeFin、rWin、uTradeAllMax、rTradeAllMax、uEquityFinal、btDays、btYears、rEquivalentCumuProfitOrLossFinalNormYear 不算drawdown、夏普值與累計曲線
Unit Test: Github
- Source:
Example
let arrOhlc = [
{ time: '2020-01-01T00:00:00', Open: 100, High: 101, Low: 99, Close: 100 },
{ time: '2020-01-01T04:00:00', Open: 100, High: 106, Low: 100, Close: 105 },
{ time: '2020-01-01T08:00:00', Open: 105, High: 107, Low: 102, Close: 103 },
{ time: '2020-01-01T12:00:00', Open: 103, High: 104, Low: 94, Close: 95 },
{ time: '2020-01-01T16:00:00', Open: 95, High: 98, Low: 92, Close: 93 },
{ time: '2020-01-01T20:00:00', Open: 93, High: 99, Low: 95, Close: 97 },
]
//ordersSubmit, 4張下單: 前3張分別於後續K棒觸發止盈或止損, 第4張未觸發(未平倉)
let kpDef = { uTrade: 100, timeEnd: '', priceEnd: '', modeResult: '', uFee: 0.05 }
let ordersSubmit = [
{ mode: 'long', timeStart: '2020-01-01T00:00:00', priceStart: 100, priceTakeProfit: 105, priceStopLoss: 97, ...kpDef },
{ mode: 'long', timeStart: '2020-01-01T04:00:00', priceStart: 105, priceTakeProfit: 110.25, priceStopLoss: 101.85, ...kpDef },
{ mode: 'short', timeStart: '2020-01-01T08:00:00', priceStart: 103, priceTakeProfit: 97.85, priceStopLoss: 106.09, ...kpDef },
{ mode: 'long', timeStart: '2020-01-01T16:00:00', priceStart: 93, priceTakeProfit: 105, priceStopLoss: 85, ...kpDef },
]
let orders = await calcOrders(arrOhlc, ordersSubmit, { uIni: 1000 })
let summary = calcOrdersSummarySimple(ott, 1000, orders, '2020-01-01T00:00:00', '2020-01-01T20:00:00')
console.log(summary)
// => {
// numTrade: 4,
// numTradeFin: 3,
// rWin: '66.67%',
// uTradeAllMax: 200,
// rTradeAllMax: '20.00%',
// uEquityFinal: 1006.6999999999999,
// btDays: 0,
// btYears: '0.0',
// rEquivalentCumuProfitOrLossFinalNormYear: '0.00%',
// timeOhlcStart: '2020-01-01T00:00:00',
// timeOhlcEnd: '2020-01-01T20:00:00'
// }
Parameters:
| Name | Type | Attributes | Default | Description | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
ott |
function | 輸入時區時間函數,傳入時間字串回傳dayjs時間物件(可用src/ott.mjs或自行以dayjs包裝) |
||||||||||||
uIni |
Number | 輸入初始資金正數 |
||||||||||||
ordersAll |
Array | 輸入已結算訂單陣列(經calcOrders設定timeStart、timeEnd、uTrade、uEquity、modeResult) |
||||||||||||
timeOhlcStart |
String | 輸入回測起始秒時間字串 |
||||||||||||
timeOhlcEnd |
String | 輸入回測結束秒時間字串 |
||||||||||||
opt |
Object |
<optional> |
{}
|
輸入設定物件,預設{} Properties
|
Returns:
回傳精簡版回測摘要物件
- Type
- Object
(async) calcSummary(ott, uIni, orders, timeOhlcStart, timeOhlcEnd, optopt) → {Promise}
- Description:
基於全部交易單重算累積收益並統計回測摘要
依timeStart排序後重算各已平倉單之uEquity、uCumuProfitOrLoss與rCumuProfitOrLoss(多策略各自結算後合併重算用) 再執行calcOrdersRatio與calcOrdersSummary,並附加timeTest、timeOhlcStart、timeOhlcEnd、uIni欄位 uIni非正數或timeOhlcStart、timeOhlcEnd非有效字串時reject
Unit Test: Github
- Source:
Example
let arrOhlc = [
{ time: '2020-01-01T00:00:00', Open: 100, High: 101, Low: 99, Close: 100 },
{ time: '2020-01-01T04:00:00', Open: 100, High: 106, Low: 100, Close: 105 },
{ time: '2020-01-01T08:00:00', Open: 105, High: 107, Low: 102, Close: 103 },
{ time: '2020-01-01T12:00:00', Open: 103, High: 104, Low: 94, Close: 95 },
{ time: '2020-01-01T16:00:00', Open: 95, High: 98, Low: 92, Close: 93 },
{ time: '2020-01-01T20:00:00', Open: 93, High: 99, Low: 95, Close: 97 },
]
//ordersSubmit, 4張下單: 前3張分別於後續K棒觸發止盈或止損, 第4張未觸發(未平倉)
let kpDef = { uTrade: 100, timeEnd: '', priceEnd: '', modeResult: '', uFee: 0.05 }
let ordersSubmit = [
{ mode: 'long', timeStart: '2020-01-01T00:00:00', priceStart: 100, priceTakeProfit: 105, priceStopLoss: 97, ...kpDef },
{ mode: 'long', timeStart: '2020-01-01T04:00:00', priceStart: 105, priceTakeProfit: 110.25, priceStopLoss: 101.85, ...kpDef },
{ mode: 'short', timeStart: '2020-01-01T08:00:00', priceStart: 103, priceTakeProfit: 97.85, priceStopLoss: 106.09, ...kpDef },
{ mode: 'long', timeStart: '2020-01-01T16:00:00', priceStart: 93, priceTakeProfit: 105, priceStopLoss: 85, ...kpDef },
]
let orders = await calcOrders(arrOhlc, ordersSubmit, { uIni: 1000 })
let summary = await calcSummary(ott, 1000, orders, '2020-01-01T00:00:00', '2020-01-01T20:00:00')
console.log(summary)
// => {
// timeTest: '2026-08-01T22:27:10+08:00', //執行當下時間
// timeOhlcStart: '2020-01-01T00:00:00',
// timeOhlcEnd: '2020-01-01T20:00:00',
// uIni: 1000,
// btDays: 0,
// btYears: '0.0',
// numTrade: 4,
// numTradeFin: 3,
// numTradeUnsettled: 1,
// uTradeAllMax: 200,
// rTradeAllMax: '20.00%',
// numTradeAllMax: 2,
// uDrawdownMax: 3.1000000000000227,
// rDrawdownMax: '0.31%',
// rEquivalentDrawdownMax: '1.51%',
// rSharpe: 7.675814289051032,
// rWin: '66.67%',
// uEquityFinal: 1006.6999999999999,
// uCumuProfitOrLossFinal: 6.700000000000006,
// rCumuProfitOrLossFinal: '0.67%',
// rCumuProfitOrLossFinalNormYear: '0.00%',
// rEquivalentCumuProfitOrLossFinal: '3.35%',
// rEquivalentCumuProfitOrLossFinalNormYear: '0.00%'
// }
Parameters:
| Name | Type | Attributes | Default | Description |
|---|---|---|---|---|
ott |
function | 輸入時區時間函數,傳入時間字串回傳dayjs時間物件(可用src/ott.mjs或自行以dayjs包裝) |
||
uIni |
Number | 輸入初始資金正數 |
||
orders |
Array | 輸入已結算訂單陣列(經calcOrders) |
||
timeOhlcStart |
String | 輸入回測起始秒時間字串 |
||
timeOhlcEnd |
String | 輸入回測結束秒時間字串 |
||
opt |
Object |
<optional> |
{}
|
輸入設定物件,預設{} |
Returns:
回傳Promise,resolve為回測摘要物件
- Type
- Promise
(async) closeAndSummaryOrders(ott, fdOhlc, fdParam, uIni, timeOhlcStart, timeOhlcEnd, keyOhlc, ordersSubmit, fdTest, optopt) → {Promise}
- Description:
結算下單清單並輸出訂單、摘要與報告檔案
以w-data-tdprovide讀取fdOhlc與fdParam資料夾數據,依timeOhlcStart至timeOhlcEnd範圍取keyOhlc之K線序列 經calcOrders結算ordersSubmit與calcSummary統計後,輸出orders.json、summary.json、report.html至fdTest資料夾 fdOhlc或fdParam非資料夾、uIni非正數、timeOhlcStart或timeOhlcEnd非有效字串或非4小時整數倍區間時reject
Unit Test: Github
- Source:
Example
import fs from 'fs'
let arrOhlc = [
{ time: '2020-01-01T00:00:00', Open: 100, High: 101, Low: 99, Close: 100 },
{ time: '2020-01-01T04:00:00', Open: 100, High: 106, Low: 100, Close: 105 },
{ time: '2020-01-01T08:00:00', Open: 105, High: 107, Low: 102, Close: 103 },
{ time: '2020-01-01T12:00:00', Open: 103, High: 104, Low: 94, Close: 95 },
{ time: '2020-01-01T16:00:00', Open: 95, High: 98, Low: 92, Close: 93 },
{ time: '2020-01-01T20:00:00', Open: 93, High: 99, Low: 95, Close: 97 },
]
//建立數據資料夾, fdOhlc放K線序列, fdParam放指標參數序列
fs.mkdirSync('./data-ohlc', { recursive: true })
fs.mkdirSync('./data-param', { recursive: true })
fs.writeFileSync('./data-ohlc/btc.json', JSON.stringify(arrOhlc), 'utf8')
let ordersSubmit = [{
mode: 'long',
timeStart: '2020-01-01T00:00:00',
priceStart: 100,
uTrade: 100,
priceTakeProfit: 105,
priceStopLoss: 97,
timeEnd: '',
priceEnd: '',
modeResult: '',
uFee: 0.05,
}]
await closeAndSummaryOrders(ott, './data-ohlc', './data-param', 1000, '2020-01-01T00:00:00', '2020-01-01T20:00:00', 'btc', ordersSubmit, './result')
console.log(fs.readdirSync('./result'))
// => [ 'orders.json', 'report.html', 'summary.json' ]
Parameters:
| Name | Type | Attributes | Default | Description | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
ott |
function | 輸入時區時間函數,傳入時間字串回傳dayjs時間物件(可用src/ott.mjs或自行以dayjs包裝) |
||||||||||||||||||||||
fdOhlc |
String | 輸入儲存K線(ohlc)序列資料夾字串,各序列以 |
||||||||||||||||||||||
fdParam |
String | 輸入儲存指標參數序列資料夾字串 |
||||||||||||||||||||||
uIni |
Number | 輸入初始資金正數 |
||||||||||||||||||||||
timeOhlcStart |
String | 輸入回測起始秒時間字串 |
||||||||||||||||||||||
timeOhlcEnd |
String | 輸入回測結束秒時間字串 |
||||||||||||||||||||||
keyOhlc |
String | 輸入K線序列key字串 |
||||||||||||||||||||||
ordersSubmit |
Array | 輸入下單陣列(格式同calcOrders之orders) |
||||||||||||||||||||||
fdTest |
String | 輸入輸出結果資料夾字串 |
||||||||||||||||||||||
opt |
Object |
<optional> |
{}
|
輸入設定物件,預設{} Properties
|
Returns:
回傳Promise,無resolve值,於fdTest輸出結果檔案
- Type
- Promise
genKLinesTpSl(arrOhlc, optopt) → {Object}
- Description:
計算各K線進場後之止盈止損先觸結果
以各根K線收盤價為進場價,往後逐根K線掃描first-touch,long以Low<=進場價*(1-sl)判止損、High>=進場價*(1+tp)判止盈 short以High>=進場價*(1+sl)判止損、Low<=進場價*(1-tp)判止盈,同根同時觸發時皆先判止損 末根K線與掃描至序列末仍未觸發者視為未結,win為-1且pnl與hold為NaN
Unit Test: Github
- Source:
Example
let arrOhlc = [
{ time: '2020-01-01T00:00:00', Open: 100, High: 101, Low: 99, Close: 100 },
{ time: '2020-01-01T04:00:00', Open: 100, High: 106, Low: 100, Close: 105 },
{ time: '2020-01-01T08:00:00', Open: 105, High: 107, Low: 102, Close: 103 },
{ time: '2020-01-01T12:00:00', Open: 103, High: 104, Low: 94, Close: 95 },
{ time: '2020-01-01T16:00:00', Open: 95, High: 98, Low: 92, Close: 93 },
{ time: '2020-01-01T20:00:00', Open: 93, High: 99, Low: 95, Close: 97 },
]
let r = genKLinesTpSl(arrOhlc, { tp: 0.05, sl: 0.05, mode: 'long' })
console.log(Array.from(r.win))
// => [ 1, 0, 0, -1, 1, -1 ]
console.log(Array.from(r.hold))
// => [ 1, 2, 1, NaN, 1, NaN ]
console.log(Array.from(r.pnl))
// => [ 0.05, -0.05, -0.05, NaN, 0.05, NaN ]
Parameters:
| Name | Type | Attributes | Default | Description | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
arrOhlc |
Array | 輸入K線陣列,各元素需含time(秒時間字串)、Close、High、Low欄位,且依時間由小至大排序 |
||||||||||||||||||||||
opt |
Object |
<optional> |
{}
|
輸入設定物件,預設{} Properties
|
Returns:
回傳物件,內含time(時間字串陣列)、pnl(Float64Array,止盈為tp、止損為-sl、未結為NaN)、win(Int8Array,1為止盈先觸、0為止損先觸、-1為未結)、hold(Float64Array,出場K線與進場K線之位移根數,未結為NaN)欄位,各陣列長度同arrOhlc且index對應
- Type
- Object
(async) genReport(ott, fpOrders, optopt) → {Promise}
- Description:
由orders.json產製績效html報表
讀取orders.json(已結算訂單陣列)後,經calcSummary統計摘要,再以genReportCore產出html報表 訂單須為已結算形(含uProfitOrLoss、uEquity等欄位),未結算單(timeEnd為空)仍可計算,計入summary.numTradeUnsettled fpOrders非有效字串或非檔案、內容非有效陣列時throw
Unit Test: Github
- Source:
Example
import fs from 'fs'
//orders為已結算訂單陣列(如runStrategy、runStrategies或closeAndSummaryOrders產出)
let orders = [{
mode: 'long',
timeStart: '2020-01-01T00:00:00',
priceStart: 100,
uTrade: 100,
priceTakeProfit: 105,
priceStopLoss: 97,
timeEnd: '2020-01-01T04:00:00',
priceEnd: 105,
modeResult: 'profit',
uFee: 0.05,
uProfitOrLoss: 4.9,
rProfitOrLoss: 0.049,
uCumuProfitOrLoss: 4.9,
uEquity: 1004.9,
}]
fs.mkdirSync('./result', { recursive: true })
fs.writeFileSync('./result/orders.json', JSON.stringify(orders), 'utf8')
let r = await genReport(ott, './result/orders.json', { name: '示範策略', uIni: 1000 })
console.log(r.summary.numTrade, r.summary.rWin, r.summary.uEquityFinal)
// => 1 100.00% 1004.9
console.log(fs.readdirSync('./result'))
// => [ 'orders.json', 'report.html' ]
Parameters:
| Name | Type | Attributes | Default | Description | |||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
ott |
function | 輸入時區時間函數,傳入時間字串回傳dayjs時間物件(可用src/ott.mjs或自行以dayjs包裝) |
|||||||||||||||||||||||||||||||||||||
fpOrders |
String | 輸入orders.json檔案路徑字串 |
|||||||||||||||||||||||||||||||||||||
opt |
Object |
<optional> |
{}
|
輸入設定物件,預設{} Properties
|
Returns:
回傳Promise,resolve為物件,內含orders(訂單陣列)、summary(摘要物件)、fpOut(輸出html路徑)欄位
- Type
- Promise
genReportCore(r, fpOut) → {undefined}
- Description:
產出html回測報告
讀取與本模組同資料夾之模板資產(tmp.html與render*.js),置換{name}、{orders}、{summary}後寫出html 報告內含權益曲線、訂單表格、時間軸與摘要
Unit Test: Github
- Source:
Example
//rr為runStrategies範例之回傳{orders,summary}
genReportCore({ name: '示範策略', orders: rr.orders, summary: rr.summary }, './report.html')
// => 於'./report.html'寫出html報告
Parameters:
| Name | Type | Description |
|---|---|---|
r |
Object | 輸入報告物件,需含name(報告名稱字串)、orders(訂單陣列)、summary(摘要物件)欄位 |
fpOut |
String | 輸入輸出html檔案路徑字串 |
Returns:
無回傳,於fpOut寫出html報告
- Type
- undefined
loadKLinesTpSl(fpJson) → {Object}
- Description:
載入止盈止損先觸結果快取檔並提供訂單重建
讀取buildKLinesTpSl產出之快取json,提供ordersFromBars依指定mode與tpsl組合與進場時間陣列重建訂單陣列 重建訂單之盈虧與累計欄位公式逐欄對齊calcOrders,可直接餵入calcOrdersSummary或calcOrdersSummarySimple fpJson非有效字串或非檔案時throw
Unit Test: Github
- Source:
Example
//fpJson為buildKLinesTpSl產出之快取檔
let r = loadKLinesTpSl('./cache/kltpsl.json')
console.log(r.meta.nBars)
// => 6
let orders = r.ordersFromBars('long', 5, ['2020-01-01T00:00:00', '2020-01-01T12:00:00'], { uTrade: 100, uIni: 1000 })
console.log(orders.map((o) => `${o.timeStart} ${o.timeEnd || 'unsettled'} ${o.modeResult || '-'}`))
// => [
// '2020-01-01T00:00:00 2020-01-01T04:00:00 profit',
// '2020-01-01T12:00:00 unsettled -'
// ]
console.log(orders[0].priceStart, orders[0].priceEnd, orders[0].uProfitOrLoss, orders[0].uEquity)
// => 100 105 4.9 1004.9
Parameters:
| Name | Type | Description |
|---|---|---|
fpJson |
String | 輸入buildKLinesTpSl產出之快取json檔案路徑字串 |
Returns:
回傳物件,內含meta(快取設定物件)、bars(以進場時間為key之結果物件)與ordersFromBars(mode, tpsl, entryTimes, opt)訂單重建函數,函數之opt可給uTrade(每單金額,預設1)、uIni(初始資金,預設1000)、rFee(手續費率,預設0.0005)、maxExitTime(出場時間大於等於此值之單視為未平倉,預設null)與skipSort(呼叫端保證entryTimes已升序時可省排序,預設false),回傳訂單陣列,快取內查無之進場時間會被濾除,未結單各盈虧欄位留空
- Type
- Object
(async) runStrategies(ott, strategies, funGetSeries, optopt) → {Promise}
- Description:
執行多策略回測
逐策略呼叫runStrategy(withSummary:false),各單附加策略sid後合併並依timeStart排序 以各策略settings.uIni總和為初始資金呼叫calcSummary統計合併摘要 個別策略執行失敗(如時間點無參數無法下單)時強制略過
Unit Test: Github
- Source:
Example
let arrOhlc = [
{ time: '2020-01-01T00:00:00', Open: 100, High: 101, Low: 99, Close: 100 },
{ time: '2020-01-01T04:00:00', Open: 100, High: 106, Low: 100, Close: 105 },
{ time: '2020-01-01T08:00:00', Open: 105, High: 107, Low: 102, Close: 103 },
{ time: '2020-01-01T12:00:00', Open: 103, High: 104, Low: 94, Close: 95 },
{ time: '2020-01-01T16:00:00', Open: 95, High: 98, Low: 92, Close: 93 },
{ time: '2020-01-01T20:00:00', Open: 93, High: 99, Low: 95, Close: 97 },
]
let arrSig = [
{ time: '2020-01-01T00:00:00', param: 1 },
{ time: '2020-01-01T04:00:00', param: 0 },
{ time: '2020-01-01T08:00:00', param: 1 },
{ time: '2020-01-01T12:00:00', param: 0 },
{ time: '2020-01-01T16:00:00', param: 0 },
{ time: '2020-01-01T20:00:00', param: 0 },
]
let funGetSeries = async (key) => {
if (key === 'btc') {
return arrOhlc
}
if (key === 'sig') {
return arrSig
}
throw new Error(`invalid key[${key}]`)
}
let settings = { uIni: 1000, uTrade: 100, rTakeProfit: 0.05, rStopLoss: 0.03, rFee: 0.0005 }
let strategies = [
{ sid: 's1', mode: 'long', keyOhlc: 'btc', conds: [{ key: 'sig', sym: '>', th: 0.5, opr: 'and' }], settings },
{ sid: 's2', mode: 'short', keyOhlc: 'btc', conds: [{ key: 'sig', sym: '<', th: 0.5, opr: 'and' }], settings },
]
let rr = await runStrategies(ott, strategies, funGetSeries)
console.log(rr.orders.map((o) => `${o.sid} ${o.timeStart} ${o.mode} ${o.modeResult || 'unsettled'}`))
// => [
// 's1 2020-01-01T00:00:00 long profit',
// 's2 2020-01-01T04:00:00 short profit',
// 's1 2020-01-01T08:00:00 long loss',
// 's2 2020-01-01T12:00:00 short loss',
// 's2 2020-01-01T16:00:00 short loss',
// 's2 2020-01-01T20:00:00 short unsettled'
// ]
console.log(rr.summary.uIni, rr.summary.numTrade, rr.summary.rWin)
// => 2000 6 40.00%
Parameters:
| Name | Type | Attributes | Default | Description | |||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
ott |
function | 輸入時區時間函數,傳入時間字串回傳dayjs時間物件(可用src/ott.mjs或自行以dayjs包裝) |
|||||||||||||||||
strategies |
Array | 輸入策略陣列,各元素需含sid與runStrategy之strategy欄位 |
|||||||||||||||||
funGetSeries |
function | 輸入序列查詢async函數,依key回傳時間序列陣列 |
|||||||||||||||||
opt |
Object |
<optional> |
{}
|
輸入設定物件,預設{} Properties
|
Returns:
回傳Promise,resolve為{orders,summary}物件
- Type
- Promise
(async) runStrategiesAndBacktest(ott, fdOhlc, fdParam, strategies, timeStart, timeEnd, fdTest, optopt) → {Promise}
- Description:
執行多策略回測並輸出訂單、摘要與報告檔案
以w-data-tdprovide讀取fdOhlc與fdParam資料夾數據,建立timeStart至timeEnd範圍之序列查詢函數後執行runStrategies 合併結果命名為'全部策略',並輸出orders.json、summary.json、report.html至fdTest資料夾 fdOhlc或fdParam非資料夾、strategies非有效陣列時throw
Unit Test: Github
- Source:
Example
import fs from 'fs'
let arrOhlc = [
{ time: '2020-01-01T00:00:00', Open: 100, High: 101, Low: 99, Close: 100 },
{ time: '2020-01-01T04:00:00', Open: 100, High: 106, Low: 100, Close: 105 },
{ time: '2020-01-01T08:00:00', Open: 105, High: 107, Low: 102, Close: 103 },
{ time: '2020-01-01T12:00:00', Open: 103, High: 104, Low: 94, Close: 95 },
{ time: '2020-01-01T16:00:00', Open: 95, High: 98, Low: 92, Close: 93 },
{ time: '2020-01-01T20:00:00', Open: 93, High: 99, Low: 95, Close: 97 },
]
let arrSig = [
{ time: '2020-01-01T00:00:00', param: 1 },
{ time: '2020-01-01T04:00:00', param: 0 },
{ time: '2020-01-01T08:00:00', param: 1 },
{ time: '2020-01-01T12:00:00', param: 0 },
{ time: '2020-01-01T16:00:00', param: 0 },
{ time: '2020-01-01T20:00:00', param: 0 },
]
//建立數據資料夾, fdOhlc放K線序列, fdParam放指標參數序列
fs.mkdirSync('./data-ohlc', { recursive: true })
fs.mkdirSync('./data-param', { recursive: true })
fs.writeFileSync('./data-ohlc/btc.json', JSON.stringify(arrOhlc), 'utf8')
fs.writeFileSync('./data-param/sig.json', JSON.stringify(arrSig), 'utf8')
let settings = { uIni: 1000, uTrade: 100, rTakeProfit: 0.05, rStopLoss: 0.03, rFee: 0.0005 }
let strategies = [
{ sid: 's1', mode: 'long', keyOhlc: 'btc', conds: [{ key: 'sig', sym: '>', th: 0.5, opr: 'and' }], settings },
{ sid: 's2', mode: 'short', keyOhlc: 'btc', conds: [{ key: 'sig', sym: '<', th: 0.5, opr: 'and' }], settings },
]
let r = await runStrategiesAndBacktest(ott, './data-ohlc', './data-param', strategies, '2020-01-01T00:00:00', '2020-01-01T20:00:00', './result')
console.log(r.name, r.summary.uIni, r.summary.numTrade, r.summary.rWin)
// => 全部策略 2000 6 40.00%
console.log(fs.readdirSync('./result'))
// => [ 'orders.json', 'report.html', 'summary.json' ]
Parameters:
| Name | Type | Attributes | Default | Description | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
ott |
function | 輸入時區時間函數,傳入時間字串回傳dayjs時間物件(可用src/ott.mjs或自行以dayjs包裝) |
||||||||||||||||||||||
fdOhlc |
String | 輸入儲存K線(ohlc)序列資料夾字串,各序列以 |
||||||||||||||||||||||
fdParam |
String | 輸入儲存指標參數序列資料夾字串 |
||||||||||||||||||||||
strategies |
Array | 輸入策略陣列,各元素需含sid與runStrategy之strategy欄位 |
||||||||||||||||||||||
timeStart |
String | 輸入回測起始秒時間字串 |
||||||||||||||||||||||
timeEnd |
String | 輸入回測結束秒時間字串 |
||||||||||||||||||||||
fdTest |
String | 輸入輸出結果資料夾字串 |
||||||||||||||||||||||
opt |
Object |
<optional> |
{}
|
輸入設定物件,預設{} Properties
|
Returns:
回傳Promise,resolve為{name,orders,summary}物件,另於fdTest輸出結果檔案
- Type
- Promise
(async) runStrategy(ott, strategy, funGetSeries, optopt) → {Promise}
- Description:
執行單一策略回測
於各時間點以conds判斷觸發下單(各條件以sym與th比較該key序列之param值,opr為'and'須全真、'or'至少一真) 觸發時以該K棒Close下單,並依settings計算止盈止損價格,再經calcOrders結算與calcOrdersRatio、calcOrdersSummary統計 僅於全部conds序列皆有值之共同時間點判斷觸發 strategy.mode非'long'或'short'、keyOhlc或conds無效、funGetSeries非函數時reject
Unit Test: Github
- Source:
Example
let arrOhlc = [
{ time: '2020-01-01T00:00:00', Open: 100, High: 101, Low: 99, Close: 100 },
{ time: '2020-01-01T04:00:00', Open: 100, High: 106, Low: 100, Close: 105 },
{ time: '2020-01-01T08:00:00', Open: 105, High: 107, Low: 102, Close: 103 },
{ time: '2020-01-01T12:00:00', Open: 103, High: 104, Low: 94, Close: 95 },
{ time: '2020-01-01T16:00:00', Open: 95, High: 98, Low: 92, Close: 93 },
{ time: '2020-01-01T20:00:00', Open: 93, High: 99, Low: 95, Close: 97 },
]
let arrSig = [
{ time: '2020-01-01T00:00:00', param: 1 },
{ time: '2020-01-01T04:00:00', param: 0 },
{ time: '2020-01-01T08:00:00', param: 1 },
{ time: '2020-01-01T12:00:00', param: 0 },
{ time: '2020-01-01T16:00:00', param: 0 },
{ time: '2020-01-01T20:00:00', param: 0 },
]
let funGetSeries = async (key) => {
if (key === 'btc') {
return arrOhlc
}
if (key === 'sig') {
return arrSig
}
throw new Error(`invalid key[${key}]`)
}
let strategy = {
mode: 'long',
keyOhlc: 'btc',
conds: [{ key: 'sig', sym: '>', th: 0.5, opr: 'and' }],
settings: { uIni: 1000, uTrade: 100, rTakeProfit: 0.05, rStopLoss: 0.03, rFee: 0.0005 },
}
let r = await runStrategy(ott, strategy, funGetSeries)
console.log(r.orders.map((o) => `${o.timeStart} ${o.mode} ${o.priceStart}->${o.priceEnd} ${o.modeResult}`))
// => [
// '2020-01-01T00:00:00 long 100->105 profit',
// '2020-01-01T08:00:00 long 103->99.91 loss'
// ]
console.log(r.summary.numTrade, r.summary.rWin, r.summary.uEquityFinal)
// => 2 50.00% 1001.8
Parameters:
| Name | Type | Attributes | Default | Description | |||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
ott |
function | 輸入時區時間函數,傳入時間字串回傳dayjs時間物件(可用src/ott.mjs或自行以dayjs包裝) |
|||||||||||||||||
strategy |
Object | 輸入策略物件,需含mode('long'或'short')、keyOhlc(K線序列key字串)、conds(條件陣列,各元素為{key,sym,th,opr})、settings({uIni,uTrade,rTakeProfit,rStopLoss,rFee})欄位 |
|||||||||||||||||
funGetSeries |
function | 輸入序列查詢async函數,依key回傳時間序列陣列 |
|||||||||||||||||
opt |
Object |
<optional> |
{}
|
輸入設定物件,預設{} Properties
|
Returns:
回傳Promise,resolve為{orders,summary}物件
- Type
- Promise
settleOrderPnl(mode, uTrade, priceStart, priceEnd, uFee) → {Number}
- Description:
計算單筆訂單之淨盈虧金額(USDT)
結算公式與calcOrders逐字對齊(bit-exact): long為uTrade*(priceEnd/priceStart)-uTrade-2uFee,short為(priceStart-priceEnd)(uTrade/priceStart)-2*uFee 供loadKLinesTpSl之ordersFromBars與buildStrategyFastSession共用,避免套件內出現第三份手抄公式
Unit Test: Github
- Source:
Example
console.log(settleOrderPnl('long', 100, 100, 105, 0.05))
// => 4.9
Parameters:
| Name | Type | Description |
|---|---|---|
mode |
String | 輸入交易方向字串,'long'或'short' |
uTrade |
Number | 輸入下單金額(USDT) |
priceStart |
Number | 輸入進場價格 |
priceEnd |
Number | 輸入出場價格 |
uFee |
Number | 輸入單邊手續費(USDT),結算扣2*uFee |
Returns:
回傳淨盈虧金額(USDT),含手續費
- Type
- Number