Global

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非時間字串時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 },
]

//建立數據資料夾, 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)序列資料夾字串,各序列以${key}.json儲存

fdParam String

輸入儲存指標參數序列資料夾字串

fpJson String

輸入輸出快取json檔案路徑字串,資料夾不存在時自動建立

opt Object <optional>
{}

輸入設定物件,預設{}

Properties
Name Type Attributes Default Description
keyOhlc String

輸入K線序列key字串

ts String

輸入起始秒時間字串

te String

輸入結束秒時間字串

tps Array <optional>
[4,5,6,7,8,9,10]

輸入止盈止損百分比數值陣列,如5代表止盈與止損皆5%,預設[4,5,6,7,8,9,10]

modes Array <optional>
['long','short']

輸入交易方向字串陣列,預設['long','short']

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
Name Type Attributes Default Description
arrOhlc Array

輸入K線陣列,各元素需含time(秒時間字串)、Close、High、Low欄位,且依時間由小至大排序,與runStrategy之funGetSeries(keyOhlc)同一份

mode String

輸入交易方向字串,'long'或'short'

serieses Object

輸入因子序列物件,key為因子名稱,value為{time,param}陣列,param須為有限數值

tpsls Array <optional>
[]

輸入預建first-touch表之{tp,sl}陣列,未列組合於evaluate時惰性建置,預設[]

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
Name Type Attributes Default Description
uIni Number <optional>
1000

輸入初始資金數字,預設1000

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
Name Type Attributes Default Description
skipSort Boolean <optional>
false

輸入呼叫端保證ordersAll已依timeStart升序時可跳過排序布林值,預設false

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)序列資料夾字串,各序列以${key}.json儲存

fdParam String

輸入儲存指標參數序列資料夾字串

uIni Number

輸入初始資金正數

timeOhlcStart String

輸入回測起始秒時間字串

timeOhlcEnd String

輸入回測結束秒時間字串

keyOhlc String

輸入K線序列key字串

ordersSubmit Array

輸入下單陣列(格式同calcOrders之orders)

fdTest String

輸入輸出結果資料夾字串

opt Object <optional>
{}

輸入設定物件,預設{}

Properties
Name Type Attributes Default Description
writeOrders Boolean <optional>
true

輸入是否輸出orders.json布林值,預設true

writeSummary Boolean <optional>
true

輸入是否輸出summary.json布林值,預設true

writeReport Boolean <optional>
true

輸入是否輸出report.html布林值,預設true

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
Name Type Attributes Default Description
tp Number

輸入止盈比例正數,如0.05代表5%

sl Number

輸入止損比例正數,如0.05代表5%

mode String <optional>
'long'

輸入交易方向字串,可為'long'或'short',預設'long'

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
Name Type Attributes Default Description
fpOut String <optional>
''

輸入輸出html檔案路徑字串,預設為與orders.json同資料夾之report.html

name String <optional>
''

輸入報表標題字串,預設為orders.json所在資料夾名

uIni Number <optional>
null

輸入初始資金數值,預設由首筆已結算單推回(uEquity - uCumuProfitOrLoss),推不出則為1000

timeOhlcStart String <optional>
''

輸入摘要統計起始秒時間字串,影響btDays與年化,預設由訂單最早timeStart推得,注意訂單起訖僅為「有單的範圍」,若實際回測窗較寬(頭尾無單)年化會被高估,建議明給

timeOhlcEnd String <optional>
''

輸入摘要統計結束秒時間字串,預設由訂單最晚timeEnd(未平倉單取timeStart)推得

withWriteSummary Boolean <optional>
false

輸入是否同時輸出summary.json(與fpOut同資料夾)布林值,預設false

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
Name Type Attributes Default Description
withCalcOrderProfitOrLoss Boolean <optional>
true

輸入是否經calcOrders結算訂單布林值,預設true

withSummary Boolean <optional>
true

輸入是否計算統計摘要布林值,預設true

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)序列資料夾字串,各序列以${key}.json儲存

fdParam String

輸入儲存指標參數序列資料夾字串

strategies Array

輸入策略陣列,各元素需含sid與runStrategy之strategy欄位

timeStart String

輸入回測起始秒時間字串

timeEnd String

輸入回測結束秒時間字串

fdTest String

輸入輸出結果資料夾字串

opt Object <optional>
{}

輸入設定物件,預設{}

Properties
Name Type Attributes Default Description
writeOrders Boolean <optional>
true

輸入是否輸出orders.json布林值,預設true

writeSummary Boolean <optional>
true

輸入是否輸出summary.json布林值,預設true

writeReport Boolean <optional>
true

輸入是否輸出report.html布林值,預設true

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
Name Type Attributes Default Description
withCalcOrderProfitOrLoss Boolean <optional>
true

輸入是否經calcOrders結算訂單布林值,預設true

withSummary Boolean <optional>
true

輸入是否計算統計摘要布林值,預設true

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