parseMarkets.js 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. import getDateInTimezone from "./getDateInTimezone.js";
  2. /**
  3. * 精确浮点数字
  4. * @param {number} number
  5. * @param {number} x
  6. * @returns {number}
  7. */
  8. const fixFloat = (number, x=3) => {
  9. return parseFloat(number.toFixed(x));
  10. }
  11. /**
  12. * 清理对象中的undefined值
  13. * @param {*} obj
  14. * @returns {Object}
  15. */
  16. const cleanUndefined = (obj) => {
  17. return Object.fromEntries(
  18. Object.entries(obj).filter(([, v]) => v !== undefined)
  19. );
  20. }
  21. /**
  22. * 解析赛事标题
  23. * @param {*} eventTitle
  24. * @returns {Object}
  25. */
  26. const parseTeamData = (eventTitle) => {
  27. let titleSpliter;
  28. if (eventTitle.includes(' vs. ')) {
  29. titleSpliter = ' vs. ';
  30. }
  31. else if (eventTitle.includes(' vs ')) {
  32. titleSpliter = ' vs ';
  33. }
  34. if (!titleSpliter) {
  35. return {
  36. teamHomeName: '',
  37. teamAwayName: '',
  38. };
  39. }
  40. const teamData = eventTitle.replace(' - More Markets', '').split(titleSpliter);
  41. return {
  42. teamHomeName: teamData[0],
  43. teamAwayName: teamData[1],
  44. };
  45. }
  46. /**
  47. * 解析盘口键值对
  48. * 示例:
  49. * {
  50. * "Yes": 0.525,
  51. * "No": 0.475
  52. * }
  53. * @param {*} outcomes
  54. * @param {*} outcomePrices
  55. * @returns {Object}
  56. */
  57. const parseOutcomes = (outcomes, clobTokenIds, bestBid=0, bestAsk=0) => {
  58. const keys = JSON.parse(outcomes);
  59. const ids = JSON.parse(clobTokenIds);
  60. return keys.reduce((obj, key, index) => {
  61. let best_ask;
  62. let best_bid;
  63. if (index === 0) {
  64. best_ask = (bestAsk).toFixed(3);
  65. best_bid = (bestBid).toFixed(3);
  66. }
  67. else if (index === 1) {
  68. best_ask = (1 - bestBid).toFixed(3);
  69. best_bid = (1 - bestAsk).toFixed(3);
  70. }
  71. obj[key] = { id: ids[index], best_ask, best_bid };
  72. return obj;
  73. }, {});
  74. }
  75. /**
  76. * 解析盘口数据
  77. * @param {*} markets
  78. * @returns {Object}
  79. */
  80. const ratioAccept = (ratio) => {
  81. if (ratio > 0) {
  82. return 'a';
  83. }
  84. return '';
  85. }
  86. const ratioString = (ratio) => {
  87. ratio = Math.abs(ratio);
  88. ratio = ratio.toString();
  89. ratio = ratio.replace(/\./, '');
  90. return ratio;
  91. }
  92. export const parseOdds = (markets) => {
  93. const odds = {};
  94. Object.keys(markets).forEach(key => {
  95. const marketData = markets[key];
  96. if (key === 'moneyline') {
  97. Object.keys(marketData).forEach(side => {
  98. const askYes = marketData[side].outcomes['Yes']['best_ask'];
  99. const tokenYes = marketData[side].outcomes['Yes']['id'];
  100. const askNo = marketData[side].outcomes['No']['best_ask'];
  101. const tokenNo = marketData[side].outcomes['No']['id'];
  102. const slug = marketData[side].market.slug;
  103. if (askYes <= 0.1 || askNo <= 0.1) {
  104. return;
  105. }
  106. const iorYes = fixFloat(1 / askYes);
  107. const iorNo = fixFloat(1 / askNo);
  108. let iorKeyYes = '';
  109. let iorKeyNo = '';
  110. switch (side) {
  111. case 'Home':
  112. iorKeyYes = 'ior_mh';
  113. iorKeyNo = 'ior_moh';
  114. break;
  115. case 'Draw':
  116. iorKeyYes = 'ior_mn';
  117. iorKeyNo = 'ior_mon';
  118. break;
  119. case 'Away':
  120. iorKeyYes = 'ior_mc';
  121. iorKeyNo = 'ior_moc';
  122. break;
  123. }
  124. odds[iorKeyYes] = { v: iorYes, ask: askYes, token: tokenYes, slug };
  125. odds[iorKeyNo] = { v: iorNo, ask: askNo, token: tokenNo, slug };
  126. });
  127. }
  128. else if (key === 'spreads') {
  129. Object.keys(marketData).forEach(handicap => {
  130. const ratio = +handicap;
  131. const askHome = marketData[handicap].outcomes['Home']['best_ask'];
  132. const tokenHome = marketData[handicap].outcomes['Home']['id'];
  133. const askAway = marketData[handicap].outcomes['Away']['best_ask'];
  134. const tokenAway = marketData[handicap].outcomes['Away']['id'];
  135. const slug = marketData[handicap].market.slug;
  136. if (askHome <= 0.1 || askAway <= 0.1) {
  137. return;
  138. }
  139. const iorHome = fixFloat(1 / askHome);
  140. const iorAway = fixFloat(1 / askAway);
  141. odds[`ior_r${ratioAccept(ratio)}h_${ratioString(ratio)}`] = { v: iorHome, ask: askHome, token: tokenHome, slug };
  142. odds[`ior_r${ratioAccept(-ratio)}c_${ratioString(ratio)}`] = { v: iorAway, ask: askAway, token: tokenAway, slug };
  143. });
  144. }
  145. else if (key === 'totals') {
  146. Object.keys(marketData).forEach(handicap => {
  147. const ratio = +handicap;
  148. const askOver = marketData[handicap].outcomes['Over']['best_ask'];
  149. const tokenOver = marketData[handicap].outcomes['Over']['id'];
  150. const askUnder = marketData[handicap].outcomes['Under']['best_ask'];
  151. const tokenUnder = marketData[handicap].outcomes['Under']['id'];
  152. const slug = marketData[handicap].market.slug;
  153. if (askOver <= 0.1 || askUnder <= 0.1) {
  154. return;
  155. }
  156. const iorOver = fixFloat(1 / askOver);
  157. const iorUnder = fixFloat(1 / askUnder);
  158. odds[`ior_ouc_${ratioString(ratio)}`] = { v: iorOver, ask: askOver, token: tokenOver, slug };
  159. odds[`ior_ouh_${ratioString(ratio)}`] = { v: iorUnder, ask: askUnder, token: tokenUnder, slug };
  160. });
  161. }
  162. });
  163. return odds;
  164. }
  165. /**
  166. * 解析胜平负盘口
  167. * @param {*} groupItemThreshold
  168. * @param {*} outcomesMap
  169. * @param {*} market
  170. * @returns {Object}
  171. */
  172. const parseMoneyline = (groupItemTitle, outcomesMap, teams, market) => {
  173. const { teamHomeName, teamAwayName } = teams;
  174. let key = '';
  175. if (groupItemTitle == teamHomeName) {
  176. key = 'Home';
  177. }
  178. else if (groupItemTitle == teamAwayName) {
  179. key = 'Away';
  180. }
  181. else if (groupItemTitle.startsWith('Draw')) {
  182. key = 'Draw';
  183. }
  184. if (!key) {
  185. return {};
  186. }
  187. return { [key]: { outcomes: outcomesMap, market } };
  188. };
  189. /**
  190. * 解析让球盘口
  191. * @param {*} groupItemTitle
  192. * @param {*} spreadsLine
  193. * @param {*} outcomesMap
  194. * @param {*} teams
  195. * @param {*} market
  196. * @returns {Object}
  197. */
  198. const parseSpreads = (groupItemTitle, spreadsLine, outcomesMap, teams, market) => {
  199. const { teamHomeName, teamAwayName } = teams;
  200. let spreadDirection = 0;
  201. if (groupItemTitle.includes(teamHomeName)) {
  202. spreadDirection = 1;
  203. }
  204. else if (groupItemTitle.includes(teamAwayName)) {
  205. spreadDirection = -1;
  206. }
  207. const spreads = spreadsLine * spreadDirection;
  208. const key = spreads > 0 ? `+${spreads}` : `${spreads}`;
  209. const spreadsMap = {};
  210. Object.keys(outcomesMap).forEach(key => {
  211. if (key == teamHomeName) {
  212. spreadsMap['Home'] = outcomesMap[key];
  213. }
  214. else if (key == teamAwayName) {
  215. spreadsMap['Away'] = outcomesMap[key];
  216. }
  217. });
  218. return { [key]: { outcomes: spreadsMap, market } };
  219. };
  220. /**
  221. * 解析大小盘口
  222. * @param {*} line
  223. * @param {*} outcomesMap
  224. * @param {*} market
  225. * @returns {Object}
  226. */
  227. const parseTotals = (line, outcomesMap, market) => {
  228. return { [line]: { outcomes: outcomesMap, market } };
  229. };
  230. /**
  231. * 解析市场数据
  232. * @param {*} markets
  233. * @param {*} teams
  234. * @returns {Object}
  235. */
  236. const parseMarketsData = (markets, teams) => {
  237. const marketsData = {};
  238. markets.forEach(market => {
  239. const { sportsMarketType, groupItemTitle, line, outcomes, clobTokenIds, bestBid=0, bestAsk=0 } = market;
  240. const outcomesMap = parseOutcomes(outcomes, clobTokenIds, bestBid, bestAsk);
  241. if (sportsMarketType === "moneyline") {
  242. if (!marketsData.moneyline) {
  243. marketsData.moneyline = {};
  244. }
  245. Object.assign(marketsData.moneyline, parseMoneyline(groupItemTitle, outcomesMap, teams, market))
  246. }
  247. else if (sportsMarketType === "spreads") {
  248. if (!marketsData.spreads) {
  249. marketsData.spreads = {};
  250. }
  251. Object.assign(marketsData.spreads, parseSpreads(groupItemTitle, line, outcomesMap, teams, market))
  252. }
  253. else if (sportsMarketType === "totals") {
  254. if (!marketsData.totals) {
  255. marketsData.totals = {};
  256. }
  257. Object.assign(marketsData.totals, parseTotals(line, outcomesMap, market));
  258. }
  259. });
  260. return marketsData;
  261. }
  262. /**
  263. * 解析赛事数据
  264. * @param {*} event
  265. * @returns {Object}
  266. */
  267. const parseEvent = (event) => {
  268. const { id, title, series, gameId, parentEventId, startTime } = event;
  269. const { teamHomeName, teamAwayName } = parseTeamData(title);
  270. const leagueId = series[0].id;
  271. const leagueName = series[0].title;
  272. const timestamp = new Date(startTime).getTime();
  273. return {
  274. id: +id,
  275. gameId: gameId ? +gameId : undefined,
  276. parentEventId: parentEventId ? +parentEventId : undefined,
  277. leagueId: +leagueId,
  278. teamHomeName, teamAwayName, leagueName,
  279. timestamp,
  280. startTime: getDateInTimezone('+8', timestamp, true),
  281. };
  282. }
  283. /**
  284. * 解析市场列表数据
  285. * @param {*} events
  286. * @returns {Object}
  287. */
  288. export const parseMarkets = (eventsData) => {
  289. const mergedMarketsData = {};
  290. Object.values(eventsData).map(event => {
  291. const item = parseEvent(event);
  292. const { markets } = event;
  293. const { teamHomeName, teamAwayName } = item;
  294. item.marketsData = parseMarketsData(markets, { teamHomeName, teamAwayName });
  295. return item;
  296. }).sort((a, b) => a.id - b.id).forEach(item => {
  297. if (item.id && !item.parentEventId) {
  298. mergedMarketsData[item.id] = cleanUndefined(item);
  299. }
  300. else if (item.parentEventId && mergedMarketsData[item.parentEventId] && item.marketsData) {
  301. const { marketsData: parentEvents } = mergedMarketsData[item.parentEventId];
  302. Object.assign(parentEvents, item.marketsData);
  303. }
  304. });
  305. return mergedMarketsData;
  306. }
  307. // export default parseMarkets;