十面埋伏分析.js 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. const logger = require("./utils/logger");
  2. const NumKit = require('./utils/num-kit')
  3. const TimeKit = require('./utils/time-kit')
  4. const ChartKit = require('./utils/chart-kit');
  5. const fs = require('fs').promises;
  6. async function readData() {
  7. let data = await fs.readFile('./data/k_lines.json', 'utf8')
  8. return JSON.parse(data)
  9. }
  10. let realCountMap = {}
  11. function getRealDragonMap(kLinesMap, dayCount, BUY_LIMIT_RATE) {
  12. let realDragonMap = {}
  13. for (let symbol in kLinesMap) {
  14. let kLines = kLinesMap[symbol]
  15. let index = kLines.length - (dayCount + 1)
  16. let kLine = kLines[index]
  17. let prevKline = kLines[index - 1]
  18. // 开盘第一天不计算
  19. if (!kLine || !prevKline) continue
  20. let rate = 100 * (kLine.Close - kLine.Open) / kLine.Open
  21. if (rate > BUY_LIMIT_RATE) {
  22. kLine.Rate = rate
  23. kLine.Profit = NumKit.getSubFloat(rate - BUY_LIMIT_RATE, 2)
  24. realDragonMap[symbol] = kLine
  25. if (!realCountMap[symbol]) {
  26. realCountMap[symbol] = kLine.Profit
  27. } else {
  28. realCountMap[symbol] += kLine.Profit
  29. }
  30. realCountMap[symbol] = NumKit.getSubFloat(realCountMap[symbol], 2)
  31. }
  32. }
  33. return realDragonMap
  34. }
  35. let fakeCountMap = {}
  36. function getFakeDragonMap(kLinesMap, dayCount, BUY_LIMIT_RATE) {
  37. let fakeDragonMap = {}
  38. for (let symbol in kLinesMap) {
  39. let kLines = kLinesMap[symbol]
  40. let index = kLines.length - (dayCount + 1)
  41. let kLine = kLines[index]
  42. let prevKline = kLines[index - 1]
  43. // 开盘第一天不计算
  44. if (!kLine || (!prevKline)) continue
  45. let rate = 100 * (kLine.Close - kLine.Open) / kLine.Open
  46. let maxRate = 100 * (kLine.High - kLine.Open) / kLine.Open
  47. if (maxRate > BUY_LIMIT_RATE && rate < BUY_LIMIT_RATE) {
  48. kLine.Rate = rate
  49. kLine.MaxRate = maxRate
  50. // kLine.Profit = NumKit.getSubFloat(rate > 0 ? rate - BUY_LIMIT_RATE : -BUY_LIMIT_RATE, 2)
  51. kLine.Profit = NumKit.getSubFloat(rate - BUY_LIMIT_RATE, 2)
  52. fakeDragonMap[symbol] = kLine
  53. if (!fakeCountMap[symbol]) {
  54. fakeCountMap[symbol] = kLine.Profit
  55. } else {
  56. fakeCountMap[symbol] += kLine.Profit
  57. }
  58. fakeCountMap[symbol] = NumKit.getSubFloat(fakeCountMap[symbol], 2)
  59. }
  60. }
  61. return fakeDragonMap
  62. }
  63. // 统计过去N日累计涨幅,跟这个无关
  64. function statisticA(kLines, index) {
  65. let startK = kLines[index - 2]
  66. let endK = kLines[index - 1]
  67. if (!startK || !endK) {
  68. return 0
  69. }
  70. let rst = NumKit.getSubFloat(100 * (endK.Close - startK.Open) / startK.Open, 2)
  71. if (rst > 30 || rst < -30) {
  72. return 0
  73. }
  74. return rst
  75. }
  76. // 过去30天的平均振幅,跟这个无关
  77. function statisticB(kLines, index) {
  78. let kCount = 0
  79. let totalRiseAndFall = 0
  80. for (let i = index - 1; i >= index - 30; i--) {
  81. let kLine = kLines[i]
  82. if (!kLines[i - 1]) break
  83. kCount += 1
  84. totalRiseAndFall += (100 * (kLine.High - kLine.Low) / kLine.Low)
  85. }
  86. if (kCount === 0) {
  87. return 0
  88. }
  89. return NumKit.getSubFloat(totalRiseAndFall / kCount, 2)
  90. }
  91. // 过去150天的平均(High - Close),跟这个无关
  92. function statisticC(kLines, index) {
  93. let kCount = 0
  94. let totalRiseAndFall = 0
  95. for (let i = index - 1; i >= index - 150; i--) {
  96. let kLine = kLines[i]
  97. if (!kLines[i - 1]) break
  98. kCount += 1
  99. totalRiseAndFall += (100 * (kLine.High - kLine.Close) / kLine.Close)
  100. }
  101. if (kCount === 0) {
  102. return 0
  103. }
  104. return NumKit.getSubFloat(totalRiseAndFall / kCount, 2)
  105. }
  106. // 过去30天的平均上影线,跟这个无关
  107. function statisticD(kLines, index) {
  108. let kCount = 0
  109. let totalRiseAndFall = 0
  110. for (let i = index - 1; i >= index - 30; i--) {
  111. let kLine = kLines[i]
  112. if (!kLines[i - 1]) break
  113. kCount += 1
  114. let target = kLine.Close > kLine.Open ? kLine.Close : kLine.Open
  115. totalRiseAndFall += (100 * (kLine.High - target) / target)
  116. }
  117. if (kCount === 0) {
  118. return 0
  119. }
  120. return NumKit.getSubFloat(totalRiseAndFall / kCount, 2)
  121. }
  122. // 过去30天的上影线幅度大于2.5%的次数,跟这个无关
  123. function statisticE(kLines, index) {
  124. let kCount = 0
  125. let count = 0
  126. for (let i = index - 1; i >= index - 30; i--) {
  127. let kLine = kLines[i]
  128. if (!kLines[i - 1]) break
  129. kCount += 1
  130. let target = kLine.Close > kLine.Open ? kLine.Close : kLine.Open
  131. let rise = (100 * (kLine.High - target) / target)
  132. count += (rise > 2.5 ? 1 : 0)
  133. }
  134. if (kCount === 0) {
  135. return 0
  136. }
  137. return count
  138. }
  139. // 过去30天的平均交易额(百万),跟这个无关
  140. function statisticF(kLines, index) {
  141. let kCount = 0
  142. let rst = 0
  143. for (let i = index - 1; i >= index - 30; i--) {
  144. let kLine = kLines[i]
  145. if (!kLines[i - 1]) break
  146. kCount += 1
  147. rst += (kLine.Turnover / 1e6)
  148. }
  149. if (kCount === 0) {
  150. return 0
  151. }
  152. return NumKit.getSubFloat(rst / kCount, 2)
  153. }
  154. // 过去30天的平均买入交易额(百万)
  155. function statisticG(kLines, index) {
  156. let kCount = 0
  157. let rst = 0
  158. for (let i = index - 1; i >= index - 30; i--) {
  159. let kLine = kLines[i]
  160. if (!kLines[i - 1]) break
  161. let rise = (kLine.BuyTurnover / 1e6)
  162. if (rise > 100) continue
  163. kCount += 1
  164. rst += rise
  165. }
  166. if (kCount === 0) {
  167. return 0
  168. }
  169. return NumKit.getSubFloat(rst / kCount, 2)
  170. }
  171. // 过去N天的平均买入交易额的比例
  172. function statisticH(kLines, index) {
  173. let kCount = 0
  174. let rst = 0
  175. for (let i = index - 1; i >= index - 7; i--) {
  176. let kLine = kLines[i]
  177. if (!kLines[i - 1]) break
  178. let rise = 100 * (kLine.BuyTurnover / kLine.Turnover)
  179. kCount += 1
  180. rst += rise
  181. }
  182. if (kCount === 0) {
  183. return 0
  184. }
  185. return NumKit.getSubFloat(rst / kCount, 2)
  186. }
  187. let dataList = []
  188. function dragonAnalysis(kLinesMap, dragonMap, dayCount) {
  189. for (let symbol in dragonMap) {
  190. let kLines = kLinesMap[symbol]
  191. let index = kLines.length - (dayCount + 1)
  192. let x = statisticA(kLines, index)
  193. let y = dragonMap[symbol].Profit
  194. // logger.info(
  195. // `${symbol}的分析(${y}%)`
  196. // // + `前7日累计涨幅${statisticA(kLines, index)}%。`
  197. // // + `前30日平均振幅${statisticB(kLines, index)}%。`
  198. // + `前30日平均(High - Close)${x}%。`
  199. // )
  200. dataList.push([x, y])
  201. }
  202. }
  203. async function main() {
  204. let kLinesMap = await readData()
  205. const FIRST_FEW_DAYS = 10 // 第几天的数据,0表示今天,1表示昨天,2表示前天,以此类推
  206. const BUY_LIMIT_RATE = 2.5 // 从什么比例入场
  207. const BAKE_TEST_DAYS = 10 // 一共回测多少天
  208. let totalProfit = 0
  209. for (let day_count = FIRST_FEW_DAYS; day_count < FIRST_FEW_DAYS + BAKE_TEST_DAYS; day_count++) {
  210. // 赚钱榜
  211. let realDragonProfit = 0
  212. let realDragonMap = getRealDragonMap(kLinesMap, day_count, BUY_LIMIT_RATE)
  213. for (let symbol in realDragonMap) {
  214. realDragonProfit += realDragonMap[symbol].Profit
  215. // logger.info(realDragonMap[symbol].Symbol, realDragonMap[symbol].Profit, realDragonMap[symbol].Rate)
  216. }
  217. logger.info("----------------赚钱榜数据分析----------------")
  218. dragonAnalysis(kLinesMap, realDragonMap, day_count)
  219. // 亏钱榜
  220. let fakeDragonProfit = 0
  221. let fakeDragonMap = getFakeDragonMap(kLinesMap, day_count, BUY_LIMIT_RATE)
  222. for (let symbol in fakeDragonMap) {
  223. fakeDragonProfit += fakeDragonMap[symbol].Profit
  224. // logger.info(fakeDragonMap[symbol].Symbol, fakeDragonMap[symbol].Profit, fakeDragonMap[symbol].MaxRate, fakeDragonMap[symbol].Rate)
  225. }
  226. logger.info("----------------亏钱榜数据分析----------------")
  227. dragonAnalysis(kLinesMap, fakeDragonMap, day_count)
  228. realDragonProfit = NumKit.getSubFloat(realDragonProfit, 2)
  229. fakeDragonProfit = NumKit.getSubFloat(fakeDragonProfit, 2)
  230. let realLength = Object.keys(realDragonMap).length
  231. let fakeLength = Object.keys(fakeDragonMap).length
  232. let realRate = NumKit.getSubFloat(realLength / (realLength + fakeLength), 2)
  233. let fakeRate = NumKit.getSubFloat(1 - realRate, 2)
  234. let expRealProfit = NumKit.getSubFloat(realRate * realDragonProfit, 2)
  235. let expFakeProfit = NumKit.getSubFloat(fakeRate * fakeDragonProfit, 2)
  236. let synProfit = NumKit.getSubFloat(expFakeProfit + expRealProfit, 2)
  237. let avgProfit = NumKit.getSubFloat(synProfit / (realLength + fakeLength), 2)
  238. let index = kLinesMap['BTC_USDT'].length - (day_count + 1)
  239. let btcK = kLinesMap['BTC_USDT'][index]
  240. let dateStr = TimeKit.getDateByMillisecond(btcK.Time)
  241. let btcUpRate = NumKit.getSubFloat(100 * (btcK.Close - btcK.Open) / btcK.Open, 2)
  242. logger.info(`${day_count}日(${dateStr}, ${realLength + fakeLength}只),赚钱榜(${realLength}只)利润${realDragonProfit}%`
  243. + `,亏钱榜(${fakeLength}只)利润${fakeDragonProfit}%`
  244. + `,赚钱榜期望利润${expRealProfit}%,亏钱榜期望利润${expFakeProfit}%,综合利润${synProfit}%,平均每只利润${avgProfit}%,BTC涨幅${btcUpRate}%`
  245. )
  246. totalProfit += avgProfit
  247. totalProfit = NumKit.getSubFloat(totalProfit, 2)
  248. }
  249. logger.info(`${dataList.length}`)
  250. ChartKit.printChart(dataList)
  251. // let dayProfit = NumKit.getSubFloat(totalProfit / BAKE_TEST_DAYS, 2)
  252. // logger.info(`利润期望值总和:${totalProfit}%,平均日化${dayProfit}%。`)
  253. // for (let symbol in fakeCountMap) {
  254. // let kLines = kLinesMap[symbol]
  255. // let startIndex = kLines.length - BAKE_TEST_DAYS
  256. // let endIndex = kLines.length - 1
  257. //
  258. // logger.info(`${symbol} 盈${realCountMap[symbol]},亏${fakeCountMap[symbol]},大于5%涨幅的共有${statisticA(kLines, startIndex, endIndex)}日。`)
  259. // }
  260. }
  261. main().catch((error) => {
  262. logger.error(error.stack);
  263. process.exitCode = 1;
  264. })