parseMarkets.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566
  1. import getDateInTimezone from "./getDateInTimezone.js";
  2. const MAKER_FEE_RATE = 0.03;
  3. const MAKER_REBATE_RATE = 0.25;
  4. /**
  5. * 精确浮点数字
  6. * @param {number} number
  7. * @param {number} x
  8. * @returns {number}
  9. */
  10. const fixFloat = (number, x=3) => {
  11. return parseFloat(number.toFixed(x));
  12. }
  13. /**
  14. * 清理对象中的undefined值
  15. * @param {*} obj
  16. * @returns {Object}
  17. */
  18. const cleanUndefined = (obj) => {
  19. return Object.fromEntries(
  20. Object.entries(obj).filter(([, v]) => v !== undefined)
  21. );
  22. }
  23. /**
  24. * 解析赛事标题
  25. * @param {*} eventTitle
  26. * @returns {Object}
  27. */
  28. const parseTeamData = (eventTitle) => {
  29. let titleSpliter;
  30. if (eventTitle.includes(' vs. ')) {
  31. titleSpliter = ' vs. ';
  32. }
  33. else if (eventTitle.includes(' vs ')) {
  34. titleSpliter = ' vs ';
  35. }
  36. if (!titleSpliter) {
  37. return {
  38. teamHomeName: '',
  39. teamAwayName: '',
  40. };
  41. }
  42. const teamData = eventTitle.replace(' - More Markets', '').split(titleSpliter);
  43. return {
  44. teamHomeName: teamData[0],
  45. teamAwayName: teamData[1],
  46. };
  47. }
  48. /**
  49. * 解析盘口键值对
  50. * 示例:
  51. * {
  52. * "Yes": 0.525,
  53. * "No": 0.475
  54. * }
  55. * @param {*} outcomes
  56. * @param {*} outcomePrices
  57. * @returns {Object}
  58. */
  59. const parseOutcomes = (outcomes, clobTokenIds, bestBid=0, bestAsk=0) => {
  60. if (!outcomes || !clobTokenIds) {
  61. return {};
  62. }
  63. const keys = JSON.parse(outcomes);
  64. const ids = JSON.parse(clobTokenIds);
  65. return keys.reduce((obj, key, index) => {
  66. let best_ask;
  67. let best_bid;
  68. if (index === 0) {
  69. best_ask = (bestAsk).toFixed(3);
  70. best_bid = (bestBid).toFixed(3);
  71. }
  72. else if (index === 1) {
  73. best_ask = (1 - bestBid).toFixed(3);
  74. best_bid = (1 - bestAsk).toFixed(3);
  75. }
  76. obj[key] = { id: ids[index], best_ask, best_bid };
  77. return obj;
  78. }, {});
  79. }
  80. /**
  81. * 解析让球方向
  82. * 让球方向为正数时,返回'a',否则返回''
  83. * @param {*} ratio
  84. * @returns {string}
  85. */
  86. const ratioAccept = (ratio) => {
  87. if (ratio > 0) {
  88. return 'a';
  89. }
  90. return '';
  91. }
  92. /**
  93. * 解析盘口比率
  94. * 比率取绝对值,并去掉小数点后的数字
  95. * @param {*} ratio
  96. * @returns {string}
  97. */
  98. const ratioString = (ratio) => {
  99. ratio = Math.abs(ratio);
  100. ratio = ratio.toString();
  101. ratio = ratio.replace(/\./, '');
  102. return ratio;
  103. }
  104. /**
  105. * 解析买单价格
  106. * 当卖单最低价格与买单最高价格差值超过最小变动价位时,返回买单最高价格+最小变动价位
  107. * 否则,返回买单最高价格
  108. * @param {*} ask
  109. * @param {*} bid
  110. * @param {*} tickSize
  111. * @returns {number}
  112. */
  113. const parseBidPrice = (ask, bid, tickSize) => {
  114. const askPrice = +ask;
  115. const bidPrice = +bid;
  116. const minTickSize = +tickSize;
  117. const bestBidPrice = fixFloat(askPrice - minTickSize);
  118. if (bestBidPrice > bidPrice) {
  119. return bestBidPrice;
  120. }
  121. return bidPrice;
  122. }
  123. /**
  124. * 解析吃单手续费比
  125. * 固定金额吃卖单时,手续费比约等于 费率*(1-价格)
  126. * @param {*} price
  127. * @returns {number}
  128. */
  129. const parseAskFee = (price) => {
  130. return fixFloat(100 * MAKER_FEE_RATE * (1 - price), 4);
  131. }
  132. /**
  133. * 解析挂单返佣比
  134. * 固定金额挂买单时,返佣比约等于 费率*(1-价格)*返佣比例
  135. * @param {*} price
  136. * @returns {number}
  137. */
  138. const parseBidRebate = (price) => {
  139. return fixFloat(100 * MAKER_FEE_RATE * (1 - price) * MAKER_REBATE_RATE, 4);
  140. }
  141. /**
  142. * 解析盘口数据
  143. * 使用卖单最优价格
  144. * @param {*} markets
  145. * @returns {Object}
  146. */
  147. export const parseOddsAsk = (markets) => {
  148. const odds = {};
  149. Object.keys(markets).forEach(key => {
  150. const marketData = markets[key];
  151. if (key === 'moneyline') {
  152. Object.keys(marketData).forEach(side => {
  153. const askYes = +marketData[side].outcomes['Yes']['best_ask'];
  154. const tokenYes = marketData[side].outcomes['Yes']['id'];
  155. const askNo = +marketData[side].outcomes['No']['best_ask'];
  156. const tokenNo = marketData[side].outcomes['No']['id'];
  157. const slug = marketData[side].market.slug;
  158. if (askYes <= 0.1 || askNo <= 0.1) {
  159. return;
  160. }
  161. const iorYes = fixFloat(1 / askYes);
  162. const iorNo = fixFloat(1 / askNo);
  163. const feeYes = parseAskFee(askYes);
  164. const feeNo = parseAskFee(askNo);
  165. let iorKeyYes = '';
  166. let iorKeyNo = '';
  167. switch (side) {
  168. case 'Home':
  169. iorKeyYes = 'ior_mh';
  170. iorKeyNo = 'ior_moh';
  171. break;
  172. case 'Draw':
  173. iorKeyYes = 'ior_mn';
  174. iorKeyNo = 'ior_mon';
  175. break;
  176. case 'Away':
  177. iorKeyYes = 'ior_mc';
  178. iorKeyNo = 'ior_moc';
  179. break;
  180. }
  181. odds[iorKeyYes] = { v: iorYes, b: -feeYes, t: 1, ask: askYes, token: tokenYes, slug };
  182. odds[iorKeyNo] = { v: iorNo, b: -feeNo, t: 1, ask: askNo, token: tokenNo, slug };
  183. });
  184. }
  185. else if (key === 'spreads') {
  186. Object.keys(marketData).forEach(handicap => {
  187. const ratio = +handicap;
  188. const askHome = +marketData[handicap].outcomes['Home']['best_ask'];
  189. const tokenHome = marketData[handicap].outcomes['Home']['id'];
  190. const askAway = +marketData[handicap].outcomes['Away']['best_ask'];
  191. const tokenAway = marketData[handicap].outcomes['Away']['id'];
  192. const slug = marketData[handicap].market.slug;
  193. if (askHome <= 0.1 || askAway <= 0.1) {
  194. return;
  195. }
  196. const iorHome = fixFloat(1 / askHome);
  197. const iorAway = fixFloat(1 / askAway);
  198. const feeHome = parseAskFee(askHome);
  199. const feeAway = parseAskFee(askAway);
  200. odds[`ior_r${ratioAccept(ratio)}h_${ratioString(ratio)}`] = { v: iorHome, b: -feeHome, t: 1, ask: askHome, token: tokenHome, slug };
  201. odds[`ior_r${ratioAccept(-ratio)}c_${ratioString(ratio)}`] = { v: iorAway, b: -feeAway, t: 1, ask: askAway, token: tokenAway, slug };
  202. });
  203. }
  204. else if (key === 'totals') {
  205. Object.keys(marketData).forEach(handicap => {
  206. const ratio = +handicap;
  207. const askOver = +marketData[handicap].outcomes['Over']['best_ask'];
  208. const tokenOver = marketData[handicap].outcomes['Over']['id'];
  209. const askUnder = +marketData[handicap].outcomes['Under']['best_ask'];
  210. const tokenUnder = marketData[handicap].outcomes['Under']['id'];
  211. const slug = marketData[handicap].market.slug;
  212. if (askOver <= 0.1 || askUnder <= 0.1) {
  213. return;
  214. }
  215. const iorOver = fixFloat(1 / askOver);
  216. const iorUnder = fixFloat(1 / askUnder);
  217. const feeOver = parseAskFee(askOver);
  218. const feeUnder = parseAskFee(askUnder);
  219. odds[`ior_ouc_${ratioString(ratio)}`] = { v: iorOver, b: -feeOver, t: 1, ask: askOver, token: tokenOver, slug };
  220. odds[`ior_ouh_${ratioString(ratio)}`] = { v: iorUnder, b: -feeUnder, t: 1, ask: askUnder, token: tokenUnder, slug };
  221. });
  222. }
  223. });
  224. return odds;
  225. }
  226. /**
  227. * 解析盘口数据
  228. * 当卖单最低价格与买单最高价格差值超过最小变动价位时
  229. * 创建新的买单价格,即买单最高价格+最小变动价位
  230. * 否则,使用买单最高价格
  231. * @param {*} markets
  232. * @returns {Object}
  233. */
  234. export const parseOddsBid = (markets) => {
  235. const odds = {};
  236. Object.keys(markets).forEach(key => {
  237. const marketData = markets[key];
  238. if (key === 'moneyline') {
  239. Object.keys(marketData).forEach(side => {
  240. const askYes = +marketData[side].outcomes['Yes']['best_ask'];
  241. const bidYes = +marketData[side].outcomes['Yes']['best_bid'];
  242. // const tokenYes = marketData[side].outcomes['Yes']['id'];
  243. const askNo = +marketData[side].outcomes['No']['best_ask'];
  244. const bidNo = +marketData[side].outcomes['No']['best_bid'];
  245. // const tokenNo = marketData[side].outcomes['No']['id'];
  246. // const slug = marketData[side].market.slug;
  247. const tick_size = marketData[side].market.orderPriceMinTickSize;
  248. if (askYes <= 0.1 || askNo <= 0.1) {
  249. return;
  250. }
  251. const bidPriceYes = parseBidPrice(askYes, bidYes, tick_size);
  252. const bidPriceNo = parseBidPrice(askNo, bidNo, tick_size);
  253. const iorYes = fixFloat(1 / bidPriceYes);
  254. const iorNo = fixFloat(1 / bidPriceNo);
  255. const rebateYes = parseBidRebate(bidPriceYes);
  256. const rebateNo = parseBidRebate(bidPriceNo);
  257. let iorKeyYes = '';
  258. let iorKeyNo = '';
  259. switch (side) {
  260. case 'Home':
  261. iorKeyYes = 'ior_mh';
  262. iorKeyNo = 'ior_moh';
  263. break;
  264. case 'Draw':
  265. iorKeyYes = 'ior_mn';
  266. iorKeyNo = 'ior_mon';
  267. break;
  268. case 'Away':
  269. iorKeyYes = 'ior_mc';
  270. iorKeyNo = 'ior_moc';
  271. break;
  272. }
  273. odds[iorKeyYes] = { v: iorYes, b: rebateYes, t: 1, ask: askYes, bid: bidYes, bid_ex: bidPriceYes, tick_size, /*token: tokenYes, slug */ };
  274. odds[iorKeyNo] = { v: iorNo, b: rebateNo, t: 1, ask: askNo, bid: bidNo, bid_ex: bidPriceNo, tick_size, /*token: tokenNo, slug */ };
  275. });
  276. }
  277. else if (key === 'spreads') {
  278. Object.keys(marketData).forEach(handicap => {
  279. const ratio = +handicap;
  280. const askHome = +marketData[handicap].outcomes['Home']['best_ask'];
  281. const bidHome = +marketData[handicap].outcomes['Home']['best_bid'];
  282. // const tokenHome = marketData[handicap].outcomes['Home']['id'];
  283. const askAway = +marketData[handicap].outcomes['Away']['best_ask'];
  284. const bidAway = +marketData[handicap].outcomes['Away']['best_bid'];
  285. // const tokenAway = marketData[handicap].outcomes['Away']['id'];
  286. // const slug = marketData[handicap].market.slug;
  287. const tick_size = marketData[handicap].market.orderPriceMinTickSize;
  288. if (askHome <= 0.1 || askAway <= 0.1) {
  289. return;
  290. }
  291. const bidPriceHome = parseBidPrice(askHome, bidHome, tick_size);
  292. const bidPriceAway = parseBidPrice(askAway, bidAway, tick_size);
  293. const iorHome = fixFloat(1 / bidPriceHome);
  294. const iorAway = fixFloat(1 / bidPriceAway);
  295. const rebateHome = parseBidRebate(bidPriceHome);
  296. const rebateAway = parseBidRebate(bidPriceAway);
  297. odds[`ior_r${ratioAccept(ratio)}h_${ratioString(ratio)}`] = { v: iorHome, b: rebateHome, t: 1, ask: askHome, bid: bidHome, bid_ex: bidPriceHome, tick_size, /*token: tokenHome, slug */ };
  298. odds[`ior_r${ratioAccept(-ratio)}c_${ratioString(ratio)}`] = { v: iorAway, b: rebateAway, t: 1, ask: askAway, bid: bidAway, bid_ex: bidPriceAway, tick_size, /*token: tokenAway, slug */ };
  299. });
  300. }
  301. else if (key === 'totals') {
  302. Object.keys(marketData).forEach(handicap => {
  303. const ratio = +handicap;
  304. const askOver = +marketData[handicap].outcomes['Over']['best_ask'];
  305. const bidOver = +marketData[handicap].outcomes['Over']['best_bid'];
  306. // const tokenOver = marketData[handicap].outcomes['Over']['id'];
  307. const askUnder = +marketData[handicap].outcomes['Under']['best_ask'];
  308. const bidUnder = +marketData[handicap].outcomes['Under']['best_bid'];
  309. // const tokenUnder = marketData[handicap].outcomes['Under']['id'];
  310. // const slug = marketData[handicap].market.slug;
  311. const tick_size = marketData[handicap].market.orderPriceMinTickSize;
  312. if (askOver <= 0.1 || askUnder <= 0.1) {
  313. return;
  314. }
  315. const bidPriceOver = parseBidPrice(askOver, bidOver, tick_size);
  316. const bidPriceUnder = parseBidPrice(askUnder, bidUnder, tick_size);
  317. const iorOver = fixFloat(1 / bidPriceOver);
  318. const iorUnder = fixFloat(1 / bidPriceUnder);
  319. const rebateOver = parseBidRebate(bidPriceOver);
  320. const rebateUnder = parseBidRebate(bidPriceUnder);
  321. odds[`ior_ouc_${ratioString(ratio)}`] = { v: iorOver, b: rebateOver, t: 1, ask: askOver, bid: bidOver, bid_ex: bidPriceOver, tick_size, /*token: tokenOver, slug */ };
  322. odds[`ior_ouh_${ratioString(ratio)}`] = { v: iorUnder, b: rebateUnder, t: 1, ask: askUnder, bid: bidUnder, bid_ex: bidPriceUnder, tick_size, /*token: tokenUnder, slug */ };
  323. });
  324. }
  325. });
  326. return odds;
  327. }
  328. /**
  329. * 解析胜平负盘口
  330. * @param {*} groupItemThreshold
  331. * @param {*} outcomesMap
  332. * @param {*} market
  333. * @returns {Object}
  334. */
  335. const parseMoneyline = (groupItemTitle, outcomesMap, teams, market) => {
  336. const { teamHomeName, teamAwayName } = teams;
  337. let key = '';
  338. if (groupItemTitle == teamHomeName) {
  339. key = 'Home';
  340. }
  341. else if (groupItemTitle == teamAwayName) {
  342. key = 'Away';
  343. }
  344. else if (groupItemTitle.startsWith('Draw')) {
  345. key = 'Draw';
  346. }
  347. if (!key) {
  348. return {};
  349. }
  350. return { [key]: { outcomes: outcomesMap, market } };
  351. };
  352. /**
  353. * 解析让球盘口
  354. * @param {*} groupItemTitle
  355. * @param {*} spreadsLine
  356. * @param {*} outcomesMap
  357. * @param {*} teams
  358. * @param {*} market
  359. * @returns {Object}
  360. */
  361. const parseSpreads = (groupItemTitle, spreadsLine, outcomesMap, teams, market) => {
  362. const { teamHomeName, teamAwayName } = teams;
  363. let spreadDirection = 0;
  364. if (groupItemTitle.includes(teamHomeName)) {
  365. spreadDirection = 1;
  366. }
  367. else if (groupItemTitle.includes(teamAwayName)) {
  368. spreadDirection = -1;
  369. }
  370. const spreads = spreadsLine * spreadDirection;
  371. const key = spreads > 0 ? `+${spreads}` : `${spreads}`;
  372. const spreadsMap = {};
  373. Object.keys(outcomesMap).forEach(key => {
  374. if (key == teamHomeName) {
  375. spreadsMap['Home'] = outcomesMap[key];
  376. }
  377. else if (key == teamAwayName) {
  378. spreadsMap['Away'] = outcomesMap[key];
  379. }
  380. });
  381. return { [key]: { outcomes: spreadsMap, market } };
  382. };
  383. /**
  384. * 解析大小盘口
  385. * @param {*} line
  386. * @param {*} outcomesMap
  387. * @param {*} market
  388. * @returns {Object}
  389. */
  390. const parseTotals = (line, outcomesMap, market) => {
  391. return { [line]: { outcomes: outcomesMap, market } };
  392. };
  393. /**
  394. * 解析市场数据
  395. * @param {*} markets
  396. * @param {*} teams
  397. * @returns {Object}
  398. */
  399. const parseMarketsData = (markets, teams) => {
  400. const marketsData = {};
  401. markets.forEach(market => {
  402. const { sportsMarketType, groupItemTitle, line, outcomes, clobTokenIds, bestBid=0, bestAsk=0 } = market;
  403. const outcomesMap = parseOutcomes(outcomes, clobTokenIds, bestBid, bestAsk);
  404. if (sportsMarketType === "moneyline") {
  405. if (!marketsData.moneyline) {
  406. marketsData.moneyline = {};
  407. }
  408. Object.assign(marketsData.moneyline, parseMoneyline(groupItemTitle, outcomesMap, teams, market))
  409. }
  410. else if (sportsMarketType === "spreads") {
  411. if (!marketsData.spreads) {
  412. marketsData.spreads = {};
  413. }
  414. Object.assign(marketsData.spreads, parseSpreads(groupItemTitle, line, outcomesMap, teams, market))
  415. }
  416. else if (sportsMarketType === "totals") {
  417. if (!marketsData.totals) {
  418. marketsData.totals = {};
  419. }
  420. Object.assign(marketsData.totals, parseTotals(line, outcomesMap, market));
  421. }
  422. });
  423. return marketsData;
  424. }
  425. /**
  426. * 解析赛事数据
  427. * @param {*} event
  428. * @returns {Object}
  429. */
  430. const parseEvent = (event) => {
  431. const { id, title, series, gameId, parentEventId, startTime } = event;
  432. const { teamHomeName, teamAwayName } = parseTeamData(title);
  433. const leagueId = series[0].id;
  434. const leagueName = series[0].title;
  435. const timestamp = new Date(startTime).getTime();
  436. return {
  437. id: +id,
  438. gameId: gameId ? +gameId : undefined,
  439. parentEventId: parentEventId ? +parentEventId : undefined,
  440. leagueId: +leagueId,
  441. teamHomeName, teamAwayName, leagueName,
  442. timestamp,
  443. startTime: getDateInTimezone('+8', timestamp, true),
  444. };
  445. }
  446. /**
  447. * 解析市场列表数据
  448. * @param {*} events
  449. * @returns {Object}
  450. */
  451. export const parseMarkets = (eventsData) => {
  452. const mergedMarketsData = {};
  453. Object.values(eventsData).map(event => {
  454. const item = parseEvent(event);
  455. const { markets } = event;
  456. const { teamHomeName, teamAwayName } = item;
  457. const marketsData = parseMarketsData(markets, { teamHomeName, teamAwayName });
  458. return { ...item, marketsData };
  459. }).sort((a, b) => a.id - b.id).forEach(item => {
  460. if (item.id && !item.parentEventId) {
  461. mergedMarketsData[item.id] = cleanUndefined(item);
  462. }
  463. else if (item.parentEventId && mergedMarketsData[item.parentEventId] && item.marketsData) {
  464. const { marketsData: parentEvents } = mergedMarketsData[item.parentEventId];
  465. Object.assign(parentEvents, item.marketsData);
  466. }
  467. });
  468. return mergedMarketsData;
  469. }
  470. /*
  471. * 解析比率信息
  472. */
  473. const parseRatio = (ratioString) => {
  474. if (!ratioString) {
  475. return null;
  476. }
  477. return parseFloat(`${ratioString[0]}.${ratioString.slice(1)}`);
  478. }
  479. /**
  480. * 解析盘口信息
  481. * @param {*} ior
  482. * @returns
  483. */
  484. const parseIor = (ior) => {
  485. const iorMatch = ior.match(/ior_(m|r|ou|wm|ot)([ao])?([hcn])?_?(\d+)?/);
  486. if (!iorMatch) {
  487. return null;
  488. }
  489. const [, type, action, side, ratio] = iorMatch;
  490. return { type, action, side, ratio };
  491. }
  492. /**
  493. * 解析盘口详情
  494. * @param {*} ior
  495. * @param {*} id
  496. * @param {*} marketsMap
  497. * @returns
  498. */
  499. export const parseIorDetail = (ior, id, marketsMap) => {
  500. const marketsData = marketsMap[id]?.marketsData;
  501. if (!marketsData) {
  502. return { ior, id, message: 'markets data not found', cause: 400 };
  503. }
  504. const iorOptions = parseIor(ior);
  505. if (!iorOptions) {
  506. return { ior, id, message: 'ior options not found', cause: 400 };
  507. }
  508. const { type, action, side, ratio } = iorOptions;
  509. let marketTypeData, outcomesSide;
  510. if (type === 'm' && !ratio) {
  511. const sideKey = side === 'h' ? 'Home' : side === 'c' ? 'Away' : 'Draw';
  512. const sideAction = action === 'o' ? 'No' : 'Yes';
  513. marketTypeData = marketsData.moneyline[sideKey];
  514. outcomesSide = sideAction;
  515. }
  516. else if (type === 'r') {
  517. const sideKey = side === 'h' ? 'Home' : side === 'c' ? 'Away' : '';
  518. let ratioDirection = 1;
  519. if (side === 'c' && action === 'a' || side === 'h' && !action) {
  520. ratioDirection = -1;
  521. }
  522. const ratioValue = parseRatio(ratio) * ratioDirection;
  523. const ratioKey = ratioValue > 0 ? `+${ratioValue}` : `${ratioValue}`;
  524. marketTypeData = marketsData.spreads?.[ratioKey];
  525. outcomesSide = sideKey;
  526. }
  527. else if (type === 'ou') {
  528. const sideKey = side === 'c' ? 'Over' : side === 'h' ? 'Under' : '';
  529. const ratioKey = parseRatio(ratio);
  530. marketTypeData = marketsData.totals[ratioKey];
  531. outcomesSide = sideKey;
  532. }
  533. const result = marketTypeData?.outcomes?.[outcomesSide];
  534. if (!result) {
  535. Logs.outDev('polymarket market type data not found', { ior, id, type, action, side, ratio, marketTypeData, outcomesSide });
  536. return { ior, id, message: 'market type data not found', cause: 400 };
  537. }
  538. return result;
  539. }