const logger = require("./utils/logger"); const NumKit = require('./utils/num-kit') const TimeKit = require('./utils/time-kit') const ChartKit = require('./utils/chart-kit'); const fs = require('fs').promises; async function readData() { let data = await fs.readFile('./data/k_lines.json', 'utf8') return JSON.parse(data) } // 统计过去N日累计涨幅,【在-1%~-20%容易有亏损,有一定关系】 function statisticA(kLines, index) { let startK = kLines[index - 10] let endK = kLines[index - 1] if (!startK || !endK) { return 0 } return parseInt(100 * (endK.Close - startK.Open) / startK.Open) + 100 } // 返回过去一共N天涨幅超过M%【这个有关的】 function statisticB(kLines, index) { let count = 0 for (let i = index - 1; i >= index - 3; i--) { let kLine = kLines[i] if (!kLines[i - 1]) break if (100 * (kLine.Close - kLine.Open) / kLine.Open < 3) continue count += 1 } return count } // 指标过滤 function filter(kLines, index) { // 一共N天涨幅超过M% if (statisticB(kLines, index) !== 0) return false // 过去N日累计涨幅,85表示亏15%,115表示赚15%,100是分界线 let upRateN = statisticA(kLines, index) if (upRateN >= 85 && upRateN <= 100) return false return true } let realCountMap = {} function getRealDragonMap(kLinesMap, dayCount, BUY_LIMIT_RATE) { let realDragonMap = {} for (let symbol in kLinesMap) { let kLines = kLinesMap[symbol] let index = kLines.length - (dayCount + 1) let kLine = kLines[index] let prevKline = kLines[index - 1] // 开盘第一天不计算 if (!kLine || !prevKline) continue // 指标过滤 if (!filter(kLines, index)) continue let rate = 100 * (kLine.Close - kLine.Open) / kLine.Open if (rate > BUY_LIMIT_RATE) { kLine.Rate = rate kLine.Profit = NumKit.getSubFloat(rate - BUY_LIMIT_RATE, 2) realDragonMap[symbol] = kLine if (!realCountMap[symbol]) { realCountMap[symbol] = kLine.Profit } else { realCountMap[symbol] += kLine.Profit } realCountMap[symbol] = NumKit.getSubFloat(realCountMap[symbol], 2) } } return realDragonMap } let fakeCountMap = {} function getFakeDragonMap(kLinesMap, dayCount, BUY_LIMIT_RATE) { let fakeDragonMap = {} for (let symbol in kLinesMap) { let kLines = kLinesMap[symbol] let index = kLines.length - (dayCount + 1) let kLine = kLines[index] let prevKline = kLines[index - 1] // 开盘第一天不计算 if (!kLine || (!prevKline)) continue // 指标过滤 if (!filter(kLines, index)) continue let rate = 100 * (kLine.Close - kLine.Open) / kLine.Open if (rate < BUY_LIMIT_RATE) { kLine.Rate = rate // kLine.Profit = NumKit.getSubFloat(rate > 0 ? rate - BUY_LIMIT_RATE : -BUY_LIMIT_RATE, 2) kLine.Profit = NumKit.getSubFloat(rate - BUY_LIMIT_RATE, 2) fakeDragonMap[symbol] = kLine if (!fakeCountMap[symbol]) { fakeCountMap[symbol] = kLine.Profit } else { fakeCountMap[symbol] += kLine.Profit } fakeCountMap[symbol] = NumKit.getSubFloat(fakeCountMap[symbol], 2) } } return fakeDragonMap } let dataLeft = [] let dataRight = [] let tempLeft = [] let tempRight = [] let dataY = [...Array(100).keys()] function dragonAnalysis(btcKLines, kLinesMap, dragonMap, dayCount) { for (let symbol in dragonMap) { let kLines = kLinesMap[symbol] let index = kLines.length - (dayCount + 1) let x = statisticA(kLines, index) let y = dragonMap[symbol].Profit // logger.info( // `${symbol}的分析(${y}%)` // // + `前7日累计涨幅${statisticA(kLines, index)}%。` // // + `前30日平均振幅${statisticB(kLines, index)}%。` // + `前30日平均(High - Close)${x}%。` // ) // 打印实际的量 // if (y > 0) { // if (dataRight[x]) { // dataRight[x] += y // } else { // dataRight[x] = y // } // } else { // if (dataLeft[x]) { // dataLeft[x] += y // } else { // dataLeft[x] = y // } // } // 打印倍数 if (y > 0) { if (tempRight[x]) { tempRight[x] += y } else { tempRight[x] = y } } else { if (tempLeft[x]) { tempLeft[x] += y } else { tempLeft[x] = y } } dataRight[x] = (tempLeft[x]?tempRight[x]/Math.abs(tempLeft[x]):0) if (dataRight[x] > 10) dataRight[x] = 10 if (dataRight[x] < 0.5) dataRight[x] = 0.5 } } async function main() { let kLinesMap = await readData() const FIRST_FEW_DAYS = 180 // 第几天的数据,0表示今天,1表示昨天,2表示前天,以此类推 const BUY_LIMIT_RATE = 0 // 从什么比例入场 const BAKE_TEST_DAYS = 30 // 一共回测多少天, 150天是熊市最没有交易量的时候 let btcKLines = kLinesMap['BTC_USDT'] let totalProfit = 0 for (let day_count = FIRST_FEW_DAYS; day_count < FIRST_FEW_DAYS + BAKE_TEST_DAYS; day_count++) { // 赚钱榜 // logger.info("----------------赚钱榜数据分析----------------") let realDragonProfit = 0 let realDragonMap = getRealDragonMap(kLinesMap, day_count, BUY_LIMIT_RATE) for (let symbol in realDragonMap) { realDragonProfit += realDragonMap[symbol].Profit // logger.info(`${realDragonMap[symbol].Symbol}, ${realDragonMap[symbol].Profit}%`) } dragonAnalysis(btcKLines, kLinesMap, realDragonMap, day_count) // logger.info("----------------亏钱榜数据分析----------------") // 亏钱榜 let fakeDragonProfit = 0 let fakeDragonMap = getFakeDragonMap(kLinesMap, day_count, BUY_LIMIT_RATE) for (let symbol in fakeDragonMap) { fakeDragonProfit += fakeDragonMap[symbol].Profit // logger.info(`${fakeDragonMap[symbol].Symbol}, ${fakeDragonMap[symbol].Profit}%`) } dragonAnalysis(btcKLines, kLinesMap, fakeDragonMap, day_count) realDragonProfit = NumKit.getSubFloat(realDragonProfit, 2) fakeDragonProfit = NumKit.getSubFloat(fakeDragonProfit, 2) let realLength = Object.keys(realDragonMap).length let fakeLength = Object.keys(fakeDragonMap).length let realRate = NumKit.getSubFloat(realLength / (realLength + fakeLength), 2) let fakeRate = NumKit.getSubFloat(1 - realRate, 2) let expRealProfit = NumKit.getSubFloat(realRate * realDragonProfit, 2) let expFakeProfit = NumKit.getSubFloat(fakeRate * fakeDragonProfit, 2) let synProfit = NumKit.getSubFloat(expFakeProfit + expRealProfit, 2) let avgProfit = NumKit.getSubFloat(synProfit / (realLength + fakeLength), 2) let index = kLinesMap['BTC_USDT'].length - (day_count + 1) let btcK = kLinesMap['BTC_USDT'][index] let dateStr = TimeKit.getDateByMillisecond(btcK.Time) let btcUpRate = NumKit.getSubFloat(100 * (btcK.Close - btcK.Open) / btcK.Open, 2) logger.info(`${day_count}日(${dateStr}, ${realLength + fakeLength}只),赚钱榜(${realLength}只)利润${realDragonProfit}%` + `,亏钱榜(${fakeLength}只)利润${fakeDragonProfit}%` + `,赚钱榜期望利润${expRealProfit}%,亏钱榜期望利润${expFakeProfit}%,综合利润${synProfit}%,平均每只利润${avgProfit}%,BTC涨幅${btcUpRate}%` ) totalProfit += avgProfit totalProfit = NumKit.getSubFloat(totalProfit, 2) } let dayProfit = NumKit.getSubFloat(totalProfit / BAKE_TEST_DAYS, 2) logger.info(`利润期望值总和:${totalProfit}%,平均日化${dayProfit}%。`) // let lastData = [] // for (let x = 0; x < dataRight.length; x++) { // lastData.push([x, dataRight[x]]) // } let rst = 'option=' + JSON.stringify(ChartKit.printBar(dataY, dataLeft, dataRight), null, 2) // let rst = 'option=' + JSON.stringify(ChartKit.printPointChart(lastData), null, 2) require('fs').writeFile('./data/option.txt', rst, 'utf8', (err) => { if (err) { logger.error('写入文件时发生错误:', err); } else { logger.info('分析结果已写入到 ./data/option.txt'); } }); } main().catch((error) => { logger.error(error.stack); process.exitCode = 1; })