| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310 |
- import getDateInTimezone from "./getDateInTimezone.js";
- /**
- * 精确浮点数字
- * @param {number} number
- * @param {number} x
- * @returns {number}
- */
- const fixFloat = (number, x=3) => {
- return parseFloat(number.toFixed(x));
- }
- /**
- * 清理对象中的undefined值
- * @param {*} obj
- * @returns {Object}
- */
- const cleanUndefined = (obj) => {
- return Object.fromEntries(
- Object.entries(obj).filter(([, v]) => v !== undefined)
- );
- }
- /**
- * 解析赛事标题
- * @param {*} eventTitle
- * @returns {Object}
- */
- const parseTeamData = (eventTitle) => {
- let titleSpliter;
- if (eventTitle.includes(' vs. ')) {
- titleSpliter = ' vs. ';
- }
- else if (eventTitle.includes(' vs ')) {
- titleSpliter = ' vs ';
- }
- if (!titleSpliter) {
- return {
- teamHomeName: '',
- teamAwayName: '',
- };
- }
- const teamData = eventTitle.replace(' - More Markets', '').split(titleSpliter);
- return {
- teamHomeName: teamData[0],
- teamAwayName: teamData[1],
- };
- }
- /**
- * 解析盘口键值对
- * 示例:
- * {
- * "Yes": 0.525,
- * "No": 0.475
- * }
- * @param {*} outcomes
- * @param {*} outcomePrices
- * @returns {Object}
- */
- const parseOutcomes = (outcomes, clobTokenIds, bestBid=0, bestAsk=0) => {
- const keys = JSON.parse(outcomes);
- const ids = JSON.parse(clobTokenIds);
- return keys.reduce((obj, key, index) => {
- let best_ask;
- let best_bid;
- if (index === 0) {
- best_ask = (bestAsk).toFixed(3);
- best_bid = (bestBid).toFixed(3);
- }
- else if (index === 1) {
- best_ask = (1 - bestBid).toFixed(3);
- best_bid = (1 - bestAsk).toFixed(3);
- }
- obj[key] = { id: ids[index], best_ask, best_bid };
- return obj;
- }, {});
- }
- /**
- * 解析盘口数据
- * @param {*} markets
- * @returns {Object}
- */
- const ratioAccept = (ratio) => {
- if (ratio > 0) {
- return 'a';
- }
- return '';
- }
- const ratioString = (ratio) => {
- ratio = Math.abs(ratio);
- ratio = ratio.toString();
- ratio = ratio.replace(/\./, '');
- return ratio;
- }
- export const parseOdds = (markets) => {
- const odds = {};
- Object.keys(markets).forEach(key => {
- const marketData = markets[key];
- if (key === 'moneyline') {
- Object.keys(marketData).forEach(side => {
- const askYes = marketData[side].outcomes['Yes']['best_ask'];
- const askNo = marketData[side].outcomes['No']['best_ask'];
- if (askYes <= 0.1 || askNo <= 0.1) {
- return;
- }
- const iorYes = fixFloat(1 / askYes);
- const iorNo = fixFloat(1 / askNo);
- let iorKeyYes = '';
- let iorKeyNo = '';
- switch (side) {
- case 'Home':
- iorKeyYes = 'ior_mh';
- iorKeyNo = 'ior_moh';
- break;
- case 'Draw':
- iorKeyYes = 'ior_mn';
- iorKeyNo = 'ior_mon';
- break;
- case 'Away':
- iorKeyYes = 'ior_mc';
- iorKeyNo = 'ior_moc';
- break;
- }
- odds[iorKeyYes] = { v: iorYes, ask: askYes };
- odds[iorKeyNo] = { v: iorNo, ask: askNo };
- });
- }
- else if (key === 'spreads') {
- Object.keys(marketData).forEach(handicap => {
- const ratio = +handicap;
- const askHome = marketData[handicap].outcomes['Home']['best_ask'];
- const askAway = marketData[handicap].outcomes['Away']['best_ask'];
- if (askHome <= 0.1 || askAway <= 0.1) {
- return;
- }
- const iorHome = fixFloat(1 / askHome);
- const iorAway = fixFloat(1 / askAway);
- odds[`ior_r${ratioAccept(ratio)}h_${ratioString(ratio)}`] = { v: iorHome, ask: askHome };
- odds[`ior_r${ratioAccept(-ratio)}c_${ratioString(ratio)}`] = { v: iorAway, ask: askAway };
- });
- }
- else if (key === 'totals') {
- Object.keys(marketData).forEach(handicap => {
- const ratio = +handicap;
- const askOver = marketData[handicap].outcomes['Over']['best_ask'];
- const askUnder = marketData[handicap].outcomes['Under']['best_ask'];
- if (askOver <= 0.1 || askUnder <= 0.1) {
- return;
- }
- const iorOver = fixFloat(1 / askOver);
- const iorUnder = fixFloat(1 / askUnder);
- odds[`ior_ouc_${ratioString(ratio)}`] = { v: iorOver, ask: askOver };
- odds[`ior_ouh_${ratioString(ratio)}`] = { v: iorUnder, ask: askUnder };
- });
- }
- });
- return odds;
- }
- /**
- * 解析胜平负盘口
- * @param {*} groupItemThreshold
- * @param {*} outcomesMap
- * @param {*} market
- * @returns {Object}
- */
- const parseMoneyline = (groupItemTitle, outcomesMap, teams, market) => {
- const { teamHomeName, teamAwayName } = teams;
- let key = '';
- if (groupItemTitle == teamHomeName) {
- key = 'Home';
- }
- else if (groupItemTitle == teamAwayName) {
- key = 'Away';
- }
- else if (groupItemTitle.startsWith('Draw')) {
- key = 'Draw';
- }
- if (!key) {
- return {};
- }
- return { [key]: { outcomes: outcomesMap, market } };
- };
- /**
- * 解析让球盘口
- * @param {*} groupItemTitle
- * @param {*} spreadsLine
- * @param {*} outcomesMap
- * @param {*} teams
- * @param {*} market
- * @returns {Object}
- */
- const parseSpreads = (groupItemTitle, spreadsLine, outcomesMap, teams, market) => {
- const { teamHomeName, teamAwayName } = teams;
- let spreadDirection = 0;
- if (groupItemTitle.includes(teamHomeName)) {
- spreadDirection = 1;
- }
- else if (groupItemTitle.includes(teamAwayName)) {
- spreadDirection = -1;
- }
- const spreads = spreadsLine * spreadDirection;
- const key = spreads > 0 ? `+${spreads}` : `${spreads}`;
- const spreadsMap = {};
- Object.keys(outcomesMap).forEach(key => {
- if (key == teamHomeName) {
- spreadsMap['Home'] = outcomesMap[key];
- }
- else if (key == teamAwayName) {
- spreadsMap['Away'] = outcomesMap[key];
- }
- });
- return { [key]: { outcomes: spreadsMap, market } };
- };
- /**
- * 解析大小盘口
- * @param {*} line
- * @param {*} outcomesMap
- * @param {*} market
- * @returns {Object}
- */
- const parseTotals = (line, outcomesMap, market) => {
- return { [line]: { outcomes: outcomesMap, market } };
- };
- /**
- * 解析市场数据
- * @param {*} markets
- * @param {*} teams
- * @returns {Object}
- */
- const parseMarketsData = (markets, teams) => {
- const marketsData = {};
- markets.forEach(market => {
- const { sportsMarketType, groupItemTitle, line, outcomes, clobTokenIds, bestBid=0, bestAsk=0 } = market;
- const outcomesMap = parseOutcomes(outcomes, clobTokenIds, bestBid, bestAsk);
- if (sportsMarketType === "moneyline") {
- if (!marketsData.moneyline) {
- marketsData.moneyline = {};
- }
- Object.assign(marketsData.moneyline, parseMoneyline(groupItemTitle, outcomesMap, teams, market))
- }
- else if (sportsMarketType === "spreads") {
- if (!marketsData.spreads) {
- marketsData.spreads = {};
- }
- Object.assign(marketsData.spreads, parseSpreads(groupItemTitle, line, outcomesMap, teams, market))
- }
- else if (sportsMarketType === "totals") {
- if (!marketsData.totals) {
- marketsData.totals = {};
- }
- Object.assign(marketsData.totals, parseTotals(line, outcomesMap, market));
- }
- });
- return marketsData;
- }
- /**
- * 解析赛事数据
- * @param {*} event
- * @returns {Object}
- */
- const parseEvent = (event) => {
- const { id, title, series, gameId, parentEventId, startTime } = event;
- const { teamHomeName, teamAwayName } = parseTeamData(title);
- const leagueId = series[0].id;
- const leagueName = series[0].title;
- const timestamp = new Date(startTime).getTime();
- return {
- id: +id,
- gameId: gameId ? +gameId : undefined,
- parentEventId: parentEventId ? +parentEventId : undefined,
- leagueId: +leagueId,
- teamHomeName, teamAwayName, leagueName,
- timestamp,
- startTime: getDateInTimezone('+8', timestamp, true),
- };
- }
- /**
- * 解析市场列表数据
- * @param {*} events
- * @returns {Object}
- */
- export const parseMarkets = (eventsData) => {
- const mergedMarketsData = {};
- Object.values(eventsData).map(event => {
- const item = parseEvent(event);
- const { markets } = event;
- const { teamHomeName, teamAwayName } = item;
- item.marketsData = parseMarketsData(markets, { teamHomeName, teamAwayName });
- return item;
- }).sort((a, b) => a.id - b.id).forEach(item => {
- if (item.id && !item.parentEventId) {
- mergedMarketsData[item.id] = cleanUndefined(item);
- }
- else if (item.parentEventId && mergedMarketsData[item.parentEventId] && item.marketsData) {
- const { marketsData: parentEvents } = mergedMarketsData[item.parentEventId];
- Object.assign(parentEvents, item.marketsData);
- }
- });
- return mergedMarketsData;
- }
- // export default parseMarkets;
|